Spring源码剖析(史上最全0.o)

文章目录

前置

  • 本文Spring源码注释版在github上的地址:(后续补上连接)

  • Idea中导入Spring源码进行学习,参考我另外一篇:Idea中导入Spring源码

一、简介Spring

1.1 定义

容器:

Spring相当于一个大型工厂,负责在程序启动时,根据各种配置信息,创建对象。因为它持有一堆对象,所以又叫容器

  • 微观上的容器

    • 存放beanDefinition的beanDefinitionMap容器本质是ConcurrentHashMap
    • 存放bean对象的singletonObjects容器,本质也是ConcurrentHashMap
  • 宏观上的容器

    即整个Spring环境,里面包括

    • Reader解析器:可将xml、yml、properties的各种文件内容,解析抽象成beanDefinition
  • Registry注册器:将beanDefinition put至beanDefinitionMap中,可以对beanDefinition进行注册和更新操作

    • 工厂beanFactory用来createBean、getBean
    • PostProcessor后置处理器对Spring中beanDefinition和bean进行扩展增强
    • 等等一系列内容

1.2 模块

  • Spring Core

    为Spring提供了基础服务支持,核心容器的主要组件是BeanFactory

  • Spring Context

    加载属性文件,扫描本地的包等,向Spring 框架提供上下文信息。

    BeanFactory使Spring成为容器,上下文模块使Spring成为框架。

  • Spring AOP

    通过配置管理特性,直接将面向切面编程功能集成到了Spring 框架中。

    同时为基于Spring 的应用程序中的对象提供了事务管理服务。

  • Spring JDBC

    提供了JDBC的抽象层,简化了JDBC编码,同时使代码更健壮。

    利用了Spring的AOP模块完成了为系统中对象提供事务管理的服务。

  • Spring ORM

    Spring没有实现自己的ORM方案,而是为当前主流的ORM框架预留了整合接口如Mybatis。

    需遵从Spring 的通用事务和DAO 异常层次结构。

  • Spring web

    集成Springmvc


1.3 概述

Spring源码概览

如图1

在这里插入图片描述


Bean

Bean的本质也是Object,正常情况下我们是通过new Demo()调用类的构造器来创建Demo类,而Spring中默认是通过ctor.newInstance()来创建Demo类的对象即bean实例


BeanDefinition(bd)

1、概念

  • BeanDefinition是创建bean的原材料,它含有用于创建bean实例的各种丰富的属性内容,这些属性直接决定了创建出来的bean是什么样子

2、beanDefinition重要属性

  • beanClass:表明bean的Class<?>类型:class com.xxx.Demo
  • scope:表示bean的类型是单例的、原型的
  • lazyInit:表明bean是否为懒加载的
  • dependsOn:表明bean是否依赖别的bean等

3、分类

  • GenericBeanDefinition:从xml中读取的内容,封装为此类型的beanDefinition
入口:BeanDefinitionReader#loadBeanDefinitions()	
public static AbstractBeanDefinition createBeanDefinition() {
	GenericBeanDefinition bd = new GenericBeanDefinition();
}
  • RootBeanDefinition:放在beanDefinitionMap中,真正用于创建bean的beanDefinition类型
入口:getMergedLocalBeanDefinition
RootBeanDefinition	mbd = new RootBeanDefinition(bd);

BeanDefinitionReader

1、作用

  • 定义规范,便于扩展的资源读取器
int loadBeanDefinitions(Resource resource)
int loadBeanDefinitions(String location)

2、内容

读取资源xml、properties、yml等类型的资源配置,将其抽象为BeandDefinition并存入beanDefinitionMap


BeanDefinitionRegistry

1、作用

// 从beanDefinitionMap移除
void removeBeanDefinition(String beanName)
// 从beanDefinitionMap中取出
BeanDefinition getBeanDefinition(String beanName)
//将beanDefinition添加到beanDefinitionMap
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
// 判断是否存在于beanDefinitionMap
boolean containsBeanDefinition(String beanName)

2、registery接口结构

实现类SimpleBeanDefinitionRegistry
实现类DefaultListableBeanFactory
实现类GenericApplicationContext
	子类AnnotationConfigApplicationContext

BeanFactory(bf)

1、定义

是访问Spring容器的根接口

2、作用

getBean(从一级缓存中获取bean,没有则createBean)

3、分类

如图16所示

在这里插入图片描述

BeanFactory
    
子接口ConfigurableBeanFactory
	子接口ConfigurableListableBeanFactory
		实现类DefaultListableBeanFactory
		实现类AbstractBeanFactory
			子类AbstractAutowireCapableBeanFactory
				子类DefaultListableBeanFactory
    
子接口ApplicationContext
    子接口WebApplicationContext
    子接口ConfigurableApplicationContext
    	子接口ConfigurableWebApplicationContext
    	实现类AbstractApplicationContext
    		子类GenericApplicationContext
    			子类AnnotationConfigApplicationContext   

DefaultListableBeanFactory

  • 继承了BeanFactory,故可以getBean

  • 实现了BeanDefinitionRegister,故可以对beanDefinition进行CRUD

Bean的生命周期

bf的实现们应该遵循标准的Bean生命周期,创建对象(实例化和初始化)组件和顺序如下(BeanFactory接口注释)

BeanNameAware#setBeanName  
BeanClassLoaderAware#setBeanClassLoader   
BeanFactoryAware#setBeanFactory  
ResourceLoaderAware#setResourceLoader  
ApplicationEventPublisherAware#setApplicationEventPublisher  
MessageSourceAware#setMessageSource   
ApplicationContextAware#setApplicationContext
ServletContextAware#setServletContext
   
    
BeanPostProcessor#postProcessBeforeInitialization
InitializingBean#afterPropertiesSet  
RootBeanDefinition#getInitMethodName
BeanPostProcessor#postProcessAfterInitialization
  
    
DestructionAwareBeanPostProcessor#postProcessBeforeDestruction  
DisposableBean's {@code destroy}
RootBeanDefinition#getDestroyMethodName自定义销毁方法

PostProcessor

1.定义

后置处理器

2.分类

BeanFactoryPostProcessor(bfpp)

1)作用:

bfpp用于修改bf,而bf中最重要的属性就是BeanDefinitionMap中的bd属性,通常我们用的最多的是修改其属性BeanDefinition内容

除此之外,大部分bfpp的作用就是扫描并注册所需的bd

2)重要实现

  • ConfigurationClassPostProcessor(是bfpp子接口bdrpp的实现类):用于解析Spring的@ComponentScan、@Configuraion、@Bean、@Import、@PropertyResource等注解
  • PropertyPlaceholderConfigurer:将解析占位符${“spring.redis.host”}对应的值127.0.0.1
BeanPostProcessor(bpp)

1)作用:

初始化赋值后:

BeforeInitialization:对Bean增强

init-method

afterInitialization:对Bean增强

2)bpp三大子接口

其子接口还有更细粒度的后置方法

前置说明Instantiation表示:实例化,而Initialization表示:初始化

  • DestructionAwareBeanPostProcessor:postProcessBeforeDestruction在bean销毁之前执行

  • InstantiationAwareBeanPostProcessor:

    postProcessBeforeInstantiation:在实例化前对bean进行增强

    postProcessAfterInstantiation:在实例化后对bean进行增强

    • 子接口SmartInstantiationAwareBeanPostProcessor
    实现类AbstractAutoProxyCreator#postProcessBeforeInstantiation:createProxy创建代理对象
    	子类AbstractAdvisorAutoProxyCreatorAOP
    		子类InfrastructureAdvisorAutoProxyCreatorSpring事务中用于创建代理对象
    		子类AspectJAwareAdvisorAutoProxyCreator:
    			子类AnnotationAwareAspectJAutoProxyCreator:resolveBeforeInstantiation方法中  														涉及AOPAdvisors的创建
    
  • MergedBeanDefinitionPostProcessor

实现类InitDestroyAnnotationBeanPostProcessor:找到此bean中被@PosConstructor注解、@PreDestroy注												解修饰的init方法 、destroy方法
	子类CommonAnnotationBeanPostProcessor类:找到此bean中被@Resource修饰的属性和方法
    
实现类AutowiredAnnotationBeanPostProcessor实现类:找到bean中被@Autowired注解、和@Value属性,并将被此注解修饰													的属性设置到bd中

FactoryBean

1、内容:本身是一个Bean

public class MyFactoryBean implements FactoryBean<Car> {
	@Override
	public Car getObject() throws Exception {
		return new Car();
	}

	@Override
	public Class<?> getObjectType() {
		return Car.class;
	}
}
<bean id="myFactoryBean" class="com.mjp.factorybean.MyFactoryBean"></bean>

2、作用

和beanFactory一样都是用来创建对象的,只不过FactoryBean

  • 在创建对象的过程中,不需要像BeanFactory那样要严格遵循Spring定义的一套流程即Bean的生命周期。可以在getObject中使用最简单的new Cat(“xiaomi”, 1)方式创建对象

  • Spring会先创建FactoryBean,我们只需要调用重写的getObject方法就可以获得对应的对象,这样可以自定义getObject的实现方式,更加灵活

  • MyFactoryBean 和 Car对二者对象都交由Spring管理

    Car car = (Car) classPathXmlApplicationContext.getBean("myFactoryBean");
    // 加上&就可以获取对应的FactoryBean
    MyFactoryBean myFactoryBean = (MyFactoryBean)classPathXmlApplicationContext.getBean("&myFactoryBean");
    

3、使用场景

当一个类依赖关系很复杂时,可以使用factoryBean。

eg:Mybatis提供了sqlSessionFactoryBean对象,该对象返回Spring一个sqlSessionFactory对象,Spring拿到该对象可以直接创建会话。至于sqlSessionFactory的依赖关系,由MyBatis自己定义并维护,Spring不用关心。

package org.mybatis.spring;

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>{
    private SqlSessionFactory sqlSessionFactory;
    
    public SqlSessionFactory getObject(){
        return this.sqlSessionFactory;
    }
}

Aware

1、内容

如果一个bean对象在操作过程中,想要获取Spring容器中的其它对象,此类可以通过实现Aware接口的方式满足此诉求。

2、实现步骤如下

  • 由Spring管理的类A,想要获取Spring容器中的Xxx对象
  • 则A实现对应XxxAware接口
  • A中定义Xxx属性
  • A中声明getXxx方法

A想获得Spring容器中的bean工厂对象BeanFactory,则

public class A implements BeanFactoryAware {
	private BeanFactory beanFactory;
	
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	protected BeanFactory getBeanFactory() {
		return this.beanFactory;
	}
    
    public void func() {
        // 使用beanFactory
    }
}

这样就可以执行BeanFactory bf = a.getBeanFactory;

3、原理

invokeAwareMethods


Multicaster 和 Listeners

1、定义

(事件的)多播器即广播器和(事件的)监听器

2、作用

在Spring Bean初始化过程中,当aware、beanPostProcessor-before、init、beanPostProcessor-after,每个动作执行完成后,都可以通过多播器和监听器为他们自定义执行动作


Environment

1、内容

当前运行程序的环境

2、作用

对程序的profiles(bean定义-xml或注解)和properties进行建模

3、获取

  • 实现EnvironmentAware
package com.mjp.aware;

@Component
public class MyEnvironmentAware implements EnvironmentAware {
	private Environment environment;

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	public void func() {
		String[] profiles = environment.getActiveProfiles();
		for (String profile : profiles) {
			System.out.println(profile);
		}

		String property = environment.getProperty("spring.redis.host");
		System.out.println(property);
	}
}
  • 配置类
@Configuration
@ComponentScan("com.mjp.aware")
@PropertySource(value = "classpath:redis.properties")在src/main/respirces路径下
public class MyApplication {
}

4、EmbeddedValueResolverAware

还可以实现接口EmbeddedValueResolverAware来获取系统的环境、配置信息

@Component
public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware {
	private StringValueResolver resolver;

	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		this.resolver = resolver;
	}

	public void func() {
		// 场景1:从systemProperties中读取
		String userName = resolver.resolveStringValue("${user.name}");
		System.out.println(userName);

		// 场景2:从systemEnvironment中读取
		String osName = resolver.resolveStringValue("${os.name}");
		System.out.println(osName);

		// 场景3:从resources资源路径下读取redis.properties
		String port = resolver.resolveStringValue("${spring.redis.port}");
		System.out.println(port);
	}
}

DefaultSingletonBeanRegistry中三级缓存

和BeanDefinitionRegistry对bd进行CRUD不同外,在DefaultSingletonBeanRegistry(见名知意:对单例bean进行CRUD)类下,存在三级缓存:

1、singletonObjects

1)名称:一级缓存

2)内容:beanName - 成品bean(即完成了实例化,也完成了初始化赋值)

2、earlySingletonObjects

1)名称:二级缓存

2)内容

beanName - 半成品bean(只完成了实例化,未完成初始化-便将对象提前暴露

3)作用

与一级缓存的不同是,当bean被放在这个二级缓存中时,即使bean还处于创建过程中,也可以通过getBean被获得。这样就可以提前用于循环依赖检测

3、singletonFactories

1)名称:三级缓存

2)内容:beanName -Lambda表达式(函数式接口:可使用动态代理的方式创建bean的cglib代理对象、也可以直接返回原bean对象)

三级缓存中没有存放对象,而是存放了创建代理对象的lambda表达式。完整的对象(普通对象和代理对象),都存在一级缓存中

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  • addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	this.singletonFactories.put(beanName, singletonFactory);//singletonFactory函数式接口
}

3)作用

  • 通过lambda表达式,可以动态的创建代理对象(当然也可以不使用bpp的动态代理,直接返回原对象)可以不用,但是必须要有!(可以不用天赋,但是必须要有天赋0.o)
  • 创建完成代理对象后,就可以使用代理对象覆盖普通对象,普通对象就无存在的必要了。
  • 同理如果不需要代理对象,则普通对象创建完成并放入一级缓存后,即可将k-v(lambda表达式)从三级缓存中删除

4、一到三级缓存内容如图13所示

在这里插入图片描述


配置类

1、定义

被以下6种注解修饰的类即配置类

@Configuration、@Bean、@Component、@ComponentScan、@Import、@ImportSource

这些配置类要结合@Configuration注解一起使用,否则Spring无法将其加载到容器

@Configuration
@ComponentScan(basePackages = "com.mjp.bean")
public class MyConfigurationClass {
}


@Configuration
@PropertySource("classpath:redis.properties")
public class RedisGateway { 
    
	@Value("${spring.redis.host}")
	private String ip;
}

Bean类型

1、用户自定义

BeanDefinition属性int ROLE_APPLICATION = 0;

eg:@Service注解修饰的类

2、复杂的配置类bean,1

eg:FactoryBean.getObject获取的bean

3、容器bean和用户无关,int ROLE_INFRASTRUCTURE = 2;

eg:Spring中自带的bfpp-ConfigurationClassPostProcessor

4、判断是否为Spring的bean

根据beanName判断是用户自定义bean还是Spring的bean

内部类BeanPostProcessorChecker(本身是bpp)方法
private boolean isInfrastructureBean(@Nullable String beanName) {
	if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
		BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
		return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE);
	}
	return false;
}

二、IOC

定义

1、IOC(控制翻转)

  • 原本,实例的产生由程序员自己new出来
  • 但是,对象的创建和依赖注入动作本身和业务不相关
  • 完全可以抽象成框架来自动完成(Spring)

2、DI

IOC是目标,DI依赖注入是手段

A类中使用B类,不在A类通过new B()的方法创建b,而是将B在外部创建好后,通过

  • new A(b)构造函数
  • funA(B b)
  • Filed.set属性

等方式传递(注入)给A类使用

ClassPathXmlApplicationContext

这里我们以xml形式

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("txt.xml");
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

介绍下ClassPathXmlApplicationContext类的setConfigLocations

setConfigLocations
protected String resolvePath(String path) {
	return getEnvironment().resolveRequiredPlaceholders(path);
}

1、getEnvironment

创建StandardEnvironment环境

setConfigLocations-->>
	resolvePath-->>
		getEnvironment -->>
			createEnvironment-->>
				return new StandardEnvironment()				
StandardEnvironment类方法				
protected void customizePropertySources(MutablePropertySources propertySources) {
    // 加载系统properties,内含user.name = admin
	propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	// 加载系统Environment
	propertySources.addLast(new 	  SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    // 二者都是propertySources,底层都是native方法,通过VM获取系统环境信息
}	

2、解析占位符

注意:这里假如txt.xml文件名称不叫txt.xml,而是使用"${user.name}"的占位符方式,则系统解析如下

AbstractPropertyResolver#resolveRequiredPlaceholders

public String resolveRequiredPlaceholders(String text) {
	// 1.创建占位符${}解析器类PropertyPlaceholderHelper
	this.strictHelper = createPlaceholderHelper(false);
	// 2.解析
	return doResolvePlaceholders(text, this.strictHelper);
}
解析文件名占位符

“${user.name}.xml” -> “admin.xml”

// 底层是调用PropertyPlaceholderHelper的replacePlaceholders#parseStringValue
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
  • 函数式编程,当执行到函数式接口的方法时,会调用参数:函数式编程方法getPropertyAsRawString
replacePlaceholders方法大概过程就是
1、${user.name}将$、{}去掉,获取"user.name"
2、执行PropertySourcesPropertyResolver#getPropertyAsRawString
3、获取propertySources集合
    - src/main/resources路径下的所有文件
    - systemProperties(内含user.name = admin)
    - systemEnvironment
4Object value = propertySource.getProperty(key);
本质从map-systemProperties中根据key取出来val,即admin(我们么系统c盘user.name默认是admin)

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);

@Configuration
@ComponentScan("com.mjp")
public class MyApplication {
}

上面ClassPathXmlApplicationContext是以xml形式启动服务,这里也简单阐述下以注解形式启动的前置过程

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	// 步骤一:
	this();
	// 步骤二:将构造参数中MyApplication抽取为bd并加入bdMap
	register(annotatedClasses);
    // 步骤三:
	refresh();
}

这里我们就只阐述步骤一,步骤三就是十三个具体方法

步骤一方法体

// 1.在调用其构造器之前,会先调用父类构造器,完成bf的创建,这样在后续法二、createBeanFactory中就直接拿bf用了,无需再创建

public AnnotationConfigApplicationContext() {
	// 2.AnnotationConfigUtils.registerAnnotationConfigProcessors完成5个Spring内置internalXxx(bfpp)的添加至bdMap(下文有具体阐述)
	this.reader = new AnnotatedBeanDefinitionReader(this);
    
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}

要搞清楚IOC,只需要搞清楚refresh(),下面就是refresh中具体的十二个方法

法一、prepareRefresh

1、记录容器启动时刻

2、initPropertySources初始化在上下文中的所有占位符属性资源:扩展点1,留给子类在容器刷新的时候去扩展

3、获取Environment对象并验证properties对应的map中是否包含某个key

4、创建早期事件集合(默认为空),一旦多播器可用,就会使用多播器广播早期事件

  • 对应registerListeners方法

法二、obtainFreshBeanFactory

refreshBeanFactory

createBeanFactory

1、创建bf

return new DefaultListableBeanFactory(getInternalParentBeanFactory());

2、忽略三个Aware

new 工厂,在调用super-this方法时,会忽略三个Aware接口

ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);	

后续初始化invokeAwareMethods时,会补上这三个接口

bf设置属性

这里只是为bf设置简单几个属性,更多的属性是在法四中完成设置的

  • 是否允许beanDefinition被覆盖、是否允许循环依赖
if (this.allowBeanDefinitionOverriding != null) {
	beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
	beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}

默认走不到,可以通过子类重写customizeBeanFactory方法,强制set

loadBeanDefinitions

假设xml配置内容如下,分析下此方法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="demo" class="com.mjp.Demo"/>
    <context:component-scan base-package="com.mjp"></context:component-scan>
</beans>

一句话概括

  • 将xml内容解析成Document对象
  • 将Document转为Element节点对象
  • 解析节点

1、重载方法1-入参为beanFactory

  • 为beanFactory创建一个BeanDefinitionReader,专门用于解析xml文件即解析xml文件中beans标签的子标签内容

  • beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)):设置实体解析器

    • 在解析xml文件时,需要判断xml中标签是否满足规范,常见的规范有.xsd(普遍) 和 .dtd
    • xml文件默认通过联网读取地址http://www.springframework.org/schema/beans/spring-beans.xsd获取标签规范
    • 在未联网的情况下,也可以通过读取本地META-INF/spring.schemas文件,内含xsd文件内容。读取的实现方式就是实现EntityResolver接口

2、重载方法2-入参为reader

获取configLoactions

3、重载方法3-入参为location

将字符串location转换为Resource[]数组

4、重载方法4-入参为resources

public interface BeanDefinitionReader{
    int loadBeanDefinitions(Resource resource);
}
  • 可以选择XmlBeanDefinitionReader、也可以选择PropertiesBeanDefinitionReader

5、重载方法5-入参为encodedResource

doLoadBeanDefinitions
// 1.
Document doc = doLoadDocument(inputSource, resource);
// 2.
int count = registerBeanDefinitions(doc, resource);

1、doLoadDocument

将inputSource输入字符流(xml内容)转换为Document对象

Document doc = doLoadDocument(inputSource, resource);
//doc是xml内容的实体类,里面内容:
<beans>
    子元素
    	各自<>标签属性
    		  标签属性
    				属性值
封装成Document对象-本质是一个Node节点
  • getValidationModeForResource:根据当前customizeBeanFactoryxml是dtd(2)模式还是xsd(3)模式,来检验对应的xml内容是否合法

  • xml -> Document

    具体如何将xml -> Document不建议看

2、registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) {
	//创建ParserDelegate解析Document对象
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

	// 3.解析
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
}

3、registerBeanDefinitions#parseBeanDefinitions

根据Document(表示XML中的元素文档)获取的Element对象本身也是Node

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){
	if (delegate.isDefaultNamespace(root)) {
        // 根节点(beans)即<beans>标签
		NodeList nl = root.getChildNodes();
        // 解析beans的所有子标签,eg:<bean>、<context:component-scan>等
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
            // 只解析标签<>,跳过xml中的换行符等内容
			if (node instanceof Element) {
				Element ele = (Element) node;
				// 如果标签是默认命名空间的,eg:bean
				if (delegate.isDefaultNamespace(ele)) {
					parseDefaultElement(ele, delegate);
				}else {
    				//如果标签是用户元素,eg:component-scan、aop-config
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
}
parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	// 1.xml中的命名空间http://www.springframework.org/schema/context
	String namespaceUri = getNamespaceURI(ele);
		
	// 2.先根据命名空间url获取对应的handler处理器
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	// 3.再使用处理器找到对应的解析器BeanDefinitionParser(接口)具体的Parser实现类
    // 4.再使用具体的ComponentScanBeanDefinitionParser#parse 
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
	// 1.获取base-package路径"com.mjp"
	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
		
	// 2.创建ClassPathBeanDefinitionScanner扫描器
	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		
	// 步骤三:解析base-package下的所有类,抽取为bd
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		
	//步骤四:解析internalXxx
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
}

步骤三:doScan解析component-scan路径下的所有类

底层调用的ClassPathBeanDefinitionScanner#doScan。

这里先不赘述,后续再注解开发,解析@ComponentScan注解时,底层也是这个,再展开说

步骤四:registerComponents解析internalXxx

– >> AnnotationConfigUtils.registerAnnotationConfigProcessors

方法执行结果如图7所示,完成了5个internalXxx的创建并加入bdmap
在这里插入图片描述

这5个internalXxx本质是bfpp,其中最重要的是internalConfigurationAnnotationProcessor对应ConfigurationClassPostProcessor

当基于注解开发@ComponentScan时,在后续的法五-invokeBFPP时,会创建此bfpp的实例,并利用他完成Spring中配置类的bd解析,将所有的满足条件的配置类,添加到bdMap,便于后续的创建对象


parseDefaultElement

–>> processBeanDefinition

解析xml中的bean标签

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){
	// 步骤一:先new GenericBeanDefinition
	// 再根据<bean id="demo429" class="com.mjp.aop.demo.Demo429"/>标签,完成bd属性赋值
	// beanName = id  和 beanClass:class com.xxx 的赋值
    // 标签如有其他属性replaced-method、constructor-arg也会完成bd属性赋值
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		
	// 步骤二:使用registry完成注册bd,底层就是beanDefinitionMap.put(beanName, beanDefinition)
	BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}

bd属性值大体如图4所示

在这里插入图片描述

法三、prepareBeanFactory

方法内容:为bf设置属性

// 步骤一.设置SPEL表达式#{}解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

// 步骤二.添加属性编辑注册器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

// 3.向bean工厂中添加用于处理ApplicationContextAware接口的ApplicationContextAwareProcessor-bpp
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

// 4.将environment、systemProperties、systemEnvironment加入bf

Spel

步骤一:setBeanExpressionResolver

1、使用场景

@Value("#{20-2}")
private Integer age;

2、设置SPEL表达式#{}解析器

beanFactory.(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

3、Spel表达式解析

这里设置spel表达式解析器,后续调用此解析器

populateBean -->> ibp.postProcessProperties -->>
				  AutowiredAnnotationBeanPostProcessor#postProcessProperties -->>

AutowiredAnnotationBeanPostProcessor#inject -->>
Object value = beanFactory.resolveDependency -->>
 			   DefaultListableBeanFactory#resolveDependency -- >> doResolveDependency->

evaluateBeanDefinitionString -->> AbstractBeanFactory#evaluateBeanDefinitionString -->>

return this.beanExpressionResolver.evaluate这里使用的就是2中注册的
    
StandardBeanExpressionResolver-->>
StandardBeanExpressionResolver#evaluate
// 先从缓存中获取SPELExpression,没有再创建
Expression expr = this.expressionCache.get(value);
if (expr == null) {
	expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext);//SpelExpression
	this.expressionCache.put(value, expr);
}

return expr.getValue(sec);//底层使用SpelExpression进行解析

// 底层是
OpMinus#getValueInternal,就是拿到左侧20,右侧2,一系列判断,结果20-2=18

addPropertyEditorRegistrar

步骤二:addPropertyEditorRegistrar

1、背景

public class Person {
	private String name;
	private Address address;
}

public class Address {
	private String province;
	private String city;
}
  • 定义Person类,其中address属性为Address类,由省和城市2个属性组成
  • 正常情况下,我们是new Address(),然后分别set省和城市两个字段
  • 这里可以通过自定义Editor,直接将"江苏_徐州"字符串赋值给Address类的province和city属性

2、自定义编辑器

public class AddressPropertyEditor extends PropertyEditorSupport {
    // 将"江苏_徐州" -> 赋值给address的两个属性
	@Override
	public void setAsText(String addressText) {//"江苏_徐州"
		Address address = new Address();
		String[] split = addressText.split("_");
		address.setProvince(split[0]);
		address.setCity(split[1]);
		setValue(address);
	}
}

3、将自定义编辑器交由Spring管理

<bean id="person" class="com.mjp.bean.edit.Person">
	<property name="name" value="mjp"></property>
	<property name="address" value="江苏_徐州"></property>
</bean>
	
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="com.mjp.bean.edit.Address" 	  			       value="com.mjp.edit.AddressPropertyEditor">
			</entry>
			</map>
	</property>
</bean>

4、原理分析

1)通过xml配置,将自定义编辑器AddressPropertyEditor添加到CustomEditorConfigurer(Bfpp)的属性customEditors中

  1. 法五、执行CustomEditorConfigurer此bfpp#postProcessBeanFactory时,会将Map类型的customEditors注册到bf中(key:Address类、Value:自定义AddressPropertyEditor)
