Spring之IoC与AOP

Spring之IoC与AOP

1 Spring概述

1.1 Spring简介

Spring官网:http://spring.io
Spring(Spring Framework)是分层的full-stack(全栈)轻量级开源框架,以IoC和AOP为内核,提供了展现层Spring MVC和业务层事务管理等众多的企业级应用技术,同时还能整合开源世界从多著名的第三方框架和类库,已经成为使用最多的Java EE企业应用开源框架。

1.2 Spring的优势

  • 方便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交友Spring进行控制,避免硬编码所造成的过度耦合。
  • AOP编程的支持
    通过Spring的AOP功能,方便进行面向切面的编程,许多通过OOP不容易实现的功能可以通过AOP实现。
  • 声明式事务的支持
    通过Spring提供的@Transactional注解,声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作。
  • 整合集成各种优秀框架
    Spirng可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz、Mybatis等)的直接支持。
  • 降低Java EE API的使用难度
    Spring对Java EE API(如JDBC、JavaMail、远程调用等)进行了略微的封装,使这些API的使用难度降低。
  • 源码是经典的Java学习范例
    Spring的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣,它的源码无疑是Java技术的最佳时间的范例。

1.3 Spring的核心结构

Spring是一个分层非常清晰并且依赖关系、职责定位非常明确的轻量级框架。主要包括几大模块:数据处理模块、Web模块、AOP/Aspects模块、Core Container模块和Test模块,Spring依靠这些基本模块实现了一个融合现有解决方案的零侵入的轻量级框架。

Spring架构

  • Spring核心容器(Core Container)
    是Spring框架最核心的部分,它负责Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,还有多种Spring应用上下文的实现,所有Spring模块都构建于核心容器之上。
  • 面向切面编程(AOP)/Aspects
    Spring对面向切面编程提供了丰富的支持。该模块是Spring应用系统中开发切面的基础,与DI一样,AOP可以帮助应用对象解耦。
  • 数据访问与集成(Data Access/Integration)
    Spring的JDBC和DAO模块封装了大量样板代码,使得数据库访问代码变得简洁,还可以避免数据库资源释放失败而引起的问题。另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进行了集成,如Hibernate、Mybatis等。
  • Web模块
    该模块提供了Spring MVC框架,还提供了多种构建和其他应用交互的远程调用方案。Spring MVC在Web层提升了应用的松耦合水平。
  • Test模块
    为使得开发者能够很方便的进行测试,Spring提供了测试模块以致力于Spring应用的测试

2 核心思想IoC与AOP

2.1 IoC

  • 什么是IoC
    IoC:Inversion of Control(控制反转),是一个技术思想(非技术实现),对象创建、实例化、管理的权力交给IoC容器处理,需要使用的对象从IoC容器中获取。
  • IoC解决了什么问题
    IoC解决了对象之间的耦合问题,对象的创建与实例化有IoC容器管理。
  • IoC与DI的区别
    DI:Dependancy Injection(依赖注入)
    IoC容器负责创建、实例化、管理对象,DI负责进行对象之间依赖的注入

2.2 AOP

  • 什么是AOP

AOP:Aspect oriented Programing,面向切面/方面编程

AOP是OOP的延续,OOP是一种垂直继承体系,AOP是一种横向抽取机制,将横切逻辑代码和业务逻辑代码分离

  • AOP解决了什么问题
    在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
  • 为什么叫面向切面编程
    [切]:指横切逻辑,原有业务逻辑代码不能够变动,只能操作横切逻辑代码,所以面向切面
    [面]:横切逻辑往往影响的是很多方法,每个方法如同一个点,多个点构成一个面

3 Spring IoC高级应用

3.1 Spring IoC基础知识

3.1.1 BeanFactory与ApplicationContext区别

BeanFactory是Spring框架中IoC容器的顶级接口,它定义了一些基础功能与规范。
ApplicationContext是BeanFactory的一个子接口,所以ApplicationContext具备BeanFactory提供的全部功能。
通常,我们称BeanFactory为Spring IoC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,例如国际化支持和资源访问(xml,properties等)
在这里插入图片描述

3.1.1.1 启动IoC容器的方式
3.1.1.1.1 java环境下启动IoC容器
  • ClassPathXmlApplicationContext:从类的根目录下加载配置文件(推荐使用)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
