一 Spring-AOP
1.对SpringAOP理解
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP(Aspect-Oriented Programming:面向切面编程):将哪些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理,日志管理,权限控制)封装抽成一个可重用的模块,这个模块被命名为“切面”,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可拓展性和可维护性。
SpringAOP基于动态代理实现:
- 如果被代理的对象,已经实现某个接口,则SpringAOP会使用JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
- 如果被代理的对象,没有实现某个接口,就无法使用JDK Proxy去处理代理了,这时候Spring会使用Cglib,基于继承的方式,生成一个被代理类对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类)。
2.AOP通知
概念:AOP将抽取出来的共性功能称为通知;
通知类型:以通知在上下文中的具体位置作为划分
解释:通知就是需要增强的方法内容以及执行位置的结合。
前置通知-before,返回通知-after-returning,异常通知-after-throwing,后置通知-after,环绕通知-around.
<!-- aop配置--><aop:config>
<!-- 配置切面--><aop:aspect id="mian" ref="logger">
<!-- 配置切点--><aop:pointcut id="cut" expression="execution(public * com.itheima.service.*.*(..))"></aop:pointcut>
<!-- 配置通知-->
<!-- 前置通知--><aop:before method="beforeLog" pointcut-ref="cut"></aop:before>
<!-- 后置通知--><aop:after method="afterLog" pointcut-ref="cut"></aop:after>
<!-- 返回通知--><aop:after-returning method="afterReturningLog" pointcut-ref="cut"></aop:after-returning>
<!-- 异常通知--><aop:after-throwing method="afterThrowingLog" pointcut-ref="cut"></aop:after-throwing><aop:around method="aroundLog" pointcut-ref="cut"></aop:around></aop:aspect></aop:config>
3.AOP连接点
AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点。
解释:具备添加通知能力的方法位置,就是连接点,也就是所有类的所有方法
4.AOP切点
AOP将可能被抽取共性功能的方法称为切入点,切入点是连接点的子集。
解释:成功添加了通知方法的位置,就是切点。
<aop:pointcut id="dian" expression="execution(public * com.apesource.service.*.*(..))"/>
5.AOP目标对象
就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的
解释:被代理对象,就是目标对象
6.AOP织入
就是将挖掉的功能回填的动态过程
解释:将通知添加到切点的过程ing,就是织入。
补充:AOP切面:切点+通知
7.SpringAOP+AspectJ实现
1.坐标
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency>
2.配置
<?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: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 id="accountSErviceImp" class="com.itheima.service.AccountServiceImp"></bean><bean id="logger" class="com.itheima.util.Logger"></bean>
<!-- aop配置--><aop:config>
<!-- 配置切面--><aop:aspect id="mian" ref="logger">
<!-- 配置切点--><aop:pointcut id="cut" expression="execution(public * com.itheima.service.*.*(..))"></aop:pointcut>
<!-- 配置通知-->
<!-- 前置通知--><aop:before method="beforeLog" pointcut-ref="cut"></aop:before>
<!-- 后置通知--><aop:after method="afterLog" pointcut-ref="cut"></aop:after>
<!-- 返回通知--><aop:after-returning method="afterReturningLog" pointcut-ref="cut"></aop:after-returning>
<!-- 异常通知--><aop:after-throwing method="afterThrowingLog" pointcut-ref="cut"></aop:after-throwing><aop:around method="aroundLog" pointcut-ref="cut"></aop:around></aop:aspect></aop:config>
</beans>
切点表达式配置语法:
execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
1.修饰符可以省略代表任意
execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
execution(* *.*.*.类名称.方法名称(参数列表))
eg:execution(void *.*.*.ServiceImp.findAll())
4.包名可以使用“..”代表任意个数
execution(* *..类名称.方法名称(参数列表))
eg:execution(void *..ServiceImp.findAll())
5.类名与方法名可以使用“*”代表任意
execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
execution(* *...*.*(..))如果有参数
int======>int
String===>java.lang.String
二 Spring中bean的一生
1.bean实例化的基本流程-底层
Spring容器在进行初始化时,会将xml配置的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建bean实例对象,创建号的Bean对象存储在一个名为singletonObject的Map集合中,当调用getBean方法时,则最终从该Map集合中取出Bean实例对象返回。
Bean 实例化的基本流程
加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;
将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象; 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。
2.Spring 还有其他类型的 “后处理器”
后处理器接口 | 作用阶段 | 核心功能 |
---|---|---|
BeanFactoryPostProcessor | Bean 定义加载后,Bean 实例化前 | 修改 BeanDefinition(Bean 的元信息),例如: - 动态修改 Bean 的属性值、作用域 - 新增 Bean 定义(如 Spring 的 PropertyPlaceholderConfigurer 用于解析${} 占位符) |
BeanDefinitionRegistryPostProcessor | BeanFactoryPostProcessor 的增强版,更早执行 | 向容器中注册新的 BeanDefinition(比BeanFactoryPostProcessor 有更高的优先级),例如:- 动态扫描并注册 Bean(Spring Boot 的 @ComponentScan 底层用到类似机制) |
InstantiationAwareBeanPostProcessor | Bean 实例化前后(比BeanPostProcessor 更早) | 干预 Bean 的实例化过程,例如: - 阻止默认实例化,返回自定义 Bean 对象 - 处理属性注入前的逻辑(如 @Autowired 的依赖查找) |
DestructionAwareBeanPostProcessor | Bean 销毁前 | 处理 Bean 销毁前的逻辑,例如: - 资源释放、状态清理等 |
Spring的后处理器是Spring对外开放的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,以及动态修改Bean的作用。
Spring主要有两种后处理器
- BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例话之前执行;
- BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行;
3.Bean工厂后处理器 – BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么 Spring就会 回调该接口的方法,用于对BeanDefinition注册和修改的功能。
修改:
1. 创建BeanFactoryPostProcessor实现类并重写方法
2. 注入实现类
注册:
1. 创建BeanFactoryPostProcessor实现类并重写方法
2. 注入实现类
Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用 于注册BeanDefinition操作
public class MyBeanFactoryPostProcessor2 implements
BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory
configurableListableBeanFactory) throws BeansException {}@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry
beanDefinitionRegistry) throws BeansException {BeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName("com.apesource.pojo.Student");beanDefinitionRegistry.registerBeanDefinition("stu",beanDefinition);}
}
4.Bean后处理器-BeanPostProcessor
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程。
public class MyBeanPostProcessor implements BeanPostProcessor{public Object postProcessBeforeInitialization(Object bean, String beanName){System.out.println("初始化之前执行");return bean;}public Object postProcessAfterInitialization(Object bean, String beanName){System.out.println("初始化之后执行");return bean;}
5.spring-bean的生命周期
从Bean实例化之后,及通过反射创建出对象之后,到Bean成为一个完整对象最终存储到单例池中,这个过程被成为SPringBean的生命周期。
大致分为三个阶段
- Bean的实例化阶段:spring框架会取出BeanDefinition的信息进行判断当前bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化。
- Bean的初始化阶段:Bean创建之后还仅仅是个”半成品“,还需要对Bean的实例进行填充,执行一些aware接口方法,执行BeanPostProcessor方法,执行InitializingBean接口的初始化方法。执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段。
- Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池sinletonObjects去了,及完成了SPringBean的整个生命周期。
6.Spring Bean的初始化过程涉及如下几个过程:
Bean实例的属性填充
Aware接口属性注入
BeanPostProcessor的before()方法回调
InitializingBean接口的初始化方法回调 自定义初始化方法init回调
BeanPostProcessor的after()方法回调
8.常用的Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接 触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了 ,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。
三 循环依赖-解决
1. Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)。
循环依赖
含义:多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用"。
Spring提供了 三级缓存 存储完整Bean实例和半成品Bean实例,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map
public class DefaultSingletonBeanRegistry ... {//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存" Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存" Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再次提取对象.
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下
- UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
- UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
- UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
- UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
- UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存; UserService 注入UserDao;
- UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。