代理模式
我们知道, Spring AOP 的主要作用就是不通过修改源代码的方式, 将非核心功能代码织入来实现对方法的增强. 那么 Spring AOP 的底层如何实现对方法的增强? 实现的关键在于使用了代理模式
代理模式的作用就是为其它对象提供一种代理, 以控制对这个对象的访问, 用于解决在直接访问对象时带来的各种问题. 代理主要分为两种方式: 静态代理和动态代理
静态代理
静态代理主要通过将目标类与代理类实现同一个接口, 让代理类持有真实类对象, 然后在代理类方法中调用真实类方法, 在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的
示例代码:
- /**
- * 代理类与目标类的共同接口
- */
- public interface Subject {
- void request();
- void response();
- }
- /**
- * 目标类
- */
- public class RealSubject implements Subject {
- @Override
- public void request() {
- System.out.println("执行目标对象的 request 方法......");
- }
- @Override
- public void response() {
- System.out.println("执行目标对象的 response 方法......");
- }
- }
- /**
- * 代理类
- */
- public class ProxySubject implements Subject {
- private Subject subject;
- public ProxySubject(Subject subject) {
- this.subject = subject;
- }
- @Override
- public void request() {
- System.out.println("before 前置增强");
- subject.request();
- System.out.println("after 后置增强");
- }
- @Override
- public void response() {
- System.out.println("before 前置增强");
- subject.response();
- System.out.println("after 后置增强");
- }
- }
- public class Main {
- public static void main(String[] args) {
- // 目标对象
- Subject realSubject = new RealSubject();
- // 代理对象 通过构造器注入目标对象
- Subject proxySubject = new ProxySubject(realSubject);
- proxySubject.request();
- proxySubject.response();
- }
- }
运行结果:
before 前置增强
执行目标对象的 request 方法......
after 后置增强
before 前置增强
执行目标对象的 response 方法......
after 后置增强
通过以上的代码示例, 我们不难发现静态代理的缺点. 假如我们的 Subject 接口要增加其它的方法, 则 ProxySubject 代理类也必须同时代理这些新增的方法. 同时我们也看到, request 方法和 response 方法所织入的代码是一样的, 这会使得代理类中出现大量冗余的代码, 非常不利于扩展和维护. 为了解决静态代理的这些缺陷, 于是有了动态代理
动态代理
与静态代理相比, 动态代理的代理类不需要程序员自己手动定义, 而是在程序运行时动态生成
动态代理可以分为 JDK 动态代理和 CgLib 动态代理
1.JDK 动态代理
JDK 动态代理与静态代理一样, 目标类需要实现一个代理接口, 它的开发步骤如下:
1. 定义一个 java.lang.reflect.InvocationHandler 接口的实现类, 重写 invoke 方法
2. 将 InvocationHandler 对象作为参数传入 java.lang.reflect.Proxy 的 newProxyInstance 方法中
3. 通过调用 java.lang.reflect.Proxy 的 newProxyInstance 方法获得动态代理对象
4. 通过代理对象调用目标方法
示例代码:
- /**
- * 自定义 InvocationHandler 的实现类
- */
- public class JdkProxySubject implements InvocationHandler {
- private Subject subject;
- public JdkProxySubject(Subject subject) {
- this.subject = subject;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("before 前置通知");
- Object result = null;
- try {
- result = method.invoke(subject, args);
- }catch (Exception ex) {
- System.out.println("ex:" + ex.getMessage());
- throw ex;
- }finally {
- System.out.println("after 后置通知");
- }
- return result;
- }
- }
- public class Main {
- public static void main(String[] args) {
- // 获取 InvocationHandler 对象 在构造方法中注入目标对象
- InvocationHandler handler = new JdkProxySubject(new RealSubject());
- // 获取代理类对象
- Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);
- // 调用目标方法
- proxySubject.request();
- proxySubject.response();
- }
- }
运行结果:
before 前置通知
执行目标对象的 request 方法......
after 后置通知
before 前置通知
执行目标对象的 response 方法......
after 后置通知
2.CgLib 动态代理
CgLib 动态代理的原理是对指定的业务类生成一个子类, 并覆盖其中的业务方法来实现代理. 它的开发步骤:
1. 定义一个 org.springframework.cglib.proxy.MethodInterceptor 接口的实现类, 重写 intercept 方法
2. 获取 org.springframework.cglib.proxy.Enhancer 类的对象
3. 分别调用 Enhancer 对象的 setSuperclass 和 setCallback 方法, 使用 create 方法获取代理对象
4. 通过代理对象调用目标方法
示例代码:
- /**
- * 自定义 MethodInterceptor 实现类
- */
- public class MyMethodInterceptor implements MethodInterceptor {
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println("before 前置通知");
- Object result = null;
- try {
- result = methodProxy.invokeSuper(obj, args);
- }catch (Exception ex) {
- System.out.println("ex:" + ex.getMessage());
- throw ex;
- }finally {
- System.out.println("after 后置通知");
- }
- return result;
- }
- }
- public class Main {
- public static void main(String[] args) {
- // 获取 Enhancer 对象
- Enhancer enhancer = new Enhancer();
- // 设置代理类的父类 (目标类)
- enhancer.setSuperclass(RealSubject.class);
- // 设置回调方法
- enhancer.setCallback(new MyMethodInterceptor());
- // 获取代理对象
- Subject proxySubject = (Subject)enhancer.create();
- // 调用目标方法
- proxySubject.request();
- proxySubject.response();
- }
- }
运行结果:
before 前置通知
执行目标对象的 request 方法......
after 后置通知
before 前置通知
执行目标对象的 response 方法......
after 后置通知
3. 两种代理的区别
JDK 动态代理和 CgLib 动态代理的主要区别:
JDK 动态代理只能针对实现了接口的类的接口方法进行代理
CgLib 动态代理基于继承来实现代理, 所以无法对 final 类, private 方法和 static 方法实现代理
Spring AOP 的代理
Spring AOP 中的代理使用的默认策略是:
如果目标对象实现了接口, 则默认采用 JDK 动态代理
如果目标对象没有实现接口, 则采用 CgLib 进行动态代理
如果目标对象实现了接口, 且强制 CgLib 代理, 则采用 CgLib 进行动态代理
来源: https://www.cnblogs.com/liantdev/p/10132680.html