if (this.customEditors != null) {
	this.customEditors.forEach(beanFactory::registerCustomEditor);
}

3)实例化bean执行initBeanWrapper-registerCustomEditors,从bf获取customEditors并注册到PropertyEditorRegistrySupport的属性Map<Class<?>, PropertyEditor> customEditors中

propertyEditor = registrar.registerCustomEditor();
this.customEditors.put(requiredType, propertyEditor);
  1. 后续初始化方法populate-resolveValueIfNecessary-会从PropertyEditorRegistrySupport中findCustomEditor从customEditors此Map中拿到我们自定义的AddressPropertyEditor
  • 使用AddressPropertyEditor-执行重写方法setAsText-将addresss属性赋值给PropertyEditorSupport的value
  • 最后editor.getValue获取新的值

法四、postProcessBeanFactory

子类重写此方法,做扩展(MVC和Boot有具体扩展实现)

法五、invokeBeanFactoryPostProcessors

1、方法内容

  • Spring自带的bfpp 以及 从容器中找到上文存入的internalXxx-bfpp
  • 然后getBean创建bfpp实例化对象
  • 最后调用bfpp的post方法

2、执行简图

如图5所示,bfpp先后执行关系

在这里插入图片描述

3、执行流程概述

步骤一:先执行外方法入参传递过来的bfpp集合(默认为空)

步骤二:再执行子接口BeanDefinitionRegistryPostProcessor-bdrpp

  • 优先执行实现了PriorityOrdered接口的实现类
  • 再执行实现了Ordered接口的实现类
  • 最后执行没有实现可排序接口的实现类
  • 因为子接口继承了父接口,所以也要执行父接口bfpp-postProcessBeanFactory

步骤三:再执行父接口BeanFactoryPostProcessor-bfpp

  • 优先执行实现了PriorityOrdered接口的实现类
  • 再执行实现了Ordered接口的实现类
  • 最后执行没有实现可排序接口的实现类

4、执行流程图

如图6所示
在这里插入图片描述

5、 invokeBeanFactoryPostProcessors方法源码

// 此set用于存储执行过的bfpp,防止重复执行
Set<String> processedBeans = new HashSet<>();

// 定义2个“大”集合,存放父接口bfpp、子接口bdrpp
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

// ==================================步骤二:执行bdrpp=================================
// 4.创建集合用于存储当前正在处理的bdrpp
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

// 5.从bf中获取所有bdrpp接口的所有实现类bd
String[] postProcessorNames =
			beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);


// --------------------------------PriorityOrdered ----------------------------------
for (String ppName : postProcessorNames) {
	// 5.1如果实现类实现了PriorityOrdered接口,则优先处理
	if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
		// 5.2获取bdrpp实现类的bean对象即getBean,将bean添加到当前集合中
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		// 5.3放入set中表示已经执行过,防止重复执行(但这里还未执行此ppName)
		processedBeans.add(ppName);
	}
}
// 5.4 对当前集合进行排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 5.5将当前集合中的bdrpp合并到大集合中
registryProcessors.addAll(currentRegistryProcessors);
// 5.6执行bdrpp的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 5.7清空当前集合
currentRegistryProcessors.clear();


// --------------------------------Ordered ----------------------------------
// 6.再次获取当前容器中实现了BDRPP接口的实现类(因为上述5.6的执行过程中可能新增了bdrpp所以要重新再获取一下最新的)
postProcessorNames = beanFactory.getBeanNamesForType(
    BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
	// 6.1如果实现类实现了Ordered接口,则次优先处理
	if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
		// 同上5.2
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		// 同上5.3
		processedBeans.add(ppName);
	}
}
// 同上5.4-5.7
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();


// --------------------------------剩下的 ----------------------------------
boolean reiterate = true;
while (reiterate) {
	reiterate = false;
	// 7.重新获取没有实现PriorityOrdered接口也没有实现Ordered接口的bdrpp同上进行处理
	postProcessorNames = beanFactory.getBeanNamesForType(
        BeanDefinitionRegistryPostProcessor.class, true, false);
	for (String ppName : postProcessorNames) {
		if (!processedBeans.contains(ppName)) {
			currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
			processedBeans.add(ppName);
			reiterate = true;
		}
	}
	// 这里即没有实现PriorityOrdered也没有实现Ordered,但是还是进行了sort排序的原因是:为了和上述处理		风格上保持一致
	// 因为没有实现二者,所以可随意排序,bdrpp们谁先执行,没有影响
	sortPostProcessors(currentRegistryProcessors, beanFactory);
	registryProcessors.addAll(currentRegistryProcessors);
	invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
	currentRegistryProcessors.clear();
}


// 8.bdrpp继承了父接口自动获取了postProcessBeanFactory方法,在此执行
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// 9.方法入参传递过来的bfpp中可能有子接口bdrpp实现类,也可能有bfpp接口实现类
// 如果有bfpp实现类,则在此调用bfpp-postProcessBeanFactory
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);


// ==================================步骤三:执行bfpp=================================
// 流程完全同理bdrpp
String[] postProcessorNames =
	beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
		---
	}
}

6、源码分析1

上述步骤6中重新再获取容器中的bdrpp原因

1)原因:bdfpp方法内部可以再添加一个bdrpp

2)实现:

  • 自定义bdrpp1
public class Bdrpp1 implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("1.Bdrpp1#postProcessBeanDefinitionRegistry");
		// 在bdrpp1中,添加bdrpp2
		BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(Bdrpp2.class);
		registry.registerBeanDefinition("bdrpp2", bdb.getBeanDefinition());
	}
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("4.Bdrpp1#postProcessBeanFactory");
	}
	
	@Override
	public int getOrder() {
		return 0;
	}
}
  • bdrpp2
public class Bdrpp2 implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
	public Bdrpp2() {
		System.out.println("2.创建了Bdrpp2");
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("3.Bdrpp2#postProcessBeanDefinitionRegistry");
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("5.Bdrpp2#postProcessBeanFactory");
	}

	@Override
	public int getOrder() {
		return 0;
	}
}
  • xml
<bean id="bdrpp1" class="com.mjp.beanfactorypostprocessor.bdrpp.Bdrpp1"></bean>
  • 执行打印
1.Bdrpp1#postProcessBeanDefinitionRegistry
2.创建了Bdrpp2
3.Bdrpp2#postProcessBeanDefinitionRegistry
4.Bdrpp1#postProcessBeanFactory
5.Bdrpp2#postProcessBeanFactory
  • 分析
    • 第一次是从bf中获取的bdrpp即xml中定义了的bdrpp1
    • 再执行ddrpp1#postProcessBeanDefinitionRegistry时,又向容器中添加了bdrpp2了
    • 所以后续需要重新再去容器中获取bdrpp集合,防止漏了bdrpp2

ConfigurationClassPostProcesor

1、类型

public class ConfigurationClassPostProcessor 
	implements BeanDefinitionRegistryPostProcessor,PriorityOrdered{
}

是bdrpp,且执行优先级最高

2、来源

1)来源1:基于xml配置开发:ClassPathXmlApplicationContext

上文parseCustomElement方法

2)来源2:基于注解配置类开发:AnnotationConfigApplicationContext

我们这里以注解开发为例分析

3、作用

在bean实例化之前,找到注解修饰的启动类并解析,完成相关类抽取为bd的操作,bd存入bdMap中,这样在bean实例化时才能使用

// 启动类:MyApplication - 是启动的构造函数中的参数
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
    MyApplication.class);

// 配置类:MyApplication,被@Configuration注解修饰
@Configuration
@ComponentScan("com.mjp.aop")
@EnableAspectJAutoProxy
public class MyApplication {
}

所以:MyApplication是启动类-配置类
  • new 构造方法,将MyApplication对应bd存入bdMap
  • bdrpp执行时,判断此启动类bd,是否加了@Configuration注解
  • 如果加了,则解析启动类上的其它注解
    • @ComponentScan
    • @EnableAspectJAutoProxy

4、此bdrpp执行流程图概述

  • 先执行自己作为bdrpp的方法分支
  • 再执行继承了父接口bfpp的方法分支

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

入口:因为此类是bdrpp,而且优先级最高,所以会在上述步骤二中的5.6进行方法执行invokeBeanDefinitionRegistryPostProcessors -->> processConfigBeanDefinitions概述如下

// 创建集合存储配置类的bd
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

// ========================将用户自定义的启动类-配置类存入configCandidates===================
// 从当前bf中获取所有的已注册beanNames(用户自定义启动类-配置类 和 Spring容器自带的)
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
	BeanDefinition beanDef = registry.getBeanDefinition(beanName);
	// 三.判断bd是否为配置类(显然我们的启动类是配置类)
	else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    	// 将启动类-配置类,加入待解析候选集合
		configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    }
}

// 五.配置类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
		this.metadataReaderFactory, this.problemReporter, this.environment,
		this.resourceLoader, this.componentScanBeanNameGenerator, registry);
do {
	// 六.循环(某个注解里面可能还有另外的注解,eg:SpringBoot中的启动类)解析启动类-配置类
	parser.parse(candidates);
    
   // 七.调用@EnableAspectJAutoProxy注解中@Import(AspectJAutoProxyRegistrar.class),AspectJAutoProxyRegistrar#registerBeanDefinitions完成相关bd的存入
   this.reader.loadBeanDefinitions(configClasses);
}
while (!candidates.isEmpty());
checkConfigurationClassCandidate

步骤三:

判断我们的启动类,是否为配置类,是则返回ture,否则返回false,于此同时为bd赋值属性:

  • 如果是被@Configuration注解修饰的配置类,则bd的属性为full
  • 如果被另外5个注解修饰的配置类,则bd的属性为lite
public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
	
	// 3.1 判断当前bd元数据是否被@Configuration注解修饰,是的话为bd此属性设置值"full"
    // 启动类-配置类符合是Full
	if (isFullConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
    
	// 3.2判断当前bd元数据是否被@Component、ComponentScan、Import、ImportResource、@Bean注解修饰,是的话为bd此属性设置值"lite"
    // 启动类配置类不是lite
	else if (isLiteConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		// 非配置类则false
		return false;
	}

	// 3.3是配置类则true
	return true;
}
parse

步骤六解析

for (BeanDefinitionHolder holder : configCandidates) {
	BeanDefinition bd = holder.getBeanDefinition();
	// 6.1判断bd是否为自定义启动类-配置类
	if (bd instanceof AnnotatedBeanDefinition) {
        // 解析启动类-配置类
		parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
	}
    
	// 6.2bd是否为Spring自带的类
	else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
		parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
	}
				
   	else {
		parse(bd.getBeanClassName(), holder.getBeanName());
	}

// 6.3 这里就是解析SpringBoot自动装配-@Import(AutoConfigurationImportSelector.class)之处
this.deferredImportSelectorHandler.process();

解析启动类-配置类parse -->> processConfigurationClass

// 如果启动类-配置类被Conditional注解修饰了,则有可能需要skip
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), 	     ConfigurationPhase.PARSE_CONFIGURATION)) {
	return;
}

// 6.1.1当前类是否被其他类@Import过,如果是则不处理
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
	if (configClass.isImported()) {
		if (existingClass.isImported()) {
			existingClass.mergeImportedBy(configClass);
		}
		return;
	}
	else {
		this.configurationClasses.remove(configClass);
		this.knownSuperclasses.values().removeIf(configClass::equals);
    }
}


// 6.1.2简单的包装,因为类可能有父类,如果有父类则循环去解析,这里的sourceClass是全路径名
SourceClass sourceClass = asSourceClass(configClass);
do {
	// 6.1.3解析
	sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);

6.1.3 doProcessConfigurationClass

// 1.如果当前类被@Component直接修饰
// @Configuration注解内部也含有@Component,所以我们的启动类-配置类会进入方法
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
	//被@Component修饰的类,可能含有内部类,内部类上也可以加@Component注解,所以此方法需要递归处理
	//正常情况下@Component修饰的类,不会再定义内部类,所以方法体一般不会执行
	processMemberClasses(configClass, sourceClass);
}

// 2.如果当前类被@PropertySources注解修饰
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
	sourceClass.getMetadata(), PropertySources.class,
	org.springframework.context.annotation.PropertySource.class)) {
	// 则解析注解描述的路径下的文件内容,将其加载到bf中的multiplePropertySource属性下
	// (eg:@PropertySource("classpath:redis.properties") )
	if (this.environment instanceof ConfigurableEnvironment) {
		processPropertySource(propertySource);
	}
}

// 3. 如果类上添加了@ComponentScans、@ComponentScan注解,则扫描basePackages下的所有bean类,转换成ConfigurationClass配置类
// 因为basePackages下的bean类上除了加(@Controller、@Service、@Repository、@Mapper、@Component注解外也有可能加@ComponentScan)所以这里需要循环处理
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
		sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
		!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    
	for (AnnotationAttributes componentScan : componentScans) {
        // 解析注解指定包下的所有组件bd
		Set<BeanDefinitionHolder> scannedBeanDefinitions =
				this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
		for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
			BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
			if (bdCand == null) {
				bdCand = holder.getBeanDefinition();
			}
            
			// 再判断basePackages下扫描出来并加载到bdMap中的bd,是否也加了配置类注解的6种之一,如果是则需要递归处理
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
				// 正常情况下,我们自定义的注解如@Service修饰的类,不会再引入配置类注解
				parse(bdCand.getBeanClassName(), holder.getBeanName());
			}
		}
	}
}


// 4.解析@Import注解
// getImports找出被@Import注解修饰类,processImports并将他们实例化添加到集合中
processImports(configClass, sourceClass, getImports(sourceClass), true);

// 5. 处理@ImportResource注解,解析xml
AnnotationAttributes importResource =
		AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);

if (importResource != null) {
	String[] resources = importResource.getStringArray("locations");
	Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			
    for (String resource : resources) {
		String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
	}
}


// 6.处理@Bean注解
// 获取当前配置类中是否有被@Bean注解修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

		
// 7.Java8后接口的方法上可以加@Bean注解
		processInterfaces(configClass, sourceClass);

// 8. 如果配置类有父类,那么也会对父类进行解析
if (sourceClass.getMetadata().hasSuperClass()) {
}
@PropertyResource注解
  • ssrc/main/resources/redis.properties文件
spring.redis.host=127.0.0.1
spring.redis.password=123456
## 其它
  • 启动类-配置类
@Configuration
@PropertySource("classpath:redis.properties")
public class MyApplication { 
}
  • 源码解析
private void processPropertySource(AnnotationAttributes propertySource) {
    // 1.解析@PropertyResource注解中的value属性(可以是多个文件)
    // 这里只有一个文件{"classpath:redis.properties"}
	String[] locations = propertySource.getStringArray("value")
	for (String location : locations) {
		// 这个方法占位符的解析,防止xxx.properties的命名是${myName}.properties这种
		String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
        
		Resource resource = this.resourceLoader.getResource(resolvedLocation);
		// createPropertySource才是真正干活的方法,将redis.properties内容全部读取并添加到集合中
		// 但是只是将文件内容加载过来,并未对对象的属性进行赋值
		addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
	}
}
@ComponentScan注解
@Configuration
@ComponentScan(basePackages = "com.mjp")
public class MyApplication {
}

解析配置类@ComponentScan用户自定义注解类

package com.mjp.bean;
@Service
public class MyService {
}

componentScanParser.parse源码解析流程

1、这里调用了ComponentScanAnnotationParser#parse

// 整个方法分2步,步骤一:获取注解对应的属性值;步骤二:解析

Set<String> basePackages = new LinkedHashSet<>();
// 1.解析注解属性值basePackages,结果是String[]
// 我们只有只有一个{"com.mjp"}
String[] basePackagesArray = componentScan.getStringArray("basePackages");

// 2.根据获取到注解属性值,进行解析
return scanner.doScan(StringUtils.toStringArray(basePackages));

2、ClassPathBeanDefinitionScanner#doScan

Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
    // 整个方法分为2步:
	// 1.扫描basePackage,将其指定路径下的被@Service等注解修饰的类都找出来抽取为bd
	Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
   	
    // 2.为这些bd赋值属性
	for (BeanDefinition candidate : candidates) {
		//
	}
}
    
//3.返回basePackages下所有被所扫到的bds
return beanDefinitions;

3、包扫描findCandidateComponents -->> scanCandidateComponents

Set<BeanDefinition> candidates = new LinkedHashSet<>();
// 1.拼接包扫描路径 classpath*:com/mjp/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
	resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			
// 2.获取资源路径下的所有类(Xxx.class,那些没有被@Service等注解修饰的类即普通类也暂时被获取到)
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

for (Resource resource : resources) {
	// 3.创建元数据读取器(内含AnnotationMetadata类的注解信息)
	MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    
    // 4.判断元数据是否为候选组件(@Component、@Controller、@Service、@Respository、@Mapper)
	if (isCandidateComponent(metadataReader)) {
	// 4.创建bd并赋值属性
	ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
	sbd.setResource(resource);
	sbd.setSource(resource);
	if (isCandidateComponent(sbd)) {
		candidates.add(sbd);
	}		
}

// 返回最终找到的所有注解类
return candidates;
@Import注解

这里我们直接结合SpringBoot的自动装配解析

@SpringBootApplication
	@SpringBootConfiguration
		@Configuration
	@EnableAutoConfiguration
		@AutoConfigurationPackage
			@Import(AutoConfigurationPackages.Registrar.class)
		@Import(AutoConfigurationImportSelector.class)
	@ComponentScan
@SpringBootApplication(scanBasePackages = "com.mjp")
public class ApplicationLoader {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
        springApplication.run(args);
    }
}

processImports源码解析

processImports(configClass, sourceClass, getImports(sourceClass), true);
getImports

找到被@Import注解修饰的类

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	// 获取被@Import注解修饰的类
	Set<SourceClass> imports = new LinkedHashSet<>();
	// 为了递归处理
	Set<SourceClass> visited = new LinkedHashSet<>();
	// 收集被@Import注解修饰的类(此方法是递归方法)
	collectImports(sourceClass, imports, visited);
	return imports;// 主要是AutoConfigurationImportSelector
}
  • 第一此调用collectImports
collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) {
	// 1.递归第一次,sourceClass是ApplicationLoader启动类

	if (visited.add(sourceClass)) {
		// 2.获取sourceClass(ApplicationLoader)上的所有注解 :@SpringBootApplication注解
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			// 很明显@SpringBootApplication注解不是@Import注解
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				// 递归:第二次调用collectImports
                // 入参sourceClass赋值为annotation即@SpringBootApplication注解
				collectImports(annotation, imports, visited);
			}
		} 
        
		// 最终将被@Import注解修饰的类都放入集合(总共有2个)
		imports.addAll(
               sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}
  • 第二次调用collectImports
// 1.sourceClass是@SpringBootApplication注解

if (visited.add(sourceClass)) {
	// 2.获取sourceClass(SpringBootApplication)上的所有注解,for循环 + 递归
    // @SpringBootConfiguration,不含@Import,后续递归会结束
    // @ComponentScan,不含@Import,后续递归会结束
	// @EnableAutoConfiguration,for循环到此时,会再递归
    	//@AutoConfigurationPackage
    		//@Import(AutoConfigurationPackages.Registrar.class)
		//@Import(AutoConfigurationImportSelector.class)
	
    // 因为这里我们主要分析的是自动装配,所以就直接说结论
    // 获取到AutoConfigurationImportSelector和AutoConfigurationImportSelector
	for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			// 很明显@SpringBootApplication注解不是@Import注解
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				// 递归:第二次调用collectImports
                // 入参sourceClass赋值为annotation即@SpringBootApplication注解
				collectImports(annotation, imports, visited);
			}
		} 
        
		// 最终将被@Import注解修饰的类都放入集合(总共有2个)
    	// 用于自动装配的是:AutoConfigurationImportSelector
		imports.addAll(
               sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
processImports

1、实例化被@Import注解修饰的类

// 这里直接以用于完成自动装配的类AutoConfigurationImportSelector为例

for (SourceClass candidate : importCandidates) {
//是否为ImportSelector的实现类
	if (candidate.isAssignable(ImportSelector.class)) {
		Class<?> candidateClass = candidate.loadClass();
        
		// 1.实例化AutoConfigurationImportSelector
		ImportSelector selector = BeanUtils.instantiateClass(
            candidateClass, ImportSelector.class);

     	// 2.尝试(实际不是在这里)完成自动自动装配
		if (selector instanceof DeferredImportSelector) {
		this.deferredImportSelectorHandler.handle(
					configClass, (DeferredImportSelector) selector);
		}
  	}
}

2、完成自动装配

看似是在ConfigurationClassParser#handle -->> 中完成自动装配,实际不再此处

// deferredImportSelectors != null
if (this.deferredImportSelectors == null) {
	DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
	handler.register(holder);
    // 真正自动装配处,但是此时进不来
	handler.processGroupImports();
}
else {
    //走到了else流程
	this.deferredImportSelectors.add(holder);
}

真正调用handler.processGroupImports()处是在parse标题下6.3方法中

this.deferredImportSelectorHandler.process -->>

public void process() {
    // deferredImportSelectors != null
	List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
	this.deferredImportSelectors = null;
	if (deferredImports != null) {
		DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
		deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
		deferredImports.forEach(handler::register);
        // 自动转装配!
		handler.processGroupImports();
	}
}

handler.processGroupImports -->> grouping.getImports() -->> this.group.process -->>

AutoConfigurationImportSelector#process -->>getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {	
    // 1.自动装配
    // 获取自动配置类
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    // 2.过滤
    // 去重
	configurations = removeDuplicates(configurations);
	// 留下当前项目中所需要的装配类!!!
    // 你的服务没有用到Kafka、Redis等就将他们过滤掉、你的服务用到了MyBatis就会留下JDBC相关的装配类
    // 用到了@Controller就会留下webMVC、Servlet相关的装配类
	configurations = filter(configurations, autoConfigurationMetadata);
	return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations自动装配!!!

1)方法图示意

如图38所示
在这里插入图片描述

  • 就是将spring-boot-actoconfigure jar包下META-INF/spring.factories,文件中的所有内容加载存入到map中

  • 然后根据指定的key:EnableAutoConfiguration获取对应的val:100+配置类

