Spring 代理对象提前生成 (还未调用构造方法实例化 bean)

代码示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试类如下:

在这里插入图片描述

输出结果如下:

在这里插入图片描述

这里发现了一个问题,我们定义了 ScopeProxyBean 是 prototype 模式的,但是现在 hashCode() 却是同一个,是因为在使用 @Autowired 注入的时候就已经把值确定了,只调用了一次 getBean() 操作,所以不管你是否配置了 bean 的 Scope 是 singleton 还是 prototype 模式,最终拿到的都是同一个对象。

如果想要解决这个问题,让 @Autowired 注入就是要 prototype 的。可以使用代理。可以在 @Scope 中按照如下配置:

在这里插入图片描述

但是今天这里不使用这种方式,而是采用提前代理的方式(自定义 TargetSource 方式) 解决这个问题。

提前生成代理对象

可以从 resolveBeforeInstantiation() 方法中做文章。此时注意这里还是在 doCreateBean() 真正创建对象之前,就已经在做拦截操作了,这里要是有值返回就直接不会去真正实例化 bean 了。

在这里插入图片描述

进入 AbstractAutoProxyCreator 代码如下:这里有一样非常关键的方法,getCustomTargetSource() 方法获取自定义的目标对象,如果这个方法有返回值,就表示你已经自定义了目标对象(所以这就是你可以下手的地方),Spring 会去为这个自定义目标对象创建代理对象,但是条件是设么呢?那就要追踪 getCustomTargetSource() 方法了,看这个方法满足什么条件才会有返回值?

在这里插入图片描述

进入到 getCustomTargetSource() 方法,如下:只有当 customTargetSourceCreators 变量不为 null,该方法才会有返回值,那就着重看这个 customTargetSourceCreators 变量的值是怎么来的

在这里插入图片描述

发现该后置处理器暴露了一个接口 setCustomTargetSourceCreators() 可以设置变量 customTargetSourceCreators 的值

在这里插入图片描述

如果要往 AbstractAutoProxyCreator 后置处理器中设置值,注意这是一个内置的后置处理器,所以你必须在这个后置起之前先注册到容器中,可以实现权重接口,就和上次社会 InterceptorNames 一个道理,源码如下:

在这里插入图片描述

然后此时 AbstractAutoProxyCreator 类中 customTargetSourceCreators 变量就有值了, customTargetSourceCreator 就是我们自定义的 creator,继承了一个 AbstractBeanFactoryBasedTargetSourceCreator 类,源码如下:

在这里插入图片描述

然后自定义的 TargetSource 如下所示: 直接继承 AbstractBeanFactoryBasedTargetSource 类,getTarget() 方法实现逻辑和 @Scope 中的 SimpleBeanTargetSource 类一样,就是去调用 getBean() 创建 bean,但是注意这个 BeanFactory 是深拷贝出来的新的 BeanFactory,里面踢除了 @Aspect 切面的入口类。

在这里插入图片描述

然后再回到 getCustomTargetSource() 方法,继续向下执行

在这里插入图片描述

执行到 getTargetSource() 方法,进入方法内部,源码如下:

在这里插入图片描述

只有 createBeanFactoryBasedTargetSource() 方法返回值才会往下执行代码,上面我们已经自定义好了 targetSource,直接继承这个AbstractBeanFactoryBasedTargetSource 类,代码如下:

在这里插入图片描述

然后再回到调用处 getTargetSource() 方法,此时 targetSource != null,代码往下执行,这里调用了一个 getInternalBeanFactoryForBean() 方法主要是拷贝 BeanFactory和踢除切面功能,然后重新给这个 beanName new 了一个 BeanDefinition ,并且还是 prototype 多例,并且 BeanFactory 是新拷贝出来的了。

在这里插入图片描述

进入 getInternalBeanFactoryForBean() 方法内部,主要是拷贝了一个 BeanFactory,然后新的 BeanFactory 把切面生成代理类入口给踢除了,这样在使用新的 BeanFactory 调用 getBean() 方法就不会受 @Aspect 切面的影响,源码如下:

在这里插入图片描述

回到上一层调用处处,源码如下:此时 targetSource 已经有值了,并且是我们返回的自定义的 targetSource

在这里插入图片描述

自己定义的 targetSource 对象,源码如下:

在这里插入图片描述

再回到 getCustomTargetSource() 方法调用处,源码如下:此时我们把我们自己返回的 targetSource 传进去了,注意 targetSource 里面的 getTarget() 方法才会真正返回一个 bean 实例,会去调用 getBean() 操作。

