Java Web 开发:动态代理
代理(Proxy)

代理是一种结构型设计模式,可以允许我们生成对象的替代品。代理控制着对于原对象的访问,同时也允许在原对象的方法之前前后做一些处理,便可以实现在原方法执行前后都会执行某段代码逻辑的功能。这个也是面向切面编程的指导思想。
代理模式在我们日常生活中用处还是相当广泛的,比如海外购网站就是一个代理。海外购网站负责代理用户到国外的电商网站去下单购买商品,也可以在商品送达到海外购网站时,再执行进一步加固操作,再次转送给用户。
代理模式在软件开发过程中的应用场景也非常常见。在客户端以及客户端访问的目标类对象中间,额外再引入一个第三方代理类对象。如果直接访问目标类对象,就是执行对应的方法;如果客户端访问的是代理类对象,那么不仅可以访问对应的方法,还会再方法的执行前后执行对应的前置、后置通知。

静态代理
public interface UserService {
void insert();}public class UserServiceImpl implements UserService { @Override public void insert() { System.out.println("目标类执行了insert方法"); }}public class UserServiceProxy implements UserService {
UserService target;
public UserServiceProxy(UserService target) { //注入委托类对象 this.target = target; }
@Override public void insert() { System.out.println("代理之前打印一个日志"); target.insert(); System.out.println("代理之后打印一个日志"); }}@Test public void test1(){ UserServiceProxy proxy = new UserServiceProxy(new UserServiceImpl()); proxy.insert(); }代理模式最大的优点在于可以不更改目标类代码的前提下,扩展目标类代码的功能。
静态代理最大的缺点在于代码较为冗余,每代理一个类,便要手动编写一个代理类;代理对象和目标类对象均实现了接口,如果接口发生了修改,不仅目标类需要更改,代理类也需要同步发生修改,维护成本变高了很多。
因此,我们希望可以在程序运行过程中,动态地生成一个代理类对象,这样处理任务更加的方便。这也便是我们接下来介绍的动态代理。
JDK动态代理
静态代理,顾名思义,便是在编译时,就已经实际存在了该class文件;而动态代理,在编译时期,实际上并不存在该class文件,而是程序在运行阶段动态生成了字节码。JDK动态代理,即JDK给我们提供的动态生成代理类的方式,无需引入第三方jar包,但是使用JDK动态代理有一个先决条件,那就是目标类对象必须实现了某个接口;如果目标类对象没有实现任何接口,则JDK动态代理无法使用。
如果使用JDK提供的动态代理,那么需要借助于如下几个类
-
java.lang.reflect.ProxyAPI 参数 返回值 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) loader表示目标类使用的类加载器;interfaces表示目标类所实现的接口类型;h表示处理器,用来规定代理的内部细节 返回一个实现指定接口的代理类实例对象;代理类对象和目标类对象实现相同的接口类型 -
java.lang.reflect.InvocationHandlerAPI 参数 返回值 public Object invoke(Object proxy, Method method, Object[] args) proxy表示JDK帮助开发者生成的代理类对象,这个参数一般不用理会;method表示的是目标类中的方法;args表示执行目标类方法时传递的参数;三个参数合在一起表示的含义表示代理类如何来代理、增强目标类里面的方法 代理类执行完对应的方法时它的返回值
public class ProxyFactory {
Object target;
public ProxyFactory(Object target) { this.target = target; }
public Object newProxyInstance(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
//代理类如何代理 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理之前签订合约"); Object invoke = method.invoke(target, args); System.out.println("代理完毕转账确认"); return invoke; } }); }}@Test public void test2(){ UserService userService = new UserServiceImpl(); //对哪个目标类进行代理,我们对UserServiceImpl进行代理 //生成代理类对象 UserService userServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
//代理类如何代理 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理之前签订合约"); Object invoke = method.invoke(userService, args); System.out.println("代理完毕转账确认"); return invoke; } });; //代理类对象执行insert方法,什么逻辑呢?主要是invoke里面的代码逻辑 userServiceProxy.insert(); }利用线上监测工具以及反编译工具,可以看到生成的代理类对象源码
public final class $Proxy0extends Proxyimplements UserService { private static Method m1; private static Method m3; private static Method m2; private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) { super(invocationHandler); }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.cskaoyan.pattern.proxy.UserService").getMethod("insert", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } }
public final boolean equals(Object object) { try { return (Boolean)this.h.invoke(this, m1, new Object[]{object}); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }
public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }
public final int hashCode() { try { return (Integer)this.h.invoke(this, m0, null); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //主要关注insert方法 public final void insert() { try { this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }}Cglib动态代理
Cglib(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。我们可以借助于Cglib来帮助我们动态地生成代理类对象。Cglib可以弥补JDK动态代理的不足,JDK要求目标类必须实现了某个接口,才可以执行代理功能;而Cglib对此无任何要求,主要原因在于Cglib扩展的代理类会继承自目标类。所以这也要求我们的目标类不能是final修饰。
可以通过引入Spring相关的依赖(spring-aop)来使用cglib动态代理
其中提供了Enhancer.create方法,传入对应的委托类的class和InvocationHandler来创建代理对象
其中InvocationHandler的invoke方法用来指定做一个什么样的增强
UserService userService = new UserService();UserService userServiceProxy = (UserService) Enhancer.create(UserService.class, (InvocationHandler) (o, method, objects) -> { System.out.println("hello world"); Object result = method.invoke(userService, objects); return result;});文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!