3.1.1.1.2 Web环境下启动IoC容器
  • 从xml启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置Spring ioc容器的配置⽂件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app>
  • 从配置类启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
	<display-name>Archetype Created Web Application</display-name>
<!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
	<context-param>
		<param-name>contextClass</param-name>
		<param-value>org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext</param-value>
	</context-param>
	<!--配置启动类的全限定类名-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>com.ljfngu.SpringConfig</param-value>
	</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
	<listener>
		<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
	</listener>
</web-app>

3.1.2 XML模式

  • 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
3.1.2.1 实例化Bean的三种方式
  • 无参构造
    在默认情况下,它会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建失败。
<!--配置service对象-->
<bean id="userService" class="com.ljfngu.service.impl.UserServiceImpl">
</bean>
  • 使用静态方法创建
<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.ljfngu.factory.UserFactoryBean"
factory-method="getUserService"></bean>
  • 使用实例化方法创建
    和使用静态方法创建类似,区别是使用的不是static修饰的方法创建,而是类中的一个普通方法。
<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="userFactoryBean"
class="com.ljfngu.factory.instancemethod.UserFactoryBean"></bean>
<bean id="userService" factory-bean="userFactoryBean" factorymethod="getUserService"></bean>
3.1.2.2 Bean的作用范围以及生命周期
3.1.2.2.1 作用范围
  • 在Spring框架管理Bean对象的创建时,Bean对象默认是单例的,但是它支持配置的方式改变作用范围。
    在这里插入图片描述
  • 不同作用范围的生命周期
    • 单例模式:singleton
      与容器一致,容器创建时创建,容器销毁时销毁
    • 多例模式:prototype
      使用对象时创建,长时间不使用将被GC回收
  • Bean标签属性
    • id:bean的唯一标识
    • class:用于指定创建bean对象的全限定类名
    • name:用于给bean指定一个或多个名称,用空格分隔
    • factory-bean:用于指定创建当前bean对象的工厂bean的唯一标识,使用该属性将失效class属性
    • factory-method:用户指定创建当前bean对象的工厂方法,与factory-bean配合使用
    • scope:用于指定bean的作用范围
      -init-method:用户指定bean对象的初始化方法,在bean装配后调用
    • destroy-method:用于指定bean对象的销毁方法,此方法会在bean销毁前执行。仅当作用范围为singleton时起作用。
3.1.2.3 DI注入的xml配置
  • 依赖注入分类
    • 按照注入的方式
      • 构造函数注入:利用带参构造函数实现对类成员的赋值
      • set方法注入:通过类成员的set方法实现赋值
    • 按照注入的数据类型分类
      • 基本类型和String:注入的数据类型是基本类型或者是字符串类型的数据
      • 其他Bean类型:注入的数据类型是对象,该对象要求在IoC容器中存在
      • 复杂类型: 注入的数据类型是Array,List,Set,Map,Properties中的一种

3.1.3 注解模式

  • xml中标签与注解的对应
xml形式对应的注解形式
标签@Component、@Controller、@Service、@Repository,注解在类上
标签的scope属性@Scope(“protoryp”),注解在类上
标签的init-method属性@PostConstruct,注解在方法上
标签的destroy-method属性@PreDestroy,注解在方法上
  • DI依赖注入的注解实现方式
    • @Autowired,由Spring提供的注解,需要导入包org.springframwokr.beans.factory.annotation.Autowird,采取的策略为按照类型注入,可以配合@Qualifler指定bean名称注入
    • @Resource,由J2EE提供,需要导入包javax.annotation.Resource,默认按照名称注入1
  • @Configuration注解,表明当前类为配置类
  • @ComponentScan注解,替代context:component-scan,指定扫描的包路径
  • @PropertySource注解,引入外部属性配置文件
  • @Import 引入其他配置类
  • @Value 对象变量赋值,可以直接赋值,也可以使用${}读取配置文件中的信息
  • @Bean注解,将方法返回对象加入IoC容器

3.2 Spring IoC高级特性

3.2.1 lazy-init延迟加载

默认关闭,开启延迟加载,bean将会在getBean时加载。

3.2.2 FactoryBean和BeanFactory

BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,
具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;
Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean), FactoryBean可以⽣成
某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。
Bean创建的三种⽅式中的静态⽅法和实例化⽅法和FactoryBean作⽤类似, FactoryBean使⽤较多,尤
其在Spring框架⼀些组件中会使⽤,还有其他框架和Spring框架整合时使⽤