2)方法体

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
    getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
return configurations;
  • getSpringFactoriesLoaderFactoryClass:获取目标key
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    // map的key:spring.factories中
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration
	return EnableAutoConfiguration.class;
}
  • loadFactoryNames : 获取map,并map…getOrDefault(key)
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    // 1.获取key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryClassName = factoryClass.getName();
    // 2.获取map :spring.factories内容
    // 3.map.getOrDefault(key) 
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
@Bean注解

1、自定义配置类

@Configuration
public class MyBeanClass {
	@Bean
	public Car createCar() {
		return new Car();
	}
}

2、源码解析

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
	// 1.获取配置类中被@Bean注解修饰的方法:createCar
    // 使用createCar作为bd的名称,后续再创建真正的Car对象
	Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
	return beanMethods;
}
  • ConfigurationClassPostProcessor此bfpp除了执行自己本身的postProcessBeanDefinitionRegistry方法外,还继承了父类的ppbf方法,在8-invokeBeanFactoryPostProcessors时执行

– >>postProcessBeanFactory -> enhanceConfigurationClasses

// Cglib动态代理!
// 为配置类设置beanClass属性为class com.mjp.MyApplication$$EnhancerBySpringCGLIB$$88ae87d1
enhanceConfigurationClasses(beanFactory);

//再注册一个bpp-ImportAwareBeanPostProcessor是InstantiationAwareBeanPostProcessorAdapter类型
// 这个bpp作用是在配置类populatevBean时,为其设置属性bf
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));

enhanceConfigurationClasses

//原启动类配置类
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);

// 代理类:class com.mjp.MyApplication$$EnhancerBySpringCGLIB$$8808c8ed
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
	beanDef.setBeanClass(enhancedClass);//为启动类-配置类,此bd设置beanClass属性为代理类
}

3、bean的创建

后文五、注解开发-@Bean


PropertySourcesPlaceholderConfigurer

1、作用

完成了添加嵌入值解析器,用于解析占位符"${redis.host}"

2、执行流程

在执行bfpp时,会执行此bfpp

PropertySourcesPlaceholderConfigurer#processProperties->doProcessProperties

beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);

3、使用

  • 这里只是添加了内嵌的值解析器,还未执行解析
  • 在初始化中使用解析器解析占位符,执行流程参考@Value注解的解析流程

法六、registerBeanPostProcessors

1、方法内容

在bean对象实例化之前,将bpp注册到bf中

  • 注册过程类似方法五中的bfpp,都是getBeanNamesForType根据Bp类型找到所有的bdName[]
  • 然后直接getBean,创建bean实例
  • 将实例bpp存入beanPostProcessors集合中
  • 不过这里的bpp注册到bf中,不是立马调用,而是在后续初始化initializeBean之前调用。

法七、initMessageSource

国际化(涉及到SpringMvc的,不同语言展示不同,就是使用这个进行解析区别)

  • 如果用户没有自定义,则使用Spring默认的DelegatingMessageSource处理message

法八、initApplicationEventMulticaster + 法十、registerListeners + 法十二、finishRefresh

创建多播器对象,放入bf

传统观察者模式

背景:

被观察者维护观察者们的信息,一旦被观察者触发了注册成功这件事,将后续一系列动作推送给观察者们

  • 抽象观察者
public interface RegisterSuccessObserver {
    void update(Long iphone);
}
  • 观察者们
@Service
public class IssueNewConsumerCouponObserve implements RegisterSuccessObserver{
    @Override
    public void update(Long iphone) {
        System.out.println("发送新人优惠券");
    }
}

@Service
public class SendMsgObserve implements RegisterSuccessObserver{
    @Override
    public void update(Long iphone) {
        System.out.println("发送注册成功短信");
    }
}
  • 被观察者
@RestController
public class UserAppControllerSubject {
    @Resource
    private RegisterService registerService;

    @Resource
    private List<RegisterSuccessObserver> registerSuccessObservers;

    public void func(Long iphone, String pwd) {
        // 注册
        boolean success = registerService.register(iphone, pwd);
        if (success) {
            for (RegisterSuccessObserver observer : registerSuccessObservers) {
                observer.update(iphone);
            }
        }
    }
}

Spring观察者模式

事件

1、定义

即传统观察者模式中,被观察者执行的动作(如上述register注册方法)。这里将被观察者的动作,抽象为一个事件,放入类中

2、具体事件类

  • EventObject类 -> 子类ApplicationEvent -> 子类ContextRefreshedEvent上线文刷新完毕事件(上线文刷新完毕就对应此动作)
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);//方法入参为事件类,观察者根据不同的事件,做不同的动作
}
监听器

1、定义

传统观察者模式中的观察者-可能有多个

2、具体的观察者接口|类

  • EventListener接口 -> 子接口ApplicationListener -> 实现类
多播器

1、定义

传统观察者模式中,被观察者中含有观察者们,一旦被观察者执行了某事件,则被观察者会循环遍历所有观察者,调用观察者对应的应对方法。这个循环调用的内容就多播器

2、内容

内含监听器集合

事件源

1、定义

传统观察者模式中,UserAppControllerSubject#func,这个func方法就是事件源,发布事件者

2、作用

  • 由func方法来发布这个事件即调用register方法
  • 一旦发布了事件,多播器就会执行,将事件播放出去
  • 监听器监听到事件后,做对应处理
优点

将传统的观察者模式中,被观察者和观察者进行解耦(实际上发布-订阅模式就是从观察者模式演变而来的一种解耦设计模式: 发布者不再维护订阅者们的信息 )


spring观察者处理流程

准备好N个事件
  • ApplicationEvent抽象类的各种子类
创建多波器-initApplicationEventMulticaster
// 初始化多播器(多播器中有监听器集合)
protected void initApplicationEventMulticaster() {
    //1.创建多播器
	this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    // 2、多播器注入bf
	beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
	}

1、多波器

1)类SimpleApplicationEventMulticaster

2)内容

  • 多播器中的属性要有监听器
父类AbstractApplicationEventMulticaster中有监听器集合,子类默认自动继承
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);

private class ListenerRetriever {

		// 监听器集合
		public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

		public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

		// 是否预过滤监听器
		private final boolean preFiltered;
}
  • 父类具有将监听器加入多播器的add方法,子类默认继承
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
	// 1.锁定监听器助手对象
	synchronized (this.retrievalMutex) {
		// 2.如果已经注册,则删除已注册的监听器对象,为了避免重复的监听器对象
		Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
		if (singletonTarget instanceof ApplicationListener) {
			this.defaultRetriever.applicationListeners.remove(singletonTarget);
		}
		// 3.多播器添加监听器对象
		this.defaultRetriever.applicationListeners.add(listener);
		// 4.跟新了,所以要清空监听器助手缓存map
		this.retrieverCache.clear();
	}
}
准备好N个监听器
  • 接口ApplicationListener各种实现类
将监听器注册到多播器中-registerListeners(法十)
protected void registerListeners() {
	// 1.方式一:从applicationListeners集合中获取监听器对象,并添加到多播器applicationEventMulticaster中
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// 2.方式二:从容器中获取所有实现了ApplicationListener接口的bdName
	// getBeanNamesForType这个方法在bfrpp和bpp中都有使用,根据接口类型查询对应的实现类bdNames
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		// 并添加到多播器applicationEventMulticaster中
		getApplicationEventMulticaster().addApplicationListenerBean(
           listenerBeanName);
	}

		
	// 3.获取早期的事件
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	// 如果早期事件不为空(prepareRefresh方法猴中默认为空),则事件源发布事件-获取多播器,广播早期事件
	this.earlyApplicationEvents = null;
	if (earlyEventsToProcess != null) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}
事件源发布事件-finishRefresh(法十二)

1、事件源发布事件

publishEvent(new ContextRefreshedEvent(this));
// publishEvent方法相当于事件源,参数是事件
  • 本质:获取多播器,将事件广播出去
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
  • invokeListener:多播器会循环调用监听器的处理方法
// 根据事件的类型ResolvableType
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 根据type和事件,获取对应的监听器们,并循环调用监听器的处理事件方法
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
	Executor executor = getTaskExecutor();
	if (executor != null) {
		executor.execute(() -> invokeListener(listener, event));
	}else {
		invokeListener(listener, event);
	}
}
// invokeListener中后续方法体:
listener.onApplicationEvent(event);//即多播器循环调用监听的处理事件方法
  • 监听器ApplicationListener接口的各个实现类,接收到事件后,会根据事件类型,通过if-else匹配具体处理事件的方法

Spring这里没有监听器,但是SpringBoot中有,我们上文提到的@Import注解在SpringBoot中的自动装配流程时,自动装配spring.factories文件中有监听器

org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

法九、onRefresh

1、方法内容

创建特定上下文子类中的特殊 Bean

2、SpringBoot场景

  • 创建ServletWebServerApplicationContext中的tomcatWebServerBeanFactory这个bean

  • SpringBoot启动时run–>> refreshContext -->> createWebServer -->>

// getBean创建tomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 利用tomcatServletWebServerFactory创建Tomcat tomcat = new Tomcat();
this.webServer = factory.getWebServer(getSelfInitializer());
				

法十、registerListeners

上文观察者

法十一、finishBeanFactoryInitialization

方法内容:实例化、初始化剩下的所有非懒加载的单例

setConversionService

1、作用

将类型转换的service设置到beanFactory中

2、场景

SpringMVC项目中,页面输入123 ,Spring会使用ConversionService,获取其内部的各种Converter,将内容转换为Integer

3、ConversionService中添加的Converter分类

  • Converter : S -> T
  • GenericConverter : 1 -> N(TypeDescriptor基本类型+包装类型)
  • ConverterFactory:N -> N

4、自定义Converter

1)目标:将江苏_徐州转换为Address,属于String -> Object

2)Converter分类:属于1->1即S->T,所以实现Converter 接口即可

public class MyConvertor implements Converter<String, Address> {
	@Override
	public Address convert(String source) {
		String[] split = source.split("_");
		Address address = new Address();
		address.setProvince(split[0]);
		address.setCity(split[1]);
		return address;
	}
}

3)xml中配置ConversionService

<bean id="myConvertor" class="com.mjp.conversion.MyConvertor"></bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
		<property name="converters">
			<set>
				<ref bean="myConvertor"></ref>
			</set>
		</property>
	</bean>

5、和上述自定义Editor的区别

  • 自定义Editor的setAsText(String addressText)方法只能是String类型
  • Converter类型不限制,只要是S->T类型的都可以转换

freezeConfiguration

1、作用

冻结所有的bd,因为接下来就是实例化bean了。至此,所有的bd不能再被修改了


preInstantiateSingletons

总体概述:1.getBean -> 2.doGetBean -> 3.createBean -> 4.doCreateBean,后续方法前的标签即表示所隶属的上述四个中的哪个阶段

0.getMergedLocalBeanDefinition

1、内容

如果某个bd有有父bd,则通过此方法将此bd转换成RootBD,并且将其缓存至mergedBeanDefinitions。

后续在实例化此bd时,会直接从缓存中获取此RootBD,会先创建父类,再创建子类。

2、实战

1)bean.xml

<bean id="abstractParent" abstract="true">
	<property name="name" value="mjp" />
	<property name="age" value="30" />
</bean>

<bean id="myChild" class="com.mjp.bean.MyChild" parent="abstractParent">
	<property name="name" value="son"/>
	<property name="height" value="180"/>
</bean>
  • abstract标签,表明这个bean是抽象的,不需要具体的定义或实现
  • parent标签,指明当前bean的父bean。这样myChild就同时具有了自己的属性,以及父bean-abstractParent的age属性

2)MyChild

public class MyChild {
	private String name;
	private Integer age;
	private Integer height;
	// 省略getter、setter、toString
}

3)测试

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("txt.xml");

MyChild myChild = (MyChild) classPathXmlApplicationContext.getBean("myChild");
System.out.println(myChild);//MyChild{name='son', age=30, height=180}

4)作用

abstract标签的作用有些类似于抽象类,一般就是作为模板或者抽取出公共的属性字段,然后让子类继承就可以了

  • 注意
1、可以定义“抽象”父类
public class AbstractParent {
    private String name;
    private Integer age;
 
    //省略getter和setter方法
}
2、也可以不定义AbstractParent
3、代码层面子类MyChild不需要extends父类

5)综上:这里就是将abstract对应的bean的属性赋值给将它作为父类的bean中。实现子类合并父类bean属性


0.isFactoryBean
transformedBeanName

转换bean名称子类

  • 如果bean实现了FactoryBean,bean的名称为&beanName,需要去掉前缀
  • 或者有的bean有aliases别名,也需要解析为规范名称
getSingleton

从1-3级缓存中获取对象

1、内容

判断当前bean是否实现了FactoryBean接口

2、作用

如果实现了FactoryBean接口,则创建对应的此实现了接口的bean

3、实战

public class MyFactoryBean implements FactoryBean<Car> {
	@Override
	public Car getObject() throws Exception {
		return new Car();
	}

	@Override
	public Class<?> getObjectType() {
		return Car.class;
	}
}
<bean id="myFactoryBean" class="com.mjp.factorybean.MyFactoryBean"></bean>

4、啥时候创建的Car对象,即何时会调用MyFactoryBean#getObject

//1.先通过getBean方式,创建FactoryBean-这个对象必须先创建
Object bean = getBean("&" + beanName);

// 2.只有使用了下面方式,才会创建MyFactoryBean<Car> Car对象
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("txt.xml");
Car car = (Car) context.getBean("myFactoryBean");
  • getBean(“myFactoryBean”)-> getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean ->getObject()

5、二者存放位置

  • 创建好的MyFactoryBean对象,是放在一级缓存singletonObjects中
  • 如果需要且创建了的Car对象,是放在factoryBeanObjectCache中(key:myFactoryBean,val:Car对象),也是交由Spring管理,但不是在一级缓存中的

6、其他

中T对象默认是单例的,可以通过重写FactoryBean的方法返回false

则第一次调用getObject创建对象后,Spring不会帮我们管理放入factoryBeanObjectCache缓存中,缓存中没有,则后续每次都会创建新的T


2.getDependsOn

1、作用

保证当前bean所依赖的bean先初始化

2、实战

先递归执行依赖bean的创建

1)场景:depends-on标签

<bean id="myFactoryBean" class="com.mjp.MyFactoryBean" depends-on="myChild"></bean>

2)作用

  • xml中bean的定义顺序 决定了bean的创建和销毁顺序
a
b
  • 如果想改变bean的创建顺序,先b再a,正常情况下需要挪动bean.xml中b、a。这样很不方便
  • 此时可以使用depends-on标签,表明a依赖b,那么在创建a时,就会先去创建b

2.getSingleton
// 从一级缓存中获取bean
sharedInstance = getSingleton(beanName, () -> {					
	return createBean(beanName, mbd, args);	
});
  • 函数式接口

第二个参数,是个函数式接口。当getSingleton方法,内部执行到singletonFactory.getObject()方法时,就会调用这个函数式接口方法执行createBean

beforeSingletonCreation

1、内容

singletonsCurrentlyInCreation记录当前beanName正在被创建中,后续三级缓存会用

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

对应的,当bean完整创建完成后,会调用afterSingletonCreation

2、作用

bean的初始化和实例过程如图9

在这里插入图片描述

  • 如果在创建过程中时,想getBean,这个时候先尝试去一级缓存中取,一级缓存中没有,再通过singletonsCurrentlyInCreation来判断,当前bean是在创建过程中尚未完成创建。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先从一级缓存中取(只有创建结束,才会存入一级缓存)
	Object singletonObject = this.singletonObjects.get(beanName);
    // 一级缓存中取不到(但是可能在创建过程中),再判断是否在创建中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 再尝试去二级缓存中取
		singletonObject = this.earlySingletonObjects.get(beanName);
         // 取不到
		if (singletonObject == null && allowEarlyReference) {
            // 再从三级缓存中取(创建过程中:bean实例化之后,populateBean之前会先加入三级缓存)
			ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);	
		}
	}
	return singletonObject;
}
  • 为什么不通过为bean上加个属性来描述bean的创建状态(创建中、创建完成)

    如果是这种方式,那我就需要遍历所有的bd来判断哪些创建完成了。不如直接通过集合判断简单-isSingletonCurrentlyInCreation(beanName)


3.resolveBeanClass

1、内容

根据RootBeanDefinition + BeanName生成Class对象

2、源码解析

resolveBeanClass -> doResolveBeanClass ->resolveBeanClass -> 
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
  • 本质反射,从RootBeanDefinition的className属性:字符串"com.xx"获取对应的Class对象

3.prepareMethodOverrides

1、内容

当beanDefinition中包含了lookup-method 或 replace-method标签时,会产生覆盖操作

2、作用

1)lookup-method标签:解决单例对象中引用原型对象,保证原型对象每次都是新对象

  • 单例对象不需要每次都创建,第一次创建完成后就放在一级缓存中。
  • 而原型对象每次使用的时候都会重新创建。如果想在单例中引用原型对象,就必须使用lookup-method标签

2)举例

  • 单例类
@Data
public class Apple{
	private Banana banana;
}
  • 原型类
public class Banana{
}
  • xml
<bean id="apple" class="com.mjp.lookupmethod.Apple">
	<property name="banana" ref="banana">
	</property>
</bean>

<bean id="banana" class="com.mjp.lookupmethod.Banana" scope="prototype"></bean>
  • 测试
public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
        "txt.xml");
    
	Apple apple = (Apple) context.getBean("apple");
	System.out.println(apple.getBanana());
	System.out.println(apple.getBanana());
}
	
输出:
@Banana123
@Banana123
  • 问题描述

Apple是单例的,Banana是原型的,apple中引用了banana,即使你banana是原型的,但是每次获取的时候仍是相同的对象(因为一级缓存缓存Apple的时候,把其属性Banana一起缓存了,后续就不会再创建新的Banana了)。

如果想每次apple.getBanana()是不同的对象即满足原型,则必须使用lookup-method标签,此标签的作用就是:单例对象中引用原型对象

3、lookup-method实战

public class Fruit {
}

public class Apple extends Fruit{
	public Apple() {System.out.println("apple");}
}

public class Banana extends Fruit{
	public Banana() {System.out.println("banana");}
}

public abstract class FruitPlate {
	public abstract Fruit getFruit();
}
  • xml
<bean id="apple" class="com.mjp.lookupmethod.Apple"></bean>
<bean id="banana" class="com.mjp.lookupmethod.Banana"></bean>

<bean id="fruitPlateApple" class="com.mjp.lookupmethod.FruitPlate">
	<lookup-method name="getFruit" bean="apple"></lookup-method>
</bean>

<bean id="fruitPlateBanana" class="com.mjp.lookupmethod.FruitPlate">
	<lookup-method name="getFruit" bean="banana"></lookup-method>
</bean>
  • 测试
public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("txt.xml");
		
   FruitPlate apple = (FruitPlate) context.getBean("fruitPlateApple");
	apple.getFruit();

	FruitPlate banana = (FruitPlate) context.getBean("fruitPlateBanana");
	banana.getFruit();
}
	
输出:
apple
banana

4、源码解析

  • 当bn为fruitPlateApple和fruitPlateBanana时,会获取MethodOverride对象,此对象的name属性对应lookup-method标签中的name值,bean属性同理。

    最关键的是此方法会将MethodOverride对象的overload属性设置为false

mo.setOverloaded(false);
  • 后续实例化createBeanInstance->instantiateBean时,会获取实例化策略方法getInstantiationStrategy

    根据是否有lookup-method 和 replace-method获取对应的索引下标值

public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {

	// 如果bd没有override的覆盖,则索引位置为0
	private static final int PASSTHROUGH = 0;

	// 如果bd有lookup-method标签的覆盖,则索引位置为1
	private static final int LOOKUP_OVERRIDE = 1;

	// 如果bd有replace-method标签的覆盖,则索引位置为2
	private static final int METHOD_REPLACER = 2;
	
}
  • 获取到对应的索引值后,会根据索引下标值,从CALLBACK_TYPES数组中获取对应的Interceptor或不用拦截器NoOp
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
				{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
  • 获取到对应的Interceptor或不用拦截器后 + 也通过getInstantiationStrategy获取到了实例化策略,则开始执行实例化instantiate,此时会setCallBack的类型
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// 实例化的时候,假如我们的bd有looup-method标签,则走else
		if (!bd.hasMethodOverrides()) {
			
		}
		else {
			// Must generate CGLIB subclass.必须生成cglib子类
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}
  • CGLIB动态代理!!!,看到enhance就想到Cglib动态代理

1)方法调用流程

instantiateWithMethodInjection ->

子类CglibSubclassingInstantiationStrategy#instantiateWithMethodInjection ->

instantiate ->

createEnhancedSubclass -> enhancer.setCallbackTypes(CALLBACK_TYPES);

上述instantiate执行完成后,一级缓存singletonObject中的key:fruitPlateApple,value:Cglib动态代理的FruitPlateApple如图10所示

在这里插入图片描述

2)拦截器

  • 当执行fruitPlateApple.getFruit()方法时
  • 执行CglibSubclassingInstantiationStrategy#intercept,通过拦截器的方式,每次创建的时候,如果Apple是单例则从一级缓存中取
  • 如果Apple是原型,则会创建新的对象,成功解决了单例对象中引用原型对象问题
this.owner.getBean(lo.getBeanName())
其中lo.getBeanName()即apple
然后再执行getBean("apple")流程即可创建Apple对象

3.resolveBeforeInstantiation

1、内容

在doCreateBean之前给InstantiationAwareBeanPostProcessor接口实现类一个机会,让其返回目标bean的动态代理对象(是用户自定义的动态代理方式,而非Spring的AOP)

2、作用

当在创建bean时,在执行doCreateBean之前,会去判断,当前bf中是否有InstantiationAwareBeanPostProcessor-此bpp接口的实现类。如果有尝试使用bpp的实现类去创建bean

3、实战

  • 待创建的Bean
public class NotThroughDoCreateBeanMethod {
	public void eat() {
		System.out.println("eat");
	}
}
  • 自定义InstantiationAwareBeanPostProcessor接口实现类
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		if (beanClass == NotThroughDoCreateBeanMethod.class) {
			// 通过Cglib动态代理的方式,返回目标对象的代理对象
			Enhancer enhancer = new Enhancer();
            // 设置动态代理类(子类)的父类(目标类),cglib动态代理基于继承
			enhancer.setSuperclass(NotThroughDoCreateBeanMethod.class);
            // 设置回调,即设置Interceptor拦截
			enhancer.setCallback(new MyMethodInterceptor());
            // 创建cglib动态代理类(即目标类的子类)
			NotThroughDoCreateBeanMethod proxy = (NotThroughDoCreateBeanMethod) enhancer.create();
			return proxy;
		}
		return null;
	}
}
  • 自定义Cglib方法拦截器
public class MyMethodInterceptor implements MethodInterceptor {
	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		// 其中o是代理对象,method是目标类要拦截的方法
        // 这里可以在调用目标对象方法之前、后,做方法增强
		// 这里会调用动态代理类的父类(目标类)中的方法
		return methodProxy.invokeSuper(o, objects);
	}
}
  • xml
<bean id="notThroughDoCreateBeanMethod" class="com.mjp.beanpostprocessor.NotThroughDoCreateBeanMethod">
</bean>
        
<bean id="myInstantiationAwareBeanPostProcessor" class="com.mjp.beanpostprocessor.MyInstantiationAwareBeanPostProcessor">
</bean>
  • 测试类
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("txt.xml");

NotThroughDoCreateBeanMethod bean = context.getBean(NotThroughDoCreateBeanMethod.class);
bean.eat();

4、源码分析

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
	Object bean = null;
	if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
		// bf中有InstantiationAwareBeanPostProcessor接口的实现类
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			Class<?> targetType = determineTargetType(beanName, mbd);
			//如果实现类中通过cglib返回此bd对应的bean的代理对象,则直接返回代理对象
			bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
		}
			mbd.beforeInstantiationResolved = (bean != null);
	}
		return bean;
}
  • 当执行resolveBeforeInstantiation时,会判断是否有InstantiationAwareBeanPostProcessor接口的实现类
  • 如果有,则执行其postProcessBeforeInstantiation方法。
  • 用户可以在before方法中,通过Cglib、Jdk动态代理创建代理对象,并指定目标对象的拦截器

Spring中五种创建对象方式

1、resolveBeforeInstantiation

2、doCreateBean中反射方式-正常的bd都使用这种形式完成创建

3、FactoryBean

4、FactoryMethod

@Bean
public Car createCar() {
	return new Car();
}
  • doCreateBean -->> createBeanInstance
if (mbd.getFactoryMethodName() != null) {
	return instantiateUsingFactoryMethod(beanName, mbd, args);
}

