Spring-wired actor
由於 actor 例項化的非常具體的方式,將依賴項注入到 actor 例項中並非易事。為了干預 actor 例項化並允許 Spring 注入依賴項,應該實現幾個 akka 擴充套件。首先是 IndirectActorProducer
:
import akka.actor.Actor;
import akka.actor.IndirectActorProducer;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
/**
* An actor producer that lets Spring autowire dependencies into created actors.
*/
public class SpringWiredActorProducer implements IndirectActorProducer {
private final ApplicationContext applicationContext;
private final Class<? extends Actor> actorBeanClass;
private final Object[] args;
public SpringWiredActorProducer(ApplicationContext applicationContext, Class<? extends Actor> actorBeanClass, Object... args) {
this.applicationContext = applicationContext;
this.actorBeanClass = actorBeanClass;
this.args = args;
}
@Override
public Actor produce() {
Class[] argsTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
argsTypes[i] = null;
} else {
argsTypes[i] = args[i].getClass();
}
}
Actor result = null;
try {
if (args.length == 0) {
result = (Actor) actorBeanClass.newInstance();
} else {
try {
result = (Actor) actorBeanClass.getConstructor(argsTypes).newInstance(args);
} catch (NoSuchMethodException ex) {
// if types of constructor don't match exactly, try to find appropriate constructor
for (Constructor<?> c : actorBeanClass.getConstructors()) {
if (c.getParameterCount() == args.length) {
boolean match = true;
for (int i = 0; match && i < argsTypes.length; i++) {
if (argsTypes[i] != null) {
match = c.getParameters()[i].getType().isAssignableFrom(argsTypes[i]);
}
}
if (match) {
result = (Actor) c.newInstance(args);
break;
}
}
}
}
}
if (result == null) {
throw new RuntimeException(String.format("Cannot find appropriate constructor for %s and types (%s)", actorBeanClass.getName(), Arrays.toString(argsTypes)));
} else {
applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties(result, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Cannot instantiate an action of class " + actorBeanClass.getName(), e);
}
return result;
}
@Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) actorBeanClass;
}
}
此生成器在返回 actor 例項之前例項化 actor 並注入依賴項。
我們可以通過以下方式使用 SpringWiredActorProducer
為建立演員準備 Props
:
Props.create(SpringWiredActorProducer.class, applicationContext, actorBeanClass, args);
但是最好將該呼叫包裝到以下 spring bean 中:
import akka.actor.Extension;
import akka.actor.Props;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* An Akka Extension to inject dependencies to {@link akka.actor.Actor}s with
* Spring.
*/
@Component
public class SpringProps implements Extension, ApplicationContextAware {
private volatile ApplicationContext applicationContext;
/**
* Creates a Props for the specified actorBeanName using the
* {@link SpringWiredActorProducer}.
*
* @param actorBeanClass The class of the actor bean to create Props for
* @param args arguments of the actor's constructor
* @return a Props that will create the named actor bean using Spring
*/
public Props create(Class actorBeanClass, Object... args) {
return Props.create(SpringWiredActorProducer.class, applicationContext, actorBeanClass, args);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
你可以在建立 actor 的任何地方(甚至在 actor 本身中)自動裝配 SpringProps
,並通過以下方式建立 spring-wired actor:
@Autowired
private SpringProps springProps;
//...
actorSystem.actorOf(springProps.create(ActorClass.class), actorName);
//or inside an actor
context().actorOf(springProps.create(ActorClass.class), actorName);
假設 ActorClass
擴充套件了 UntypedActor
並且具有用 @Autowired
註釋的屬性,那麼這些依賴關係將在例項化後立即注入。