3.2.3 后置处理器

Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使⽤上是有所区别的。
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情。

Spring生命周期
在这里插入图片描述

3.2.3.1 BeanPostProcessor

BeanPostProcessor是针对Bean级别的处理,可以针对某个具体Bean。

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏。
定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。

3.2.3.2 BeanFactoryProcessor

BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,典型应⽤:PropertyPlaceholderConfigurer

@FunctionalInterface
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

此接⼝只提供了⼀个⽅法,⽅法参数为ConfigurableListableBeanFactory,该参数类型定义了⼀些⽅法,其中有个⽅法名为getBeanDefinition的⽅法,我们可以根据此⽅法,找到我们定义bean 的BeanDefinition对象。

  • BeanDefinition对象:我们在 XML 中定义的 bean标签, Spring 解析 bean 标签成为⼀个JavaBean,这个JavaBean 就是 BeanDefinition。

注意:调用BeanFactoryPostProcessor方法时,这个时候bean尚未实例化,此时bean刚被解析成一个BeanDefinition对象

4 Spring IoC源码深度剖析

4.1 Spring IoC容器初始化主体流程

4.1.1 Spring IoC的容器体系

IoC容器是Spring的核⼼模块,是抽象了对象管理、依赖关系管理的框架解决⽅案。 Spring 提供了很多的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器 必须遵从的⼀套原则,具体的容器实现可以增加额外的功能,⽐如我们常⽤到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等⼀系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等⼀系列的内容。 Spring IoC 容器继承体系⾮常聪明,需要使⽤哪个层次⽤哪个层次即可,不必使⽤功能⼤⽽全的。

BeanFactory顶级接口方法
在这里插入图片描述
BeanFactory容器继承体系
在这里插入图片描述
通过其接⼝设计,我们可以看到我们⼀贯使⽤的 ApplicationContext 除了继承BeanFactory的⼦接⼝,还继承了ResourceLoader、 MessageSource等接⼝,因此其提供的功能也就更丰富了。

4.1.2 Bean生命周期关键时机点

Bean对象创建的几个关键时机点代码层级的调用都在AbstractApplicationContext类的refresh方法中

关键点Spring源码触发代码
构造器refresh#finishBeanFactoryInitialization(beanFactory)
BeanFactoryPostProcessor初始化refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanFactoryPostProcessor方法调用refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanPostProcessor初始化refresh#registerBeanPostProcessors(beanFactory)
BeanPostProcessor方法调用refresh#registerBeanPostProcessors(beanFactory)

4.1.3 Spring IoC容器初始化主体流程

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 第⼀步:刷新前的预处理
			prepareRefresh();

			// 第⼆步:获取BeanFactory;默认实现是DefaultListableBeanFactory,加载BeanDefition 并注册到 BeanDefitionRegistry
			// 获得新鲜的BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 第三步: BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等)
			prepareBeanFactory(beanFactory);

			try {
				// 第四步: BeanFactory准备⼯作完成后进⾏的后置处理⼯作
				postProcessBeanFactory(beanFactory);

				// 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
				registerBeanPostProcessors(beanFactory);

				// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
				initMessageSource();

				// 第⼋步:初始化事件派发器
				initApplicationEventMulticaster();

				// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
				onRefresh();

				// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
				registerListeners();

				/*
				 第⼗⼀步:
				初始化所有剩下的⾮懒加载的单例bean
				初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
				填充属性
				初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、 init-method⽅法)
				调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
			*/
				finishBeanFactoryInitialization(beanFactory);

				// 第⼗⼆步:完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件 (ContextRefreshedEvent)
				finishRefresh();
			}

			.....
		}
	}

4.2 BeanFactory创建流程

4.2.1 获取BeanFactory子流程

时序图:
在这里插入图片描述

4.2.2 BeanDefinition加载解析及注册子流程

4.2.2.1 子流程关键步骤
  • Resource定位:对BeanDefinition的资源定位过程。通俗讲就是找到定义JavaBean信息的XML文件或添加了注解的类,将其封装成Resource对象。
  • BeanDefinition载入:把用户定义好的JavaBean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
  • 注册BeanDefinition到IoC容器