在这里插入图片描述

进入到 createProxy() 源码如下:我们穿进去的 targetSource 保存到了 ProxyFactory 对象的 targetSource 变量中了,然后就是去创建这个 bean 的代理实例了。

在这里插入图片描述

因为这是个代理对象,当他调用方法的时候,必然会触发 invoke() 方法,所以看到 invoke() 逻辑里面,源码如下:

在这里插入图片描述

此时的 TargetSource 是我们自定义的,所以会调用我们自己的 getTarget() 方法,源码如下:此时注意这里的 BeanFactory 是从原来的 BeanFactory 中拷贝出来的,并且还把切面功能踢除了,这里就不会再发生代理对象的生成了。

在这里插入图片描述

然后调用 getBean() 方法,直接实例化 ScopeProxyBean,至此我们提前在创建 bean 实例之前完成了 bean 创建。

通过这种方式创建的 bean,在真正去调用的时候才会触发去调用 getBean() 流程创建真实的实例,并且 BeanFactory 是深拷贝出来的,会把 @Aspect 切面踢除了的,就算你配置了 @Aspect 功能也无效。

还要注意一点就通过这种方式提前创建的 bean 默认都是 prototype 多例的,Spring 内部将其修改成 prototype 多例模式,具体原因还未知…

至此,上面的测试代码,for 循环 10 次最终会打印出 10 个不同的 hashCode 值,如下所示:

在这里插入图片描述

会自定义 TargetSource 了,那么去分析 @Scope 注解下面的配置就很清晰了。

在这里插入图片描述

只是入口不一样而已,自定义 TargetSource 是从 resolveBeforeInstantiation() 方法开始。

在这里插入图片描述

而 @Scope 的支持是从 invokeAwareMethods() 方法开始的
在这里插入图片描述
在这里插入图片描述

详细对 @Scope 的源码解析在另一篇文章中…

最后配上源码方便复制粘贴

配置类和实体类

/**
 * 演示 ScopeProxy
 */
@Configuration
@ComponentScan(basePackages = {"com.gwm.spring.proxy"})
@EnableAspectJAutoProxy
public class ScopedMainConfig {
}


@Component
@Scope("prototype")
//@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopeProxyBean {
	public void code() {
		System.out.println(this.hashCode());
	}
}


自定义 TargetSource


public class MyCustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
	public MyCustomTargetSourceCreator() {
		System.out.println("MyCustomTargetSourceCreator....构造方法");
	}

	@Override
	protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
		System.out.println("MyCustomTargetSourceCreator---------->beanName="+beanName);
		if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
			BeanDefinition beanDefinition = ((ConfigurableListableBeanFactory) getBeanFactory()).getBeanDefinition(beanName);
			if (!beanClass.isAssignableFrom(ScopeProxyBean.class)) {

				return new MyCustomTargetSource();
			}
		}
		return null;
	}
}

public class MyCustomTargetSource extends AbstractBeanFactoryBasedTargetSource implements Serializable {
	private static final long serialVersionUID = 1949093163787033304L;

	@Override
	public Object getTarget() throws Exception {
		System.out.println("MyCustomTargetSource...被调用了....");
		return getBeanFactory().getBean(this.getTargetBeanName());

	}
}
@Component
public class MyBeanPostProcessor implements BeanPostProcessor, PriorityOrdered,BeanFactoryAware {

	private BeanFactory factory;

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof AbstractAutoProxyCreator) {
			System.out.println("postProcessAfterInitialization---------->beanName="+beanName);
			AbstractAutoProxyCreator creator = (AbstractAutoProxyCreator) bean;
			//AnnotationAwareAspectJAutoProxyCreator creator = (AnnotationAwareAspectJAutoProxyCreator) bean;
			// 注册 MyTargetSourceCreator
			MyCustomTargetSourceCreator customTargetSourceCreator = new MyCustomTargetSourceCreator();
			customTargetSourceCreator.setBeanFactory(factory);

			creator.setCustomTargetSourceCreators(customTargetSourceCreator);
		}

		/** 这里记得返回 */
		return bean;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.factory = beanFactory;
	}

	@Override
	public int getOrder() {
		return 45;
	}
}

测试代码

	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(ScopedMainConfig.class);

		ScopeProxyBeanDemo bean = context.getBean(ScopeProxyBeanDemo.class);
		for (int i = 0; i < 10; i++) {
			bean.test();
		}

	}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔道不误砍柴功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值