首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Spring BeanDefinition类名包含实例化bean的null

Spring BeanDefinition类名包含实例化bean的null
EN

Stack Overflow用户
提问于 2020-09-10 16:41:46
回答 3查看 473关注 0票数 1

我想在Spring完全实例化之后,以某种方式对它进行后置处理。

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

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

请参阅以下可核实的例子:

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

产出:

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

Spring版本:5.2.7 version版本: 1.8.0_172

EN

回答 3

Stack Overflow用户

发布于 2020-09-10 17:49:57

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

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

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

代码语言:javascript
复制
interface MyBean {
  ...
  Class<?> getRealClass();
}

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

总之,它可能会指出一些设计问题,因为实际上bean是没有创建的,所以我想不出任何这样的处理的具体示例,因为原始bean甚至不存在.

票数 0
EN

Stack Overflow用户

发布于 2020-09-10 19:58:49

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

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

information.

  • In

  • 创建了InvocationHandler的自定义实现,并传入所需的bean

  • 应用程序监听器,获得了自定义的

代码:

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

}

票数 0
EN

Stack Overflow用户

发布于 2020-09-10 20:58:30

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

因此,可以直接从configurableListableBeanFactory检索类名。

代码语言:javascript
复制
    @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());
        }
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63834134

复制
相关文章

相似问题

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