4.2.2.2 BeanDefinition载入过程分析
  • step1:子流程入口:AbstractRefreshableApplicationContext#refreshBeanFactory#loadBeanDefinitions(beanFactory)
  • step2:依次调用多个类的loadBeanDefinitions方法:AbstractRefreshableApplicationContext->AbstractXmlApplicationContext->AbstractBeanDefinitionReader->XmlBeanDefinitionReader,一直执行到XmlBeanDefinitionReader的doLoadBeanDefinitions方法
  • step3:解析xml文件的bean元素为BeanDefinitionHolder对象,然后完成注册,将BeanDefinition对象放入Map中

DefaultListableBeanFactory中对应存储BeanDefinition的定义

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
4.2.2.3 时序图

时序图

4.2.3 Bean创建子流程

  • 通过开始的关键时机点分析,Bean创建子流程入口在AbstractApplicationContext#refresh()方法的finishBeanFactoryInitialization(beanFactory)处
// Instantiate all remaining (non-lazy-init) singletons.
// bean创建子流程入口
finishBeanFactoryInitialization(beanFactory);
  • 进入finishBeanFactory
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		// 实例化所有立即加载的单例bean
		beanFactory.preInstantiateSingletons();
	}
  • 继续进入DefaultListableBeanFactory类的preInstantiateSingletons方法,通过代码发现,工厂bean和普通bean最终都是通过getBean的方法获取实例
// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							// 实例化当前bean
							getBean(beanName);
						}
					}
				}
				else {
					// 实例化当前bean
					getBean(beanName);
				}
			}
		}
  • 通过getBean方法,我们定位到doGetBean方法,这个方法中代码很多,直接找到核心部分
				// Create bean instance.
				// 创建单例bean
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// 创建bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
  • 接着进入到AbstractAutowireCapableBeanFactory类的方法,找到以下部分
try {
			// 创建bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
  • 接着进入doCreateBean方法,方法中两个核心代码逻辑如下所示
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 创建bean实例,尚未设置属性
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		......
		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// Bean属性填充
			populateBean(beanName, mbd, instanceWrapper);
			// 调用初始化方法,应用BeanPostProcessor后置处理器
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

4.2.4 lazy-init延迟加载机制原理

  • lazy-init延迟加载机制分析
    普通Bean的初始化是在容器启动初始化阶段执行的,而被lazy-init=true修饰的bean则是在从容器中第一次进行context.getBean()时进行触发。Spring启动的时候会把所有bean信息解析转化成Spring能够识别的BeanDefinition并存到HashMap里以供下面的初始化时使用,然后对每个BeanDefinition进行处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进行初始化并依赖注入。
		// Trigger initialization of all non-lazy singleton beans...
		// 初始化所有非lazy-init的单例bean
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 非抽象对象,是单例的,非延迟加载的
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}
  • 总结
    • 对于被修饰为lazy-init的bean,Spring容器初始化阶段不会对该bean进行初始化并依赖注入,当第一次进行getBean时才进行初始化并依赖注入
    • 对于非懒加载的bean,getBean的时候会从缓存中取,因为容器初始化阶段bean已经初始化完成并缓存了起来

4.2.5 Spring IoC循环依赖问题

4.2.5.1 什么是循环依赖

循环依赖其实就是循环引用,也就是两个或两个以上的Bean互相持有对方对象的依赖关系,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。

Spring中循环依赖的场景有:

  • 构造器的循环依赖(构造器注入)
  • Field属性的循环依赖(set注入)

其中,构造器的循环依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是三级缓存,提前暴漏对象的方法

4.2.5.2 循环依赖处理机制
  • 单例bean构造器参数循环依赖(无法解决)
  • prototype原型bean循环依赖(无法解决),Spring不支持原型bean的循环依赖
  • 单例bean通过设置属性或者@Autowired进行循环依赖
    Spring的循环依赖是利用三级缓存处理的,理论依据基于Java的引用传递,当获取对象的引用时,对象的属性时可以延后设置的,但是构造器必须时在获取引用之前。
    Spring通过属性设置或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFatory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring IoC容器中。
    • Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器
    • ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在于Spring容器中。
    • Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
    • ClassB调用setClassA方法,Spring从容器中获取ClassA,因为第一步已经提前暴露了ClassA,因此可以获取到ClassA的实例
      • ClassA通过Spring容器获取ClassB,完成了对象初始化操作
    • 这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题

5 Spring AOP高级应用

5.1 AOP相关术语

