Java SpringBeanDefinition类名包含实例化bean的null

Java SpringBeanDefinition类名包含实例化bean的null,java,spring,Java,Spring,我想在Springbean完全实例化之后,以某种方式对其进行后期处理 但是,当发生ContextRefreshedEvent之后,我无法从ConfigurableListenerFactory获取原始bean类名(因为它是代理的) 我无法从ApplicationContext获取bean类,因为它是由JDK动态代理代理的。 问题-如何获得原始bean的类 请参见下面的可验证示例: import java.lang.reflect.Proxy; import java.util.Objects;

我想在Springbean完全实例化之后,以某种方式对其进行后期处理

但是,当发生
ContextRefreshedEvent
之后,我无法从
ConfigurableListenerFactory
获取原始bean类名(因为它是代理的)

我无法从
ApplicationContext
获取bean类,因为它是由JDK动态代理代理的。 问题-如何获得原始bean的类

请参见下面的可验证示例:

import java.lang.reflect.Proxy;
import java.util.Objects;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;

public class ApplicationContextRefreshedEventTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigurationClass.class);
        MyBean myBean = applicationContext.getBean(MyBean.class);
        myBean.hello();
    }
}

@Configuration
class MyConfigurationClass {

    @Bean
    public MyBean myBean() {
        return new MyBeanImp();
    }

    @Bean
    public MyAppEventListener myAppEventListener() {
        return new MyAppEventListener();
    }

    @Bean
    static MyBeanPostProcessor myBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }
}

class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("myBean")) {
            final Class<?> aClass = bean.getClass();
            return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(),
                    (proxy, method, args) -> method.invoke(bean, args));
        } else {
            return bean;
        }
    }
}

class MyAppEventListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    ConfigurableListableBeanFactory configurableListableBeanFactory;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        final String[] beanDefinitionNames = event.getApplicationContext().getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames) {
            final BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanName);
            final String beanClassName = beanDefinition.getBeanClassName();
            if (Objects.isNull(beanClassName)){
                System.out.println(beanDefinition);
            }
        }
    }
}


interface MyBean {
    void hello();
}

class MyBeanImp implements MyBean {
    @Override
    public void hello() {
    }
}

Spring版本:5.2.7。发布JDK版本:1.8.0_172


我认为您不能从侦听器简单地访问原始bean类:在应用程序上下文中,当侦听器执行时,bean已经有一个代理

另外,请注意,由于
java.lang.Proxy
与接口一起工作,因此实际上没有“底层”bean,只有实现该接口的接口和代理

因此,您可以在bean的接口中公开该方法,该接口将获取该类并实现代理,以便它将委托给该方法

interface MyBean {
  ...
  Class<?> getRealClass();
}
接口MyBean{
...
类getRealClass();
}
另一种方法是创建
Class
Class
的映射。这个映射将包含一个代理类作为一个键映射到“真实”bean类作为一个值,bean后处理器将向这个映射添加条目,这个映射可以在稍后的侦听器期间从应用程序上下文访问,但同样,这不是一个简单的方法


总而言之,它可能会指出一些设计问题,因为实际上bean并没有被创建,所以我想不出任何具体的操作系统示例,因为原始bean甚至不存在…

事实证明,在SpringJava配置中(与XML配置相比)在bean定义中没有bean类名称的概念。bean是使用
@Configuration
类作为工厂创建的,不保留真正的bean定义名称

我能够实现我的目标:在Spring上下文初始化完成后调用bean的原始方法(bean被代理),如下所示:

  • 创建
    InvocationHandler
    的自定义实现,并传入所需的bean信息
  • 在应用程序中,侦听器获取了我的自定义
    调用处理程序
  • 代码:

    public class MyInvocationHandler implements InvocationHandler {
    
        private Object bean;
        private ProfilingController controller;
    
        public MyInvocationHandler(Object bean) {
            this.bean = bean;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // Custom logic here - irrelevant for the purpose of this example
          return method.invoke(bean, args);
        }
    
        public Object getBean() {
            return bean;
        }
    }
    
    
    public class PostProxyInvocationProcessor implements ApplicationListener<ContextRefreshedEvent> {
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            ApplicationContext applicationContext = event.getApplicationContext();
            final String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanName : beanDefinitionNames) {
                final Object proxy = applicationContext.getBean(beanName);
                if (proxy instanceof Proxy) {
                    final MyInvocationHandler invocationHandler = (MyInvocationHandler) Proxy.getInvocationHandler(proxy);
                    final Object bean = invocationHandler.getBean();
                    final Class<?> aClass = bean.getClass();
                    final Method[] methods = aClass.getMethods();
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(MyCustomAnnotation.class)) {
                            try {
                                method.invoke(bean);
                            }
                            catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    
    公共类MyInvocationHandler实现InvocationHandler{
    私有对象bean;
    专用Profiling控制器;
    公共MyInvocationHandler(对象bean){
    this.bean=bean;
    }
    @凌驾
    公共对象调用(对象代理、方法、对象[]args)抛出Throwable{
    //这里的自定义逻辑-与本例无关
    invoke(bean、args);
    }
    公共对象getBean(){
    返回豆;
    }
    }
    公共类PostProxy处理器实现ApplicationListener{
    @凌驾
    ApplicationEvent(ContextRefreshedEvent事件)上的公共无效{
    ApplicationContext ApplicationContext=event.getApplicationContext();
    最终字符串[]beanDefinitionNames=applicationContext.getBeanDefinitionNames();
    for(字符串beanName:beanDefinitionNames){
    最终对象代理=applicationContext.getBean(beanName);
    if(代理的代理实例){
    final MyInvocationHandler invocationHandler=(MyInvocationHandler)Proxy.getInvocationHandler(Proxy);
    最终对象bean=invocationHandler.getBean();
    最终类aClass=bean.getClass();
    final方法[]methods=aClass.getMethods();
    用于(方法:方法){
    if(方法.isAnnotationPresent(MyCustomAnnotation.class)){
    试一试{
    调用(bean);
    }
    捕获(IllegalAccessException | InvocationTargetException e){
    e、 printStackTrace();
    }
    }
    }
    }
    }
    }
    

    }我认为Bean定义名称实际上就是Bean名称。

    因此,可以直接从configurableListableBeanFactory

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            final ApplicationContext applicationContext = event.getApplicationContext();
            final String[] beanDefinitionNamesFromAppContext = applicationContext.getBeanDefinitionNames();
    
            for (String beanDefinitionName: beanDefinitionNamesFromAppContext) {
                System.out.println( configurableListableBeanFactory.getBean(beanDefinitionName).getClass());
            }
        }
    

    bean后处理器的全部目的是通过将bean对象包装到您的示例中的代理中来更改bean对象。所以“原始”类应该不再相关,它“透明地”成为代理。为什么需要访问原始bean类?@MarkBramnik执行额外的bean处理,但仅在完成实例化(加载所有bean)之后。这将返回class
    com.sun.proxy.$ProxyXXX
    -您可以尝试。您正在寻找MyBeanImp类吗?我正在寻找MyBeanImpl bean。
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            final ApplicationContext applicationContext = event.getApplicationContext();
            final String[] beanDefinitionNamesFromAppContext = applicationContext.getBeanDefinitionNames();
    
            for (String beanDefinitionName: beanDefinitionNamesFromAppContext) {
                System.out.println( configurableListableBeanFactory.getBean(beanDefinitionName).getClass());
            }
        }