Java代理模式实例详解【静态代理与动态代理】
本文实例讲述了Java代理模式。分享给大家供大家参考,具体如下:
即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。
Java的代理模式是Java中比较常用的设计模式,分为2中代理:静态代理与动态代理(JDK动态代理和cglib动态代理)
优点:
- 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
- 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动。
缺点:
- 很明显的一点就是反射机制,没有高安全性,性能也相对来讲低一些。
代理模式这种设计模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式。代理对象代为执行目标对象的方法,并在此基础上进行相应的扩展。看起来是有点拗口,首先介绍一个原则:开闭原则(对扩展开放,对修改关闭)。一种好的设计模式甚至是架构,都是在不修改原有形态的基础上扩展出新的功能。
事例场景:
小张是一个普普通通的码农,每天勤勤恳恳地码代码。某天中午小张刚要去吃饭,一个电话打到了他的手机上。“是XX公司的小张吗?我是YY公司的王AA”。“哦,是王总啊,有什么事情吗?”。沟通过后,小张弄明白了,原来客户有个需求,刚好负责这方面开发的是小张,客户就直接找到了他。不过小张却没有答应客户的请求,而是让客户找产品经理小李沟通。
是小张着急去吃面而甩锅吗?并不是,只是为了使故事可以套到代理模式上。我们先看一下代理模式的定义: * 为其他对象提供一种代理,以控制对这个对象的访问。(Provide a surrogate or placeholder for another object to control access to it)
对照定义,码农小张可以映射为其他对象,产品经理小李为小张的代理。我们通过JAVA代码,表述上面事例。
一、静态代理
什么是静态代理:静态代理就是在程序运行前就已经确定代理类与代理对象的代理模式
静态代理模式就是如上图所示,构造三个类实现他们的关系。
首先会思考的一点就是为什么需要实现同一个接口,如果不实现同一个接口,一样可以“代理”功能,所以为什么非要实现同一个接口。我个人认为不实现统一接口的话应该叫做聚合而不是代理;然后,实现统一接口能够使代理类与被代理类之间的联系,提高代码的复用性又能解耦。
package staticProxy; /** *接口 */ public interface DAOInterface { public void add(); public void delete(); public void update(); public void query(); }
package staticProxy; /** *被代理类 */ public class UserDao implements DAOInterface{ @Override public void add() { System.out.println("在目标对象中执行add"); } @Override public void delete() { System.out.println("在目标对象中执行delete"); } @Override public void update() { System.out.println("在目标对象中执行update"); } @Override public void query() { System.out.println("在目标对象中执行query"); } }
package staticProxy; /** * 代理类 * */ public class UserDaoProxy implements DAOInterface{ UserDao userDao = null; public UserDaoProxy(UserDao userDao){ this.userDao = userDao; } @Override public void add() { userDao.add(); System.out.println("记录日志add"); } @Override public void delete() { userDao.delete(); System.out.println("记录日志delete"); } @Override public void update() { userDao.update(); System.out.println("记录日志update"); } @Override public void query() { userDao.query(); System.out.println("记录日志query"); } }
静态代理就是写死了在代理对象中执行这个方法前后执行添加功能的形式,每次要在接口中添加一个新方法,则需要在目标对象中实现这个方法,并且在代理对象中实现相应的代理方法;利用Java的反射机制,动态的调用生成代理对象,就能完成动态代理的需求。
二、动态代理
1、JDK动态代理
在代理类管理器的新建代理实例方法中,必须要获得类的加载器、类所实现的接口、还有一个拦截方法的句柄。
在句柄的invoke中如果不调用method.invoke则方法不会执行。在invoke前后添加通知,就是对原有类进行功能扩展了。创建好代理对象之后,proxy可以调用接口中定义的所有方法,因为它们实现了同一个接口,并且接口的方法实现类的加载器已经被反射框架获取到了。
package JDKAgency; /** * DAO接口 */ public interface DAO { void add(); void update(); void delete(); void query(); }
package JDKAgency; /** * DAO的实现类 */ public class DAOImpl implements DAO { @Override public void add() { System.out.println("添加的方法"); } @Override public void update() { System.out.println("更新的方法"); } @Override public void delete() { System.out.println("删除的方法"); } @Override public void query() { System.out.println("查询的方法"); } }
package JDKAgency; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理类管理器 */ public class ProxyManager implements InvocationHandler {//动态代理的核心处理器接口 private Object object; public ProxyManager(Object object) { this.object = object; } public Object createNewInstance() { Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(),//真实对象的类加载器 object.getClass().getInterfaces(),//真实对象的所有接口 this);//代理对象 return o; } @Override //代理对象 执行业务的方法 参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before......权限检测");//前置通知 Object invoke = method.invoke(object, args);//动态调用执行方法 System.out.println("after......日志监控");//后置通知 return invoke; } }
package JDKAgency; /** * 测试 */ public class JTest { public static void main(String[] args) { //创建真实对象 DAO dao = new DAOImpl(); DAO o =(DAO) manager.createNewInstance(); //JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。 o.add(); o.query(); } }
注意:JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
补充:
JavaJDK动态代理报错java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to *。
javaJDK动态代理是Java原生代理模式。
注意:JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
正确的转型方案:
//创建处理器对象 ProxyManager manager = new ProxyManager(dao); //生成动态代理对象 // DAO o = (DAO) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), manager);
2、cglib动态代理
cglib动态代理是web应用框架常用的一种动态代理方式。cglib是动态生成被代理类的子类,注意:是子类。
他需要先引入asm与cglib的jar包。如下图:
接着废话不多说,看代码分析:
package CGlibAgency; /** * 被代理类 */ public class User { public void saveUser(){ System.out.println("保存对象。"); } public void updateUser(){ System.out.println("修改对象。"); } }
package CGlibAgency; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 代理类管理器 */ public class Interceptor implements MethodInterceptor {//cgLib需要实现MethodInterceptor接口,cgLib是基于类的,动态的生成代理类(被代理类的子类) private Enhancer enhancer = new Enhancer();//Enhancer是Cglib代理的重要对象 public Object createProxy(Class superClass){ enhancer.setSuperclass(superClass);//获取父类的Class enhancer.setCallback(this);//设置方法的回调,类似于JDK动态代理中的Proxy与InvocationHandler实现类的绑定回调 return enhancer.create();//返回代理类的对象 } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("'权限验证'"); Object aSuper = methodProxy.invokeSuper(o, objects);//通过虚拟的代理对象的代理方法调用父类的方法,参数中一个是父类,一个是所有的子类对象数组(??) System.out.println("'日志收集'"); return aSuper; } }
package CGlibAgency; /** * 测试类 */ public class JTest { public static void main(String[] args) { Interceptor interceptor = new Interceptor();//创建代理管理对象 Object proxy = interceptor.createProxy(User.class);//创建一个代理类 System.out.println(User.class.getTypeName());//查看代理类的类型 User user = (User) proxy;//转型,子类自动向上转型 user.saveUser(); user.updateUser(); } }
总结:
代理模式不仅可以降低模块儿之间的耦合,还能做到高复用,简化代码等。spring的AOP模块就是最直观的代理模式,使用了动态代理来实现,在spring中的许多模块中都具有动态代理的影子。
更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。