(一)Spring AOP原理
Spring AOP是基于动态代理来实现AOP的,但是在讲之前我们要来先认识一下代理模式
1.代理模式
其实代理模式很好理解,简单来说就是,原本有一个对象,然后来了另一个对象(我们称为代理),我们想访问对象时,不再直接对目标方法进行调用,是通过代理类间接调用
为什么要使用代理模式?因为可能有些情况下,一个对象不适合或者不能直接引用另一个对象,代理模式就可以起到很好的中介作用
既然我们不是直接访问对象,是通过代理类访问,那我们就可以在代理类中对我们想要访问的对象来进行功能的增强
代理模式中的主要角色
subject:也就是业务接口类,可以是抽象类也可以是接口(不一定有,但我们怎么实现)
RealSubject:业务实现类,具体业务的执行,也就是被代理的对象
Proxy:代理类,RealSubject的代理
代理模式分为静态代理和动态代理
静态代理:由程序员创建代理类并且实现,在程序运行前代理类的class文件就存在
静态代理:在程序运行时,再通过反射机制动态创建
1)静态代理
我们可以简单把代理模式用房屋出租的例子来写出来
首先我们要先写一个房东类,然后给代理开放一个接口,然后定义个代理类,帮房东出租房子
@Slf4j
public class HouseProxy implements HouseSubject{HouseSubject subject;public HouseProxy(HouseSubject subject) {this.subject = subject;}@Overridepublic void rentHouse() {log.info("代理---开始代理");subject.rentHouse();log.info("代理---结束代理");}
}
public interface HouseSubject {public void rentHouse();
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class RealHouseSubject implements HouseSubject {@Overridepublic void rentHouse() {log.info("房东---开始出租");}
}
我们可以看到,这个静态代理虽然完成了目标对象的代理,但是代码都写死了,如果我们要新添加别的方法,就需要全部再添加一遍,很不灵活
这时就需要我们的动态代理了
2)动态代理
相比于静态代理,动态代理时更灵活的我们不需要针对每个⽬标对象都单独创建⼀个代理对象,⽽是把这个创建代理对象的⼯作推迟到程序运⾏时由JVM来实现.也就是说动态代理在程序运⾏时,根据需要动态创建⽣成.
java对动态代理进行了实现,常见的实现方式有两种:JDK动态代理 CGLIB动态代理
JDK动态代理
1.我们还是要先定义一个接口及其实现类(也就相当于静态代理的房东类和他提供的接口)
2.自定义一个InvocationHandler接口并且重写invoke方法,在invoke方法中调用目标方法,并且自定义一些逻辑处理
3.通过Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)方法创建代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JDKInvocationHandler implements InvocationHandler {private Object object;public JDKInvocationHandler(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("中介开始代理");final Object invoke = method.invoke(object, args);System.out.println("中介结束代理");return invoke;}
}
我们使用的时候也很简单
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {HouseSubject houseSubject=new RealHouseSubject();HouseSubject proxy= (HouseSubject) Proxy.newProxyInstance(houseSubject.getClass().getClassLoader(), new Class[]{HouseSubject.class},new JDKInvocationHandler(houseSubject));proxy.rentHouse();}
}
只需要满足我们需要传递的参数即可
那我们现在来简单讲解一下这个代码中比较陌生的地方
1.invovationHandler
这个接口是java动态代理的关键接口,他定义了一个单一方法invoke(),用来处理被代理对象的方法调用
2.Proxy
Proxy类中使用频率最高的方法就是newProxyInstance()这个方法就是来生成一个代理对象的
首先第一个参数需要我们传递的时classLoader,然后第二个参数需要我们传递一个接口,第三个需要我们传递一个要代理的对象
这里因为第二个参数是要提供一个接口的,但是我们又可以不提供这个接口,这就导致JDK动态代理有时候是无法实现的,这时就需要我们使用CGLIB动态代理
CGLIB动态代理
1.定义个被代理类(这里就可以不提供接口了)
2.自定义MethodIntercepter 并且重写intercept方法,类似于invoke
4.通过enhancer类的create()来创建代理类
但是用之前我们需要先添加依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
代码的结构和JDK动态代理是很像的,所以这里就不写了,但是我们还是来讲一下两者代码间的差别
首先就是我们实现的接口不同了,这个接口定义的方法是intercept()用来增强目标
我们这里多路一个参数,但是MethodProxy与Method是类似的,都是调用被代理的方法
再来看看create方法
type就是被代理的类型,callback就是实现后的代理对象
(二)代理原码中的一个重要参数
源码里有个重要的属性叫proxyTargetClass 默认是false,但是可以通过程序设置,那么这个值的true,false又代表了什么呢?
我们再来强调一下,JDK动态代理只能代理接口,不可以直接代理类,CGLIB可以代理接口,也可以代理类
Spring中默认proxyTargetClass的值是false,也就是优先使用JDK代理,只有代理类的时候才使用CGLIB代理 但是SpringBoot2.x之后默认值就是true了,默认是使用CGLIB代理
我们可以在配置文件中更改这个默认值
面试题:
1)什么是AOP?
AOP是面向切面编程,是一种思想-------对一类特定问题的统一处理就。比如Spring实现了AOP这种思想
2)Spring AOP的实现方式有哪些?
我们可以使用@Aspect注解,自定义注解,xml的方式(但是现在不常用了),还可以基于代理的方式实现
3)Spring AOP的实现原理?
Spring AOP是通过动态代理实现的,分别是JDK动态代理,CGLIB动态代理
4)Spring使用了那种方式?
在Spring我们两种方式都是用了,在源码中有一个proxyTargetClass这个参数,他的默认值是false,默认使用JDK代理,当我们提供了接口时我们就是用JDK代理,如果我们没有提供接口我们就是用CGLIB代理
在我们的Spring Boot中,这个参数的默认值时true,就是默认使用CGLIB动态代理
5)JDK和CGLIB的区别?
首先就是JDK动态代理是我们spring原生就提供了的,CGLIB则是需要我们外部引入依赖,并且两者的使用实现的接口,调用的方法都是不同,但是使用上的不同不是最重要的,JDK动态代理无法对没有提供接口的类进行代理,而CGLIB动态代理是可以的,也就是说CGLIB可以直接代理类,JDK不可以