听说 Spring Bean 的创建还有一条捷径?

这篇具有很好参考价值的文章主要介绍了听说 Spring Bean 的创建还有一条捷径?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


在 Spring Bean 的创建方法中,有如下一段代码:

AbstractAutowireCapableBeanFactory#createBean:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {
	//...
	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	}
	try {
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		return beanInstance;
	}
	//...
}

我们平时说的 Bean 的创建逻辑都是指 doCreateBean 方法中的逻辑,在松哥前面几篇文章中,凡是涉及到 Bean 的创建流程的,我说的也都是 doCreateBean 方法的流程。

但是小伙伴们注意,在 doCreateBean 方法执行之前,其实还有一个 resolveBeforeInstantiation 方法会先执行,而这个方法可能就直接产生一个 Bean 了!如果这个方法直接产生一个 Bean 了,那么 doCreateBean 方法中的逻辑就不会生效了。

那么 resolveBeforeInstantiation 方法存在的意义是什么呢?其实大家从该方法的注释上大概也能看出一些端倪出来了:

给 BeanPostProcessor 一个机会去创建一个代理对象,用这个代理对象来代替目标 Bean。

1. resolveBeforeInstantiation

看下面的源码小伙伴们一定要先搞清楚两个比较相似的单词,否则看到后面就乱了:

  • instantiation:实例化,从 Class 到 Bean 就是实例化。
  • initialization:初始化,给 Bean 做各种配置就是初始化。

搞明白这两个单词,我们来看源码。

首先我先来和小伙伴们稍微梳理一下 resolveBeforeInstantiation 方法。

AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:

@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
	Object bean = null;
	if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
		// Make sure bean class is actually resolved at this point.
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			Class<?> targetType = determineTargetType(beanName, mbd);
			if (targetType != null) {
				bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
				if (bean != null) {
					bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
				}
			}
		}
		mbd.beforeInstantiationResolved = (bean != null);
	}
	return bean;
}

小伙伴们看一下,这里有一个判断条件,mbd.isSynthetic 是判断是否是一个合成 Bean,这种一般都是 Spring 定义的,我们自定义的 Bean 一般都不属于这一类,然后后面的 hasInstantiationAwareBeanPostProcessors 方法则是判断当前是否存在 InstantiationAwareBeanPostProcessor 类型的后置处理器,如果存在,则进入到 if 分支中。

如果我们想要在 resolveBeforeInstantiation 方法中就完成 Bean 的处理,那么就需要自己提供一个 InstantiationAwareBeanPostProcessor 类型的后置处理器。

接下来会调用两个方法:

  • applyBeanPostProcessorsBeforeInstantiation:从名字可以看出来,这个是在实例化之前触发的方法,所以这个方法的参数还是 Class,因为还未实例化。
  • applyBeanPostProcessorsAfterInitialization:从名字可以看出来,这个是在初始化之后出发的方法,所以这个方法的参数是 Bean,因为此时已经完成了初始化了。

1.1 applyBeanPostProcessorsBeforeInstantiation

@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
	for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
		Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
		if (result != null) {
			return result;
		}
	}
	return null;
}

这个地方就很简单了,就是执行 InstantiationAwareBeanPostProcessor 类型的后置处理器的 postProcessBeforeInstantiation 方法。

1.2 applyBeanPostProcessorsAfterInitialization

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
		throws BeansException {
	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessAfterInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

这个是执行 BeanPostProcessor 的 postProcessAfterInitialization 方法。

所以这块的源码其实并不难,道理很简单。

1.3 案例

松哥写一个简单的案例小伙伴们来看下。

假设我有一个 BookService,如下:

public class BookService {

    public void hello() {
        System.out.println("hello javaboy");
    }
}

然后我再创建一个 InstantiationAwareBeanPostProcessor 类型的后置处理器,并且重写前面提到的 postProcessBeforeInstantiation 和 postProcessAfterInitialization 方法:

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == BookService.class) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                String name = method.getName();
                System.out.println(name + " 方法开始执行了...");
                Object invoke = proxy.invokeSuper(obj, args);
                System.out.println(name + " 方法执行结束了...");
                return invoke;
            });
            BookService bookService = (BookService) enhancer.create();
            return bookService;
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean.getClass() ========= " + bean.getClass());
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

在 postProcessBeforeInstantiation 方法中,如果要创建的 Bean 是 BookService,则这里通过 Enhancer 来创建一个 CGLIB 的代理对象,如果是其他的 Bean 的创建,则调用父类方法即可。这样重写之后,就会导致在 1.1 小节中,获取到的 result 就是一个代理的 BookService 对象。

在 postProcessAfterInitialization 方法中,我未做任何额外处理,就是把拿到的 Bean 打印了一下,此时我们拿到手的 Bean 其实就是前面 postProcessBeforeInstantiation 方法生成的代理对象,然后这里调用父类方法去返回,实际上就是把参数 Bean 原封不动返回。