5、createBeanInstance-Supplier

  • 定义待创建的类
public class Demo1 {
	private String name;
	public Demo1(String name) {
		this.name = name;
	}
	public Demo1() {
	}
	//省略get、set
}
  • 定义获取待创建类的类
public class CreateDemo1 {
	public static Demo1 createDemo1() {
		return new Demo1("mjp");
	}
}
  • 定义Bpp
public class MySupplierPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		BeanDefinition bd = beanFactory.getBeanDefinition("demo1");
		// 这里需要强转为gbd,原因是只有gbd才可以setInstanceSupplier,RootBeanDefinition不可以
		GenericBeanDefinition gbd = (GenericBeanDefinition) bd;
		// setInstanceSupplier方法入参为Supplier函数式接口,() -> T, 这里是CreateDemo1::createDemo1
		gbd.setInstanceSupplier(CreateDemo1::createDemo1);
		gbd.setBeanClass(Demo1.class);
	}
}
  • xml
<bean id="demo1" class="com.mjp.beanfactorypostprocessor.supplier.Demo1"></bean>
<bean class="com.mjp.beanfactorypostprocessor.supplier.MySupplierPostProcessor"></bean>
  • 测试类
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("txt.xml");
  • 源码解析

    doCreateBean -->>createBeanInstance -->>

// 判断bd是否含有实例供应器,此处相当于一个回调方法,通过回调方法即函数接口,创建bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
  return obtainFromSupplier(instanceSupplier, beanName);
 }

obtainFromSupplier -->>Object instance = instanceSupplier.get() -->> supplier.get()即调用函数式接口

会回调gbd.setInstanceSupplier(CreateDemo1::createDemo1)中的函数式接口CreateDemo1::createDemo1创建demo1对象


4.createBeanInstance

1、方法作用

使用适当的实例化策略为指定的 Bean 创建新实例:工厂方法factoryMethod、Supplier、构造函数@Autowired或简单实例化(默认使用此方式)

2、源码分析

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	// 1.获取class对象
	Class<?> beanClass = resolveBeanClass(mbd, beanName);

	// 2.判断bd是否含有实例供应器,此处相当于一个回调方法,通过回调方法创建bean
	// 对应Spring创建对象方式中的Supplier函数式接口
   	// 正常情况下不会使用这种方式
	Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
	if (instanceSupplier != null) {
		return obtainFromSupplier(instanceSupplier, beanName);
	}

	// 3.如果工厂方法不为空,则使用工厂方法来实例化bean
	// 正常情况下不会使用这种方式,@Bean方式是这种
	if (mbd.getFactoryMethodName() != null) {
		return instantiateUsingFactoryMethod(beanName, mbd, args);
	}

	// Shortcut when re-creating the same bean...
	boolean resolved = false;
	boolean autowireNecessary = false;
    
	// 4.类可能有多个构造函数,有参无参,有参也可能有多个。所以Spring要根据参数的个数、类型来确定要调用的构造函数
	// 在使用构造函数完成实例化时,Spring会将此构造器保存在缓存中,这样再创建相同bean时,Spring就不用上述确定构造器过程了,直接知道使用哪个构造器(工厂方法同理)
	if (args == null) {
		synchronized (mbd.constructorArgumentLock) {
		// 4.1当Spring确定了构造器时,会将其缓存在resolvedConstructorOrFactoryMethod字段中,resolved=true
			// 通常走不进来,所有resolved仍为false,所以4.2也不会走
			if (mbd.resolvedConstructorOrFactoryMethod != null) {
				resolved = true;
				autowireNecessary = mbd.constructorArgumentsResolved;
			}
		}
	}
		
    // 5.如果之前已经解析过构造器了,不需要再次确认过程了
	if (resolved) {
		if (autowireNecessary) {
			return autowireConstructor(beanName, mbd, null, null);
		}
		else {
			// 实例化
			return instantiateBean(beanName, mbd);
		}
	}

	// 4.默认:使用无参构造器,实例化bean
	return instantiateBean(beanName, mbd);
}

4.instantiateBean

方法内容

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	// 1.先获取实例化策略,再实例化
	Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
	// 2.将实例对象包装成BeanWrapper
	BeanWrapper bw = new BeanWrapperImpl(beanInstance);
	// 3.设置editor 和 convertor
	initBeanWrapper(bw);
	return bw;
}
getInstantiationStrategy

1、作用

获取实例化策略

2、代码

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

return 	this.instantiationStrategy;

3、实例化策略分类

如图11所示父类SimpleInstantiationStrategy3种(对应3个重载的instantiate方法),子类CglibSubclassingInstantiationStrategy2(对应2个重载的instantiateWithMethodInjection方法)种,共计5种实例化策略

在这里插入图片描述

4、注意

虽然getInstantiationStrategy方法返回的是new CglibSubclassingInstantiationStrategy()策略类。

但是实例化的策略不一定就是Cglib动态代理的对象(无参+有参构造),还有可能是其父类SimpleInstantiationStrategy中的3种策略(子类默认继承父类的方法)


instantiate

1、实例化(先getInstantiationStrategy获取实例化策略,再实例化)

2、代码

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); 
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
	// 判断是否有methodOverrides列表(如果bd定义中有lookup-method标签 或 replace-method标签)则列表不为空
	// 1.正常情况下无,!无,则true
	if (!bd.hasMethodOverrides()) {
		Constructor<?> constructorToUse;
		synchronized (bd.constructorArgumentLock) {
			// 2.如果此bean之前已经解析过使用哪个构造器了,这里直接从resolvedConstructorOrFactoryMethod字段中获取缓存的构造器即可
			constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            // 正常情况下true
			if (constructorToUse == null) {
				final Class<?> clazz = bd.getBeanClass();
				try {
					if (System.getSecurityManager() != null) {
						constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
					else {
                       // 3.获取构造器
						constructorToUse = clazz.getDeclaredConstructor();
					}
					bd.resolvedConstructorOrFactoryMethod = constructorToUse;
				}
				catch (Throwable ex) {
					throw new BeanInstantiationException(clazz, "No default constructor found", ex);
				}
			}
		}
        
        // 4.执行ctor.newInstance()创建对象
		return BeanUtils.instantiateClass(constructorToUse);
		}
		
    else {
		// Must generate CGLIB subclass.
		// 5.bean定义信息中有lookup-method标签 或 replace-method标签,会调用子类Cglib的此方法,然后调用instantiate实例化对象
		// 返回的是一个Cglib动态代理的对象!
		return instantiateWithMethodInjection(bd, beanName, owner);
	}
}

initBeanWrapper

1、内容

  • 设置convertor
  • 注册Editor

2、代码

protected void initBeanWrapper(BeanWrapper bw) {
	bw.setConversionService(getConversionService());
	registerCustomEditors(bw);
}

3、分析

1)设置convertor

参考上文的setConversionService方法

2)注册Editor

参考上文prepareBeanFactory#addPropertyEditorRegistrar

  • 从bf获取customEditors并注册到PropertyEditorRegistrySupport的属性customEditors中

4、作用

这里通过initBeanWrapper为bw做了扩展

  • 扩展1:可以在初始化时,使用自定义的Editor对属性进行自定义赋值:setAsText
  • 扩展2:自定义Convertor,设置属性转换器1 -> 1(T->R)、1-> N ,N->N

4.applyMergedBeanDefinitionPostProcessors

1、内容

  • xml形式
public class Demo2 {
	public void myInit() {	
	}
}
<bean id="demo2" class="com.mjp.beanpostprocessor.init.Demo2" init-method="myInit"></bean>
  • 注解形式1
@Component
public class Demo2 {
	@Resource
    private Demo3 demo3;
    
    @Autowired
    private Demo4 demo4;
    
    @Value("${redis.port}")
    private Integer port;
    
	@PostConstruct
	public void myInit() {
	}
    
    @PreDestroy
	public void myInit() {
	}
}


@Component
public class Demo3 {
}

@Component
public class Demo4 {
}
<context:component-scan base-package="com.mjp.beanpostprocessor.init"></context:component-scan>
这里所有Demo234类都在init包下
  • 注解形式2
@Configuration
public class MyApplication {

	@Bean(initMethod = "myInit")
	public Car getCar(){
		return new Car();
	}
}
public class Car{
	public String myInit(){
		return "xxx";
	}
}

2、作用

bean实例化之后,使用bpp-MergedBeanDefinitionPostProcessor接口的相关实现类完成bd属性的设置

比如后续执行被@PostConstruct注解修饰的myInit方法

  • CommonAnnotationBeanPostProcessor类:找到此bean中被@Resource修饰的属性和方法

    以及其父类InitDestroyAnnotationBeanPostProcessor:找到此bean中被@PosConstructor注解、@PreDestroy注解修饰的tinit方法 、destroy方法和被注解

  • AutowiredAnnotationBeanPostProcessor实现类:处理@Autowired注解和@Value,并将被此注解修饰的属性和方法设置到bd中

这里只是将注解修饰的属性内容设置到bd中,后续populateBean的时,才会完成初始化赋值

3、@PostConstructor注解源码解析

1)解析注解修饰的方法

bpp实现类:CommonAnnotationBeanPostProcessor类#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	// 1.处理@PostConstruce注解 和 @PreDestroy注解并将方法名称设置到bd中
	super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
	// 2.这里是处理@Resource注解的
	InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
	// 3.将@Resource注解修饰信息设置到bd中
	metadata.checkConfigMembers(beanDefinition);
}
  • 这里主要分析1中@PostConstruce注解 和 @PreDestroy注解
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	// 1.获取此bean的生命周期元素(initMethods、targetClass、destroyMethods)				  
    LifecycleMetadata metadata = findLifecycleMetadata(beanType);
    
	// 2.将initMethods信息和destroyMethods信息,设置到bd属性字段中(externallyManagedInitMethods、externallyManagedDestroyMethods)
	metadata.checkConfigMembers(beanDefinition);
}
  • findLifecycleMetadata(找到此bean中被@PostConstructor和@PreDestroy注解修饰的方法)

    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
        
    	// 1.获取metadata构建生命周期元素
    	metadata = buildLifecycleMetadata(clazz);
        
        // 2.将获取到的metadata存入缓存,后续populateBean填充属性时,会再次调用findAutowiringMetadata方法,那时就可以直接从缓存中取了
    	this.lifecycleMetadataCache.put(clazz, metadata);
    	return metadata;
    }
    
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
	// 1.实例化后的回调方法-@PostConStruct
	List<LifecycleElement> initMethods = new ArrayList<>();
	// 2.销毁前的方法@PreDestroy
	List<LifecycleElement> destroyMethods = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
		final List<LifecycleElement> currInitMethods = new ArrayList<>();
		final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

		// 3.反射获取当前clazz的所有方法Method[] methods(构造函数、@postConstructor修饰的自定义方法、getter、setter、toString等)
		// 并依次让其调用下面方法第二个参数的lambda表达式
		// 我们主要关注被@postConstructor注解修饰的自定义方法
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			// 3.1当前方法的注解中是否包含了@PostConstrctor注解
			if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
				// 如果有,则将其封装成LifecycleElement(方法名称)元素,并存储起来
				LifecycleElement element = new LifecycleElement(method);
				currInitMethods.add(element);
			}
            
			// 3.2当前方法的注解中是否包含了@PreDestroy注解
			if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
				currDestroyMethods.add(new LifecycleElement(method));
			}
		});

		// 4.将本次clazz对象中所有被@PostConstuctor注解修饰的方法封装成的LifecycleElement元素添加到集合中
		initMethods.addAll(0, currInitMethods);
		destroyMethods.addAll(currDestroyMethods);

		// 5.获取父类方法,再次循环处理(父类方法中也可能包含上述两个注解修饰的方法,也需要处理)
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}

2)将注解修饰的方法信息,设置到bd对应的属性中

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	// 1.获取此bean的生命周期元素(initMethods、targetClass、destroyMethods)				 
    LifecycleMetadata metadata = findLifecycleMetadata(beanType);
	// 2.将initMethods信息和destroyMethods信息,设置到bd属性字段中(externallyManagedInitMethods、externallyManagedDestroyMethods)
	metadata.checkConfigMembers(beanDefinition);
}

上述注解的解析是1,而解析注解方法后,通过2.将注解修饰的方法名称、定义等信息设置到bd中

3)init方法被调用前分析

@PostConstructor注解修饰的init方法,方法调用不在这儿而是

  • populateBean初始化
  • 设置aware接口属性
  • bpp-before(内含:init-method,在此处被调用InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization)
  • bpp-after

4)其它注解修饰的属性被初始化时机

  • CommonAnnotationBeanPostProcessor的@Resource修饰属性被注入的时机
  • AutowiredAnnotationBeanPostProcessor的@Autowired注解修饰的属性 和 @Value被注入的时机

populateBean#postProcessProperties#findAutowiringMetadata时,直接从缓存injectionMetadataCache中获取被注解修饰的属性类信息metadata,完成初始化赋值


4.addSingletonFactory

1、作用

解决循环依赖问题

2、内容

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  • lambda表达式

    () -> getEarlyBeanReference(beanName, mbd, bean)

  • addSingletonFactory

    this.singletonFactories.put(beanName, singletonFactory);
    
    • 提前将key(beanName)-val(用于创建代理对象的lambda表达式即函数式接口),存入三级缓存中
    • 后续在获取循环依赖的对象时,可直接从三级缓存中拿lambda表达式创建对象,无需再getBean创建
  • 回调机制
    当执行singletonFactory.getObject时,就会调用getEarlyBeanReference方法

getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
    //AOP:使用bpp,为实例化后初始化之前的bean,创建代理对象。
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
		if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
			SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
			exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
		}
	}
    
    // 没有AOP动态代理时,直接返回exposedObject
	return exposedObject;
}

AOP动态代理:

Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new   SingletonTargetSource(bean));

return proxy;

创建代理对象的时机

如图14所示,对象B和C都需要创建代理对象

在这里插入图片描述

1)场景1

对象a中属性对象b需要生成代理对象,则在a初始化赋值时,检测到b对象需要被代理,则此时直接生成b的代理对象即可

2)场景2

对象c在整个bean的生命周期内,都没有别的对应引用它。检测到c对象需要生成代理对象,则在初始化过程中的最后bpp-after方法中生成c的代理对象


4.populateBean

1、方法作用

填充属性值(属性分为:基本类型和引用类型)

注入类型

1、引用类型

在给beanA填充属性时,如果属性的类型是引用类型B时,且有多个相同类型的值(B b1、B b2),就需要指明按照注入的类型,是按照名称注入(b1、b2),还是按照类型注入(B)

public class Demo6 {
	private String myName;
	private Address myAddress;
}
  • xml配置
<bean id="myAddress" class="com.mjp.bean.edit.Address">
	<property name="province" value="js"></property>
	<property name="city" value="xz"></property>
</bean>

<bean id="demo6" class="com.mjp.populate.Demo6" autowire="byName">
	<property name="myName" value="mjp"></property>
	<property name="myAddress" value="myAddress"></property>
</bean>

这里autowire注入类型有5种:byName、byType、no(默认)、default、constructor


DI依赖注入-方法解析

1、类

@Service
public class Demo4 {

	@Autowired
	private Demo5 demo5;
}

@Service
public class Demo5 {
}

2、xml

<context:component-scan base-package="com.mjp.populate"></context:component-scan>

3、源码

1)populateBean整体流程:简化版(主要看步骤二autowireByName、步骤三postProcessProperties)

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

	// 步骤一:将对象属性name、属性val封装为PropertyValues对象
	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

	// 步骤二:判断注入类型(默认注入类型为no)
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
		MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
		// 2.1根据名称自动注入:byName
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
			autowireByName(beanName, mbd, bw, newPvs);
		}
		
		// 2.2根据类型自动注入(内容同上):byType
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}
		pvs = newPvs;
	}

	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
	boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

	PropertyDescriptor[] filteredPds = null;
	if (hasInstAwareBpps) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
        
		// 步骤三:遍历所有InstantiationAware相关的bpp,调用postProcessProperties方法完成相关注解的属性赋值
		// 这里可以自定义注解,然后通过bpp,进行属性赋值
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				// 3.1执行(AutowiredAnnotationBeanPostProcessor完成@Autowired和@Bean注解修饰的属性赋值
				// CommonAnnotationBeanPostProcessor的@Resource修饰的属性的inject
				// PropertyValues类即name、val
				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				pvs = pvsToUse;
			}
		}
	}

	if (pvs != null) {
		// 步骤四:上述通过bpp的postProcessProperties方法完成了@Autowired或@Resource的DI,但是常规的比如我们Demo6类在xml中定义property标签完成属性值赋值
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

2)步骤二:

这里以byName为例解析autowireByName

protected void autowireByName(
	String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

	// 2.1 找到bean中需要依赖注入的属性(即非简单的属性:Map、Set、List、对象等)
	String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
	for (String propertyName : propertyNames) {
		if (containsBean(propertyName)) {
			// 2.2 创建属性bean:getBean-doGetBean-createBean-doCreateBean
			Object bean = getBean(propertyName);
			pvs.add(propertyName, bean);	
		}
	}
}
  • 简单属性:isSimpleProperty方法定义:8中基本类型isPrimitive + Enum + CharSequence、Number、Date、URI、Locale
  • 非简单属性,就是除了简单属性外的其他属性:数组、集合、Map、对象等

3)步骤三:postProcessProperties

@Autowired注解

bpp:AutowiredAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 1.找到被@Autowired注解修饰的属性(这里可以直接从缓存中获取到)
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	
    // 2.执行属性注入
    metadata.inject(bean, beanName, pvs);
	return pvs;
}

1、findAutowiringMetadata

缓存injectionMetadataCache专门用于记录被@Autowired注解修饰的属性类

  • 上文applyMergedBeanDefinitionPostProcessors-postProcessMergedBeanDefinition方法执行时即实现类AutowiredAnnotationBeanPostProcessor-postProcessMergedBeanDefinition执行时会去解析被@Autowired注解修饰的属性,并记录到injectionMetadataCache中
  • 所以,在此处可以直接从缓存injectionMetadataCache中获取被@Autowired注解修饰的属性类信息

2、inject属性注入

会去调用AutowiredAnnotationBeanPostProcessor内部类AutowiredFieldElement#inject方法,完成依赖属性的创建-getBean和属性的赋值field.set,方法概述如下

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)  {
	// 2.1.获取filed属性(name:属性名称,type:属性类型)
	Field field = (Field) this.member;
	DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
	desc.setContainingClass(bean.getClass());
	Set<String> autowiredBeanNames = new LinkedHashSet<>(1);

	TypeConverter typeConverter = beanFactory.getTypeConverter();//默认是SimpleTypeConverter
	
    // 2.2从bf中获取依赖值:即获取被@Autowired注解修饰的bean
	Object value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    
	if (value != null) {
		ReflectionUtils.makeAccessible(field);
		// 2.3 通过反射set方式,为bean设置属性value内容
		field.set(bean, value);
	}
}

2.2 resolveDependency

获取依赖属性bean对象–>> doResolveDependency

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)  {
	// 1.查找与Filed属性type匹配的候选bean,构成map
    // key:beanName,val-Class@1665:class com.xxx不是真正的属性bean对象(真正的属性bean对象是Demo5@1994这种)
	// 正常情况map的size==1
	Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    
	Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
	// 2.属性的name
	String autowiredBeanName = entry.getKey();
	// 2.候选属性bean:具体内容为class com.xxx.
	Object instanceCandidate = entry.getValue();

	if (instanceCandidate instanceof Class) {
		// 3.获取属性对象:方法内容就是执行getBean,获取属性对象(doGetBean-createBean-doCreateBean)!!!
		instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
	}
    
	// 属性对象:Demo5@3250,这个才是真正的属性bean对象
	Object result = instanceCandidate;
	return result;
}

@Resource注解

若依赖属性是通过@Resource注解注入的

@Service
public class Demo4 {
	@Resource
	private Demo5 demo5;
}

则步骤三postProcessProperties的解析如下:

这里就是CommonAnnotationBeanPostProcessor-bpp#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 1.找到被@Resource注解修饰的属性
	InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    
	// 2.inject
	metadata.inject(bean, beanName, pvs);
	return pvs;
}

1、findResourceMetadata

和@Autowired注解一样,都是直接从缓存中获取的被@注解修饰的依赖属性的内容信息。两个注解的区别主要是2、inject

– >> InjectionMetadata#inject

if (this.isField) {
    // 2.1获取属性
	Field field = (Field) this.member;

    // 2.2 创建属性对象、并完成属性赋值
	field.set(target, getResourceToInject(target, requestingBeanName));
}

2.2 getResourceToInject

–>> ResourceElement#getResourceToInject -->> getResource -->> autowireResource -->> getBean(doGetBean-createBean-doCreateBean)

//本质也是通过getBean的方式获取依赖属性bean对象Demo5@1234
Object resource = factory.getBean(name, element.lookupType);

设置非注解修饰的属性值

1、内容

public class Demo6 {
	private String myName;
	private Address myAddress;
	// 省略get、set
}
  • xml
<bean id="myAddress" class="com.mjp.bean.edit.Address">
	<property name="province" value="js"></property>
	<property name="city" value="xz"></property>
</bean>

<bean id="demo6" class="com.mjp.populate.Demo6" autowire="byName">
	<property name="myName" value="mjp"></property>
	<property name="myAddress" ref="myAddress"></property>
</bean>

2、分析步骤四populateBean-applyPropertyValues

resolveValueIfNecessary -->>

// 2.根据pv解析出originalValue封装的对象,即具体的依赖属性值(基本类型或引用类型)
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

1)依赖属性为简单类型:String

else if (value instanceof TypedStringValue) {
	//解析String类型,并获取依赖属性值(这里是通过LiteralExpression#getValue获取值)
    Object valueObject = evaluate(typedStringValue);
}

2)依赖的属性为引用类型:比如Address

// 是否为引用类型(属性为普通对象)
if (value instanceof RuntimeBeanReference) {
	RuntimeBeanReference ref = (RuntimeBeanReference) value;
	return resolveReference(argName, ref);
}

解析依赖属性值resolveReference -->> 并通过getBean创建依赖属性对象

3)其它类型

同理解析其它Map-ManagedMap、List-ManagedList、数组-ManagedArray等引用类型

4)属性赋值

最后将解析出来的全部依赖属性都添加都集合中,并且统一遍历集合中的依赖元素,将依赖属性值赋值给对象

// 3.将解析出来的依赖属性值添加到深拷贝集合中
deepCopy.add(new PropertyValue(pv, convertedValue));

// 4.完成全部的依赖属性赋值
bw.setPropertyValues(new MutablePropertyValues(deepCopy));

setPropertyValue – >> processLocalProperty -->> ph.setValue(valueToApply) -->> BeanWrapperImpl.BeanPropertyHandler#setValue – >>

writeMethod.invoke(getWrappedInstance(), value)
  • 其中writeMethod即对象的setXxx设置属性方法
  • value:即依赖属性对象bean
  • getWrappedInstance():通过wrappedObject字段获取bean本身,此字段在上文populateBean#new BeanWrapperImpl方法中完成的赋值,即将实例化的bean赋值给此wrappedObject字段

反射调用对象的setXxx方法完成属性赋值

public void setProvince(String province) {
	this.province = province;
}

public void setAddress(Address address) {
	this.address = address;
}

DI依赖注入总结

1、使用@Resource、@Autowired完成依赖注入

  • 本质是bpp的后置增强,底层是调用field.set(bean, val),其中依赖的val是通过getBean方法获取的

2、使用setXxx方法完成依赖注入

  • 本质是method.invoke,即通过反射获取bean对应的依赖属性的set方法即bean.setXxx(val),其中依赖的val也是通过getBean获取的

4.initializeBean

1、源码

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	// 1.如果当前bean实现了Aware接口,这里需要设置aware接口属性
	invokeAwareMethods(beanName, bean);
	
	// 2.bpp-before(会执行@PostConStructor注解修饰的方法即init-method标签)
	Object	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

	// 3.执行用户自定义的init方法
	invokeInitMethods(beanName, wrappedBean, mbd);

	// 4.bpp-after:aop
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

	return wrappedBean;
}

大体内容如图15所示

在这里插入图片描述

前置点

1、invokeAwareMethods设置Aware接口属性,这里只涉及到了3个Aware接口。其他Aware接口的属性是在before方法的ApplicaitonContextProcessor-bpp中完成设置

2、用户自定义init-method

  • 在执行before初始化方法时,如果用户自定义了@PostConstructor注解修饰initMethod,则自定义的init-method会在此处执行

  • 如果init-method是通过xml中init-method=“自定义方法”,则在后续invokeInitMethods方法中执行init-method

  • 如果既有注解@PostConstructor修饰,又在xml中init-method标签指定,相同的init方法名称只会执行一次。

    实现逻辑是before-bpp的时候执行init-method,等到invokeInitMethods方法时,会去判断注册集合mbd.isExternallyManagedInitMethod(initMethodName)是否存在了,存在了就不会再执行init-method了(因为我们解析完成@PostConstructor注解后,会将其修饰的init-method内容存入externallyManagedInitMethods集合中,如果此集合有值,则说明有@PostConstructor注解修饰了此方法,那么就交由bpp-before去完成执行吧)


