一.静态代理
代理类在代码运行前存在,这种代理的方式称为静态代理,这种方式的代理类是通我们自己编写来实现,一般情况下,静态代理模式的代理类和委托类实现同一个接口。
简单地代码实现如下:Lets GO
/** * @Author zhangfu * @Date 23:17 2019/8/5
* 接口 */ public interface UserManager { public void addUser(String userId, String userName); public void delUser(String userId); public String findUser(String userId); public void modifyUser(String userId, String userName); }
/** * @Author: 13394 * @CreateDate: 2019/8/5 23:22 * 真实对象 */ public class UserManagerImpl implements UserManager { @Override public void addUser(String userId, String userName) { System.out.println("添加用户!!!!!!!"); } @Override public void delUser(String userId) { System.out.println("删除用户!!!!!!!"); } @Override public String findUser(String userId) { System.out.println("查询用户!!!!!!!"); return null; } @Override public void modifyUser(String userId, String userName) { System.out.println("修改用户!!!!!!!"); } }
/** * @Author: 13394 * @CreateDate: 2019/8/5 23:25
* 代理类 */ public class ProxyUserManagerImpl implements UserManager { private UserManager userManager; public ProxyUserManagerImpl(UserManager userManager) { this.userManager = userManager; } @Override public void addUser(String userId, String userName) { System.out.println("添加用户开始!!!!!!!!!!!!!!!"); userManager.addUser(userId,userName); System.out.println("添加用户结束!!!!!!!!!!!!!!!!!"); } @Override public void delUser(String userId) { } @Override public String findUser(String userId) { return null; } @Override public void modifyUser(String userId, String userName) { } }
/** * @Author: 13394
* 测试类 */ public class ClientTest { public static void main(String[] args) { UserManager userManager=new ProxyUserManagerImpl(new UserManagerImpl()); userManager.addUser("1","张三"); } }
二.动态代理,JDK内置的Proxy实现
什么是动态代理:
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口
public interface Subject { void hello(String str); String bye(); }
//委托类
public class RealSubject implements Subject{ @Override public void hello(String str) { System.out.println("Hello " + str); } @Override public String bye() { System.out.println("Goodbye"); return "Over"; } }
//中介类
public class InvocationHandlerDemo implements InvocationHandler { // 这个就是我们要代理的真实对象 private Object subject; // 构造方法,给我们要代理的真实对象赋初值 public InvocationHandlerDemo(Object subject) { this.subject = subject; } /* * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象 * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上 */ public Object createProxyIntance(){ return Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在代理真实对象前我们可以添加一些自己的操作 System.out.println("Before method"); System.out.println("Call Method: " + method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object obj = method.invoke(subject, args); // 在代理真实对象后我们也可以添加一些自己的操作 System.out.println("After method"); return obj; } }
public class ClientTest { public static void main(String[] args) { // 我们要代理的真实对象 Subject realSubject = new RealSubject(); //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 InvocationHandlerDemo handler = new InvocationHandlerDemo(realSubject); Subject subject=(Subject)handler.createProxyIntance(); String result = subject.bye(); System.out.println("Result is: " + result); System.out.println("代理的类型:"+subject.getClass().getName()); } }
中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。
三.动态代理,cjlib 代理
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。而java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
</dependency>
委托类是java动态代理的代码。上面
/** * cjlib 动态代理 * @author 86133 * */ public class CjlibDynamicProxy implements MethodInterceptor { Object targetObject; //目标对象 /** * 动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例 * @param obj * @return */ public Object getProxyObject(Object obj){ this.targetObject=obj; //增强器,动态代码生成器 Enhancer enhancer=new Enhancer(); //回调方法 enhancer.setCallback(this); //设置生成类的父类类型 enhancer.setSuperclass(targetObject.getClass()); //动态生成字节码并返回代理对象 return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object result = proxy.invoke(targetObject,args); return result; } }
public class ClientTest { public static void main(String[] args) { CjlibDynamicProxy cjlibDynamicProxy=new CjlibDynamicProxy(); Subject subjects=(Subject) cjlibDynamicProxy.getProxyObject(realSubject); String result1 = subjects.bye(); System.out.println("结果:"+result+"代理类型:"+subjects.getClass().getName()); } }
四. SpringAop 的两种代理方式(Jdk的动态代理和Cjlib的动态代理)
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
如果spring会使用JDK的java.lang.reflect.Proxy类,它允许Spring动态生成一个新类来实现必要的接口,织入通知,并且把对这些接口的任何调用都转发到目标类。
如果spring使用CGLIB库生成目标类的一个子类,在创建这个子类的时候,spring织入通知,并且把对这个子类的调用委托到目标类。
参考博客:https://www.cnblogs.com/hadoop-dev/p/7095464.html,
https://www.cnblogs.com/best/p/5679656.html#_label3,
https://www.cnblogs.com/yulinfeng/p/7811965.html (动态代理源码解析)