设计模式之动态代理
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<>();
public void save() {
System.out.println("模拟保存用户");
concurrentHashMap.put("1", "张三");
}
public String find() {
System.out.println("模拟查找用户");
return concurrentHashMap.get("1");
}
}
//代理对象
public class UserDaoProxy implements IUserDao
{
private UserDao ud = new UserDao();
public void save() {
System.out.println("代理操作,开启事务");//模拟的增强处理
ud.save();
System.out.println("代理操作,关闭事务");//模拟的增强处理
}
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================null
proxy================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();
}
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$$d229b515
method=============save
proxy=============save()V
模拟保存用户
方法执行返回=============null
obj=============proxy.staticProxy.UserDao$$EnhancerByCGLIB$$d229b515
method=============find
proxy=============find()Ljava/lang/String;
模拟查找用户
方法执行返回=============张三
CGlib生成的代理对象包含一个子对象EnhancerByCGLIB字样在名称里面。
以上是关于设计模式之动态代理的主要内容,如果未能解决你的问题,请参考以下文章