主要内容出自:Java知识体系最强总结(2020版)
一、Spring Beans
1、容器中bean的生命周期
2、bean的作用域
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
注意: 缺省的Spring bean 的作用域是Singleton
。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。
3、Spring框架中的单例bean是线程安全的吗?
不是
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal
进行处理,解决线程安全问题。
二、IOC与DI
IOC 控制反转
“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器
IOC作用:
- 由容器管理对象的创建和整个生命周期,并进行依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
- 解耦,由容器去维护具体的对象
IOC的实现方式:依赖注入(DI)和依赖查找
依赖注入(DI)
让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法
或者带参数的构造器
或者接口
,使容器可以在初始化时组装对象的依赖关系。
依赖注入与依赖查找方式相比,主要优势为:
- 查找定位操作与应用代码完全无关。
- 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
- 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。
IOC实现原理:
- 加载配置文件,解析成 BeanDefinition 放在 Map 里。
- 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
三、AOP
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
1、AOP 有哪些实现方式?Spring AOP 和 AspectJ AOP 有什么区别?
AOP实现的关键在于 代理模式
,AOP代理主要分为静态代理和动态代理
。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
-
AspectJ是静态代理的增强
,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段
将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。 -
Spring AOP使用的动态代理
,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时
在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
2、JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理:
-
JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
-
如果代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
-
就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
3、AOP中特定名词
-
切面(Aspect):切面是通知和切点的结合。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
-
连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
-
通知(Advice):在AOP术语中,切面的工作被称为通知。
-
切点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
-
引入(Introduction):引入允许我们向现有类添加新方法或属性。
-
目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。
-
织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
4、Spring AOP的通知类型
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning ):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
注意:环绕通知,最后不要忘了把结果return出去
同一个aspect,不同advice的执行顺序:
①没有异常情况下的执行顺序:
环绕通知 around before advice
前置通知 before advice
目标方法 target method 执行
环绕通知 around after advice
后置通知 after advice
返回通知 afterReturning
②有异常情况下的执行顺序:
环绕通知 around before advice
前置通知 before advice
目标方法 target method 执行
环绕通知 around after advice
后置通知 after advice
异常通知 afterThrowing
异常发生 java.lang.RuntimeException:
5、Spring AOP内部调用失效问题
一个需要进行AOP增强的类,外部调用methodA()且该方法中调用methodB(),调用methodB()不会执行AOP的增强逻辑。
原因:真正执行methodA()的是目标对象,那么methodA()中调用的methodB()是目标对象的方法,而不是代理对象的。也就自然不会执行AOP的增强逻辑
/**
* 目标对象类
*/
@Service
public class TestAopService {
public void methodA() {
System.out.println("method A run"