invokeAwareMethods

1、方法内容

如果当前bean实现了XxxAware接口,则为当前bean设置对应的Xxx属性

2、举例

如果想获取Spring的上下文ApplicationContext,则只需要实现对应的上下文Aware:ApplicationContextAware

public class MyApplicationContextAware implements ApplicationContextAware {
	private ApplicationContext applicationContext;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
    
	public void func() {
		Environment environment = applicationContext.getEnvironment();
	}
}

3、原理

ApplicationContextAware接口有专门的bpp-ApplicationContextAwareProcessor#postProcessBeforeInitialization在初始化对象之前来处理。执行步骤如下

  • MyApplicationContextAware实例化
  • MyApplicationContextAware初始化之前,先执行bpp-ApplicationContextAwareProcessor#postProcessBeforeInitialization
public Object postProcessBeforeInitialization(final Object bean, String beanName) {
	invokeAwareInterfaces(bean
	return bean;
}                      
private void invokeAwareInterfaces(Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
        
        // 专门用于读取资源路径下和环境中的 k-v值
		if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
        
        // 很明显我们的MyApplicationContextAware实现了ApplicationContextAware接口
		if (bean instanceof ApplicationContextAware) {
           //所以会调用ApplicationContextAware的setApplicationContext,将属性设置
			((ApplicationContextAware) bean).setApplicationContext(
                this.applicationContext);
		}
	}
}

applyBeanPostProcessorsBeforeInitialization

1、方法内容

bpp-postProcessBeforeInitialization

2、具体bpp

  • InitDestroyAnnotationBeanPostProcessor:会执行@PostConStructor注解修饰的方法即init-method标签
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
	metadata.invokeInitMethods(bean, beanName);
	return bean;
}
  • ApplicationContextAwareProcessor:invokeAwareInterfaces
private void invokeAwareInterfaces(Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
 
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}  
        // 其它Aware
	}
}

invokeInitMethods

1、方法内容

1)执行用户自定义init-method

2)如果bean是InitializingBean类型,则执行((InitializingBean) bean).afterPropertiesSet()

  • 作用

在返回完整的成品对象(实例化+初始化)之前,允许再操作一次bean(自定义属性验证 和 初始化工作)

  • 实战
public class Demo6 implements InitializingBean {
	private String myName;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		if (Objects.equals(this.myName, "mjp")) {
			System.out.println("yes");
		} else {
			this.myName = "error";
		}
	}
}

applyBeanPostProcessorsAfterInitialization

aop: 调用bpp(AnnotationAwareAspectJAutoProxyCreator)#postProcessAfterInitialization方法#wrapIfNecessary#createProxy,生成代理对象

后续AOP模块详解


2.getSingleton
try {
	// 函数式接口,会调用createBean
	singletonObject = singletonFactory.getObject();
	newSingleton = true;
}

当getSingleton方法执行完singletonObject = singletonFactory.getObject()之后:本质调用lambda表达式函数式接口doCreateBean,后续流程如下

创建的bean存一级缓存
if (newSingleton) {
	// 将新创建的对象加入一级缓存中
	addSingleton(beanName, singletonObject);
}
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		// 1.将新创建的对象加入一级缓存中
		this.singletonObjects.put(beanName, singletonObject);
		
        // 2.将key-beanName对应的val:创建bean的lambda表达式从三级缓存汇中删除
		this.singletonFactories.remove(beanName);
            
       // 3.将key-beanName,对应的半成品bean对象从二级缓存中删除(二级缓存的存在是为了解决循环依赖,这里已经完成bean的创建了,不再需要了)
		this.earlySingletonObjects.remove(beanName);
        
        // 已注册的单例集
		his.registeredSingletons.add(beanName);
	}
}

循环依赖

循环依赖定义
  • beanA实例化完成后,开始初始化给属性赋值了
  • A中有属性B,则需要先完成B的创建
  • 创建对象B,实例化后,初始化时发现对象B有属性有A,而此时A对象未完成创建的整个流程。
  • 就导致了循环依赖

具体示例如图12

在这里插入图片描述


Spring解决循环依赖流程

如果是构造器的循环依赖,无法解决。如果是set属性的循环依赖,可以通过三级缓存存储创建a对象的lambda表达式,这样b初始化时在从容器中获取A对象时,一定能够获取到A对象,无需再走创建A流程,解了图12中的循环

1、依赖类

  • A
public class A {
	private B b;
	// 省略get、set
}
  • B
public class B {
	private A a;
	// 省略get、set
}

2、xml循环依赖

<bean id="a" class="com.mjp.cycle.A">
	<property name="b" ref="b"></property>
</bean>

<bean id="b" class="com.mjp.cycle.B">
	<property name="a" ref="a"></property>
</bean>
创建A
实例化A
  • 先getSingleton从缓存中取,此时1-3级缓存中都没有a对象,所以去创建a

  • 经过createBeanInstance方法后,A对象已创建:A@1727(其中属性b=null)

  • 向三级缓存中添加创建对象a的lambda表达式()->getEarlyBeanreference(a, mbd, A@1727),此时三级缓存内容如图17所示
    在这里插入图片描述

初始化A
  • 执行populateBean#applyPropertyValues#resolveValueIfNecessary#resolveReference#getBean:

    即获取属性b对象,开始执行下面的创建B:getBean-doGetBean-createBean-doCreateBean

创建B
实例化B
  • 先getSingleton从缓存中取,此时1-3级缓存中都没有b对象,所以去创建b

  • 经过createBeanInstance方法后,B对象已创建:B@2215(其中属性a=null)

  • 向三级缓存中添加创建对象b的lambda表达式()->getEarlyBeanreference(b, mbd, B@2215),此时三级缓存内容如图18所示

在这里插入图片描述

初始化B

1、执行populateBean#applyPropertyValues#resolveValueIfNecessary#resolveReference#getBean,获取属性a对象,开始执行getBean-doGetBean

2、在执行doGetBean时,会去getSingleton缓存中尝试获取a

  • 先从一级缓存中获取a,没有
  • 再从二级缓存中获取a,也没有
  • 再从三级缓存中获取a,key:beanName=a,有。val是lambda表达式
// 1.从三级缓存中获取a,al即lambda表达式
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    // 2.执行函数式接口的方法,即回调()->getEarlyBeanreference方法
	singletonObject = singletonFactory.getObject();
        
    // 3.将a-半成品,加入二级缓存
	this.earlySingletonObjects.put(beanName, singletonObject);
        
    // 4.将a-lambda表达式,从三级缓存中删除(因为已存入二级缓存了,三级缓存就没必要存了)
	this.singletonFactories.remove(beanName);
}
  • 执行2:()->getEarlyBeanreference(a, mbd, A@1727)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	// 省略bpp后置操作
        
    // 返回对象A@1727
	return exposedObject;
}

3、getSingleton方法执行完成后,缓存内容如图19所示

在这里插入图片描述

  • 此时B对象已经获取到了属性A对象值A@1727,准备赋值setPropertyValues,本质就是通过反射,获取对象B的setA方法,完成a属性的赋值,此役B的实例化和初始化均已完成。

4、添加对象b到一级缓存

当b对象通过createBean方法完成实例化和初始化之后,会执行下面动作

// 将新创建的对象B@2215加入一级缓存中,并从二级、三级缓存中删除
addSingleton(beanName, singletonObject);


protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
         // B加入一级缓存
		this.singletonObjects.put(beanName, singletonObject);
            
        // B从三级缓存中删除
		this.singletonFactories.remove(beanName);
            
        // B从二级缓存中删除(此时二级缓存本身就没B)
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

此时缓存内容如图20所示

在这里插入图片描述

继续a对象的初始化

上述创建A-初始化A时,通过getBean()方法,经过创建B步骤的一系列操作后,获取到B的完整对象即B@2215(属性A值A@1727),后续对象a的操作如下:

1、setPropertyValues:对象a,执行属性setB操作

本质就是通过反射,获取对象A的setB方法,完成b属性的赋值,此役A的实例化和初始化均已完成。

此时的A对象为:A@1727[属性b值B@2215(A@1727)]

2、添加对象a到一级缓存

当a对象通过createBean方法完成实例化和初始化之后,同样会执行下面动作

// 将新创建的对象A@1727加入一级缓存中,并从二级、三级缓存中删除
addSingleton(beanName, singletonObject);


protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
         // A加入一级缓存
		this.singletonObjects.put(beanName, singletonObject);
            
        // A从三级缓存中删除
		this.singletonFactories.remove(beanName);
            
        // A从二级缓存中删除(此时二级缓存本身就没B)
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}
  • 此时缓存内容如图21所示

在这里插入图片描述

3、最终结果

  • 至此对象a和对象b的创建均已完成,解决了循环依赖问题,此时A中有B,B中有A。最终缓存内容如图22所示

在这里插入图片描述

  • 只有一级缓存singletonObjects中有完整的a、b内容(二者你中有我,我中有你),二级和三级缓存中没值

Spring解决循环依赖总结

1、实例化a后,将a-lambda表达式加入三级缓存

2、初始化a,getBean创建b,实例化b后,将b-lambda表达式加入三级缓存

3、初始化b,从三级缓存中,获取a-lambda表达式,通过函数式接口获取a对象值(半成品),执行b对象的属性a赋值操作,至此完成了b的初始化动作。

  • 顺带将a半成品存入二级缓存,并从三级缓存中删除a
  • b的创建完成后,将b的成品加入一级缓存中,然后将b从三级缓存中删除

4、初始化a时,创建b流程已经完成,获取到了完整的b对象,此时执行a对象的属性b赋值操作

5、将经过实例化和初始化后的a对象,加入一级缓存中,并将其半成品从二级缓存中删除


循环依赖扩展

1、如果不涉及代理,完全可以将三级缓存完成的事情交由二级缓存去做,也可以解决循环依赖问题

  • 三级缓存,存:key-lambda表达式。改为二级缓存存key-半成品bean
  • 三级缓存,取,key-lamda表达式。改为二级缓存取

2、一旦涉及动态代理,就必须有三级缓存,原因|作用

  • 三级缓存的val是lambda表达式,而非直接的bean对象
  • lambda表达式的作用就是回调机制可以动态执行,所谓的动态就是,即可以这样,也可以那样,是可选择的
  • lambda表达式的内容是getEarlyBeanReference方法,此方法的作用就是可以使用动态代理bpp-getEarlyBeanReference生成cglib代理对象、也可以直接返回原对象(但归根结底,exposedObject只有一份对象)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				// 1.要么是获取动态代理的对象返回
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
        
   	// 2.要么是获取原对象
	return exposedObject;
}

3、cglibA生成 和 被B获取的时机分别为:

  • 动态代理对象(cglibA@xxx)生成时机:A对象polulateBean之后,initializeBean#applyBeanPostProcessorsAfterInitialization,即bpp-after
  • 对象B初始化赋值属性a时,直接获取动态代理的a对象动态代理对象(cglibA@xxx)获取时机:B对象实例化之后populateBean之前,getEarlyBeanReference#bpp-getEarlyBeanReference。这样创建完成B后b(cglibA@xxx)

法十二、finishRefresh

上文观察者

除此之外,SpringBoot中在此完成tomcat的启动

ServletWebServerApplicationContext#finishRefresh

@Override
protected void finishRefresh() {
	super.finishRefresh();
	WebServer webServer = startWebServer();//webServer.start()
}

三、AOP

3.1 定义

1、定义

Aspect(切面)- Oriented(面向)- Programming (编程):面向切面编程

2、作用

允许在运行时动态地创建一个代理对象,这个代理对象可以在不修改原始代码的情况下,增加额外的功能或者横切关注点(如日志、事务管理等),从而增强原始对象的功能

3、Spring中AOP实现方式

动态代理是AOP的一种实现手段

  • 动态代理方式有2中:Jdk动态代理和Cglib代理

3.2 动态代理

关于jdk、cglib以及bytebuddy的动态代理基础知识,参考我的另一片文章:

动态代理

3.3 实例化前的准备

1、依赖

  • gradle依赖

因为我们是在spring源码中学习的aop,学习需要在gradle中引入Aspect的依赖

dependencies {
    // 添加AspectJ依赖
    implementation 'org.aspectj:aspectjweaver:1.9.7'
}
  • pom

如果你在spring项目中需要使用AOP,则引入pom依赖

如果报错:Cannot instantiate interface org.springframework.boot.SpringApplicationRunListener

引入的aop和spring boot starter,二者版本改要一致
spring-boot-starter-aop
spring-boot-starter-web

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
   <version>3.0.6</version>
 </dependency>

<!--面向切面支持包,Spring要支持AOP必需要导入这个包-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.22</version>
</dependency>

aop组成

1、切面代码实现类

  • 切面类
@Slf4j
@Component
@Aspect
public class LoginAspect {
	// 1.定义切点Pointcut
    @Pointcut("execution(* com.example.springaopdemo.controller.UserController.*(..))")
    public void pointcut(){}
	
    // 2.定义通知Advice(处理)
   
    @Before("pointcut()")
    public void before(){
        log.println("do before,,,,");
    }
    @After("pointcut()")
    public void after(){
        log.println("do after.....");
    }
    @AfterReturning("pointcut()")
    public void afterReturning(){
        log.println("do afterReturning....");
    }
    @AfterThrowing("pointcut()")
    public  void afterThrowing(){
        // 方法执行异常才会走进来
        log.println("do afterThrowing....");
    }
	//这里注意,环绕式通知必须自己写返回结果
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object oj=null;
        log.println("之前");
        try{
            oj=joinPoint.proceed();//调用目标方法
        }catch(Throwable e){
            throw new RuntimeException(e);
        }
        log.println("之后");
        return oj;
    }
}

2、aop组成

如图23所示

在这里插入图片描述

切面

  • 定义:Aspect

  • 组成:多个Advisor(Advisor = 切点 Pointcut + Advice)

  • 实体内容:LoginAspect

  • 一个项目可以有多个切面类

1)切点

  • 定义:哪些包下的哪些类的哪些方法,需要被增强(Advice通知)
  • 组成:execution路径表达式
  • 实体内容:@Pointcut(“execution(* com.xxx.UserController.*(…))”)
  • 表达式解析:如图25所示

在这里插入图片描述

execution(* com.xxx.User+.*(…)) :匹配com.xxx包下User类以及其所有子类的所有方法。

2)通知

  • 定义:Advice处理

  • 组成:处理时机(before、After、Around)、处理内容(AfterReturning、AfterThrowing)

    单个Aspect:

    • around方法体-joinPoint.proceed()之前部分
  • berore

    • joinPoint.proceed()即目标对象的目标方法
    • around方法体-joinPoint.proceed()之后部分
    • after
    • afterReturning

    通知执行顺序如图24所示

    在这里插入图片描述

  • 实体内容:5个注解对应的方法

3)连接点

  • 定义:JoinPoint

xml方式-生成对应的bd

这里先以xml形式分析bd的生成,后续会再分析注解形式(二者主要是解析生成bd的方式不同,后续流程是一样的)

1、实战

  • 目标类
public class Demo429 {
	public Integer delete(Integer id) {
		System.out.println("删除id = "+id+"的数据");
		return 1;
	}
}
  • 切面类(非注解形式)
public class MyAspect {
	// 1.定义切点Pointcut
    public void pointcut(){}
	
    // 2.定义通知Advice(处理)
    public void myBefore(){
        log.println("do before,,,,");
    }

    public void myAfter(){
        log.println("do after.....");
    }

    public void myAfterReturning(){
        log.println("do afterReturning....");
    }

    public  void myAfterThrowing(){
        // 方法执行异常才会走进来
        log.println("do afterThrowing....");
    }
    
	//这里注意,环绕式通知必须自己写返回结果
    public Object myAround(ProceedingJoinPoint joinPoint){
        Object oj=null;
        log.println("之前");
        try{
            oj=joinPoint.proceed();//调用目标方法
        }catch(Throwable e){
            throw new RuntimeException(e);
        }
        log.println("之后");
        return oj;
    }
}
  • xml
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 定义切面 Bean -->
	<bean id="myAspect" class="com.mjp.aop.demo.MyAspect"></bean>
	<bean id="demo429" class="com.mjp.aop.demo.Demo429"/>
	<aop:config>
        <!-- 允许使用aspect -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
       
		<!-- 将切面与切点和通知关联 -->
		<aop:aspect ref="myAspect">
			<!-- 定义切点 -->
			<aop:pointcut id="pointcut" expression="execution(* com.mjp.aop.demo.Demo429.*(..))"/>
			<!-- 定义通知 -->
			<!-- 1.环绕通知 -->
			<aop:around pointcut-ref="pointcut" method="myAround"/>

			<!-- 2.前置通知 -->
			<aop:before pointcut-ref="pointcut" method="myBefore"/>
			
			<!-- 3.后置通知 -->
			<aop:after pointcut-ref="pointcut" method="myAfter"/>
			
			<!-- 4.后置通知 -->
			<aop:after-returning pointcut-ref="pointcut" method="myAfterReturning"/>
			
			<!-- 异常通知 -->
			<aop:after-throwing pointcut-ref="pointcut" method="myAfterThrowing"/>
		</aop:aspect>
	</aop:config>
</beans>

2、xml解析-parseCustomElement

在refreshBeanfactory-loadBeanDefinitions-doLoadBeanDefinitions方法中,有默认类型的标签解析,还有自定义类型的标签解析,显然aop-config标签属于自定义的标签

if (delegate.isDefaultNamespace(ele)) {
    // beans、bean等标签
	parseDefaultElement(ele, delegate);
}else {
    // 这里会解析xml中的component-scan,会向bdmap中添加5个internalProcessor
    // 也可以解析aop-config标签
	delegate.parseCustomElement(ele);
}

EnableAspectJ
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

显然aop:aspectj-autoproxy这个标签也是用户标签,需要使用parseCustomElement方法进行解析

1、xml标签的解析过程

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);
	// 1)根据命名空间url获取对应的handler处理器
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

	// 2)使用Handler找到Parser
	// 3) 使用Parser解析
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

1)根据命名空间,找到对应的Handler

// 根据命名空间namespaceUri获取对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  • 会去namespaceUri中
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

找对应的处理器Handler:使用spring-aop模块下resources/META-INF/spring.handlers文件下的指定AopNamespaceHandler

2)根据Handler,找到对应的Parser-AspectJAutoProxyBeanDefinitionParser

public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("aspectj-autoproxy", 
		       new AspectJAutoProxyBeanDefinitionParser());
	}
}

3)使用Parser解析

AspectJAutoProxyBeanDefinitionParser#parse

BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
		BeanDefinitionRegistry registry, @Nullable Object source) {
    
	return registerOrEscalateApcAsRequired(
        AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

存入bdMap中

  • key:"org.springframework.aop.config.internalAutoProxyCreator

  • val:bd-AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator类继承关系

AnnotationAwareAspectJAutoProxyCreator -->>
	AspectJAwareAdvisorAutoProxyCreator -->> 
    	AbstractAdvisorAutoProxyCreator -->> 
    		AbstractAutoProxyCreatorAOP创建代理对象createProxy:getEarlyReference、postProcessBeforeInstantiation)-->>
    			SmartInstantiationAwareBeanPostProcessor-->>
    				InstantiationAwareBeanPostProcessor

parseAspect

parseCustomElement–>> NamespaceHandlerSupport#parse -->> ConfigBeanDefinitionParser#parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		// 步骤一.注册自动代理模式创建器-AspectJAwareAdvisorAutoProxyCreator
		configureAutoProxyCreator(parserContext, element);

    	// 步骤二.解析标签下的子标签:aspect、pointcut还是advisor
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

1、前置

Advice接口有5个对应的Advice实现类,分别对应aspect的5个子标签

<aop:around pointcut-ref="pointcut" method="myAround"/>
<aop:before pointcut-ref="pointcut" method="myBefore"/>
<aop:after pointcut-ref="pointcut" method="myAfter"/>
<aop:after-returning pointcut-ref="pointcut" method="myAfterReturning"/>
<aop:after-throwing pointcut-ref="pointcut" method="myAfterThrowing"/>

子类如图26所示
在这里插入图片描述

2、parseAspect整体流程简化如下

/**
	 * <bean id="myAspect" class="com.mjp.aop.demo.MyAspect"></bean>
	 * <aop:aspect ref="myAspect">
	 * 			<aop:pointcut id="pointcut" expression="execution(* com.mjp.aop.demo.Demo429.*(..))"/>
	 * 			<aop:around pointcut-ref="pointcut" method="myAround"/>
	 */
private void parseAspect(Element aspectElement, ParserContext parserContext) {
	String aspectId = aspectElement.getAttribute(ID);
        
	// 步骤一.获取aop:aspect ref 值:myAspect
	String aspectName = aspectElement.getAttribute(REF);
	List<BeanDefinition> beanDefinitions = new ArrayList<>();
	List<BeanReference> beanReferences = new ArrayList<>();

	// 步骤二.解析aop:aspect标签下的子标签:aop:pointcut、aop:around、aop:before等
	NodeList nodeList = aspectElement.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
		Node node = nodeList.item(i);
		// 步骤三. 判断是否为Advice(around、before、after、afterReturning)
		if (isAdviceNode(node, parserContext)) {
			// 步骤四. 将advice标签转换为对应的bd(beanClass属性为AspectJAroundAdvice),存入bdMaps
			AbstractBeanDefinition advisorDefinition = parseAdvice(aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
        	// 加入集合
			beanDefinitions.add(advisorDefinition);
		}
	}

	// 步骤五、将5个封装的advisorPointCut-bd,以及其他信息,封装成AspectComponentDefinition-bd,
	AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
    // 并存入containingComponents队列中,后续可以直接从queue队列中获取bds的信息
	parserContext.pushContainingComponent(aspectComponentDefinition);

	// 步骤六、解析pointcut标签:<aop:pointcut id="pointcut" expression="execution(* com.mjp.aop.demo.Demo429.*(..))"/>
	List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
	for (Element pointcutElement : pointcuts) {
		parsePointcut(pointcutElement, parserContext);
	}
}

3、步骤四详解

步骤二遍历<aop:aspect ref=“myAspect”>标签下的子标签进行解析,这里我们以aop:around标签为例,分析后续的步骤四

1)方法目标

将Around此advice标签转换为对应的bd(beanClass属性为AspectJAroundAdvice)

2)创建AspectJAroundAdvice所需内容

在创建AspectJAroundAdvice时一定会调用其构造方法

public AspectJAroundAdvice(
		Method aspectJAroundAdviceMethod, 
		AspectJExpressionPointcut pointcut, 
		AspectInstanceFactory aif) {
		super(aspectJAroundAdviceMethod, pointcut, aif);
}

其构造方法内容三个参数:

  • Method aspectJAroundAdviceMethod(标签方法-method),index = 0

  • AspectJExpressionPointcut pointcut(pointcut-ref),index = 1

  • AspectInstanceFactory aif(bf),index = 2

    所以步骤四内容很明确,先创建上述三个参数,然后再创建AspectJAroundAdvice-bd

private AbstractBeanDefinition parseAdvice(
			String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
	// 4.1 创建AspectJAroundAdvice构造方法中,所需的第一样东西Method
	RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
	methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
	// <aop:around pointcut-ref="pointcut" method="myAround"/>
	// 获取method标签内容:myAround
	methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
	methodDefinition.setSynthetic(true);

	// 4.2 创建AspectJAroundAdvice构造方法中,所需的第三样东西Factory(内含bf属性,有了bf则可以获取任意bd)
	// SimpleBeanFactoryAwareAspectInstanceFactory通过实现BeanFactoryAware接口并重写setBeanFactory,可以获取bf属性
	RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
	aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
	aspectFactoryDef.setSynthetic(true);

	// 4.3 创建AspectJAroundAdvice构造方法中,所需的第二样东西point-cut(将point-cut值设置在构造方法index=1处表示第二个参数)
    //	同时也创建bd-AspectJAroundAdvice
	AbstractBeanDefinition adviceDef = createAdviceDefinition(adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
					beanDefinitions, beanReferences);
	// 至此,三样参数都已经设置到AspectJAroundAdvice-bd的构造方法属性constructorArgumentValues中了

    // 4.4 在adviceDef(advice)外再包一层,配置成advisor
	RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
	advisorDefinition.setSource(parserContext.extractSource(adviceElement));
    advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
	// 此时bd结构为:外层bd-AspectJPointcutAdvisor(其constructorArgumentValues中有一个参数为bd-AspectJAroundAdvice),AspectJAroundAdvice的constructorArgumentValues中又有3个参数method、bf、point-cut

	// 4.5 将此bd-AspectJAroundAdvice加入defaultListableBeanFactory的bdMap中,并返回
	parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
	return advisorDefinition;
}

