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
注释的属性,那么这些依赖关系将在实例化后立即注入。