Spring是广大java程序员必学的一个框架,Spring的底层源码十分繁琐,封装特别多的方法,今天就从源码的角度浅谈Spring AOP的底层原理。
首先,第一个问题:什么是AOP?
与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,在这些自上而下的过程中会产生一些横切性的问题,这些横切性的问题和我们主业务的逻辑关系不大,会散落在代码的各个角落,造成难以维护,AOP的编程性的问题就是把业务逻辑和横切的问题进行分离,从而达到解耦的目的,使代码的通用性和开发效率高。
2.AOP的应用场景:
(1)日志记录
(2)权限验证
(3)效率检查
(4)事务管理
…
3.Spring AOP的底层技术
JDK动态代理
Cglib代理
编译时期的织入还是运行时期的织入?两者是在运行时织入
初始化时期织入还是获取对象时期的织入?通过源码分析,可以知道是在初始化时期织入。
4.Spring AOP和AspectJ的关系
Spring AOP提供两种编程风格
@AspectJ support ----------利用AspectJ的注解
Schema-based AOP support ----------xml
证明:Spring,通过源码分析了,我们可以知道Spring底层使用的是JDk或者CGLIB来完成的代理,并且在官网上Spring给出了AspectJ的文档,和SpringAOP是不同的。
AOP是我们要达到的编程目标,面向切面。SpringAOP是实现AOP具体的一个手段。比如AOP是发财,卖肾就是实现发财的一个手段。
AspectJ也可以实现AOP,他跟SpringAOP是同一个级别的,为什么他可以实现AOP了,Spring还要干一个SpringAOP出来,在Spring2.5的时候,也有一个自己的AOP,但是他的语法相当蛋疼,就是别人学起来很难,于是他就借助了AspectJ实现了AOP的语法。
5.AOP开发中相关术语
6.Spring AOP基于AspectJ开发步骤
(1)在配置类开启Spring对AspectJ编码风格的支持(通过注解,还可以通过xml)
@EnableAspectJAutoProxy
(2)声明一个切面类,并交给Spring管理
(3)声明一个切点
(4)声明一个通知
7.证明Spring使用JDK动态代理还是CGLIB代理(大误区):
看一下下面这两种情况:
(1)没有实现接口的
public class Test {
public static void main(String[] args){
//初始化
AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Appconfig.class);
//IndexDaoImpl没有继承接口
IndexDaoImpl dao= (IndexDaoImpl) annotationConfigApplicationContext.getBean("dao");
dao.query();
}
}
(2)有实现接口的
public class Test2 {
public static void main(String[] args){
//初始化
AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Appconfig.class);
IndexDao dao= annotationConfigApplicationContext.getBean(IndexDao.class);
dao.query();
}
}
如果没有接口用CGLIB代理,如果有接口用JDK代理,这句话对不对???
错了,
加上proxyTargetClass=true 这句话后前面使用有接口的情况变成了CGLIB代理
proxyTargetClass=flase 就会变成JDK代理,但是这个值默认就是false,如果这个值是true的话,他就会强制使用CGLIB,不管你有没有接口
那怎么什么时候JDK代理,什么时候CGLIB代理,是有条件的,条件是什么?
取决与proxyTargetClass值和有没有接口,如果proxyTargetClass是true,他就会使用CGLIB,不管有没有接口。如果这个值为false(默认为false),他有接口则使用JDK动态代理,如果没有接口就使用CGLIB代理。
8.那从IndexDaoimpl对象到CGLIB代理对象,经历了什么?
IndexDaoImpl—>init(初始化)---->getBean----->cglib Proxy
那他是在init的时候成为的还是在getBean的时候成为了代理对象?
可以看一下下面这个Spring源码,这段可以说是Spring最核心的代码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
Map var4 = this.singletonObjects;
synchronized(this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject != NULL_OBJECT ? singletonObject : null;
}
singletonObjects返回的是一个ConcurrentHashMap,这是个线程安全的HashMap,这个map就是Spring中所谓的IOC容器,key是名字,value是包名
从这边可以看出Spring织入在初始化(也就是把对象放进IOC容器)的时候就已经完成了,这边是从IOC容器里面拿出来get的。