1.代理概念
- 什么是代理
- 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好的隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间
- 什么是静态代理
- 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
- 什么是动态代理
- 在程序运行时,运用反射机制动态创建而成,无需手动编写代码
- JDK动态代理
- CGLib动态代理
- 在程序运行时,运用反射机制动态创建而成,无需手动编写代码
2.静态代理
-
什么是静态代理
- 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
- 通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的
-
优点
- 代理使客户端不需要知道实现类是什么怎么做的,而客户端只需知道代理即可
- 方便增加功能、扩展业务逻辑
-
缺点
- 代理类中出现大量冗余的代码,非常不利于扩展和维护
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度
-
代码示例
package com.gen;interface PayService {void pay(); }class AliPayService implements PayService {@Overridepublic void pay() {System.out.println("支付宝支付");} }class StaticProxyPayService implements PayService {private PayService payService;public StaticProxyPayService(PayService payService) {this.payService = payService;}@Overridepublic void pay() {System.out.println("代理代码begin");this.payService.pay();System.out.println("代理代码end");} }public class Main {public static void main(String[] args) {new StaticProxyPayService(new AliPayService()).pay();} }
3.JDK动态代理
-
JDK动态代理与静态代理一样,目标类需要实现一个代理接口,再通过代理对象调用目标方法
-
编写流程
定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法public interface InvocationHandler {/*** @param proxy 被代理的对象* @param method 要调用的方法* @param args 方法调用时所需参数*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
-
代码示例
package com.gen;import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy;interface PayService {void pay(); }class AliPayService implements PayService {@Overridepublic void pay() {System.out.println("支付宝支付");} }class JdkProxy implements InvocationHandler {/*** 目标对象*/private Object targetObj;/*** 获取代理对象** @param targetObj* @return*/public Object newProxyInstance(Object targetObj) {this.targetObj = targetObj;// 绑定关系,也就是和具体的哪个实现类关联return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {System.out.println("JDK代理代码begin");result = method.invoke(targetObj, args);System.out.println("JDK代理代码end");} catch (Exception e) {e.printStackTrace();}return result;} }public class Main {public static void main(String[] args) {PayService payService = (PayService) new JdkProxy().newProxyInstance(new AliPayService());payService.pay();} }
4.CGLib动态代理
-
CGLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理
-
代码示例
package com.gen;import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;interface PayService {void pay(); }class AliPayService implements PayService {@Overridepublic void pay() {System.out.println("支付宝支付");} }class CglibProxy implements MethodInterceptor {/*** 目标对象*/private Object targetObj;/*** 获取代理对象** @param targetObj* @return*/public Object newProxyInstance(Object targetObj) {this.targetObj = targetObj;Enhancer enhancer = new Enhancer();// 设置代理类的父类enhancer.setSuperclass(this.targetObj.getClass());// 设置回调函数enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object result = null;try {System.out.println("CGLib代理代码begin");result = methodProxy.invokeSuper(o, args);System.out.println("CGLib代理代码end");} catch (Exception e) {e.printStackTrace();}return result;} }public class Main {public static void main(String[] args) {PayService payService = (PayService) new CglibProxy().newProxyInstance(new AliPayService());payService.pay();} }
5.总结
-
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,解耦和易维护
-
两种动态代理的区别
- JDK动态代理:要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以用CGLib动态代理
- CGLib动态代理它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展
- JDK动态代理是自带的,CGLib需要引入第三方包
- CGLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理
-
spring AOP中的代理使用的默认策略
- 如果目标对象实现了接口,则默认采用JDK动态代理
- 如果目标对象没有实现接口,则采用CGLib动态代理
- 如果目标对象实现了接口,程序里面依旧可以指定使用CGLib动态代理