首先定义一个自定义注解,这个注解帮助我们去标记需要被我们自己工厂管理使用的Bean,同样我也希望能够结合使用 Spring 帮我将 Bean 创建并管理起来。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
@Component
public @interface FactoryExecutor {
ActionEnum[] actionType() default {};
}
这里是注解的使用,我们通过使用注解 @FactoryExecutor
标记不同的业务处理类。
@FactoryExecutor(actionType = {ActionEnum.RETREAT})
public class BackxxxExecute extends AbstractExecute {
@Resource
private XxxService xxxService;
@Resource
private XxTmpService xxTmpService;
@Override
public void execute(ExecuteBo info) {
batchProcess(info, false, false, true);
....
handler(info);
}
通过 autoRegisterHandler
方法从Spring容器中将标记为 @FactoryExecutor
注解 已经帮我们创建好的Bean对象找出来,然后 Put 到我们自己定义的 STRATEGY 容器中去,并对外提供 getStrategy
方法获取对应的策略使用。
@Component
public class XxxExecutorFactory {
private static final Map<Integer, AbstractXxxExecute> STRATEGY = new HashMap<>();
@Resource
private ApplicationContext context;
public static XxxExecute getStrategy(ActionEnum actionEnum) {
if (null == actionEnum || !STRATEGY.containsKey(actionEnum.getCode())) {
throw exception(XXXX_ACTION_NOT_EXISTS);
}
return STRATEGY.get(actionEnum.getCode());
}
@PostConstruct
private synchronized void autoRegisterHandler() {
String[] beanNames = context.getBeanNamesForAnnotation(FactoryExecutor.class);
for (String beanName : beanNames) {
Object beanInstance = context.getBean(beanName);
StockExecutor taxTypeAnnotation = beanInstance.getClass().getAnnotation(FactoryExecutor.class);
ActionEnum[] actions = taxTypeAnnotation.actionType();
for (ActionEnum action : actions) {
STRATEGY.put(action.getCode(), (AbstractStockExecute) beanInstance);
}
}
}
}
关于 getBeanNamesForAnnotation
方法,从Spring源码可以看到具体实现是在 DefaultListableBeanFactory
这个类中。 是通过从 beanDefinitionMap
中去遍历查找有没有包含我们的自定义注解,有的话会将bean的名称放到List内最终返回。
public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
List<String> result = new ArrayList();
Iterator var3 = this.beanDefinitionNames.iterator();
String beanName;
while(var3.hasNext()) {
beanName = (String)var3.next();
BeanDefinition bd = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (bd != null && !bd.isAbstract() && this.findAnnotationOnBean(beanName, annotationType) != null) {
result.add(beanName);
}
}
var3 = this.manualSingletonNames.iterator();
while(var3.hasNext()) {
beanName = (String)var3.next();
if (!result.contains(beanName) && this.findAnnotationOnBean(beanName, annotationType) != null) {
result.add(beanName);
}
}
return StringUtils.toStringArray(result);
}
关于 context.getBean(beanName)
方法,在这里如果我们需要获取的bean已经被Spring创建好放在单例池了,就会直接从单例池获取并返回,如果没有,就会走Spring的Bean创建流程,这里就不再展开了。