代理模式介绍
代理模式是一种常用的设计模式,其作用就是为目标对象提供额外的访问方式,在不修改目标对象的前提下,扩展目标对象的额外功能,比如统计执行时间,打印日志等。
代理模式分为两种:静态代理和动态代理。
需求:假如不想改动原有代码情况下,并记录用户保存方法的执行时间。示例代码如下:
接口
public interface UserService { public void saveUser(User user);}
实现类
public class UserServiceImpl implements UserService { private UserDao userDaoImpl; @Override public void saveUser(User user) { userDaoImpl.save(user); } ...}
静态代理实现
静态代理是在程序运行前产生的,一般来说,代理类和目标类实现同一接口或者有相同的父类
代理类
public class UserServiceProxyImpl implements UserService { private UserService UserServiceImpl;//代理类持有一个委托类的对象引用 public UserServiceProxyImpl(UserService UserServiceImpl) { this.UserServiceImpl = UserServiceImpl; } @Override public void saveUser(User user) { long startTime = System.currentTimeMillis(); System.out.println("开始记录时间"); delegate.dealTask(taskName); // 将请求分派给委托类处理 long endTime = System.currentTimeMillis(); System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒"); }}
产生代理对象的静态工厂类
public class UserServiceProxyFactory { public static UserService getInstance() { return new UserServiceProxyImpl(new UserServiceImpl()); }}
客户端
public class ClientTest { public static void main(String[] args) { UserService UserServiceProxy = UserServiceProxyFactory.getInstance(); UserServiceProxy.saveUser(new User("1","张三")); }}
运行结果
开始记录时间保存用户信息方法耗时0.01毫秒
静态代理优缺点:
优点:
1、业务类UserServiceImpl只需要关注业务逻辑本身,保证了业务类的重用性。
2、客户端Client和业务类UserServiceImpl之间没有直接依赖关系,对客户的而言屏蔽了具体实现。
缺点:
1、代理对象的一个接口只服务于一种接口类型的对象,静态代理在程序规模稍大时就无法使用。
2、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
动态代理实现
动态代理是程序在运行过程中在JVM内部动态产生的代理对象,代理类可以实现对多种类的代理。
动态代理又分为两种:JDK动态代理和CGLIB动态代理。
JDK动态代理
JDK动态代理需先声明一个代理类和目标类之间的中间类,此中间类需要实现jdk中的一个接口InvocationHandler。源码如下:
package java.lang.reflect;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;public class JDKProxy implements InvocationHandler { private Object target; //持有目标对象的引用 public JDKProxy(Object target){ this.target = target; } //创建代理对象 public Object createProxy(){ //1.得到目标对象的classloader ClassLoader classLoader = target.getClass().getClassLoader(); //2.得到目标对象的实现接口的class[] Class<?>[] interfaces = target.getClass().getInterfaces(); //3.第三个参数需要一个实现InvocationHandler接口的对象 //3-1.第一种写法,让当前类实现InvocationHandler,第三个参数写this return Proxy.newProxyInstance(classLoader, interfaces, this); //3-2.第二种写法,第三个参数用匿名内部类的形式,先注释掉 /*return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println("开始记录时间"); Object ret = method.invoke(target, args); //执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出 long endTime = System.currentTimeMillis(); System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒"); return ret; } });*/ } /* 在代理实例上执行目标对象的方法 参数1 就是代理对象,一般不使用 参数2 它调用的方法的Method对象 参数3 调用的方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println("开始记录时间"); Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出 long endTime = System.currentTimeMillis(); System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒"); return ret; }}
客户端
public class ProxyTest { @Test public void test1() { UserService userService = new UserServiceImpl(); //1.创建目标对象 JDKProxy factory = new JDKProxy(userService); // 2.通过JKDProxy完成代理对象创建 UserService userServiceProxy = (UserService)factory.createProxy(); userServiceProxy.saveUser(new User("1","张三")); }}
JDK动态代理中,需要关注的两点:
1、Proxy.newProxyInstance(classLoader, interfaces, this); 底层是怎么创建的代理对象
2、invoke方法是什么时候执行的,谁来调用的此方法
<font color=red>解析1>></font>怎么产生代理对象:
@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); }}//继续看newInstance方法private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } }}
由此可以看出创建代理对象,是利用反射,先获取目标对象的构造器,然后在通过构造反射生成代理对象
<font color=red>解析2>></font>invoke方法什么时候被调用:
我们通过一个工具类把生成的代理对象的字节码输出到磁盘,然后通过反编译来查看代理对象有哪些内容
工具类如下:
public class ProxyGeneratorUtils { /** * 把代理类的字节码写到硬盘上 * @param fileName 文件名 * @param path 路径信息 * @param clazz 目标类的接口数组 */ public static void writeProxyClassToHardDisk(String fileName, String path, Class<?>[] clazz) { byte[] classFile = ProxyGenerator.generateProxyClass(fileName, clazz); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } //主方法 public static void main(String[] args) { ProxyGeneratorUtils.writeProxyClassToHardDisk("$JDKProxy1","F:/$JDKProxy1.class",UserServiceImpl.class.getInterfaces()); }}
运行main方法生成代理对象字节码文件,用JD.exe反编译打开如下
public final class $JDKProxy1 extends Proxy implements UserService { private static Method m1; private static Method m3; private static Method m0; private static Method m2; public $JDKProxy1(InvocationHandler arg0) throws { super(arg0); } public final void saveUser(User arg0) throws { try { super.h.invoke(this, m3, new Object[]{arg0}); } catch (RuntimeException | Error arg2) { throw arg2; } catch (Throwable arg3) { throw new UndeclaredThrowableException(arg3); } } ...
用一张图来说明调用userServiceProxy代理对象的saveUser()方法内部发生了什么
CGLIB动态代理
cglib动态代理也需要一个产生代理对象的中间类,此类需实现MethodInterceptor接口,此接口在cglib包中,目前已经被spring整合,在spring-core核心包中<br>
产生代理对象的中间类
import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor { private Object target; //持有目标对象的引用 public CglibProxy(Object target){ this.target = target; } //创建代理对象 public Object createProxy(){ Enhancer enhancer = new Enhancer(); //1.创建Enhancer enhancer.setSuperclass(target.getClass()); //2.传递目标对象的class enhancer.setCallback(this); //3.设置回调操作(相当于InvocationHanlder) return enhancer.create(); } //相当于InvocationHanlder中的invoke @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println("开始记录时间"); Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出 long endTime = System.currentTimeMillis(); System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒"); return ret; }}
客户端
public class ProxyTest { @Test public void test1() { UserService userService = new UserServiceImpl(); //1.创建目标对象 CglibProxy factory = new CglibProxy(customerService); // 2.通过CglibProxy完成代理对象创建 UserService userServiceProxy = (UserService)factory.createProxy(); userServiceProxy.saveUser(new User("1","张三")); }}
产生代理对象字节码,用JD.exe反编译如下
import java.lang.reflect.Method;import org.springframework.cglib.core.ReflectUtils;import org.springframework.cglib.core.Signature;import org.springframework.cglib.proxy.Callback;import org.springframework.cglib.proxy.Factory;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class UserServiceImpl$$EnhancerByCGLIB$$1772a9ea extends UserServiceImpl implements Factory { ... public final void saveUser(User paramUser) { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null){ tmp4_1; CGLIB$BIND_CALLBACKS(this); } if (this.CGLIB$CALLBACK_0 != null) return; super.saveUser(paramUser); } ...}
JDK动态代理和CGLIB动态代理区别:
1、JDK动态代理是针对于接口代理,目标类必须实现了接口,产生的代理对象也会实现该接口。
2、CGLIB代理是采用继承,产生的代理对象继承于目标类,所以目标类和目标方法不能用final修饰。
共同学习,写下你的评论
评论加载中...
作者其他优质文章