名词解释
Joinpoint(连接点)它指的是那些可以用于把增强代码加入到业务主线中的点。在Spring框架AOP思想的技术实现中,也只支持方法类型的连接点
Pointcut(切入点)它指的是那些已经把增强代码加入到业务主线进来之后的连接点。
Advice(通知/增强)它指的是切面类中用于提供增强功能的方法。并且不同的方法增强的时机是不一样的。分类:前置通知 后置通知 异常通知 最终通知 环绕通知
Target(目标对象)它指的是代理的目标对象,即被代理对象。
Proxy(代理)它指的是一个类被AOP织入增强后,产生的代理类,即代理对象。
Weaving(织入)它指的是把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Aspect(切面)它指的是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类。例如,事务切面,它里面定义的方法就是和事务相关的,如开启事务,提交事务,回滚事务等等,不会定义其他与事务无关的方法。

5.2 Spring中AOP的代理选择

Spirng实现AOP思想使用的是动态代理技术
默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术,步过我们可以通过配置的方式,让Spring强制使用CGLIB。

5.3 Spring中AOP的配置方式

在Spring的AOP配置中,也和IoC配置一样,支持3种配置方式。

  • 使用XML配置
  • 使用XML+注解组合配置
  • 使用纯注解配置

5.4 Spring中AOP实现

5.4.1 XML模式

Spring是模块化开发的框架,使用AOP就引入AOP的jar

  • 坐标
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.4</version>
</dependency>
  • AOP核心配置
 <bean id="logUtil" class="com.ljfngu.utils.LogUtil"></bean>
 <aop:config>
	 <aop:aspect id="logAdvice" ref="logUtil">
		 <aop:before method="printLog" pointcut="execution(public * com.ljfngu.service.impl.TransferServiceImpl.updateAccountByCardNo(com.ljfngu.pojo.Account))"></aop:before>
	 </aop:aspect>
 </aop:config>
  • 改变代理方式的配置
<aop:config proxy-target-class="true">
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectjautoproxy>
  • 物种通知类型
    • 前置通知:aop:before
      在切入点方法执行之前执行,可以获取切入点方法的参数,并对其进行增强.
    • 正常结束通知:aop:after-returning
    • 异常通知:aop:after-throwing
      异常通知的执行时机是在切入点执行产生异常之后执行,如果未产生异常,则不会执行该通知
    • 最终通知:aop:after
      最终通知的执行时机是在切入点方法执行完成之后,切入点方法返回之前执行,无关是否发生异常
    • 环绕通知:aop:around

5.4.2 XML+注解模式

  • XML中开启Spring对注解AOP的支持
<aop:aspectj-autoproxy/>
  • 代码示例
@Component
@Aspect
public class LogUtil {
 	@Pointcut("execution(* com.lagou.service.impl.*.*(..))")
 	public void pointcut(){}
 	
    @Before("pointcut()")
    public void beforePrintLog(JoinPoint jp){
    	Object[] args = jp.getArgs();
    	System.out.println("前置通知,参数:"+ Arrays.toString(args)); 
    }
    
    @AfterReturning(value = "pointcut()",returning = "rtValue")
    public void afterReturningPrintLog(Object rtValue){
		System.out.println("后置通知,返回值:"+rtValue);
	}
	
    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowingPrintLog(Throwable e){
    	System.out.println("异常通知,异常:"+e);
    }
    
    @After("pointcut()")
    public void afterPrintLog(){ 
    	System.out.println("๋最终通知");
    }
	@Around("pointcut()")
	public Object aroundPrintLog(ProceedingJoinPoint pjp){
		Object rtValue = null;
		try{
			System.out.println("前置通知");
			// 获取参数
			Object[] args = pjp.getArgs();
			// 执行切入点方法
			rtValue = pjp.proceed(args);
			System.out.println("后置通知");
		} catch(Throwable t) {
			System.out.println("异常通知");
			t.printStackTrace();
		} finally {
			System.out.println("最终通知");
		}
		return rtValue;
	}

}

5.4.3 注解模式

使用注解@EnableAspectJAutoProxy替换配置文件中的配置 <aop:aspectj-autoproxy/>

5.5 Spring声明式事务的支持

编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务
声明式事务:通过xml或者注解胚子和的方式达到事务控制的目的就叫做声明式事务

5.5.1 事务回顾

