【问题标题】:Spring programmatic bean autowired as proxy instead of targetSpring 编程 bean 自动装配为代理而不是目标
【发布时间】:2018-05-01 19:27:15
【问题描述】:

我创建了一个注解,用于创建填充了来自环境的值的 ThreadPoolTask​​Executors。但是,当我自动装配 bean 时,它给了我一个代理,并且调用代理上的方法会给出错误的值。

如果我手动访问目标类,那么我会得到正确的值。

Executor exec = (Executor) ((Advised) executor).getTargetSource().getTarget();
ThreadPoolTaskExecutor taskExec = (ThreadPoolTaskExecutor) exec;

对于为什么要获得代理 bean,我一直在摸不着头脑,但似乎无法弄清楚。

我正在使用一个注释来导入我的注册器类,该类实现了 ImportBeanDefinitionRegistrar 来注册 bean。注册商代码如下:

public class ExecutorEnumerationRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    public static final String CORE_POOL_SIZE = "corePoolSize";
    public static final String MAX_POOL_SIZE = "maxPoolSize";
    public static final String QUEUE_CAPACITY = "queueCapacity";
    public static final String THREAD_NAME_PREFIX = "threadNamePrefix";
    private static final String REJECTED_EXECUTION_HANDLER = "rejectedExecutionHandler";
    private static final String NAMES = "names";
    private static final String REJECTED_HANDLER = "rejectedHandler";
    private Environment env;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(ThreadPoolTaskExecutorCreator.class.getName(), true);
        final String[] beanNames = (String[]) attrs.get(NAMES);
        final String[] policyClass = (String[]) attrs.get(REJECTED_HANDLER);
        for (int x = 0; x < beanNames.length; x++) {
            createAndRegisterBean(beanNames[x], policyClass[x], registry);
        }
    }

    private void createAndRegisterBean(String name, String policyClass, BeanDefinitionRegistry registry) {
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ThreadPoolTaskExecutor.class);
        bd.setAutowireCandidate(true);
        bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        MutablePropertyValues mpv = bd.getPropertyValues();
        populateProperties(mpv, name, policyClass);
        registry.registerBeanDefinition(name, bd);
    }

    private void populateProperties(MutablePropertyValues mpv, String name, String policyClass) {
        mpv.add(CORE_POOL_SIZE, Integer.valueOf(env.getProperty(name + "." + CORE_POOL_SIZE)));
        mpv.add(MAX_POOL_SIZE, Integer.valueOf(env.getProperty(name + "." + MAX_POOL_SIZE)));
        mpv.add(QUEUE_CAPACITY, Integer.valueOf(env.getProperty(name + "." + QUEUE_CAPACITY)));
        try {
            mpv.add(REJECTED_EXECUTION_HANDLER, Class.forName(policyClass).newInstance());
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        mpv.add(THREAD_NAME_PREFIX, name + "-");
    }

    @Override
    public void setEnvironment(Environment environment) {
        env = environment;
    }

}

导入注册器的注解:

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE) 
@Documented 
@Import(ExecutorEnumerationRegistrar.class) 
public @interface ThreadPoolTaskExecutorCreator{ 

    String[] names(); 

    String[] rejectedHandler() default ThreadPoolPolicyHandlers.CALLER_RUNS_POLICY; 

} 

我已经使用以下代码进行了测试: Spring Boot 类:

@EnableDiscoveryClient
@ComponentScan("my.test.classes")
@ThreadPoolTaskExecutorCreator(names = {"testExecutor"}, rejectedHandler = ThreadPoolPolicyHandlers.DISCARD_POLICY) 
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, 
    SessionAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class, 
    JpaRepositoriesAutoConfiguration.class,
    JndiDataSourceAutoConfiguration.class, 
    JndiConnectionFactoryAutoConfiguration.class,
    RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
public class TestBoot {

    public static void main(String[] args) {
        SpringApplication.run(TestBoot.class, args);
    }   
}

spring-boot-starter-parent 1.4.5.RELEASE 的所有版本

我编写了一个 JUnit 测试来检查值并且它通过了。 唯一不工作的情况是当我在 Spring Boot eureka 应用程序中自动装配它时。 我能做些什么让它不自动装配代理 bean?我已经搜索了文档并查看了所有相关的类,但我没有看到任何与它为什么是代理相关的内容。另外,为什么通过代理访问时会给出错误的值?

【问题讨论】:

  • “它给出的值不正确”是什么意思?能否分享一下代码中ThreadPoolTaskExecutor 的注入和使用代码...?
  • 我只是在另一个 spring bean 中自动装配它。我正在打印核心池大小、最大池大小和队列容量的值。它返回默认值而不是创建时发送的属性。
  • 我在问,因为你的代码对我来说效果很好:) 你至少可以分享 Spring / Spring Boot 的版本吗?
  • 啊,是的,我应该把它放在问题中。对于那个很抱歉。它是 Spring Boot 1.4.5 和 Spring 4.3.4。我尝试使用 SpringBootApplication 和我创建的单例 bean 在一个简单的测试项目中对此进行测试,但它仍然提供了一个代理。
  • 用 1.4.5 试过你的代码,它运行得很好。似乎您有一些额外的配置会破坏它。所以如果你分享更多的配置代码就完美了(不需要业务逻辑)

标签: java spring spring-boot proxy threadpoolexecutor


【解决方案1】:

您似乎缺少注册 ImportBeanDefinitionRegistrar 实例的代码(在您的示例中为 ExecutorEnumerationRegistrar ) 所以注册ImportBeanDefinitionRegistrar有两种方式,直接使用@Import注解或者实现ImportSelector接口,可以给你更多通用的配置选项。

对于您的情况,只需在 Configuration 类上添加 @Import({ExecutorEnumerationRegistrar.class}) 即可。

@EnableDiscoveryClient
@ComponentScan("my.test.classes")
@ThreadPoolTaskExecutorCreator(names = {"testExecutor"}, rejectedHandler = ThreadPoolPolicyHandlers.DISCARD_POLICY)
// THIS IS REQUIRED
@Import({ExecutorEnumerationRegistrar.class})
// See Above
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, 
SessionAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, 
JpaRepositoriesAutoConfiguration.class,
JndiDataSourceAutoConfiguration.class, 
JndiConnectionFactoryAutoConfiguration.class,
RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
public class TestBoot {

    public static void main(String[] args) {
        SpringApplication.run(TestBoot.class, args);
    }   
}

并且只记得在自动装配ThreadPoolTaskExecutor 的实例时使用@Qualifier。查看示例组件

@Component
public class Component {

    @Autowired
    @Qualifier("testExecutor")
    private ThreadPoolTaskExecutor exec;

    // you methods
}

【讨论】:

  • 我已经通过我的 ThreadPoolTask​​ExecutorCreator 注释导入注册器。我也为此添加了代码。我已经多次调试和测试过这段代码。它进入注册器并创建 bean。它还自动装配它。问题是,它正在自动装配代理。
猜你喜欢
  • 2012-07-14
  • 2014-09-08
  • 2017-06-08
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-04
相关资源
最近更新 更多