设计模式之动态代理

Posted 木禾子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之动态代理相关的知识,希望对你有一定的参考价值。

springAOP,是相对于java中的面向对象(OOP)的一个概念。在面向对象中有一些公共的行为,像日志记录,权限验证等增强逻辑如果都使用面向对象来做,会在每个业务方法中都写上重复的代码,造成代码冗余。

而AOP指的是面向切面编程,定义一个切面,用切面去切相应的方法,就可以织入增强的逻辑相应的方法是指核心的业务逻辑,切面逻辑使用代理模式来实现

什么是代理模式?

代理代表某个真实的对象代理提供了对真实对象另外的访问方式——即通过代理对象访问真实对象。真实对象又可称目标对象。

这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

涉及到的编程原则就是开闭原则:不要随意去修改别人已经写好的代码或者方法,如果需要修改,可以通过代理的方式来扩展该方法。

举个例子:假设我们想邀请一位明星,那么并不是直接对接明星,而是联系明星的经纪人来达到同样的目的。明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。

静态代理

静态代理,简单点来说就是在程序运行之前,代理类和被代理类的关系已经确定。静态代理的实现首先要定义一个公共的接口,然后代理类和被代理类都实现这个接口,如下:

//接口public interface IUserDao { void save(); String find();}
//目标对象public class UserDao implements IUserDao{
private ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();
@Override public void save() { System.out.println("模拟保存用户"); concurrentHashMap.put("1", "张三"); }
@Override public String find() { System.out.println("模拟查找用户"); return concurrentHashMap.get("1"); }
}
//代理对象public class UserDaoProxy implements IUserDao{ private UserDao ud = new UserDao();
@Override public void save() { System.out.println("代理操作,开启事务");//模拟的增强处理 ud.save(); System.out.println("代理操作,关闭事务");//模拟的增强处理 }
@Override public String find() { return ud.find(); }
}

静态代理最大的缺点:我们得为每一个服务创建代理类,工作量太大不易管理;同时接口一旦发生改变,代理类也得相应修改。

动态代理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建

JDK动态代理:Java为我们创建了Proxy类,动态生成的代理类有一个共同的父类叫Proxy,我们需要告诉Proxy做什么。我们不能像静态代理那样把自己的代码放在Proxy类,因为Proxy类不是我们自己创建的。Java提供了动态处理器InvocationHandler接口。

InvocationHandler的工作是响应代理的任何调用,它是代理收到方法调用后,请求做实际工作的对象

JDK实现动态代理,是通过Proxy的静态方法newProxyInstance来创建代理对象的,该方法有三个参数:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

ClassLoader loader:指定当前目标对象使用的类加载器

Class<?>[] interfaces:目标对象实现的接口的集合,使用泛型方式确认类型

InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的invoke方法,会把当前执行目标对象的方法作为参数传入

public void useJDKProxy() { System.out.println("-------useJDKProxy-------"); final UserDao ud = new UserDao(); // 生成代理对象 IUserDao iud = (IUserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), /** * 因为InvocationHandler是个接口,所以可以使用匿名内部类的方式进行实现 */ new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy================"+proxy.getClass().getName());//模拟的增强处理 Object result = null; result = method.invoke(ud, args);//目标方法 System.out.println(method.getName()+"================"+result);//模拟的增强处理 return result; } }); iud.save(); iud.find(); }

执行结果:

-------useJDKProxy-------proxy================com.sun.proxy.$Proxy0模拟保存用户save================nullproxy================com.sun.proxy.$Proxy0模拟查找用户find================张三

从输出可以看出JDK生成的代理对象名称为:com.sun.proxy.$Proxy0,关于动态生成代理的名称,debug调用栈到Proxy.ProxyClassFactory.apply:

以下两行代码就是生成代理对象名称的源码:

private static final String proxyClassNamePrefix = "$Proxy";String proxyName = proxyPkg + proxyClassNamePrefix + num;

CGLIB代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,需要通过CGLib实现动态代理。

CGLib创建代理对象是通过net.sf.cglib.proxy.Enhancer实现的,代理收到方法调用后,请求做实际工作的对象是MethodInterceptor

public class CglibProxy implements MethodInterceptor {  private Object target; public Object getInstance(final Object target) { this.target = target; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(this.target.getClass());//设置目标对象 enhancer.setCallback(this);//设置代理拦截器MethodInterceptor return enhancer.create(); }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("obj============="+obj.getClass().getName()); System.out.println("method============="+method.getName()); System.out.println("proxy============="+proxy.getSignature()); Object result=proxy.invokeSuper(obj, args); System.out.println("方法执行返回============="+result); return result;    }}
//测试方法public void useCGlibProxy() { System.out.println("--------useCGlibProxy--------"); CglibProxy cglibProxy = new CglibProxy();        UserDao ud=(UserDao)cglibProxy.getInstance(new UserDao());
ud.save(); ud.find(); }

执行结果:

--------useCGlibProxy--------obj=============proxy.staticProxy.UserDao$$EnhancerByCGLIB$$d229b515method=============saveproxy=============save()V模拟保存用户方法执行返回=============nullobj=============proxy.staticProxy.UserDao$$EnhancerByCGLIB$$d229b515method=============findproxy=============find()Ljava/lang/String;模拟查找用户方法执行返回=============张三

CGlib生成的代理对象包含一个子对象EnhancerByCGLIB字样在名称里面。


以上是关于设计模式之动态代理的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之动态代理

Java动态代理之通俗理解

设计模式之代理模式和动态代理

Spring之静态/动态代理模式

设计模式之动态代理

代理模式之JDK动态代理