5.5.1.1 事务的概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功,从而确保了数据的准确与安全

5.5.1.2 事务的四大特性
  • 原子性(Atomiccity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
  • 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
  • 隔离性(Isolation) 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,每个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障,也不应该对其有任何影响。
5.5.1.3 事务的隔离级别
  • Serializable(串行化):可避免脏读、不可重复读、幻读。安全级别最高。
  • Repeatable Read(可重复读):可避免脏读、不可重复读的情况发生,幻读有可能发生。安全级别第二。
  • Read Commited(读已提交):可避免脏读情况发生,不可重复读和幻读一定会发生。安全级别第三。
  • Read Uncommited(读未提交):安全级别最低,以上情况均无法保证

MySql默认的隔离级别是:REPEATABLE READ
Oracle默认的隔离级别是:READ COMMITTED

5.5.1.4 事务的传播行为

事务往往在service层进行控制,如果出现service层方法A调用另外一个service层方式B,A和B方法本身都已经被添加了事务控制,那么A调用B的时候,就需要进行事务的一些协商,这就叫做事务的传播行为。
A调用B,我们站在B的角度来观察定义事务的传播行为

PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,就加入到这个事务中,这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PORPAGATION_REQUIRED类似的操作

5.5.2 Spring中事务的API

PlatformTransactionManager:此接口是Spring的事务管理核心接口。Spring本身并不支持事务实现,只负责提供标准,应用底层支持什么样的事务,需要提供具体实现类。此处也是策略模式的具体应用。在Spring框架中,也为我们内置了一些具体策略,如:DataSourceTransactionManager,HibernateTransactionManger等等。

DataSourceTransactionManager 归根结底是横切逻辑代码,声明式事务要做的就是使用AOP来将事务控制i逻辑织入到业务代码

5.5.3 Spring声明式事务配置

  • 纯xml模式,需要导入spirng-tx,spring-context,spring-jdbc,aspectjweaver等jar包
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
		<tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
	</tx:attributes>
</tx:advice>
<aop:config>
  <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.ljfngu.edu.service.impl.TransferServiceImpl.*(..))"/>
</aop:config>

  • 基于xml+注解
    • 配置事务管理器,开启spring对注解事务的支持
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManage r">
    	<property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    
    • 在接口、类或者方法上添加@Transactional注解
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    
  • 基于纯注解
    Spring基于注解启动开发的事务控制配置,只需要把xml配置部分改为注解实现。用@EnableTransactionManagement注解替换配置文件中的<tx:annotation-driven transactionmanager=“transactionManager”/>

6 Spring AOP源码深度剖析

6.1 代理对象创建

6.1.1 AOP基础用例准备

Bean定义

@Component
public class TestBean {
	public void tech(){
	System.out.println("test......");
	}
}

Aspect定义

@Component
@Aspect
public class TestAspect {
	@Pointcut("execution(* com.ljfngu.*.*(..))")
	public void pointcut(){
	}
	@Before("pointcut()")
	public void before() {
		System.out.println("before method ......");
	}
}

测试用例

/**
* 测试⽤例: Aop 代理对象创建
*/
@Test
public void testAopProxyBuild(){
	ApplicationContext applicationContext = new
	AnnotationConfigApplicationContext(SpringConfig.class);
	TestBean testBean = applicationContext.getBean(TestBean.class);
	testBean.tech();
}

6.1.2 时机点分析

在new AnnotationConfigApplicationContext(SpringConfig.class)之后,getBean之前,我们所创建的TestBean对象已经产生,该对象是一个代理对象(Cglib代对象),因此在容器初始化过程中目标Bean已经完成了代理,返回了代理对象。

6.1.3 代理对象创建流程

调用链:

初始化Bean,包括Bean后置处理初始化,Bean的一些初始化方法的执行(init-method),Bean的实现的声明周期相关接口的属性注入
AbstractAutowireCapableBeanFactory#initializeBeanObject,RootBeanDefinition)
	循环执行后置处理器
	AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
		创建代理对象的后置处理器
		AbstractAutoProxyCreator#postProcessAfterInitialization
			AbstractAutoProxyCreator#wrapIfNecessary
				AbstractAutoProxyCreator#createProxy
					ProxyFactory#getProxy
						ProxyFactory#getAopProxyFactory
						AopProxy#createAopProxy
						JdkDynamicAopProxy/ObjenesisCglibAopProxy#getProxy