3)4.3详情

private AbstractBeanDefinition createAdviceDefinition(
			Element adviceElement, ParserContext parserContext, String aspectName, int order,
			RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

	// 4.3.1 创建bd(其beanClass属性即AspectJAroundAdvice)
	RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));

	// 4.3.2 为bd填充属性

	// 4.3.3 解析pointcut-ref内容
	// <aop:around pointcut-ref="pointcut" method="myAround"/>
	// 获取pointcut-ref属性值:"pointcut"(显然这里是字符串)
	Object pointcut = parsePointcutProperty(adviceElement, parserContext);
	RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
	// 构造方法的index = 1,参数为pointcutRef(即构造方法的第二个参数内容)
	cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
	beanReferences.add(pointcutRef);
	cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    
	return adviceDefinition;
}

4)4.4 bd结构

  • 先创建bd-AspectJAroundAdvice(constructorArgumentValues中含有3个参数method、bf、point-cut)

  • 再在bd的基础上,又将其封装成一个bd-AspectJPointcutAdvisor#0(constructorArgumentValues中有1个参数:bd-AspectJAroundAdvice)

    结构如图27所示
    在这里插入图片描述

  • 其他Advice的四个实现类(AspectJMethodBeforeAdvice、AspectJMethodAfterAdvice等)同理bd-AspectJAroundAdvice,最终结构如图28

在这里插入图片描述

5个Advice实现类对应的bd,外层都又封装了一层Advisor,对应5个Advisor(每个AspectJPointcutAdvisor#0类都是Advisor接口的实现类)

4、步骤六详解

/**
	<aop:pointcut id="pointcut" expression="execution(* com.mjp.aop.demo.Demo429.*(..))"/>
*/
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    // 6.1 获取aop:pointcut标签的属性 id=值pointcut
	String id = pointcutElement.getAttribute(ID);
    
	// 6.2 获取aop:pointcut 属性expression=内容
	String expression = pointcutElement.getAttribute(EXPRESSION);
    
	AbstractBeanDefinition pointcutDefinition = null;

	// 6.3 创建bd-AspectJExpressionPointcut(持有id、expression内容)
	pointcutDefinition = createPointcutDefinition(expression);

	String pointcutBeanName = id;
	// 6.4 将bd注册到bf
	parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);

	// 6.5 将AspectJExpressionPointcut-bd再封装为PointcutComponentDefinition
	// 并将封装后的bd存入containingComponents队列中
	// 此时此queue有2个内容了,还有一个是AspectComponentDefinition(内含5个Advisor-bd)
	parserContext.registerComponent(new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));

	return pointcutDefinition;
}

至此,bdMap中完整的bd如图29所示
在这里插入图片描述

  • myAspect:即切面类
  • #0-4:5个AspectJPointCutAdvisor(constructorArgumentValues属性含有持有的Advice,eg:AspectJAroundAdvice)
  • internal:对应AnnotationAwareAspectJAutoProxyCreator-bd(bpp)
  • pointcut:对应AspectJExpressionpointcut

注解方式-生成对应的bd

1、注解形式

  • AspectJ注解类
package com.mjp.aop.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;

@Component
@Aspect
public class LoginAspect {
	// 1.定义切点Pointcut
    @Pointcut("execution(* com.mjp.aop.demo.Demo429.*(..))")
    public void pointcut(){}
	
    // 2.定义通知Advice(处理)
   
    @Before("pointcut()")
    public void before(){
        log.println("do before,,,,");
    }
    @After("pointcut()")
    public void after(){
        log.println("do after.....");
    }
    @AfterReturning("pointcut()")
    public void afterReturning(){
        log.println("do afterReturning....");
    }
    @AfterThrowing("pointcut()")
    public  void afterThrowing(){
        // 方法执行异常才会走进来
        log.println("do afterThrowing....");
    }
	//这里注意,环绕式通知必须自己写返回结果
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object oj=null;
        log.println("之前");
        try{
            oj=joinPoint.proceed();//调用目标方法
        }catch(Throwable e){
            throw new RuntimeException(e);
        }
        log.println("之后");
        return oj;
    }
}
  • 启动类
@Configuration
@ComponentScan("com.mjp.aop")
@EnableAspectJAutoProxy
public class MyApplication {
}

因为自定义的Aspect切面类会被@Component注解修饰,@ComponentScan注解扫描时,会将对应的切面类bd加载到bdmap中

2、简述xml-component-scan标签和@ComponentScan的区别

1)@ComponentScan

在IOC中我们知道,通过ConfigurationClassPostProcessor-bpp解析@ComponentScan注解下的basepackage下的自定义@Component注解修饰的类,底层干活的方法是ClassPathBeanDefinitionScanner#doScan,所以我们自定义的LoginAspect会被扫描并加载到bdMap中

2)<context:component-scan 标签

  • 因为component-scan标签属于parseCustomElement
  • 底层解析方法是BeanDefinitionParser接口#parse
  • 真正负责解析的是接口的实现类:ComponentScanBeanDefinitionParser#parse
  • 此方法底层也是调用的ClassPathBeanDefinitionScanner#doScan,所以和@ComponentScan注解一样,底层都是通过doScan方法,将basePackage下被@Component注解修饰的LoginAspect类,生成bd,加入bdMap中

EnableAspectJ
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}

在执行bfpp时,invokeBeanDefinitionRegistryPostProcessors -->> ConfigurationClassPostProcesor##postProcessBeanDefinition

1、解析注解获取AspectJAutoProxyRegistrar

会处理@Import注解中的AspectJAutoProxyRegistrar,并完成实例化

2、AspectJAutoProxyRegistrar#registerBeanDefinitions,存储相应bd

processConfigBeanDefinitions -->>this.reader.loadBeanDefinitions(configClasses) -->>

AspectJAutoProxyRegistrar#registerBeanDefinitions

– >> AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

存入bdMap中 key:"org.springframework.aop.config.internalAutoProxyCreator"AnnotationAwareAspectJAutoProxyCreatoval:bd-AnnotationAwareAspectJAutoProxyCreator


parseAspect

其他Aspect组件(Advisor = PointCut + Advice)bd以及对应的bean生成时机

Q:

通过注解形式,我们只生成了被@Component注解修饰的MyAspectJ切面类。在上文xml方式-生成对应的bd中由图29,我们知道最终的bdMap除了MyAspectJ切面类外,还有5个Advisor-bd 和 一个Pointcut-bd,那么注解形式,他们是在哪儿生成并加入bdMpa中的呢

A:

SpringBoot项目

refresh -->> onRefresh(此时已经完成了invokeBeanFactoryPostProcessors-bfpp的执行) -->>

– >> ServletWebServerApplicationContext#onRefresh -->> createWebServer -->> getWebServerFactory

– >> getBean创建tomcatServletWebServerFactory

– >> resolveBeforeInstantiation

– >> applyBeanPostProcessorsBeforeInstantiation

– >> AbstractAutoProxyCreator#postProcessBeforeInstantiation

– >> AspectJAwareAdvisorAutoProxyCreator#shouldSkip

– >> AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

–>>AnnotationAwareAspectJAutoProxyCreator# buildAspectJAdvisors

遍历所有的beanName,找到我们的自定义切面类

if (this.advisorFactory.isAspect(beanType)) {
	List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
}

– >> getAdvisors真正干活的方法

  • 反射获取切面内的所有方法
  • 遍历方法(around、before等),通过new Advisor、new Advice的方式创建Advisor = PointCut + Advice

创建bpp实例
  • xml解析阶段configureAutoProxyCreator()生成的AnnotationAwareAspectJAutoProxyCreator-bd(是个bpp)或 注解@EnableAspectJAutoProxy方式生成的AnnotationAwareAspectJAutoProxyCreator-bd
  • 通过registerBeanPostProcessors注册bpp方法,会通过getBean创建对应的bpp实例

3.4 核心对象的创建

resolveBeforeInstantiation

介绍

  • 此方法在IOC法十一的3中
  • 作用是:在doCreateBean之前给InstantiationAwareBeanPostProcessor接口实现类一个机会,让其返回目标bean的动态代理对象,即AOP
  • 而AnnotationAwareAspectJAutoProxyCreator正是此接口的实现类

实例化AspectJ切面类依赖分析

创建切面所需要的依赖对象

  • 在AOP的组成中,我们知道切面 = 切点 + Advisor
  • 要想创建切面类MyAspect实例,就要先创建Advisor和Pointcut
  • 图29中,我们有5个Advisor即AspectJPointcutAdvisor#0-4,所以需要创建这5个Advisor

1、创建Advisor以及其依赖

  • Advisor构造方法
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {
	this.advice = advice;
	this.pointcut = advice.buildSafePointcut();
}

之前IOC实例化时,实例化策略一般都是无参构造器(因为一个类不写构造函数,默认为其添加无参构造方法)instantiateBean实例化bean,但是这里AspectJPointcutAdvisor类中只有-有参构造,没有无参构造。所以要想创建Advisor,只能用有参构造器,而且还需要先创建其参构造的参数Advice

2、创建Advice以及其依赖

在parseAspectJ方法步骤四中我们知道要创建Advice,是通过有参构造方法,其构造方法中有3个参数,所以需要先创建这三个参数对应的实例

  • Advice构造方法中三个参数的实例化
  • 这三个参数的实例化都是默认的无参实例化

3、Aspect切面类整体创建流程如图30所示

在这里插入图片描述


实例化MyAspect切面类

1、resolveBeforeInstantiation

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
	Object bean = null;
    
	// bf中有InstantiationAwareBeanPostProcessor接口的实现类
	// 显然AOP相关的bpp-AnnotationAwareAspectJAutoProxyCreator是其实现类
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		// 执行实现类的before方法,如果实现类中通过cglib返回此bn对应的bean的代理对象,则直接返回代理
		bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
		if (bean != null) {
			bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
		}
		return bean;
}

2、AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization

–>> AbstractAutoProxyCreator#postProcessAfterInitialization

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
	// 将cacheKey加入advisedBeans!(后续advisedBeans此Map直接决定是否走createProxy方法)
    // AspectJ整个postProcessAfterInitialization方法走完后,此map中5个Advisor作为key,val都是false,本身AspectJ-fasle
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return null;
}

1)isInfrastructureClass

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

显然AspectJ不是Advisor、Pointcut、Advice类型,所以返false

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

本质就是判断我们的MyAspect类是否被@Aspect注解修饰

2)AbstractAutoProxyCreator#shouldSkip

判断当前的类是否需要被自动代理

– >> AspectJAwareAdvisorAutoProxyCreator#shouldSkip

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
	// 1.调用AspectJAwareAdvisorAutoProxyCreator子类AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
    // 找到所有的Advisor即bdMap中的5个Advisor,并将他们实例化
	List<Advisor> candidateAdvisors = findCandidateAdvisors();

	for (Advisor advisor : candidateAdvisors) {
		if (advisor instanceof AspectJPointcutAdvisor &&
			((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
			return true;
		}
	}
    // 2.调用AspectJAwareAdvisorAutoProxyCreator父类AbstractAutoProxyCreator#shouldSkip
	return super.shouldSkip(beanClass, beanName);
}

3、findCandidateAdvisors

这里就从创建切面类MyAspect,转换到需要先找到Advisor,从方法返回结果List看,元素是Advisor实例,所以此方法完成了:找到所有的Advisor-bd,并实例化他们

– >> AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {
    // 1.找到xml定义的切面类中的所有Advisor
	List<Advisor> advisors = super.findCandidateAdvisors();
	
    // 因为xml 和 注解,都可以定义切面类,所以要找全了(一般只会选择一种方式)
	if (this.aspectJAdvisorsBuilder != null) {
		// 2.找到被@AspectJ注解修饰的bean,同理将这些bean内部的@Around注解、@Before等注解,封装创建一个一个的Advisor,然后存入advisors
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}

– >> AbstractAdvisorAutoProxyCreator#findCandidateAdvisors

– >> BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

  • 第一步找到定义的Advisor-bd
  • 第二步:实例化他们
public List<Advisor> findAdvisorBeans() {
    // 第一步:从bf中获取所有实现了Advisor接口的bean名称(即#0-4,总计5个Advisor)
	String[] advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);

	List<Advisor> advisors = new ArrayList<>();
	// 第二步:循环实例化
	for (String name : advisorNames) {
		// getBean实例化Advisor
		advisors.add(this.beanFactory.getBean(name, Advisor.class));
	}
	return advisors;
}
  • 第一步,调用getBeanNamesForType方法获取所有的Advisor的beanNames[],有些类似于IOC中getBeanNamesForType
  • 第二步,循环这些Advisor#0-4,分别getBean(后续都以bd-AspectJPonitcutAdvisor#0这个实例化为例)

getBean-Advisor

在前置分析中我们知道Advisor的实例化策略是有参构造方式

–>> doGetBean -->> createBean -->> doCreateBean -->> 实例化createBeanInstance -->>

1、Advisor#autowireConstructor

// 因为Advisor#0,有有参构造函数,所以mbd.hasConstructorArgumentValues()=ture
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	return autowireConstructor(beanName, mbd, ctors, args);
}

1)autowireConstructor(第一次执行):此时是创建Advisor

此方法内容也分为三步

  • 第一步:找到Advisor#0的构造器,以及构造器一个参数Advice-bd
  • 第二步:实例化构造器中的Advice参数
  • 第三步:实例化自己Advisor#0
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
	// 第一步:获取到Advisor的构造方法(参数为Advice)
	candidates = (mbd.isNonPublicAccessAllowed() ?
			beanClass.getDeclaredConstructors() : beanClass.getConstructors());
    
	// 获取构造函数对应的参数:1个AspectJAroundAdvice
	ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();

	// 第二步:实例化参数(要想创建Advisor就必须先创建Advice)
	minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			
	// 第三步:实例化本身:Advisor
	bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));//实例化
	return bw;
}

2)步骤二:resolveConstructorArguments: 创建Adivce

创建Advisor,先要尝试实例化其构造参数Advice

// 这里的valueHolder.getValue()类型是bd类型的
Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());

3)resolveValueIfNecessary

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
	// 是否为引用类型(属性为普通对象)populateBean时,依赖属性是ref引用类型的
	if (value instanceof RuntimeBeanReference) {
		RuntimeBeanReference ref = (RuntimeBeanReference) value;
		return resolveReference(argName, ref);
	}
 
   
    // 显然我们这里是bd类型的(调用此方法入参为valueHolder.getValue()是bd类型)
	else if (value instanceof BeanDefinition) {// value是bd的属性内容
		BeanDefinition bd = (BeanDefinition) value;
        // 后续bd的bn就是(inner bean)#xxxx
		String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
		return resolveInnerBean(argName, innerBeanName, bd);
	}
}

4)resolveInnerBean

// 创建依赖的bean
Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null);

此方法通过createBean去尝试实例化Advice,想要创建Advisor-则需要先创建Advice,同理想要createBean-advice时,也需要先创建Method、PointCut、bf,所以后续会再次递归调用autowireConstructor方法,后续2中递归调用流程同1

2、Advice#autowireConstructor(递归第二次执行),此时是创建Advice

此方法内容也分为三步(具体内容同第一次分析)

  • 第一步找到Advice的构造器,以及构造器三个参数(Method、PointCut、bf)
  • 第二步:实例化构造器中的3个参数(同上,循环遍历cargs构造参数,挨个创建)
  • 第三步:实例化自己Advice

3、Method参数、PointCut参数的实例化createBeanInstance#instantiateBean

// 4.默认使用无参构造器,实例化bean
return instantiateBean(beanName, mbd);
  • 同理Pointcut、bf的实例化

4、总结

经过步骤1-3的不断递归,完成了单个Advisor#0的创建,再通过List的循环,完成所有Advisor的创建后,再去创建MyAspect

5、切面-xml和注解同时使用

xml形式的Advisor处理完成后,因为xml和注解可以同时使用(代码中可以定义2个切面类,一个是xml定义的,一个是纯注解定义的),所以也需要处理@AspectJ修饰的切面类(一般要么纯注解形式,要么纯xml形式)

protected List<Advisor> findCandidateAdvisors() {
    // 1.找到xml定义的切面类中的所有Advisor
	List<Advisor> advisors = super.findCandidateAdvisors();
    
	// 因为xml 和 注解,都可以定义切面类,所以要找全了(一般只会选择一种方式)
	if (this.aspectJAdvisorsBuilder != null) {
		// 2.找到被@AspectJ注解修饰的bean,同理将这些bean内部的@Around注解、@Before等注解,封装创建一个一个的Advisor,然后存入advisors
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}

被@AspectJ注解修饰的类,内部所有@注解修饰的方法,也会被解析封装为对应的Advisor

6、Aspect切面类无需动态代理

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	for (Advisor advisor : candidateAdvisors) {
		if (advisor instanceof AspectJPointcutAdvisor &&
			((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
			return true;
		}
	}
	return super.shouldSkip(beanClass, beanName);
}

显然advisor是AspectJPointcutAdvisor类型的,而且名Advisor的切面类名称为MyAspect,所以shouldSkip方法返回true

// 返回ture,return null,即切面类没有在resolveBeforeInstantiation中使用代理,其本身就无需代理
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return null;
}

实例化目标类

resolveBeforeInstantiation
  • 在AOP中,我们都知道目标类,需要创建代理类
  • 我们目标类Demo429在createBean时,也会执行resolveBeforeInstantiation,所以也会执行AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);

	if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
		// Demo429目标类,不是InfrastructureClass类型
        // Demo429目标类在shouldSkip时,((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName))返回false,equals表达式前者是MyAspect、后者是Demo429
		if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return null;
		}
	}

    // targetSource为null,不会执行createProxy,直接return null
	TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
	if (targetSource != null) {
		if (StringUtils.hasLength(beanName)) {
			this.targetSourcedBeans.add(beanName);
		}
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
		Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	return null;
}
  • 方法执行结果发现,虽然目标类需要是创建代理对象,但是不是在此创建的。所以此方法确切的作用:

在doCreateBean之前给InstantiationAwareBeanPostProcessor接口实现类一个机会,让其返回目标类的代理对象(是用户自定义的动态代理方式,而非Spring的AOP),显然这里的Demo429目标类是Spring的AOP,不是用户自定义的代理方式。用户自定义的代理方式参考上文此方法中example


applyBeanPostProcessorsAfterInitialization

1、目标类的代理类创建时机

在目标类Demo429实例化之后,会进行initializeBean初始化流程(赋值Aware、bpp-before、initMethod、bpp-after)其中bpp-after时,会生成代理

–>> AbstractAutoProxyCreator#postProcessAfterInitialization

– >> wrapIfNecessary

// 1.实例化Aspect切面类时,我们知道advisedBeans此Map存入了各种Advisor类和inner类,并没有目标类Demo429
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
	return bean;
}

// 2.Demo429目标类,不是InfrastructureClass类型
// Demo429目标类在shouldSkip时,((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName))返回false,equals表达式前者是MyAspect、后者是Demo429
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

// Create proxy if we have advice.
// 步骤三.获取所有的Advisor(对于AOP而言,Advisor就是拦截器)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

if (specificInterceptors != DO_NOT_PROXY) {
    // 这里将demo429-true存入map
	this.advisedBeans.put(cacheKey, Boolean.TRUE);
    
    // 步骤四.创建代理对象
	Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new      SingletonTargetSource(bean));
	this.proxyTypes.put(cacheKey, proxy.getClass());
	return proxy;
}

getAdvicesAndAdvisorsForBean(sortAdvisors)

上述步骤三:getAdvicesAndAdvisorsForBean

1、canApply

1)作用

判断目标类Demo429,是否在PointCut描述范围内

<aop:pointcut id="pointcut" expression="execution(* com.mjp.aop.demo.Demo429.*(..))"/>

解析出来的图29中的PointCut类中,有executation内容,这里会判断下当前的目标类Demo429是否满足表达式

2)方法时序

步骤三getAdvicesAndAdvisorsForBean

– >> AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

– >> findEligibleAdvisors

– >> findAdvisorsThatCanApply

– >> AopUtils.findAdvisorsThatCanApply -->>canApply

3)内容

  • 类匹配
// 明显我们目标类Demo429在满足表达式expression中的类,if为!true
if (!pc.getClassFilter().matches(targetClass)) {
	return false;
}
  • 方法匹配
// 获取目标类中的所有方法(Object类的方法、本身的方法)
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
	// 判断方法是否和expression表达式中方法匹配
	if (introductionAwareMethodMatcher != null ?
		introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
		methodMatcher.matches(method, targetClass)) {
				return true;
	}
}

显然目标类Demo429#delete方法 和 expression中的Demo429.*(…)是匹配的,所以canApply

2、sortAdvisors

  • 切面 = 多个Advisor
  • Advisor = PointCut + Advice
  • 这里之所以需要对Advisor进行排序,就是因为Advice的执行有先后顺序的,顺序如上图24

这里对Advice排序的算法是拓扑算法,正常情况下我们使用AOP的时候是Before + After一起使用,或者直接使用Around达到二者的效果。一般不会三者一起使用,三者如果一起使用,就会导致拓扑算法在对Advice排序的时候产生2种可能,举例

如果三者一起使用,则可能得排序结果

1)Around环绕前 -> before -> 目标方法 -> around环绕后 -> after

2)Around环绕前 -> before -> 目标方法 -> after -> around环绕后

所以,我们的AOP一般都是使用Around = before + after

本质原因是:sortAdvisors采用的topo-sort排序,是不稳定算法!关于不稳定算法对结果的影响,可以参考我的另外一篇文章:Order by limit不稳定性


createProxy

步骤四:createProxy

创建代理工厂、设置代理工厂属性、创建代理对象

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
		@Nullable Object[] specificInterceptors, TargetSource targetSource) {

// 第一步创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();

//第二步为代理工厂设置属性值
// 1.将当前要代理的对象,copy到代理工厂中
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
	if (shouldProxyTargetClass(beanClass, beanName)) {
		proxyFactory.setProxyTargetClass(true);
	}
	else {
		// 2.为代理工厂设置proxyTargetClass属性
		// 要代理的类,如果有接口,则添加接口。如果没接口则属性值为ture
        // 这个属性为true,后续createAopProxy方法在选择代理方式时“大概率”会选择Cglib
		evaluateProxyInterfaces(beanClass, proxyFactory);
	}
}

// 3.将Advisor加入代理工厂
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
    
// 4.将当前bean对象作为目标对象加入代理工厂
proxyFactory.setTargetSource(targetSource);

// 第三步:创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}

getProxy : 选择代理方式(JDK|Clgib)、创建代理对象

public Object getProxy(@Nullable ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

createAopProxy

选择代理方式(JdkDynamicAopProxy-JDK动态代理、CglibAopProxy-Cglib代理)

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 上文设置代理工厂时,proxyTargetClass = true
	if (config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
	// 1.目标类是否为接口 或 目标类是Proxy类型(显然我们的目标类Demo429不是接口,也不是Proxy类型,所以会使用CglibAopProxy)
	if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
		// 使用Jdk动态代理
		return new JdkDynamicAopProxy(config);
	}
	// 使用Cglib代理
	return new ObjenesisCglibAopProxy(config);
	}
	else {
		// 使用Jdk动态代理
		return new JdkDynamicAopProxy(config);
	}
}

getProxy

使用Cglib创建代理对象(具体实现和原理,参考我的另一篇文章:动态代理

1、创建enhancer

2、填充enhancer

  • 设置拦截链callbacks,最重要的是DynamicAdvisedInterceptor拦截器,后续就是执行它的intercept方法

3、创建代理对象createProxyClassAndInstance

–>> 子类ObjenesisCglibAopProxy#createProxyClassAndInstance

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    // 步骤一:创建代理类字节码,并返回Class
	Class<?> proxyClass = enhancer.createClass();
    
    // 步骤二:反射创建代理类
	Object proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
	return proxyInstance;
}
  1. 步骤一:enhancer#createClass
Object obj = data.get(this, getUseCache());
return nextInstance(obj);
  • data.get–>> AbstractClassGenerator#gen.generate,创建代理类字节码

  • nextInstance:创建代理对象Class: class com.xxx.Demo429 E n h a c e r B y S p r i n g C g l i b EnhacerBySpringCglib EnhacerBySpringCglibxxx

– >> Enhancer#nextInstance

– >> ReflectUtils.newInstance

– >> data.newInstance

if (classOnly) {
	return data.generatedClass;
}
  1. 步骤二:objenesis.newInstance

至此代理对象创建完成 Demo429 E n h a c e r B y S p r i n g C g l i b EnhacerBySpringCglib EnhacerBySpringCglib370@2546,所以IOC完整的doCreateBean返回的对象加入一级缓存

此时的代理对象如图31所示

在这里插入图片描述


