参考Spring复习
Spring 是一个轻量级的 IoC 和 AOP 容器框架,是为 Java 应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于 XML 的配置,基于注解的配置,基于 Java 的配置;
方便解耦,简化开发 ;
Spring 的 DI(依赖注入) 机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
Spring 提供了 AOP 技术,支持将一些通用任务,如安全,事务,日志,权限等进行集中式管理,从而提供更好的复用;
Spring 对于主流的应用框架提供了集成支持;
IoC 就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。
DI(Dependency Injection) 依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖 IoC 容器来动态注入对象需要的外部资源;也就是说,我们的程序在编写时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况,那这种依赖关系(如业务层和持久层直接的依赖关系),在使用 Spring 之后,就让 Spring 来维护了。
BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器;其中 ApplicationContext 是BeanFactory 的子接口;
Spring 容器中的 bean 可以分为5个范围:
setter 方法注入,构造器注入,静态工厂注入,实例工厂注入;
setter 方法注入
public class Person{
private String name;
private int age;
public Person(){
}
public Person(String name,String age){
this.name = name;
this.age = age;
}
public setName(String name){
this.name = name;
}
public setAge(int age){
this.age = age;
}
public getName(){
return this.name;
}
public getAge(){
return this.age;
}
}
xml 文件
<bean id="person" class="com.qixia.test.Person">
<property name="name" value="Qixia">property>
<property name="age" value="18">property>
bean>
构造器注入
无参
<bean id="Person" class="com.qixia.test.Person">bean>
有参
index 用来指定有参构造器中的参数位置,从 0 开始;
<bean id="Person" class="com.qixia.test.Person">
<constructor-arg index="0" value="Qixia">constructor-arg>
<constructor-arg index="1" value="18">constructor-arg>
bean>
静态工厂
public class DaoFactory {
//静态工厂
public static final FactoryDao getStaticFactoryDaoImpl(){
return new StaticFacotryDaoImpl();
}
}
xml
factory-method 指定生产对 id 象的静态方法 ;
<bean id="daoFactory" class="com.qixia.test.DaoFactory" factory-method="createAccountService">
bean>
实例工厂
public class InstanceFactory {
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
xml
factory-bean:用于指定实例工厂 bean 的 id;
factory-method 属性:用于指定实例工厂中创建对象的方法;
<bean id="instanceFactory" class="com.qixia.test.InstanceFactoryInstanceFactory">bean>
<bean id="factory" factory-bean="instancFactory" factory-method="createAccountService">bean>
声明:使用注解方式注入一个 bean 实例;
总结
Spring 框架并没有对单例 bean 进行任何多线程的封装处理;对于单例 bean,所有线程都共享一个单例实例 bean,因此是存在资源的竞争;
如果单例 bean 是一个无状态 bean(调用 bean 的方法不会使 bean 的字段属性发生改变),也就是线程中的操作不会对 bean 的成员执行查询以外的操作,那么这个单例 bean 是线程安全的,比如 SpringMVC 的 Controller,Service,Dao 等类,这些 bean 大多是无状态的,只关注于方法本身;
最浅显的解决办法就是将多态 bean 的作用域由 singleton 变更为 prototype;
对于原型(prototype) bean,每次创建一个新对象,也就是线程之间并不存在 bean 共享,自然是不会有线程安全的问题
注意:Spring 容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于 Bean 本身的特性!
循环依赖其实就是循环引用,也就是两个或则两个以上的 bean 互相持有对方,最终形成闭环。比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A;如下图所示:
Spring 中循环依赖的场景有:
什么情况下循环依赖可以被处理?
注意:“全部的”构造器的循环依赖是无法被解决的,只能拋出 BeanCurrentlyInCreationException 异常;
假设,有类 A 和 B,A 中通过 setter 注入了 B,而 B 中通过 setter 注入了 A
首先,创建 bean 实例主要经过以下三步:
Spring 为了解决单例的循环依赖问题,使用了三级缓存,并且这三级缓存是在 方法 getBean 中的:
创建 A 实例时,调用 getBean 方法,最终回调用到 doGetBean 方法中:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 方法1)从三个map中获取单例类
Object sharedInstance = getSingleton(beanName);
// 省略无关代码
}
else {
// 如果是多例的循环引用,则直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 省略若干无关代码
try {
// Create bean instance.
if (mbd.isSingleton()) {
// 方法2) 获取单例对象
sharedInstance = getSingleton(beanName, () -> {
try {
//方法3) 创建ObjectFactory中getObject方法的返回值
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
// 省略若干无关代码
return (T) bean;
}
第一次就会在标注的 方法1 中进行实例的获取(该方法是一个重载方法),如下代码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);//尝试获取一级缓存
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);//尝试获取二级缓存
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//尝试在三级缓存中获取,也就是在对象工厂中获取
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//将三级缓存对象移动到二级缓存之中,因此二级缓存earlySingletonObjects存放的有可能是经过AOP增强的代理对像
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
由于开始时实例 A 并未创建,那么就会继续执行方法 doGetBean 的逻辑,然后就会执行标注的 方法3(一个函数式接口方法 createBean):
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 省略无关代码
try {
//获取对象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
// 省略无关代码
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 省略代码
if (instanceWrapper == null) {
// 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 重点!!!将实例化的对象添加到singletonFactories中(暴露在三级缓存中)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化bean
Object exposedObject = bean;
try {
//为当前的实例对象进行属性的注入
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 省略无关代码
return exposedObject;
}
该方法执行完毕后就会将未完全初始化好该 bean 的情况下直接暴露在三级缓存 singletonFactories 之中;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
当 A 完成了实例化并添加进了三级缓存后,就要开始为 A 进行属性注入了,在注入时发现 A 依赖了 B,那么这个时候 Spring 又会去 getBean(b),然后反射调用 setter 方法完成属性注入;
因为 B 需要注入 A,所以在创建 B 的时候,又会去调用 getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的 getBean 是为了创建 Bean,而此时再调用 getBean 不是为了创建了,而是要从缓存中获取(方法1),由于之前 A 在实例化后已经将其放入了三级缓存 singletonFactories 中,所以此时 doGetBean 方法时就会在标注的 方法1 中进行获取,第一步,先获取到三级缓存中的工厂,第二步,调用对象工厂的 getObject 方法来获取到对应的对象,此时会将实例对象 A 移动到二级缓存之中,最终得到这个对象后将其注入到 B 中;
B 拿到 A 对象实例后顺利完成了创建对象实例后的属性注入以及初始化过程,并且完全初始化之后将自己放入到一级缓存 singletonObjects 中。
此时再返回 A 的属性注入的方法中,A 就能够顺利的拿到 B 的实例并且完成 A 的属性注入以及初始化等方法。接着通过标注的 方法2 继续进行实例的获取,就会调用重载的方法 getSingletion,最终 A 也存入了一级缓存 singletonObjects 中:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 省略无关代码
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// 省略无关代码
try {
//通过方法3后然后在该方法中直接获取已经创建的对象实例即可。
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略无关代码
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
//步骤A
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
步骤A:将实例添加到一级缓存中。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//添加单例对象到map中
this.singletonObjects.put(beanName, singletonObject);
//从早期暴露的工厂中移除,此map在解决循环依赖中发挥了关键的作用
this.singletonFactories.remove(beanName);
//从早期暴露的对象map中移除
this.earlySingletonObjects.remove(beanName);
//添加到已注册的单例名字集合中
this.registeredSingletons.add(beanName);
}
}
在整个过程中可以看出缓存(map)存放时的优先顺序。其中 singletonObjects 里面存放的是初始化之后的单例对象;earlySingletonObjects 中存放的是一个已完成实例化未完成初始化的早期单例对象;而 singletonFactories 中存放的是 ObjectFactory 对象,此对象的 getObject 方法返回值即刚完成实例化还未开始初始化的单例对象。所以先后顺序是,单例对象先存在于 singletonFactories 中,后存在于 earlySingletonObjects 中,最后初始化完成后放入 singletonObjects 中。
知道这些流程之后,我们就能够知道,A 的构造方法中依赖了 B 的实例对象,同时 B 的构造方法中依赖了 A 的实例对象的这种情况下是无法完成依赖注入的,这就是因为,加入 singletonFactories 三级缓存的前提是使用 createBeanInstance 方法调用构造器创建了实例对象!!
因此,Spring 通过将实例化后的对象提前暴露给 Spring 容器中的 singletonFactories,解决了循环依赖的问题。
在普通的循环依赖的情况下,三级缓存没有任何作用,三级缓存实际上跟 Spring 中的 AOP 相关,在 AOP 下继续观察方法 getEarlyBeanReference:
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;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
如果在开启 AOP 的情况下,那么就是调用到 AnnotationAwareAspectJAutoProxyCreator
的 getEarlyBeanReference
方法,对应的源码如下:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
这样就可以看出对 A 进行了 AOP 代理的话,那么此时 getEarlyBeanReference 将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着 B 中注入的 A 将是一个代理对象而不是 A 的实例化阶段创建后的对象;
AOP:面向切面编程(Aspect-Oriented Programming),可以说是对 OOP 的一种补充,专门用于处理一些具有横切性质的服务。指在程序运行期间动态的将某段代码切入到指定方法指定位置并进行运行的编程方式;
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect);
如何有效的查看源码:给容器中注入了什么组件(对象实例),该组件什么时候工作,该组件的功能是什么?
声明:使用注解 @EnableAspectJAutoProxy 开启 AOP 功能,而不是 xml 形式。
首先在该注解中会导入 AspectJAutoProxyRegistrar(实现接口 ImportBeanDefinitionRegistrar ) 类给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator 实例,该实例就是基于注解进行 AOP 代理对象的生成器;
AnnotationAwareAspectJAutoProxyCreator 类有如下继承关系;我们可以清楚地发现,它实现了 InstantiationAwareBeanPostProcessor接口,而该接口继承于 BeanPostProcessor 接口,说明 Spring AOP 是使用 BeanPostProcessor 来做对连接点的拦击和处理的;
当然,目前的 AnnotationAwareAspectJAutoProxyCreator 类肯定不能完成 AOP 的所有逻辑,那么肯定会在其父类中有所实现,那么就需要去关注该类的父类以及该父类的其它子类;
在我们启动容器时进行容器的加载与创建时,就会来到 AnnotationConfigApplicationContext 类中的方法 refresh() 方法;而该方法就是用来创建整个 IoC 容器的方法;由于 Spring AOP 的实现是使用 BeanPostProcessor 接口,那么在 refresh() 方法中就会调用方法 registerBeanPostProcessors(beanFactory) 来对容器中的 BeanPostProceeanssor 的实现类进行一个注册加载(创建对应的实现类对象实例);
由于 AnnotationAwareAspectJAutoProxyCreator 实现了接口 Ordered,因此会在 registerBeanPostProcessors 中的第二个位置被注册(第一个注册是实现了PriorityOrdered接口的BeanPostProcessors,第二个是实现了Ordered,最后一个注册就是常规的BeanPostProcessors实现类),第一次注册时是没有 AnnotationAwareAspectJAutoProxyCreator 实例的,因此就直接去创建一个 AnnotationAwareAspectJAutoProxyCreator 的实例对象;
接着在创建实例后(也就是一直调用到 doCreateBean方法),就会进行初始化 bean 时,也就是方法 initializeBean;
该方法中也调用了关于 Ware 接口的设置方法(如果实现了BeanNameWare,BeanFactoryWare,ApplicationContextWare接口),调用 invokeAwareMethods(beanName, bean) 执行这些接口中的方法,其中在 AOP 中就会将 BeanFactory 注入;
然后继续执行 BeanPostProcessor 的 前置处理方法和后置处理方法;
该方法中有方法 applyBeanPostProcessorsBeforeInitialization ,该方法就是获取到容器中所有的 BeanPostProcessor 的实现类,然后使用 for 循环逐个的调用 BeanPostProcessor 接口中的 postProcessBeforeInitialization方法;对应的就会在applyBeanPostProcessorsAfterInitialization 方法中执行 postProcessAfterInitialization 方法;
最终就会创建出 AnnotationAwareAspectJAutoProxyCreator 对象的实列;(注意:目前创建的对象实例属于切面的对象实例,只有当我们执行连接点时,才会使用目前创建出的实例调用对应的通知方法)
需要注意的是:AnnotationAwareAspectJAutoProxyCreator 实现的是 BeanPostProcessor 的子类 InstantiationAwareBeanPostProcessor 接口,而该接口中的前置处理方法和后置处理方法分别是:postProcessBeforeInstantiation 和 postProcessAfterInstantiation,这两个方法和 BeanPostProcessor 中的方法名称不一样;
主要在 refresh() 方法中的 finishBeanFactoryInitialization 方法中进行容器中剩余的所有的 bean 实例的创建(如果之前创建过那么直接返回,否则就逐个遍历并创建);
在方法 createBean 中会调用方法 resolveBeforeInstantiation ,而该方法发就是使用 BeanProstProcessor 返回一个代理对象(因为 AOP 使用的就是代理模式),如果没有就继续之前的创建 bean 实例的流程;
resolveBeforeInstantiation 方法中就会有如下代码:
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
在 applyBeanPostProcessorsBeforeInstantiation 方法中就会有如下代码:
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
for 循环逐个遍历哪个类实现了 InstantiationAwareBeanPostProcessor 接口,很显然,之前我们也看见 AnnotationAwareAspectJAutoProxyCreator 的父类就实现了该接口,因此 Spring AOP 就是利用这一点来拦截容器中的所有bean 并获取 AOP 中的实现的 postProcessBeforeInstantiation 方法(如果有就获取并执行);
接着就会在 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 类中使用 postProcessBeforeInstantiation 方法进行 bean 的拦截,并且判断 bean 类型,是否是切面或者是否是目标对象,最后返回对象;
注意:第一次创建目标对象时,该 bean 什么都没有(初始化尚未结束),因此返回的就是一个null!!
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
、
//将拦截的 bean 进行获取
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//判断拦截的bean是否是“增强的bean”,即,需要AOP的业务类,也就是目标对象
//第一次时,该bean不是被增强的
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
/*isInfrastructureClass方法 判断是否实现了Advisor,Pointcut,AopInfrastructureBean接口
或者判断该bean是否是切面
*/
/*
shouldSkip方法,判断是否直接跳过
该方法中会去获取切面中的所有的通知方法【List candidateAdvisors】
由于方法中会判断通知Advice是否是 AspectJPointcutAdvisor 类型的,如果是就返回true,
否则就返回false;
*/
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
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;
}
然后执行方法 postProcessAfterInitialization ,注意:此时执行 postProcessAfterInitialization 方法时,此时已经创建了 bean 的实列(因为该方法就是 bean 的后置处理方法);而在该方法中调用方法 wrapIfNecessary ,该方法就是判断是否需要给目前的 bean 实列进行一个包装(包装通知方法,即包装增强器 Advice)
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//判断是否是目标对象
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//判断是否是目标对象,经过这几步,如果是目标对象,也就是需要代理的类,
//那么就进行下面的包装;
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
/*
getAdvicesAndAdvisorsForBean 方法用来查找目标对象bean的所有的增强器,并且将能够
使用的增强器加入该bean实例中;
*/
//Object[] specificInterceptors 存储了所有可用的增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
//保存当前目标对象bena
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
方法 createProxy 创建目标对象的代理对象;
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//获取所有的增强器,即通知方法
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//将这些方法保存到代理工厂中
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//使用代理工厂创建目标方法的代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
方法 proxyFactory.getProxy 创建目标对象的代理对象;
判断是否是 JDK 动态代理对象,还是 Cglib 动态代理对象;
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
//创建 JDK 动态代理
return new JdkDynamicAopProxy(config);
}
//创建 Cglib 动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
最终在方法 proxyFactory.getProxy 中返回一个动态代理对象:
在上一步我们可以获取到了创建好的代理对象,并且该对象中保存了所有可以使用的增强器等信息数据;
如果该对象是 Cglib 创建出来的代理对象,那么当我们执行目标对象的目标方法时,就会被 CglibAopProxy中的 intercept 方法所拦截;
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
//获取目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//根据 ProxyFactory 对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
//如果该拦截器链是空的,那么直接去执行目标对象的目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//执行目标方法
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
如何获取当前目标方法的拦截器链?
List< Object > chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 方法
遍历并获取所有的增强器,并且将所有的增强器转为 MethodInterceptor[] interceptors 方法拦截器;(会判断类型是否匹配,如果匹配就直接进行一个转换,也就是适配器模式);
当上一步方法拦截器创建好之后,就会进行目标方法,通知方法的执行(proceed 方法):
在执行前,会先使用索引(开始时索引为-1)去获取拦截器中的通知方法,并且去执行该通知方法,但是在此时并未执行通知方法,而是继续获取到下一个拦截器方法(通知方法);
直到所有的拦截器方法(通知方法)获取完毕后(如果通知方法中存在前置通知,那么前置通知就是最后一个获取到的),就会正真的执行该通知方法,并且执行完毕后,就会返回执行之前获取到的拦截器方法(通知方法);
注意:在前置通知方法执行完毕后,就会去调用目标方法执行;
然后其它的通知方法就会依次执行;
public Object proceed() throws Throwable {
//判断当前的拦截器方法链(所有通知方法)的长度-1是否等于currentInterceptorIndex索引
//currentInterceptorIndex索引每次执行proceed方法会+1
//如果当前通知方法是4个(还有一个默认的ExposeInvocationIntercepter,总共5个,则
//interceptorsAndDynamicMethodMatchers长度也就是5),那么
//当执行完前置通知方法时,interceptorsAndDynamicMethodMatchers的长度就会是5-1 =4
//而此时currentInterceptorIndex索引也是4,那么就直接调用连接点方法,也就是目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//前置通知方法完成后就会直接执行目标方法(连接点)
return invokeJoinpoint();
}
//每次执行proceed方法索引会+1(开始索引为-1)
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
//执行通知方法,但是开始时并未执行通知方法,而是继续获取到下一个拦截器方法(通知方法)
//也就是当前置通知方法调用完invoke后,在前置通知执行方法中又会调用proceed方法(递归)
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的;
示例
配置类:
数据源,JDBCTemplate 以及 配置事务管理器 TransactionManager;
@Configuration
@EnableTransactionManagement
@ComponentScan(value = "com.qixia.test")
public class TxConfig {
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername("root");
druidDataSource.setPassword("song1202S");
druidDataSource.setUrl("jdbc:mysql:///test");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return druidDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(dataSource());
}
}
Dao 类
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional
public void insert() {
String sql = "insert into user(name,password,address,phone) values(?,?,?,?)";
jdbcTemplate.update(sql, "七夏", "123456", "岐山", "110120119");
//会进行回滚
int i = 10 / 0;
}
}
测试类
public class TxTest {
@Test
public void test(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TxConfig.class);
UserDao userDao = ac.getBean(UserDao.class);
userDao.insert();
}
}
Spring 的事物种类
Spring 支持编程式事务管理和声明式事务管理两种方式:
编程式事务管理使用 TransactionTemplate ;
声明式事务管理建立在 AOP 之上,其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务;
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件或配置类中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中;
声明式事务管理要优于编程式事务管理,这正是 Spring 倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
在注解 @Transactional 注解中使用 propagation 属性进行设置;
事务传播行为 | 解释 |
---|---|
PROPAGATION_REQUIRED | 如果有事务在运行就加入该事务,否则,就启动一个新的事务,并在自己的事务内运行 |
PROPAGATION_REQUIRED_NEW | 不管当前有没有事物都新建一个事物 |
PROPAGATION_SUPPORTS | 如果当前存在事物就加入该事务,否则就以非事物方式运行 |
PROPAGATION_NOT_SUPPORTED | 以非事物方式运行,如果当前存在事物则将当前事物挂起 |
PROPAGATION_MANDATORY | 当前存在事物时就加入该事务,否则就抛出异常 |
PROPAGATION_NEVER | 以非事物方式运行,如果存在当前存在事物则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行,否则新建一个事物 |
MySQL 中事物的默认隔离级别是 REPEATABLE_READ,可重复读;
隔离级别 | 解释 |
---|---|
ISOLATION_DEFAULT | 是 PlatformTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别 |
ISOLATION_READ_UNCOMMITTED | 读未提交,允许另外一个事务可以看到这个事务未提交的数据 |
ISOLATION_READ_COMMITTED | 读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新 |
ISOLATION_REPEATABLE_READ | 可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新 |
ISOLATION_SERIALIZABLE | 序列化,一个事务在执行的过程中完全看不到其他事务对数据库所做的更新 |