最后将这两个 Bean 注册到 Spring 容器中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="org.javaboy.bean.aop2.BookService" id="bookService"/>
    <bean class="org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor"
          id="myInstantiationAwareBeanPostProcessor"/>
</beans>

然后初始化 Spring 容器:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop2.xml");
BookService bs = ctx.getBean(BookService.class);
System.out.println("bs.getClass() = " + bs.getClass());
bs.hello();

最终执行结果如下:

听说 Spring Bean 的创建还有一条捷径?,spring,python,前端

可以看到,最终拿到的 BookService 就是一个代理对象,从源码层面来讲,这个代理对象在 resolveBeforeInstantiation 方法中就生成了,后续的 doCreateBean 方法实际上并未执行。

这就是 resolveBeforeInstantiation 方法的作用,实际上就是给 BeanPostProcessor 一个机会去创建一个代理对象,用这个代理对象来代替目标 Bean。

2. 源码实践

松哥为什么会关注到这个方法呢?

如果有小伙伴研究过 Spring AOP 源码,就会发现这个方法在处理 Spring AOP 的时候,有一个用武之地。

当我们在 Spring AOP 中,往往通过如下代码来定义切面:

@Component
@Aspect
@EnableAspectJAutoProxy
public class LogAspect {
    //...
}

这个类上面有一个 @Aspect 注解,那么问题来了,Spring 是如何识别出这是一个切面而非普通的 Bean 的?

答案就是在 1.1 小节中的 applyBeanPostProcessorsBeforeInstantiation 方法中,这个方法会遍历所有 InstantiationAwareBeanPostProcessor 类型的后置处理器,InstantiationAwareBeanPostProcessor 有一个子类是 AnnotationAwareAspectJAutoProxyCreator,在这个处理器中,识别出来了 LogAspect 是一个切面。

具体识别方法如下:

首先调用 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation 方法(实际上是 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 中的方法):

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
	Object cacheKey = getCacheKey(beanClass, beanName);
	if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
		if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return null;
		}
	}
	//...
}

从这个地方开始,分成了两条线:

  1. 如果是一个切面 Bean 的话,则执行第一个方法 isInfrastructureClass 就可以返回 true 了。
  2. 如果是一个普通 Bean 的话,则第一个方法会返回 false,此时就会执行第二个方法 shouldSkip(虽然该方法也会返回 false),但是该方法有一些其他的价值在里边。

2.1 切面 Bean

我们先来看 isInfrastructureClass 方法,先来看切面 Bean 是怎么处理的。

这个方法我摘了一部分出来,我们重点关注 isInfrastructureClass 方法,这个方法用来判断当前类是否是一个 Aspect:

@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
	return (super.isInfrastructureClass(beanClass) ||
			(this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
}

这里的判断主要是两方面:

  1. 调用父类的方法去判断当前类是否和 AOP 相关:
protected boolean isInfrastructureClass(Class<?> beanClass) {
	boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
			Pointcut.class.isAssignableFrom(beanClass) ||
			Advisor.class.isAssignableFrom(beanClass) ||
			AopInfrastructureBean.class.isAssignableFrom(beanClass);
	return retVal;
}

这个就不用我解释了,这些都是我们在 AOP 中的老熟人了。

  1. 调用 aspectJAdvisorFactory.isAspect 方法去判断当前类是否包含 @Aspect 注解:

AbstractAspectJAdvisorFactory#isAspect

@Override
public boolean isAspect(Class<?> clazz) {
	return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
	return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}

这代码可太好理解了,就是检查当前类是否有 @Aspect 注解。后面那个 compiledByAjc 方法是检查是否需要 ajc 编译,这个我们一般都不需要,所以,只要 hasAspectAnnotation 方法返回 true,整体上就会返回 true。

如果我们的类上包含 @Aspect 注解,那么最终就会在将当前类名加入到 advisedBeans Map 中,在 advisedBeans 这个 Map 中,key 是当前 Bean 的名称,value 则是 false 是一个标记,表示当前类不需要生成代理类。

这就是 isInfrastructureClass 方法执行的大致逻辑。

2.2 普通 Bean

如果是普通 Bean 的话,很明显 isInfrastructureClass 方法会返回 false,这就会导致 shouldSkip 方法去执行,这个方法名虽然叫 shouldSkip,但是却干了不少实事。

这个方法我会在下篇文章中和小伙伴们分享 AOP 的创建过程中再和大家详解,这里先说一句,这个方法会把各种 Aspect Bean 都收集整理起来,将来根据这些 Bean 去生成 Advisor。

好啦,这就是 resolveBeforeInstantiation 方法的作用,感兴趣的小伙伴可以自己 DEBUG 看一些哦~文章来源地址https://www.toymoban.com/news/detail-614052.html

到了这里,关于听说 Spring Bean 的创建还有一条捷径?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring-1-透彻理解Spring XML的Bean创建--IOC

    上一篇文章我们介绍了什么是Spring,以及Spring的一些核心概念,并且快速快发一个Spring项目,实现IOC和DI,今天具体来讲解IOC 能够说出IOC的基础配置和Bean作用域 了解Bean的生命周期 能够说出Bean的实例化方式 问题导入 问题1:在 bean 标签上如何配置别名? 问题2:Bean的默认作用

    2024年02月13日
    浏览(41)
  • Spring 创建 Bean 的三种方式

    在使用 Spring 框架后,对象以 Bean 的形式统一交给 IOC 容器去创建和管理。现阶段主流的方式是基于 SpringBoot 框架,基于注解的方式实现 Bean 的创建,但在原生 Spring 框架中其实存在三种创建 Bean 的方式。 BeanProcess 实体类,虽然加了 @Component 等三个注解,但只在注解方式创建

    2024年02月14日
    浏览(54)
  • Spring 容器原始 Bean 是如何创建的?

    以下内容基于 Spring6.0.4。 这个话题其实非常庞大,我本来想从 getBean 方法讲起,但一想这样讲完估计很多小伙伴就懵了,所以我们还是一步一步来,今天我主要是想和小伙伴们讲讲 Spring 容器创建 Bean 最最核心的 createBeanInstance 方法,这个方法专门用来创建一个原始 Bean 实例

    2024年02月14日
    浏览(37)
  • 【框架源码】Spring源码解析之Bean创建源码流程

    问题:Spring中是如何初始化单例bean的? 我们都知道Spring解析xml文件描述成BeanDefinition,解析BeanDefinition最后创建Bean将Bean放入单例池中,那么Spring在创建Bean的这个过程都做了什么。 Spring核心方法refresh()中最最重要的一个方法 finishBeanFactoryInitialization() 方法,该方法负责初始化

    2024年02月09日
    浏览(40)
  • 从 Spring 的创建到 Bean 对象的存储、读取

    目录 创建 Spring 项目: 1.创建一个 Maven 项目:  2.添加 Spring 框架支持: 3.配置资源文件: 4.添加启动类: Bean 对象的使用: 1.存储 Bean 对象: 1.1 创建 Bean: 1.2 存储 Bean 到容器内: 2.获取 Bean 对象: 2.1 创建 Spring 上下文: 2.2 获取指定 Bean 对象: ApplicationContext 和 BeanFactor

    2024年02月06日
    浏览(41)
  • Spring项目创建与Bean的存储与读取(DL)

    第一步,创建 Maven 项目 ,Spring 也是基于 Maven 的。 由于国外源不稳定,可能让下面第二步引入 Spring 依赖会失败,所以这里先介绍如何一下配置国内镜像源。 现成的 settings.xml 文件链接:gitee 如果你已经有了 settings.xml 文件,但没有配置 mirror ,配置内容如下: 如果你是在引

    2024年02月17日
    浏览(39)
  • B057-spring增强 依赖注入 AOP 代理模式 创建Bean

    DI:依赖注入 环境准备,即写一个spring测试,见工程 构造器注入 即使用构造器来给Bean的对象的属性赋值 MyBean OtherBean SpringTest-Context.xml SpringTest setter方法注入 即用setter方法给bean对象的属性赋值 MyBean OtherBean SpringTest-Context.xml SpringTest AOP 概念 事务管理:比如可以抽取try catch的

    2024年02月12日
    浏览(50)
  • 【Spring学习】走进spring,spring的创建和使用,spring获取Bean的几种常见方式, ApplicationContext 和 BeanFactory的区别(重点面试)

    前言: 大家好,我是 良辰丫 ,我们在上一篇文章不是简单介绍了SpringBoot嘛,为什么不学习SpringBoot,而是要开始Spring呢?Spring是SpringBoot的前身,我们先学习以前的稍微复杂的框架,才能更好的学习SpringBoot.💌💌💌 🧑个人主页:良辰针不戳 📖所属专栏:javaEE进阶篇之框架学习 🍎励志

    2024年02月08日
    浏览(46)
  • 4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

    我们要关注一个接口 BeanFactory ,它是Spring IOC容器的根接口,也是容器的入口。 类的描述中已经清楚的说明了: 我们来看一下这个接口里面的方法。 我们可以看到有各种各样的 getBean 方法,让我们可以从容器中获取到各种各样的Bean对象。 BeanFactory 有一个实现类 DefaultListab

    2024年02月05日
    浏览(39)
  • 实战讲解及分析Spring新建Bean的几种方式以及创建过程(图+文+源码)

    作为一个应用开发人员而言,会使用某一个工具分为两个层次(个人观点): 第一个层次,知道工具,会使用这个工具解决问题; 第二个层次,理解工具的实现原理。 关于Spring的学习,还在第一个层次转悠,缺少原理的研究, 随着学习的深入,开始研究些Spring源码,配合

    2023年04月08日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包