代理对象调用方法

1、明面上的方法调用

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Demo429 demo429 = context.getBean(Demo429.class);
demo429.delete(1);

2、代理的方法调用

demo429.delete(1) – >>代理类Demo429 E n h a c e r B y S p r i n g C g l i b EnhacerBySpringCglib EnhacerBySpringCglib370@2546#delete

– >> CglibAopProxy内部类DynamicAdvisedInterceptor#intercept(上文在getProxy方法中为enhacer对象设置了callbacks[]- 含有DynamicAdvisedInterceptor)

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	// 1.构造拦截器链(5个Advisor + 1个ExposeInvocationInterceptor)	
	List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
	// 2.MethodInvocation执行拦截proceed()		
	Object retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
	return retVal;			
}

3、方法执行流程

底层调用ReflectiveMethodInvocation#proceed ,方法执行流程(责任链-递归调用流程)如图37所示
在这里插入图片描述

  • 正向(黑色箭头):一直调用的BeforeAdviceInterceptor -->> before前置通知 -->> 目标方法
  • 逆向回退: 目标方法执行结束后,回退到BeforeAdviceInterceptor
    • 再回退到After -->> 执行after后置通知
    • 再回退到AfterReturn -->> 执行afterReturning通知,期间若是有异常,则直接回退到
    • AfterThrowAdvice -->> 执行异常通知

ExposeInvocationInterceptor#intercept

如上图24所示Adivce执行顺序

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 先执行AspectJAroundAdvice#invoke
public Object myAround(ProceedingJoinPoint joinPoint){
	log.println("around 之前");
	Object	oj=joinPoint.proceed();//调用目标方法
	log.println("around 之后");
	return oj;
}
  • 即先执行Around-proceed方法之前内容

2)再执行MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
	return mi.proceed();
}
  • 执行before
  • 再执行目标类的目标方法

3)再执行Around-proceed方法之后前内容

4)再执行after


3.5 AOP实战

关于aop的实战,参考我的另一篇文章:

AOP实战


四、Spring事务

4.1 事务设计思路

1、正常事务设计思路

如图34所示

在这里插入图片描述

2、如果可以使用AOP的5种Advisor,去处理事务,则如图35所示

在这里插入图片描述

  • Around:完成事务的开启 和 提交
  • Afterthrowing: 完成事务的异常回滚

3、事务中的Advisor

SpringAOP中是通过各种5种Advice完成前置、后置、环绕等增强。同样,Spring事务中也需要两个Advice完成对方法的增强

  • DefaultBeanFactoryPointcutAdviso - Advisor:TransactionInterceptor真正的拦截器
  • ExposeInvocationInterceptor- 方便维护拦截器责任链的调用

所以本质的拦截|增强,就是在TransactionInterceptor#invoke中完成的


4.2 IOC

创建bd

1、解析@EnableTransactionManagement注解

启动类上有@EnableTransactionManagement注解
 -->> @Import({TransactionManagementConfigurationSelector.class})
 		-->> TransactionManagementConfigurationSelector#selectImports

在执行bfpp时,invokeBeanDefinitionRegistryPostProcessors -->> ConfigurationClassPostProcesor##postProcessBeanDefinition

1)解析注解获取TransactionManagementConfigurationSelector

会处理@Import注解中的TransactionManagementConfigurationSelector,并完成实例化

2)TransactionManagementConfigurationSelector#selectImports,存储相应bd

protected String[] selectImports(AdviceMode adviceMode) { 
     return new String[]{
     	AutoProxyRegistrar.class.getName(),
     	ProxyTransactionManagementConfiguration.class.getName()};
     }
}
  • ProxyTransactionManagementConfiguration(bpp)- bd:用于创建代理对象
  • AutoProxyRegistrar- bd:用于解析注解信息和拦截器链

3、创建bd并存储

1)AutoProxyRegistrar

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
		for (String annType : annTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
				Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				if (mode == AdviceMode.PROXY) {
                    // mode默认就是Proxy模式,所以会执行registerAutoProxyCreatorIfNecessary
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
  • registerAutoProxyCreatorIfNecessary
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
	}

将bd:InfrastructureAdvisorAutoProxyCreator存入bdMap

父类:AbstractAdvisorAutoProxyCreator
	父类:AbstractAutoProxyCreator 实现了SmartInstantiationAwareBeanPostProcessor-bpp

2)ProxyTransactionManagementConfiguration

@Bean(
        name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
    )

@Role(2)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    // 1.注册一个能够解析@Transactional注解属性的类
    advisor.setTransactionAttributeSource(this.transactionAttributeSource());
    
    // 2.注册增强器Advice-TransactionInterceptor:本质是MethodInterceptor
    advisor.setAdvice(this.transactionInterceptor());
    return advisor;
}
  • 其中AnnotationTransactionAttributeSource用于解析@Transactional注解属性
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
        this.annotationParsers.add(new SpringTransactionAnnotationParser());
 }

SpringTransactionAnnotationParser#parseTransactionAnnotation
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
      RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
      Propagation propagation = (Propagation)attributes.getEnum("propagation");
      rbta.setQualifier(attributes.getString("value"));
      List<RollbackRuleAttribute> rollbackRules = new ArrayList();
      Class[] var6 = attributes.getClassArray("rollbackFor");
    // 等等
}
  • TransactionInterceptor:本质是MethodInterceptor

当代理对象执行方法时,最终会调用这个拦截器TransactionInterceptor#invoke

创建实例

1、创建代理对象

1)创建bpp

  • InfrastructureAdvisorAutoProxyCreator

2)使用bpp

  • InfrastructureAdvisorAutoProxyCreator:创建代理对象

2、创建事务拦截器

最终的类信息如图33所示
在这里插入图片描述


4.3 AOP

源码执行概述图如图36所示
在这里插入图片描述

源码执行流程

@Service
public class UserService {
    @Resource
    private UserPOMapper userPOMapper;
    
    @Transactional(rollbackFor = Exception.class)
    public List<UserPO> query(Long id) {
        return userPOMapper.selectByExample(构建的example);
    }
}

当执行userService.query方法时,执行的是其代理类对象Enhacer$$的query方法 -->>

CglibAopProxy#intercept–>> 获取拦截器链TransactionInterceptor -->>

ReflectiveMethodInvocation#proceed -->>

TransactionInterceptor#invoke

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
	// 1.获取事务的属性源对象
	TransactionAttributeSource tas = getTransactionAttributeSource();
    
	// 2.获取当前方法的事务属性信息(事务传播特性是:Required、回滚的规则是Exception.class)
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    
	// 三.获取配置的事务管理器:如果@Transactional注解中没指定value值即事务管理器名称,则Spring会帮我创建一个beanFactory.getBean(PlatformTransactionManager.class);作为默认事务管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    
	// 4.获取连接点的唯一标志:类名+方法
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	// 步骤五.创建事务|开启事务
	TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
	Object retVal = null;
	try {
		// 步骤六.执行被增强的方法即目标方法
		retVal = invocation.proceedWithInvocation();
	}
	catch (Throwable ex) {
		// 步骤九.事务异常时,回滚操作
		completeTransactionAfterThrowing(txInfo, ex);
		throw ex;
	}
	finally {
		// 步骤七.清除事务信息,恢复线程中老的事务信息
		cleanupTransactionInfo(txInfo);
	}
	// 步骤八.目标方法执行正常时,提交事务、释放连接等资源
	commitTransactionAfterReturning(txInfo);
	return retVal;
}

接下来分情况对每个步骤分析


4.3.1单个事务
@Service
public class UserService {
    @Resource
    private UserPOMapper userPOMapper;

    @Transactional(rollbackFor = Exception.class)
    public Integer save() {
        int result1 = insert1();
        int result2 = insert2();
        return result1 + result2;
    }
    
    private int insert1() {
        UserPO userPO = new UserPO();
        userPO.setName("mjp");
        userPO.setAge(23);
        userPO.setValid(true);
        return userPOMapper.insert(userPO);
    }

    private int insert2() {
        UserPO userPO = new UserPO();
        userPO.setName("wxx");
        userPO.setAge(18);
        userPO.setValid(true);
        return userPOMapper.insert(userPO);
    }
}
创建事务

步骤五:createTransactionIfNecessary -->>

AbstractPlatformTransactionManager#getTransaction -->>

// 先尝试从缓存中获取事务-显然第一次创建事务时,没有连接对象con的缓存,con需要走后续的创建
Object transaction = doGetTransaction();

// 创建事务
DefaultTransactionStatus status = newTransactionStatus(
		definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);//这里的true表示newTransaction = true新事务
		
// 开启事务和连接-设置连接属性
doBegin(transaction, definition);//DataSourceTransactionManager#doBegin

// 新事物设置属性
prepareSynchronization(status, definition);
return status;
  • 创建事务对象:newTransactionStatus

  • 设置事务属性-开启事务:doBegin

    • 创建Connection-数据库连接
    • 设置事务自动提交为false:con.setAutoCommit(false)
    • timeout-事务超时时间
    • 设置savePoint保存点:只有NESTED传播特性此属性才有值:当有一些列事务事务时,为了标志回滚到具体的某个事务点而非全部回滚而标志的保存点
    • 将创建的连接con存入localThread

执行目标类的目标方法

步骤六:proceedWithInvocation

1、执行inset1

2、执行insert2

此时并没有向db里面写入两条数据


清除事务信息

步骤七:cleanupTransactionInfo

1、内容

事务创建完成后,会执行目标类的目标方法,目标方法执行完成后,不是自动commit的,所以只是修改db操作执行了,但是还未提交,数据暂未发生变化。此时会先清除事务信息,恢复线程中老的事务信息

private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
			new NamedThreadLocal<>("Current aspect-driven transaction");

transactionInfoHolder.set(this.oldTransactionInfo);

2、作用

创建新事务的时候,会将新事务信息存储到transactionInfoHolder中。当目标类的目标方法执行完成后,需要将老的事务信息从新存储到transactionInfoHolder中,这样做的目的是:事务传播特性新1事务–>>新2事务场景时,新2事务con2.xxx执行完成后,能重新回到新1事务,再执行新1的con1.xxx


commit

步骤八:commitTransactionAfterReturning

– >> AbstractPlatformTransactionManager#commit -->> processCommit

else if (status.isNewTransaction()) {
      this.doCommit(status);
}

因为是新的事务,所以直接doCommit

1、正常commit

获取con连接,然后底层con.commit()

如果在commit时,没有异常,则这里就完成了2条数据的真实写入

2、异常

这里需要涉及到事务回滚操作,内容较多,专门放在后续异常回滚中讲解

3、finally-cleanupAfterCompletion

if (status.isNewTransaction()) {
     this.doCleanupAfterCompletion(status.getTransaction());
}
  • 如果当前事务是新的事务,则释放连接:con.close()

最终返回方法结果2


异常回滚
  • 业务异常
private int insert2() {
    return 1 / 0;ArithmeticException: / by zero
}
  • sql本身异常
private int insert2() {
    UserPO userPO = new UserPO();
    return userPOMapper.insert(userPO);//属性字段not null
}
MySQLIntegrityConstraintViolationException: Column 'x' cannot be null

步骤九:completeTransactionAfterThrowing

if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
 	txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}

1、判断当前抛出的异常,是否在注解@Transactional(rollbackFor = Exception.class)规则范围内,如果在则执行rollback

  • 判断抛出的异常的层级(类似:把整个异常看做一个树,Exception是根节点,然后递归抛出的异常的节点的树高)

    private int getDepth(Class<?> exceptionClass, int depth) {
        if (exceptionClass.getName().contains("Exception")) {
            return depth;
        } else {
            // 会一直找父类异常getSuperclass
            return exceptionClass == Throwable.class ? -1 : this.getDepth(exceptionClass.getSuperclass(), depth + 1);
        }
    }
    

    eg:MySQLIntegrityConstraintViolationException

    继承MySQLNonTransientException

    继承SQLException

    继承Exception

当前树高,小于rollbackFor = Exception.class,同时不匹配注解中属性noRollbackFor[],则可以执行processRollback

2、回滚-processRollback

else if (status.isNewTransaction()) {
	 // 若是新事物则可以回滚,底层调用的con.rollback()
     this.doRollback(status);
}else if{
     // 否则设置全局回滚!!!(即外层、内层全部都要回滚)
     this.doSetRollbackOnly(status);
}

4.3.2 事务传播-REQUIRED和REQUIRED_NEW
事务传播特性

事务传播特性TransactionDefinition(总共7个)

public class A{
    @Transactional(rollbackFor = Exception.class)
	public void save() {
     	insert1();
     	b.insert2();
    	// 业务
	}
}


@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void insert2() {
     insert3();
     insert4();
}

按照外层方法save中有事务,内层方法insert2是否需要支持当前事务进行分类

  • 支持:代表-REQUIRED-如果有则使用当前事务,如果没有则创建一个(内层insert2中会使用外层save中的事务)
    • NESTED:外层有事务,则创建一个savePoint保存点并使用外层事务以及其con连接、外层没事务就自己创建一个
  • 不支持:代表-REQUIRED_NEW :如果外层方法有事务,则将其挂起,自己新建一个事务

场景2

@Service
public class UserService {
    @Resource
    private UserPOMapper userPOMapper;
    @Resource
    private StudentService studentService;
    
    @Transactional(rollbackFor = Exception.class)
    public Integer save() {
        int result1 = insert1();
        int result2 = 0;
        try {
            result2 = studentService.insert2();
        } catch (Exception e) {

        }
        // 其它重要的业务
        System.out.println("其它业务逻辑");
        return result1 + result2;
    }

    private int insert1() {
        UserPO userPO = new UserPO();
        userPO.setName("insert1");
        userPO.setAge(23);
        userPO.setCtime(Date.valueOf(LocalDate.now()));
        userPO.setUtime(Date.valueOf(LocalDate.now()));
        userPO.setValid(true);
        return userPOMapper.insert(userPO);
    }
}


@Service
public class StudentService {
    @Resource
    private UserPOMapper userPOMapper;
    
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int insert2() {
        int result3 = insert3();
        int result4 = insert4();
        return result3 + result4;
    }

    private int insert3() {
        UserPO userPO = new UserPO();
        userPO.setName("insert3");
        userPO.setAge(18);
        userPO.setCtime(Date.valueOf(LocalDate.now()));
        userPO.setUtime(Date.valueOf(LocalDate.now()));
        userPO.setValid(true);
        return userPOMapper.insert(userPO);
    }

    private int insert4() {
        UserPO userPO = new UserPO();
        userPO.setName("insert4");
        userPO.setAge(18);
        userPO.setCtime(Date.valueOf(LocalDate.now()));
        userPO.setUtime(Date.valueOf(LocalDate.now()));
        userPO.setValid(true);
        return userPOMapper.insert(userPO);
    }
}

外层save创建事务

同场景一

外层save-执行目标类的目标方法

1、执行外层save方法内容-insert1

2、再执行外层save方法内容-studentService.insert2进入一个新的代理对象增强方法

内层insert2创建事务

1、直接获取外层事务的连接

执行内层方法insert2时,事务的传播特性为默认值Required(外层方法有事务则直接使用外层方法的),再次getTransaction 时,不会再创建新的事务连接,而是使用外层方法save的事务即使用相同的con连接,只会修改原事务的某些属性信息(eg:newTransaction = false表示非新事物)

getTransaction–>> doGetTransaction – >> getResource -->> doGetResource

private static Object doGetResource(Object actualKey) {
    // 这里因为外层事务在创建时,创建了连接con并存入resources
    // 使用的ThreadLocal去get
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        return null;
    } else {
        // REQUIRED传播特性下,再次创建事务并获取连接时,直接使用缓存中已有的连接对象
        Object value = map.get(actualKey);
        return value;
    }
}

2、getTransaction -->> handleExistingTransactionREQUIRED_NEW则创建新事物 、REQUIRED则修改原事务的某些属性newTransaction = fasle

if (isExistingTransaction(transaction)) {
	// 存在了事务,则直接返回原事物,不会再走后续的创建事务、doBegain开启事务连接
	return handleExistingTransaction(definition, transaction, debugEnabled);
}

REQUIRED 和 REQUIRED_NEW区别!!!

private TransactionStatus handleExistingTransaction() {
    // 如果是REQUIRED_NEW
	else if (definition.getPropagationBehavior() == REQUIRED_NEW) {
		//若内层方法的事务传播特性是REQUIRED_NEW
		// 则将外层事务挂起
    	suspendedResources = this.suspend(transaction);
       
    	// 然后创建一个新的事务
    	DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
     	this.doBegin(transaction, definition);
     	this.prepareSynchronization(status, definition);
 	 	return status;
    }
    
    // 正常情况下,事务传播特性REQUIRED,此时内层事务的newTransaction = fasle(入参)
    // 此时还是使用外层事务的事务transaction
    return this.prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, (Object)null);
 }
内层insert2执行目标方法

proceedWithInvocation

  • insert3
  • insert4

此时db中还没有数据,因为内层insert2没有commit、外层save也没有commit

内层异常回滚情况

假如内层insert2执行目标类目标方法insert3有异常

private int insert3() {
   return 1 / 0;
}

会导致内层方法在执行时,异常,内层方法会走到异常回滚逻辑

try {
	// 执行内层方法
    result = invocation.proceedWithInvocation();
} catch (Throwable var17) {
	// 内层方法insert2整体回滚、同时抛出异常!!!
    this.completeTransactionAfterThrowing(txInfo, var17);
    throw var17;
}

1、completeTransactionAfterThrowing:设置全局回滚true

else if (status.isNewTransaction()) {
	// 若是新事物则可以回滚,底层调用的con.rollback()
    this.doRollback(status);
}else if{
    // 否则设置全局回滚!!!(即外层、内层全部都要回滚)
    this.doSetRollbackOnly(status);
}

因为内层事务不是新事物,所以执行全局回滚。

再执行子类DataSourceTransactionManager#doSetRollbackOnly

protected void doSetRollbackOnly(DefaultTransactionStatus status) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject)status.getTransaction();
    	// 设置rollBackOnly属性
        txObject.setRollbackOnly();
}

REQUIRED整个内层方法的回滚逻辑:仅仅是设置一个rollbackOnly = true的标记!!!,并没有执行con.rollBack

2、补充REQUIRED_NEW:

这里想内层insert2回滚,不影响外层save-第一步insert1,则保持上述try-catch吃掉内层insert2的异常,同时在内层insert2方法修饰的事务注解上,事务传播特性使用REQUIRED_NEW:外层有事务则挂起,自己新建一个事务,则status.isNewTransaction() 为 ture,

  1. 创建内层方法事务:getTransaction
if (this.isExistingTransaction(transaction)) {
    return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
}


private TransactionStatus handleExistingTransaction(TransactionDefinition definition,   
Object transaction, boolean debugEnabled) throws TransactionException {
    
    // 如果内层事务是REUQIRED_NEW
 	if (definition.getPropagationBehavior() == REQUIRED_NEW) {
        
 		// 将外层事务挂起(解除当前线程和con的绑定,清空缓存便于下次get时为null才会去创建新的con,同时将旧事物的各种属性信息,保存到suspendedResources对象中)
        suspendedResources = this.suspend(transaction);
        newSynchronization = this.getTransactionSynchronization() != 2;//true
        
        // 创建新的事务status:isNewTransaction = true!!!
        DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        
        // 这里doBegain时,从缓存中拿不到了,会创建一个新的con,即内层insert2方法的con不再像是REQUIRED时那样和外层save方法事务使用同一个con
        // 外层的con: com.mysql.jdbc.JDBCCOnnection@4exx23
        // 内层的con: com.mysql.jdbc.JDBCCOnnection@6fxx4c
        this.doBegin(transaction, definition);          
        this.prepareSynchronization(status, definition);
        return status;
}

2)执行内层方法

方法体异常

3)异常回滚

try {
     result = invocation.proceedWithInvocation();
} catch (Throwable var17) {
      this.completeTransactionAfterThrowing(txInfo, var17);
      throw var17;
}
  • 回滚

判断isNewTransaction = true是新的事务,则直接this.doRollback(status),底层调用的con.rollBack,这个con仅仅是内层方法自己的con-@6fxx4c,不影响外层save方法的con-@4exx23

  • 恢复外层被挂起的事务

    内层执行完后,当前线程绑定的还是内层的新事务新con。

    后续外层执行commit时,肯定要使用外层原本的事务con,所以这里就要恢复被挂起的事务,将其绑定到当前线程上

completeTransactionAfterThrowing-->>processRollback

finally {
	// 恢复外层被suspended挂起的的事务
    this.cleanupAfterCompletion(status);
}

if (status.getSuspendedResources() != null) {
    Object transaction = status.hasTransaction() ? status.getTransaction() : null;
    // 将已经被挂起的外层事务,再重新和当前线程绑定,	
    this.resume(transaction, (SuspendedResourcesHolder)status.getSuspendedResources());
}

4)外层commit

抛出的异常var17-被外层方法save中的try-catch吃掉了,所以外层save可以正常走到commitTransactionAfterReturning

因为不是rollBackOnly = true,所以会正常走到processCommit

因为是新事务,所以会commit

else if (status.isNewTransaction()) {
    this.doCommit(status);
}

这里的con.commit是外层事务con-@4exx23的commit

最终执行结果:

  • 内层insert2回滚
  • 内层异常被外层save吃掉
  • 外层save中insert1正常commit

5)补充场景

  • 场景1
@Transactional(rollbackFor = Exception.class)
public Integer save() {
    int result1 = insert1();
     int result2 = 0;
     try {
         // 1.这里内层的insert2事务传播特性仍使用的REQUIRED_NEW
         // 且insert2中有异常
         result2 = studentService.insert2();
     } catch (Exception e) {
         System.out.println("");
     }

     // 2.但是业务本身异常了
     return 1 / 0;
}

结论:

  • insert2回滚

  • insert1回滚

  • save方法抛异常

  • 场景2:

@Transactional(rollbackFor = Exception.class)
public Integer save() {
    int result1 = insert1();
     // 1.这里内层的insert2事务传播特性仍使用的REQUIRED_NEW
     // 且insert2中无异常
    int result2 = studentService.insert2();
    // 2.但是业务本身异常了
    return 1 / 0;
}

结论

  • insert2-commit
  • insert1-回滚
  • save抛异常

3、外层save-commit

1)rollbackOnly = true

  • 此时内层insert2执行完了回滚逻辑,但是实际并没有真正的rollBack,仅仅设置了rollbackOnly = true
  • 因为内层insert2的异常被吃掉了,所以不会抛出来影响外层的save方法,所以
  • 可以接着执行外层save方法的commit:commitTransactionAfterReturning
else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
    // 因为rollbackOnly = true,所以这里会执行回滚!!!
    this.processRollback(defStatus, true);
    
 } else {
    // 正常情况下,能执行到commit方法中,则会执行processCommit,即底层commit
    this.processCommit(defStatus);
 }

if (unexpectedRollback) {
      throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
}

因为rollBackOnly为true,所以会执行回滚processRollback,底层是con.rollBack,因为外层、内层公用一个事务,是相同的con连接,所以con.commit对内外层都会回滚

} else if (status.isNewTransaction()) {
	// 因为外层事务是新的事物,所以会走进来
    this.doRollback(status);
}

在processRollback方法在执行回滚后,unexpectedRollback本身 = true,会抛出异常

所以save方法的最终执行结果是:

  • insert2内层方法执行了回滚
  • save-insert1方法执行了回滚
  • 同时还会抛出异常UnexpectedRollbackException到save方法中
正常情况下内层insert2-commit

步骤八:commitTransactionAfterReturning

– >> AbstractPlatformTransactionManager#commit -->> processCommit

} else if (status.isNewTransaction()) {
     unexpectedRollback = status.isGlobalRollbackOnly();
     this.doCommit(status);
}

显然我们内层的inser2方法,不是新的事务,使用的仍是外层save方法的事务,此事务的属性isNewTransaction = false,所以内层insert2方法,不会执行commit!!!

这里之所以不能在内层方法中commit的原因是:

根因是内层和外层使用的相同con,你内层不能自己先commit或rollBack了。因为

  • 如果内层方法insert2在此直接commit了

  • 此时,外层方法save中的insert1执行完毕,尚未commit

  • 内层方法insert2commit了,落了数据了

  • 当外层方法save中后续业务流程发生了异常,因为外层方法save也加了事务,所以正常情况下:

    • insert1回滚
    • insert2回滚

    但是insert2内层方法已经提交了,无法再回滚了

  • 所以,这里inset2内层方法,走不到doCommit逻辑即不会commit。其processCommit相当于啥也没干

返回内层方法insert2的执行结果:2

返回外层方法save的执行结果:insert1 + insert2 = 3

外层save-commit
  • 又回到外层save方法的事务中,此时事务的isNewTransaction = true
} else if (status.isNewTransaction()) {
    this.doCommit(status);
}

