Java JDK动态代理
前言
本篇文章主要讲解的是什么是JAVA JDK版本的动态代理,如何使用JAVA JDK版本的动态代理。对于JAVA JDK版本的动态代理的原理不会过多的描述。本篇文章旨在如果使用JDK动态代理。
必须理解和掌握的前提概念:
为了更好的掌握和使用JDK 动态代理需要了解一下概念。
1.什么是代理(proxy)
例如:由于客户端不想或者不能直接访问一个对象,此时可以通过一个称为代理的第三者来实现间接访问对象,这个过程为一个代理。
更加生活化的实例是:
例如:代购,如:在当地买不到某件商品,又或者因为当地的这件商品的价格比其他地区的贵,所以托人或者机构在其他地区购买该商品,并携带回来。
2.静态代理和动态代理
可以提前学习一下设计模式中的代理模式,会对一下内容会有一个更深的理解。
静态代理:传统的代理模式中客户端通过Proxy类调用RealSubject类和方法,同时还在代理类还可以封装其他方法。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在,代理类的接口和所代理方法都已明确制定。每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法,都被固定,这种代理称为静态代理。
静态代理的局限性:如果需要为不同的真实主题类提供代理类或者代理一个真实主题类的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。
动态代理(dynamic proxy)可以让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的RealSubject而且可以代理不同的方法。
JDK中提供的动态代理智能代理一个或者多个接口,如果需要动态代理具体类或者抽象类,可以是用CGLib(Code Generation Library)等工具
如何实现JAVA JDK动态代理
掌握了基本前提概念之后,还有几点我们需要进行明确的。
先看代理模式简易版类图:
1.为了让客户端能够一致性的对待RealSubject对象和Proxy对象,引入抽象类Subject
2.为了保持行为的一致性,Proxy和realSubject通常会实现相同的接口
3.对于接口的理解:接口这个术语有多个意思,1.接口(API)某个类的接口或该类持有的方法的集合。2.使用关键字interface声明的代码。
—————————————————————————————————————————
Java JDK动态代理
从JDK1.3开始,java语言提供了对动态代理的支持,java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类。
1.Proxy类
proxy类提供了用于创建动态代理类和实例对象的方法,他是说创建的动态代理类的父类。
常用方法:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
该方法用于返回一个动态创建的代理类的实例,方法中的第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。
2.InvocationHandler接口
InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法:
public Object invoke(Object proxy, Method method, Object[] args)
该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含3个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。
动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时调用请求会请求自动转发给InvocationHandler对象的invoke()方法,有invoke()方法来实现对请求的统一处理。
实例
某软件公司要为公司SPM系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现在使用动态代理进行设计和实现。
代码结构:总共有四个类
IAbstractUserDAO
package test; /** * @author : [WangWei] * @version : [v1.0] * @className : ISubject * @description : [抽象用户DAO类,抽象主题类] * @createTime : [2022/9/18 8:27] * @updateUser : [WangWei] * @updateTime : [2022/9/18 8:27] * @updateRemark : [描述说明本次修改内容] */ public interface IAbstractUserDAO { public Boolean findUserById(String userId); }
UserDAO
package test; /** * @author : [WangWei] * @version : [v1.0] * @className : RealSubject * @description : [用户DAO类,具体主题类] * @createTime : [2022/9/18 8:27] * @updateUser : [WangWei] * @updateTime : [2022/9/18 8:27] * @updateRemark : [描述说明本次修改内容] */ public class UserDAO implements IAbstractUserDAO { @Override public Boolean findUserById(String userId) { return null; } }
DAOLogHandler
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Calendar; import java.util.GregorianCalendar; /** * @author : [WangWei] * @version : [v1.0] * @className : InvocationHandlerImpl * @description : [请求处理程序类] * @createTime : [2022/9/18 8:31] * @updateUser : [WangWei] * @updateTime : [2022/9/18 8:31] * @updateRemark : [描述说明本次修改内容] */ public class DAOLogHandler implements InvocationHandler { private Calendar calendar; private Object subject; //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象 public DAOLogHandler(Object subject) { this.subject = subject; } /* * @version V1.0 * Title: invoke * @author Wangwei * @description 实现invoke()方法,调用在真实主题类中定义的方法 * @createTime 2022/9/25 10:21 * @param [proxy, method, args] * @return java.lang.Object */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用记录方法调用时间 this.beforeInvoke(); Object result =method.invoke(subject,args);//转发调用 //标记方法调用结束 this.afterInvoke(); return result; } //记录方法调用时间 private void beforeInvoke(){ calendar=new GregorianCalendar(); int hour=calendar.get(Calendar.HOUR_OF_DAY); int minute=calendar.get(Calendar.MINUTE); int second=calendar.get(Calendar.SECOND); String time=hour+":"+minute+":"+second; System.out.println("调用时间:"+time); } private void afterInvoke(){ System.out.println("方法调用结束"); } }
Client
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * @author : [WangWei] * @version : [v1.0] * @className : DynamicProxyDemonstration * @description : [客户端测试类] * @createTime : [2022/9/18 8:36] * @updateUser : [WangWei] * @updateTime : [2022/9/18 8:36] * @updateRemark : [描述说明本次修改内容] */ public class Client { public static void main(String[] args) { InvocationHandler handler=null; IAbstractUserDAO userDAO=new UserDAO(); handler=new DAOLogHandler(userDAO); IAbstractUserDAO proxy=null; //动态创建代理对象,用于代理一个IAbstractUserDAO类型的真实对象 proxy=(IAbstractUserDAO) Proxy.newProxyInstance(IAbstractUserDAO.class.getClassLoader(),new Class[]{IAbstractUserDAO.class},handler); proxy.findUserById("David"); } }
参考资料:
书籍:《java设计模式》–刘伟