Java SpringBeanDefinition类名包含实例化bean的null
我想在Springbean完全实例化之后,以某种方式对其进行后期处理 但是,当发生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;
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());
}
}