首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将JDK动态代理动态注入为spring -但前提是没有其他实现可用

将JDK动态代理动态注入为spring -但前提是没有其他实现可用
EN

Stack Overflow用户
提问于 2019-10-21 10:51:05
回答 1查看 1.6K关注 0票数 3

在这种情况下,我希望查找带有给定注释的接口,然后检查是否有匹配的实现可用。如果不是,我想提供一个bean,它实际上是接口的JDK代理,本质上是这样的:

代码语言:javascript
复制
@ConditionalOnMissingBean 

就可以了,除非没有为其中的每一个写一个工厂方法。

我有“有时”工作的代码--它似乎对类加载器结构非常敏感,具体而言,是否类是从弹靴胖jar加载的(工作),或者是否某些部分是从单独的类路径条目加载的(大多数情况下不起作用)。

以下是我正在做的事情:

代码语言:javascript
复制
@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");

        }

在某些情况下,豆瓣工厂似乎还没有完成,而且

代码语言:javascript
复制
  beanFactory.getBeanNamesForType() 

返回一个空列表,尽管它稍后会为该类型找到bean。我知道,处理这个问题可能并不理想--但最好能找到一种解决方案,它可以很好地处理弹簧引导。

对如何解决这个问题有什么建议吗?将bean定义标记为"ConditionalOnMissingBean“的方法也很棒。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-10-21 11:39:19

您应该使用BeanPostProcessor而不是BeanFactoryPostProcessor

BeanPostProcessor是通过组装bean操作的,而BeanFactoryPostProcessor则使用原始bean定义。阅读更多这里

代码语言:javascript
复制
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;
}

UPD:

首先,我必须指出您的IMHO(IMHO)的缺点:-您正在尝试扫描类路径以使用@RemoteEndpoint注释查找所有接口,如果当前的应用程序上下文不包含实现该接口的bean --创建一个代理bean。但是,如果我说,并不是所有标记为@RemoteEndpoint的接口都应该被考虑进去呢?开发人员应该显式地标记这些接口,然后您可以创建所有需要的bean(例如,开发人员创建一个公共服务库)。-在用@RemotingEndpoint(value=RandomService.class)注释标记impl类时,可能是指定了红色信息。您已经提到过,当您实现时,会有一个接口。

有多种方法来实现您的想法。

  1. 在bean字段上使用自定义注释而不是@Autowired。 优点:
代码语言:javascript
复制
- simply check all bean fields for presence of your custom annotation, and inject dependency (proxy or impl), using `BeanPostProcessor`.

缺点:

代码语言:javascript
复制
- developer should mark all bean dependencies with custom annotation;
- it won't work if developer would have to obtain new dependencies at runtime.

  1. 使用常规的@Autowired@Value注释注入远程服务代理--在本例中--您应该使用BeanFactoryPostProcessor (正如您已经尝试过的那样)。您必须遍历所有bean定义,收集必须注册的远程服务代理的字段名和接口的映射(依赖项元信息)。下一步是使用依赖项元信息创建和注册bean (只有在上下文中没有实现bean的情况下才创建新的bean)。春天以后会把那些田里弄得水汪汪的。但是,这些依赖项应该是单例bean。 优点:
代码语言:javascript
复制
- no mess with custom annotations;
- works at runtime

缺点:

代码语言:javascript
复制
- proxies are singletons (you need prototypes as diagram displayed).

  1. 仍然使用常规的@Autowired@Value注释和BeanFactoryPostProcessor,但是不需要注册新bean,您应该为每个接口@RemoteEndpoint注册一个FactoryBean。 优点:
代码语言:javascript
复制
- no mess with custom annotations.
- works at runtime
- prototype-scoped proxies

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58484759

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档