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