[英]Dynamically injecting a JDK dynamic proxy as spring bean - but only if no other implementation is available
我有一种情况,我想查找带有给定注释的接口,然后检查匹配的实现是否可用。 如果没有,我想提供一个实际上是接口的 JDK 代理的 bean,本质上是什么:
@ConditionalOnMissingBean
会做,除非没有为每一个编写工厂方法。
我有“有时”工作的代码 - 它似乎对类加载器结构非常敏感,特别是,类是从 springboot fat jar 加载(有效)还是某些部分是从单独的类路径条目加载的(大多数情况下不起作用) .
这就是我正在做的事情:
@Service
@Order(value = Ordered.LOWEST_PRECEDENCE)
public class RemotingImportService implements BeanFactoryPostProcessor {
private static Log log = LogFactory.getLog(RemotingExportService.class);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// scan classpath with classgraph, then:
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(MyAnnotation.class.getCanonicalName())) {
Class c = Class.forName(classInfo.getName());
if(beanFactory.getBeanNamesForType(c).length > 0) {
implFound = true;
log.info(c.getName()+" already has an implementation ... skipping");
continue;
}
// create proxy, then:
GenericBeanDefinition bdService = new GenericBeanDefinition();
bdService.setBeanClassName(classInfo.getName());
bdService.setInstanceSupplier(new ProxySupplier(proxy));
bdService.setLazyInit(true);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(classInfo.getName(), bdService);
log.info(c.getName()+" has NO implementation ... proxy registerd");
}
在某些情况下,beanfactory 似乎还没有完成,并且
beanFactory.getBeanNamesForType()
返回一个空列表,尽管它稍后会找到该类型的 bean。 我知道搞砸这可能并不理想——但如果能找到一个与 spring 引导配合得很好的解决方案会很好。
关于如何解决这个问题的任何建议? 将 bean 定义标记为“ConditionalOnMissingBean”的方法也很棒。
您应该使用BeanPostProcessor
而不是BeanFactoryPostProcessor
BeanPostProcessor
由组装的 bean 操作,而BeanFactoryPostProcessor
使用原始 bean 定义。 在这里阅读更多
public class ConditionalOnMissingProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware
{
private static final Logger LOG = Logger.getLogger(ConditionalOnMissingProcessor .class);
private ApplicationContext applicationContext;
// Ordering to last in chain.
@Override
public int getOrder()
{
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
/**
* Process each bean and inject proxy objects in fields marked with: {@ConditionalOnMissingBean}
*/
@Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
LOG.debug("Processing bean: " + beanName);
final Class clazz = bean.getClass();
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback()
{
@Override
public void doWith(final Field field)
{
try
{
if(field.isAnnotationPresent(ConditionalOnMissingBean.class))
{
ReflectionUtils.makeAccessible(field);
final String beanFieldName = field.getName();
...
// Find proper implementation in application context
// Or create a proxy.
// Inject proxy or impl into a bean
field.set(bean, <value>));
}
}
catch(IllegalAccessException e)
{
LOG.error("Cannot set " + field.getName() + " in " + beanName, e);
}
}
});
return bean;
}
升级版:
首先,我不得不说一下您的 impl 的缺点(恕我直言):-您正在尝试扫描类路径以查找带有@RemoteEndpoint
注释的所有接口,并且,如果当前应用程序上下文不包含实现此接口的 bean-创建一个代理bean。 但是,如果我说,并非所有标有@RemoteEndpoint
的接口都应该考虑在内呢? 开发人员应该明确标记这些接口,然后您可以创建所有需要的 bean(例如,开发人员制作了一个公共服务库)。 - 在使用@RemotingEndpoint(value=RandomService.class)
注释标记 impl class 时,您可能正在指定冗余信息。 当你实现一个接口时,你已经提到过。
有多种方法可以实现您的想法
在 bean 字段上使用自定义注释而不是@Autowired
。
优点:
BeanPostProcessor
注入依赖项(代理或 impl)。缺点:
使用常规@Autowired
和@Value
注释来注入远程服务代理在这种情况下,您应该使用BeanFactoryPostProcessor
(正如您已经尝试过的那样)。 您必须遍历所有 bean 定义,收集字段名称的 map 您必须注册的远程服务代理的接口(依赖项元信息)。 下一步是使用依赖元信息创建和注册 bean(仅当上下文中没有实现 bean 时才创建新的)。 Spring 稍后将自动连接这些字段。 但是,这些依赖项应该是 singleton bean。
优点:
缺点:
仍然使用常规的@Autowired
和@Value
注释以及BeanFactoryPostProcessor
,但是您应该为每个接口@RemoteEndpoint
注册一个FactoryBean
,而不是注册新的 bean。
优点:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.