因为是新事物,所以执行doCommit,底层con.commit

因为外层方法save(新事务) 和 内层方法insert2(非新事物:REQUIRED外层方法有事务则使用外层方法的事务),二者使用的同一个con,所以一旦执行con.commit相当于

  • save-insert1执行了commit
  • save-studentService.insert2
    • insert3执行了commit
    • insert4执行了commit

此时3条数据均落表


4.3.3 事务传播-NESTED

1、场景描述

其它内容都和场景2一样,只不过内部insert2的事务传播特性为NESTED

@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)

NESTED:外层方法有事务,则设置savePoint,且和外层使用相同的事务以及con连接,没有则创建新事务

2、执行结果

  • 外层save-insert1-commit成功
  • 内层insert2
    • insert3 回滚
    • insert4 回滚
  • save方法体本身成功
外层创建事务
执行外层目标方法
代理执行内层
创建内层事务

1、NESTED:外层有事务,则使用相同的事务以及con连接

  • getTransaction
if (this.isExistingTransaction(transaction)) {
     return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
}
  • handleExistingTransaction
if (definition.getPropagationBehavior() == NESTED) {
     status.createAndHoldSavepoint();
     return status;
}

2、NESTED:createAndHoldSavepoint为外层创建一个SAVEPOINT,记录外层执行的sql位置:insert1处

执行内层目标方法

1、内层正常时,因为二者公用一个con,所以无法单独的commit,只能等外层一起con.commit

2、内层异常时

catch (Throwable var17) {
  // 步骤一
  this.completeTransactionAfterThrowing(txInfo, var17);
  // 步骤二
  throw var17;
}

步骤一:completeTransactionAfterThrowing -->> processRollback -->>

if (status.hasSavepoint()) {
    status.rollbackToHeldSavepoint();
}
  • rollbackToSavepoint
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
    ConnectionHolder conHolder = this.getConnectionHolderForSavepoint();
    // 1.rollbackToSavepoint:执行对应sql语句中ROLLBACK to SAVEPOINT_1
	conHolder.getConnection().rollback((Savepoint)savepoint);
    
    // 2.将rollBackOnly = false
    conHolder.resetRollbackOnly();
    
    // 3.释放保存点SAVEPOINT
    this.setSavepoint((Object)null);

}

步骤二:将insert2的异常var17抛出去

继续执行外层目标方法剩余内容:try-catch会吃掉inest2的异常

外层commit
因为内层异常处理时,除了设置SAVEPOINT,还将rollBackOnly = false,所以直接走else逻辑,即commit
else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
     this.processRollback(defStatus, true);
} else {
     this.processCommit(defStatus);
}
SQL

整体执行流程等价sql语句

set autocommit = 0;
start transaction;

update tb_user set age = age - 1 where id = 1;
update tb_user set age = age - 1 where id = 1;
SAVEPOINT SAVEPOINT_1;

update tb_user set age = age - 1 where id = 1;
update tb_user set age = age - 1 where id = 1;
ROLLBACK to SAVEPOINT_1;

commit;

总结

REQUIRED

  • 内层、外层公用一个con
  • 内层正常时,无法单独的commit,只能等外层commit
  • 内层异常时,无法单独的rollBack,只能设置一个rooBackOnly全局回滚的标志,且会抛出异常,外层执行全局rollBack

REQUIRED_NEW

  • 内层有单独的con、外层有单独的con
  • 内层正常时,可单独的commit,即直接完成数据的落表
  • 内层异常时,单独的rollBack,即直接完成数据的回滚,且会抛出异常

五、注解开发

@ImportResource注解

1、作用

通过@ImportResource注解,可以将外部的XML配置文件加载到Spring的应用上下文中
2、使用

  • 创建类: 假设这个类不在ComponentScan扫描到的包内,属于com.mjp.test包下
@service
public class MyService{
    public void method1() {
        System.out.println("hello");
    }
}
  • 自定义xml:myApplication.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- 基于注解自动注册spring bean -->
    <context:component-scan base-package="com.mjp.test"/>
</beans>

  • 自定义配置类
@Configuration
@ImportResource(value = "classpath:myApplication.xml")
public class MyConfig {
}
  • 测试
@SpringBootApplication
public class HelloWorldMainApplication {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext run = SpringApplication.run(MyConfig.class, args);
        // 获取通过配置文件定义而被扫描到的类
        MyService bean = run.getBean(MyService.class);
        System.out.println(bean);
    }
}
  • 或自定义xml: myApplication.xml
<?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 id="createPerson" class="com.mjp.Person">
        <property name="name" value="tom"/>
        <property name="age" value="2"/>
    </bean>
</beans>

@PropertySource

1、作用

指定要加载的属性文件的资源位置

  • 可以是 src/main/resources目录下的文件资源路径下的xxx.properties
@PropertySource(value = {"classpath:redis.properties", "classpath:/tomcat.properties"})
"classpath:redis.properties""classpath:/tomcat.properties"都是可以的
 但是不允许使用通配符的方式eg:
    classpath:*.properties这种方式不允许
        
复杂src/main/resources/profile的路径下
@PropertySource(value = {"classpath:profile/test/db.properties","classpath:profile/prod/db.properties"})     
  • 也可以是file:/path.xxx.xml

2、场景

解析获取redis.properteis文件(在src.main路径下,和java包同等级的 resources包下))下的属性内容,赋值给RedisConfig类

3、使用

  • redis.properteis
##redis相关配置
spring.redis.host=127.0.0.1
spring.redis.password=123456
spring.redis.database=3
spring.redis.port=6379
spring.redis.timeout=1500
## 其它属性配置
spring.pool.timeout=777
  • 配置类
@Configuration 
@PropertySource("classpath:redis.properties") 
public class RedisConfig
	@Bean
	@ConfigurationProperties(prefix = "spring.redis")
	public RedisClient createRedisClient(){
		return new RedisClient( );
    }
}

这里就是将.properties中属性值,装配到RedisClient这个bean中

外部使用这个bean时,bean的名称一定是方法的名称createRedisClient原因是@Bean的方式,方法名称相当于xml中bean标签的id

@Value

基本用法

  • 直接赋值
@Value("127.0.0.1") 
private String ipAddress; 
  • Spel表达式:#{}
@Value("#{20-2}") 
private Integer age; 
  • 取配置文件中值
@Value("${spring.redis.host}") 
private String ipAddress; 

这里以${}为例,分析下源码

1、获取redis.properties中spring.redis.host字段的值:127.0.0.1

package com.mjp.value;

@Configuration
@PropertySource("classpath:redis.properties") 
public class RedisIpAddressConfig { 
	@Value("${spring.redis.host}") 
	private String ipAddress; 
    
    public String getIpAddress() {
		return ipAddres;
	} 
}
  • 配置类
@Configuration
@ComponentScan("com.mjp.value")
public class MyApplication {
}
  • 测试
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);

RedisIpAddressConfig bean = context.getBean(RedisIpAddressConfig.class);
String ipAddress = bean.getIpAddress();
System.out.println(ipAddress);

2、源码解析

1)解析注解@Value:得到"value" -> “${spring.redis.host}”

实例化之后,初始化之前,执行applyMergedBeanDefinitionPostProcessors,会调用bpp-AutowiredAnnotationBeanostProcessor的postProcessMergedBeanDefinition(会去解析@Autowired注解 和 @Value注解)#buildAutowiringMetadata

  • 方法第一次进来时,targetClass是RedisIpAddressConfig类的cglib代理对象(被@Configuration注解修饰的类都会生成代理对象)
  • 代理对象的解析没有内容,然后targetClass = targetClass.getSuperclass()继续while循环解析代理类的父类即真正的RedisIpAddressConfig
  • doWithLocalFields将被@Value注解修饰的属性信息进行解析并存储到metadata
ReflectionUtils.doWithLocalFields(targetClass, field -> {
			AnnotationAttributes ann = findAutowiredAnnotation(field);
			if (ann != null) {
			boolean required = determineRequiredStatus(ann);
			currElements.add(new AutowiredFieldElement(field, required));
		}
});
  • 这里的field即RedisIpAddressConfig类的ipAddress

  • findAutowiredAnnotation:找属性ipAddress上的注解

for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
	AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
	if (attributes != null) {
		return attributes;
	}
}

其中autowiredAnnotationTypes包含了2个注解@Autowired@Value
 public AutowiredAnnotationBeanPostProcessor() {
	this.autowiredAnnotationTypes.add(Autowired.class);
	this.autowiredAnnotationTypes.add(Value.class);
	this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
	ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class
                       .getClassLoader()));
}

2)解析属性: “${spring.redis.host}” -> 1270.0.1

  • 初始化populate中再次调用bpp-AutowiredAnnotationBeanostProcesso#postProcessProperties方法中metadata.inject完成属性的赋值
// 1.找到spring.redis.host对应的value:127.0.0.1
Object value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

// 2.通过反射set方式将127.0.0.1赋值给ipAddress属性
field.set(bean, value);

其中resolveDependency -->> AbstractEnvironment#resolvePlaceholders -->> AbstractPropertyResolver#resolvePlaceholders -->>

// 底层是调用PropertyPlaceholderHelper的replacePlaceholders#parseStringValue
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
	return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}

最终解析出${“spring.redis.host”}对应的值为127.0.0.1

replacePlaceholders方法大概过程就是
1、${"spring.redis.host"}将$、{}去掉,获取"spring.redis.host"
2、执行PropertySourcesPropertyResolver#getPropertyAsRawString
3、获取resources路径下的所有文件,找到propertySource对应的redis.properties
// propertySource为redis.properties、key为spring.redis.host
4Object value = propertySource.getProperty(key);
本质从map中根据key取出来val,即127.0.0.1

@Bean

1、作用

  • 创建线程池bean
  • 创建pageHelper
  • 创建配置bean

2、定义bean

@Configuration
public class MyApplication {
	@Bean
	public Car createCar() {
		return new Car();
	}

	@Bean
	public Demo getDemo() {
		Car car = createCar();
		return new Demo();
	}
}

3、使用

@Resource
private Car createCar;

@Resource
private Demo getDemo;

4、上文背景

上述法五中@Bean注解的bd解析结果

  • bd:MyApplication(beanClass = class com.mjp.MyApplication E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIB8d1)

  • bd:createCar

    • factoryBeanName:myApplication
    • factoryMethodName:createCar
  • bd:getDemo

    • factoryBeanName:myApplication
    • factoryMethodName:getDemo

5、为什么被@Configuration注解修饰的类要为其创建Cglib代理对象

  • 与普通的Bean定义不同,@Configuration类中的@Bean方法之间可以互相调用
  • 如果MyApplication 类不是代理类
    • createCar(),返回的new Car对象,假如为Car@777
    • getDemo中调用createCar()会再一次执行new Car,此时Car@888
    • 每次都返回一个新的Bean实例,从而破坏了Spring的单例模型
  • 所以,必须是代理对象,确保Bean的单例

6、MyApplication、Car、Demo三个bd的创建流程

6.1 MyApplication

1)实例化

  • bd的beanClass是cglib代理的,在doCreateBean -->> createBeanInstance -->> instantiateBean使用默认的无参构器反射创建对象造实例化

  • 此时创建的bean对象:MyApplcationen E n h a c c e r B y S p r i n g C g l i b EnhaccerBySpringCglib EnhaccerBySpringCglibxxx@xxx

  1. 加入一级缓存
  • 初始化没啥内容,直接返回代理对象即可
  • 创建完成bean后,将其加入一级缓存

6.2 Car

1)实例化

  • doCreateBean -->> createBeanInstance -->>instantiateUsingFactoryMethod使用的工厂方法方式创建的对象
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);

Object factoryBean;
Class<?> factoryClass;
boolean isStatic;

// 1.获取factoryBeanName:myApplication
String factoryBeanName = mbd.getFactoryBeanName();
// 2.可以直接从一级缓存中获取到myApplication对应的代理对象
factoryBean = this.beanFactory.getBean(factoryBeanName);

// 3.反射获取myApplication配置类的所有方法createCar
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);

// 4.实例化
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
  • 4.instantiate

ConstructorResolver#instantiate -->> SimpleInstantiationStrategy#instantiate

Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
	currentlyInvokedFactoryMethod.set(factoryMethod);
    // factoryMethod:createCar
    // factoryBean: MyApplicaiton Cglib代理类
    // 1.执行增强
	Object result = factoryMethod.invoke(factoryBean, args);		
	return result;
}
finally {
	if (priorInvokedFactoryMethod != null) {
		currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
	}
	else {
		currentlyInvokedFactoryMethod.remove();
	}
}
  • 1.factoryMethod.invoke

– >> ConfigurationClassEnhancer#intercept

return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)
public Car createCar() {
	return new Car();//Car@2603
}

至此,完成了Car的对象创建

  1. 加入一级缓存
  • 初始化没啥内容,直接返回代理对象即可
  • 创建完成bean后,将其加入一级缓存

6.3 Demo

1)实例化

  • doCreateBean -->> createBeanInstance -->>instantiateUsingFactoryMethod使用的工厂方法方式创建的对象
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);

Object factoryBean;
Class<?> factoryClass;
boolean isStatic;

// 1.获取factoryBeanName:myApplication
String factoryBeanName = mbd.getFactoryBeanName();
// 2.可以直接从一级缓存中获取到myApplication对应的代理对象
factoryBean = this.beanFactory.getBean(factoryBeanName);

// 3.反射获取myApplication配置类的所有方法:getDemo
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);

// 4.实例化
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
  • 4.instantiate

ConstructorResolver#instantiate -->> SimpleInstantiationStrategy#instantiate

Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
	currentlyInvokedFactoryMethod.set(factoryMethod);
    // factoryMethod: getDemo
    // factoryBean: MyApplicaiton Cglib代理类
    // 1.执行增强
	Object result = factoryMethod.invoke(factoryBean, args);		
	return result;
}
finally {
	if (priorInvokedFactoryMethod != null) {
		currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
	}
	else {
		currentlyInvokedFactoryMethod.remove();
	}
}
  • 1.factoryMethod.invoke

– >> ConfigurationClassEnhancer#intercept

return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)
@Bean
public Demo getDemo() {
	Car car = createCar();
	return new Demo();
}
  • 进入getDemo方法

  • 执行Car car = createCar

    • 不会执行new Car,而是执行代理类的ConfigurationClassEnhancer#intercept -->> resolveBeanReference
    Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
    			beanFactory.getBean(beanName));
    

    – >> doGetBean -->> getSingleton(beanName)直接从一级缓存中获取:Car@2603

  • 执行new Demo

至此,完成了Demo的对象创建

  1. 加入一级缓存
  • 初始化没啥内容,直接返回代理对象即可
  • 创建完成bean后,将其加入一级缓存

7、扩展

使用@Bean创建的bean,其id(相当于xml形式bean标签的id = 值)模式就是注解修饰的方法名称

不过可以在注解上添加value属性值,自定义id 值

@Bean(value = "car")
public Car getCar(){
	return new Car();
}

这个时候beanName就是car,不再是默认的getCar

ConfigurationClassPostProcessor

下面的2个注解具体解析流程参考IOC-ConfigurationClassPostProcessor的解析

@ComponentScan

@Import

定义一个类,将这个类创建并交由Spring管理,即IOC的方式有以下几种

1、@ComponentScan + @Controller|@Service|@Repository|@Component

2、@Configuration + @Bean

3、@Import

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();
}

1)普通添加

@Configuration
@Import(MyImport.class)
public class MyImportApplication {

}

此时Spring中就有了MyImport类的bean

2)实现ImportSelector接口:{@link ImportSelector}

  • 自定义ImportSelector接口实现类
public class MyImportSelector implements ImportSelector {
	/**
	 * @param importingClassMetadata 被@Import注解修饰的类的全部信息
	 * @return 需要被@Import注解,IOC的类
	 */
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		String[] strings = {"com.mjp.importdemo.Demo"};//必须是全路径名称
		return strings;
	}
}
  • 配置类
@Configuration
@Import({Car.class, MyImportSelector.class})
public class MyImportApplication {
}

通过配置类的@Import注解,我们向容器中注入了Car以及MyImportSelector类中String[]数组中包含的所有类(Demo类),注意:并不是将MyImportSelector类注入容器,而是把这个类的方法返回String[]中包含的类注入容器

3)实现ImportBeanDefinitionRegistrar接口:{@link ImportBeanDefinitionRegistrar}

  • 实现类
public class MyImportBDRegistry implements ImportBeanDefinitionRegistrar {
	/**
	 * @param importingClassMetadata 被@Import注解修饰的类的全部信息
	 * @param registry 当前bf中的registry对象(掌管所有bd的CRUD)
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 创建一个bd
		BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(Person.class);
		BeanDefinition personBD = bdb.getBeanDefinition();
        // 将bd注入容器
		registry.registerBeanDefinition("Person", personBD);
	}
}
  • 配置类
@Configuration
@Import({Car.class, MyImportSelector.class, MyImportBDRegistry.class})
public class MyImportApplication {
}

通过配置类的@Import注解,我们向容器中注入了Car以及MyImportSelector类中String[]数组中包含的所有类,以及MyImportBDRegistry类中完成了将Person类注入容器的动作

@Conditional

1、作用

满足注解的条件时,才会创建bean

2、举例

  • 自定义Condition接口实现类
public class WindowsCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment environment = context.getEnvironment();
		String property = environment.getProperty("os.name");
		return property.contains("Windows");
	}
}
public class LinuxCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment environment = context.getEnvironment();
		String property = environment.getProperty("os.name");
		return property.contains("Linux");
	}
}
  • 创建bean
@Configuration
public class MyApplication {

    // 如果满足WindowsCondition,则创建createBill这个Bean并放入容器
	@Conditional(value = {WindowsCondition.class})
	@Bean
	public Bill createBill(){
		return new Bill();
	}

    // 如果满足LinuxCondition,则创建createLinux这个Bean并放入容器
	@Conditional(value = {LinuxCondition.class})
	@Bean
	public Linux createLinux(){
		return new Linux();
	}
}
  • 测试
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);

String[] bdNames = context.getBeanDefinitionNames();
for (String bdName : bdNames) {
	// 我们的操作系统是Windows,所以这里只有createBill-beanName
	System.out.println(bdName);
}

3、作用域

  • 修饰方法:方法级别
  • 修饰类:类级别

@PostConstruct

1、作用

在实例化bean之后,初始化bean之前,进行init-method操作

2、场景

可以在配置数据源的时候使用,提前配置属性、最后释放属性-destroy

3、实现

1)@PostConstructor方式

package com.mjp.initmethod;

@Service
public class Demo {

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

	@PostConstruct
	public void myInitMethod() {
		System.out.println("init");
	}
}
  • 配置
@Configuration
@ComponentScan("com.mjp.initmethod")
public class MyApplication {
}
  • 测试
public static void main(String[] args) {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);

	Demo bean = context.getBean(Demo.class);
	bean.func();
}
输出
init
func

2)@Bean方式

public class Car {
	public void myInitMethod() {
		System.out.println("init");
	}
}
  • 配置
@Configuration
public class MyApplication {
	@Bean(initMethod = "myInitMethod")
	public Car getCar(){
		return new Car();
	}
}

在实例化Car之后,初始化Car之前会调用myInitMethod

3)实现InitializingBean接口方式 + @Bean方式

public class Demo1 implements InitializingBean {
	public void func() {
		System.out.println("demo1-func");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("demo1-init");
	}
}
  • 配置
@Configuration
public class MyApplication {
	@Bean
	public Demo1 createDemo1() {
		return new Demo1();
	}
}

4)实现bpp-BeanPostProcessor接口

重写before、after:在init-method之前或之后调用

  • bpp
@Component
public class MyInitBpp implements BeanPostProcessor {
	
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("bpp-before-init");
		return bean;
	}
	
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("bpp-after-init");
		return bean;
	}
}
package com.mjp.initmethod;

@Service
public class Demo {
	public void func() {
		System.out.println("func");
	}

	@PostConstruct
	public void myInitMethod() {
		System.out.println("init");
	}
}
  • 配置
@Configuration
@ComponentScan("com.mjp.initmethod")
public class MyApplication {
}
  • 测试
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);

Demo bean = context.getBean(Demo.class);
bean.func();
输出结果
bpp-before-init
init
bpp-after-init
func

注意:MyInitBpp的before、after方法会对所有的bean都生效

@PreDestroy

同理@PostConstructor注解的使用

@PreDestroy、@Bean(destroyMethod = “”)、实现DisposableBean接口

@Primary

容器中有多个相同类型的对象时,优先注入被@Primary注解修饰的bean对象

一般结合@Bean或@Autowired

@Profile

1、作用

动态选择不同的bean注入容器

2、场景

我们项目位于src/main/resources/profile下有2个环境的数据库连接,分别为:

  • test

    db.properties

    driverClass=com.mysql.jdbc.Driver
    test.jdbcUrl=jdbc:mysql://localhost:3306/day23?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    user=root
    password=777
    
  • prod

    db.properties

    driverClass=com.mysql.jdbc.Driver
    prod.jdbcUrl=jdbc:mysql://localhost:3306/day23?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    user=root
    password=4396
    

3、诉求

IDEA本地启动项目时,显然是线下test环境,则使用test/db.properties下的数据库连接

项目部署到线上机器时,显示是线上prod环境,则使用prod/db.properties下的数据库连接

在不改动代理的情况下,让项目自己来识别当前环境是test还是prod,动态的去选择要注入的数据库bean

3、实现

  • 动态配置类
@Configuration
@PropertySource(value = {"classpath:profile/test/db.properties","classpath:profile/prod/db.properties"})
public class MyDataSourceConfig {

    @Value("${driverClass}")
    private String driverClass ;

    @Value("${user}")
    private String user ;

    @Value("${password}")
    private String password ;

    // 若当前系统环境是test或默认,则向容器中注入testDataSource的数据库DataSource
    @Profile({"default","test"})
    @Bean
    public DataSource testDataSource(@Value("${test.jdbcUrl}") String jdbcUrl) {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(driverClass);
            ds.setJdbcUrl(jdbcUrl);
            ds.setUser(user);
            ds.setPassword(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ds;
    }

    // 若当前系统环境是prod,则向容器中注入testDataSource的数据库DataSource
    @Profile("prod")
    @Bean
    public DataSource prodDataSource(@Value("${prod.jdbcUrl}") String jdbcUrl) {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(driverClass);
            ds.setJdbcUrl(jdbcUrl);
            ds.setUser(user);
            ds.setPassword(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ds;
    }
}
  • 测试
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 人为的设置当前系统的环境
context.getEnvironment().setActiveProfiles("prod");//"test"
// 除此之外还可以在VM Options中:-Dspring.profiles.active = prod
context.register(MyDataSourceConfig.class);
context.refresh();

System.out.println(context.getBean(DataSource.class));
输出
com.mchange.v2.c3p0.ComboPooledDataSource [ 
    driverClass -> com.mysql.jdbc.Driver,  
    jdbcUrl -> jdbc:mysql://localhost:3306/day23?useUnicode=true&characterEncoding=UTF-8&useSSL=false,
    properties -> {user=******, password=******},其它属性忽略
]

4、原理

@Conditional(ProfileCondition.class)
public @interface Profile {
	String[] value();
}

@Profile注解底层还是@Conditional注解,ProfileCondition类实现了Condition接口


六、设计模式

参考我的另外一篇文章:设计模式实战

创建对象

1、单例模式

HikariDataSource#getConnection:双重校验机制。

2、工厂模式

beanFactory:getBean

3、建造者模式

BeanDefinitionBuilder

BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(Bdrpp2.class);
BeanDefinition bd = bdb.getBeanDefinition();

结构型

1、代理模式

Jdk动态代理、cglib代理

2、装饰器模式

BeanWrapperImpl

在实例化bean的时候,是将具体的bean封装在beanWrapperImpl中

3、适配器模式

AOP中的切面 = 多个Advisor组合

AdvisorAdapter适配器模式


行为型

1、观察者模式

IOC-refresh方法initApplicationEventMulticaster+registerListeners+finishRefresh

Spring观察者

2、模版模式

IOC-refresh方法整个流程分为13个步骤方法,其中onRefresh方法是空实现,留给子类去实现

onRefresh:初始化特定上下文子类中的特殊 Bean

(比如SpringBoot启动时,初始化ServletWebServerApplicationContext中的tomcatWebServerBeanFactory这个bean)

3、策略模式

AbstractApplicationContext:可以通过三种方式(策略)创建Spring上下文

  • FileSystemXmlApplicationContext
  • AnnotationConfigApplicationContext:注解启动类
  • ClassPathXmlApplicationContext:读取xml配置

4、职责链模式

AOP中的多个Advisor对应的Advice(before、after、around、afterReturning)等组成了拦截器链,依次对目标方法进行拦截-增强

参考

本文学习参考了msb、尚硅谷等网络资源~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值