6.2 Spring声明式事务控制

声明式事务很方便,纯注解,仅仅几个注解就可以控制事务
@EnableTransactionManagement
@Transactional

6.2.1 @EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

@EnableTransactionManagement注解使用@Import注解引入了TransactionManagementConfigurationSelector类,这个类又向容器中导入了两个重要的组件
在这里插入图片描述

6.2.2 加载事务控制组件

  • AutoProxyRegistrar
    AutoProxyRegistrar类的registerBeanDefinitions方法中又注册了一个组件
    在这里插入图片描述
    进入AopConfigUtils.registerAutoProxyCreatorIfNecessary ⽅法在这里插入图片描述
    发现最终注册了一个叫做InfrastructureAdvisorAutoProxyCreator的Bean,而这个类式AbstractAutoproxyCreator的子类,实现了SmartInstantiationAwareBeanPostProcessor接口
    在这里插入图片描述
    继承体系结构图如下
    在这里插入图片描述
    它实现了SmartInstantiationAwareBeanPostProcessor,说明这是一个Bean后置处理器,而且跟Spring AOP开启@EnableAspectJAutoProxy时注册的AnnotationAwareAspectJProxyCreator实现的是同一个接口,所以说,声明式事务是Spring AOP思想的一种应用
  • ProxyTransactionManagementConfiguration组件
    • ProxyTransactionManagementConfiguration是一个容器配置类,注册了一个组件transactionAdvisor,称为事务增强器,然后在这个事务增强器中又注入了两个属性:属性解析器transactionAttributeSource 和 事务拦截器transactionInterceptor
      属性解析器AnnotationTransactionAttributeSource部分源码如下
      在这里插入图片描述
      属性解析器有一个成员变量是annotationParsers,是一个集合,可以添加多种注解解析器(TransactionAnnotationParser),我们关注Spring的注解解析器,部分源码如下:
      在这里插入图片描述
      属性解析器的作用之一就是用来解析@Transactional注解
    • TransactionInterceptor 事务拦截器,部分源码如下
      在这里插入图片描述
  • 上述组件如何关联起来的
    • 事务拦截器实现了MethodInterceptor接⼝,追溯⼀下上⾯提到的InfrastructureAdvisorAutoProxyCreator后置处理器,它会在代理对象执⾏⽬标⽅法的时候获取其拦截器链,⽽拦截器链就是这个TransactionInterceptor,这就把这两个组件联系起来;
    • 构造⽅法传⼊PlatformTransactionManager(事务管理器)、 TransactionAttributeSource(属性解析器),但是追溯⼀下上⾯贴的ProxyTransactionManagementConfiguration的源码,在注册事务拦截器的时候并没有调⽤这个带参构造⽅法,⽽是调⽤的⽆参构造⽅法,然后再调⽤set⽅法注⼊这两个属性,效果⼀样。
  • invokeWithinTransaction ⽅法,部分源码如下(关注1、 2、 3、 4 标注处)
    在这里插入图片描述

声明式事务调用链

@EnableTransactionManagement 注解
1)通过@import引⼊了TransactionManagementConfigurationSelector类它的selectImports⽅法导⼊了另外两个类: AutoProxyRegistrar和ProxyTransactionManagementConfiguration
2) AutoProxyRegistrar类分析⽅法registerBeanDefinitions中,引⼊了其他类,通过AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引⼊InfrastructureAdvisorAutoProxyCreator,它继承了AbstractAutoProxyCreator,是⼀个后置处理器类
3) ProxyTransactionManagementConfiguration 是⼀个添加了@Configuration注解的配置类(注册bean)注册事务增强器(注⼊属性解析器、事务拦截器)
	属性解析器: AnnotationTransactionAttributeSource,内部持有了⼀个解析器集合Set<TransactionAnnotationParser> annotationParsers;具体使⽤的是SpringTransactionAnnotationParser解析器,⽤来解析
@Transactional的事务属性
	事务拦截器: TransactionInterceptor实现了MethodInterceptor接⼝,该通⽤拦截会在产⽣代理对象之前和aop增强合并,最终⼀起影响到代理对象
	TransactionInterceptor的invoke⽅法中invokeWithinTransaction会触发原有业务逻辑调⽤(增强事务)

  1. @Resource在jdk11中已移除,需要单独引入包 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值