有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSpringBeanDefinition类名包含实例化bean的null

我想在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() {
    }
}

输出:

Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=myConfigurationClass; factoryMethodName=myBean; initMethodName=null; destroyMethodName=(inferred); defined in com.example.MyConfigurationClass
null
Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=myConfigurationClass; factoryMethodName=myAppEventListener; initMethodName=null; destroyMethodName=(inferred); defined in com.example.MyConfigurationClass
null

enter image description here

Spring version : 5.2.7.RELEASE JDK version : 1.8.0_172


共 (3) 个答案

  1. # 1 楼答案

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

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

    1. 创建了InvocationHandler的自定义实现,并传入了所需的bean信息
    2. 在应用程序中,侦听器获取了我的自定义InvocationHandler

    代码:

    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();
                            }
                        }
                    }
                }
            }
        }
    

    }

  2. # 2 楼答案

    我认为您无法从侦听器以简单的方式访问原始bean类:在应用程序上下文中,当侦听器被执行时,已经存在bean的代理

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

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

    interface MyBean {
      ...
      Class<?> getRealClass();
    }
    

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

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

  3. # 3 楼答案

    我认为Bean定义名称实际上就是Bean名称。 enter image description here

    因此,可以直接从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());
            }
        }