文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JAVA如何实现动态代理技术

2023-05-30 22:54

关注

这篇文章主要为大家展示了“JAVA如何实现动态代理技术”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JAVA如何实现动态代理技术”这篇文章吧。

一、引出动态代理

生活中代理应该是很常见的,比如你可以通过代理商去买电脑,也可以直接找厂商买电脑,最终都是买到了电脑。程序中也一样存在代理的情况,比如要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法耗时等等,那么我们会怎么做呢?

会编写一个与目标类拥有相同接口的代理类,代理类的每个方法调用目标类的相同方法,然后在调用方法前后加上系统功能所需要的代码。

如果采用工厂模式或者配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换。

样例如下:

public class X{ public void sayHello(){ syso:say hello; }}

现在我要在这个方法之前添加一个时间,方法之后添加一个时间,计算这个方法执行的时间一共是多少.如果我没有得到sayHello源码,那么我怎么做呢?写一个代理:

public class XProxy{ private X x; public void sayHello {   startTime:  x.syHello();  endTime; }}

说明:上面的是伪代码。

把开始时间和结束时间放在这个方法的前后就可以了.
通常我们让两个方法实现同一个接口.那么client想用X也可以,想用XProxy也可以了.具体的原理图,如下图所示:

JAVA如何实现动态代理技术

二、创建动态代理类

现在试想一下,上面只是代理了一个目标类,如果多个目标类,那么是不是要创建N多个代理类呢?那样不是代码太不灵活且笨重了。当然不会。

java虚拟机可以在运行期间动态生成类,这种类是以字节码的形式生成出来的。这种动态生成的类往往呢就是代理类。即动态代理类。

JVM生成的动态代理类必须满足一定的条件,这就是必须实现一个或多个接口。所以JVM生成的动态代理只能用作具有相同接口的目标类的代理。(动态生成的类不是代理,我们只是吧这个类当成代理来用。)

Proxy动态代理的API:

JAVA如何实现动态代理技术

两个参数应该很容易理解:

第一个参数:我们知道任何一个字节码都是需要通过类加载器来加载的,那么这个动态生成的字节码也不例外,需要给它一个类加载器。

第二个参数:就是动态代理类生成,必须满足的一个条件,需要实现一个或者多个接口,否则这个生成的类字节码中就没有方法了,没有方法就失去了其功能意义。

下面我们动手来创建一个动态的代理类,大体思路为:

创建实现Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数

编码列出动态类中的所有构造方法和参数签名

编码列出动态类中的所有方法和参数签名

创建动态类的实例对象:1)用反射获取构造方法   2)编写一个最简单的invocationHandle类   3)调用构造方法创建动态类的实例对象,并将编写的InvocationHandle类的实例对象传递进去   4)打印创建对象和调用对象的没有返回的方法和getClass方法,演示调用其他有返回值方法报告了异常。

5)将创建动态类的实例对象的代理写成匿名内部类方式,简化代码。

样例分步实现如下:

(1)首先我们来完成前面3步:

package study.javaenhance;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Collection;public class ProxyTest{ public static void main(String[] args)  {  Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);  System.out.println(clazzProxy1.getName());    //上面输出的为一个类,那么一个类肯定有其构造方法和方法,下面我们来列出来    System.out.println("----------begin constructors list----------");  //1.列出构造方法  Constructor[] constructors = clazzProxy1.getConstructors();  for (Constructor constructor : constructors)   {   String name = constructor.getName();   StringBuilder sBuilder = new StringBuilder(name);   sBuilder.append('(');   Class[] clazzParams = constructor.getParameterTypes();   for (Class clazzParam : clazzParams) {    sBuilder.append(clazzParam.getName()).append(',');   }   if(clazzParams!=null && clazzParams.length != 0)    sBuilder.deleteCharAt(sBuilder.length()-1);   sBuilder.append(')');   System.out.println(sBuilder.toString());   }    //2.列出这个类字节码中的所有方法  System.out.println("----------begin methods list----------");  Method[] methods = clazzProxy1.getMethods();  for(Method method : methods){   String name = method.getName();   StringBuilder sBuilder = new StringBuilder(name);   sBuilder.append('(');   Class[] clazzParams = method.getParameterTypes();   for(Class clazzParam : clazzParams){    sBuilder.append(clazzParam.getName()).append(',');   }   if(clazzParams!=null && clazzParams.length != 0)    sBuilder.deleteCharAt(sBuilder.length()-1);   sBuilder.append(')');   System.out.println(sBuilder.toString());     }     }}

输出结果如下:

$Proxy0
----------begin constructors list----------
$Proxy0(java.lang.reflect.InvocationHandler)
----------begin methods list----------
add(java.lang.Object)
hashCode()
clear()
equals(java.lang.Object)
toString()
contains(java.lang.Object)
isEmpty()
addAll(java.util.Collection)
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
remove(java.lang.Object)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()

可以看到所有的方法均是来自Collection 和父类Object中的方法,符合我们的预期结果。接下来我们进入第四步和第五步的实现:

首先,我们来创建这个动态代理类的实例。那么直接clazzProxy1.newInstance();可不可以呢?显然是不可以的嘛.我们刚刚说了,动态生成的这个代理类只有一个构造方法,有没有无参构造方法呢?没有啊.所以,创建 一个参数的构造方法.参数类型是java.lang.reflect.InvocationHandler。 

JAVA如何实现动态代理技术

下面我们来实现:

定义一个上述接口的实例:

package study.javaenhance;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args)   throws Throwable {  // TODO Auto-generated method stub  return null; }}

调用实现创建实例动态类:

//3.创建实例对象  System.out.println("----------begin create instance object----------");  Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);  MyInvocationHandler myInvocationHandler = new MyInvocationHandler();  Collection collection = (Collection) constructor.newInstance(myInvocationHandler);  System.out.println(collection);  collection.clear();  //collection.size(); //报错,异常会发生,产生异常的原因在于其返回值为int类型,但是每一次调用一个方法都会调用到invoke方法,我们此时的invoke返回的为null,所以是没有办法转换为int类型的。

我们采用匿名内部类的方式优化:

//3.1 采用匿名内部类方式进行创建  Collection collection2 = (Collection) constructor.newInstance(new InvocationHandler()  {   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    // TODO Auto-generated method stub    return null;   }     });  System.out.println(collection2);  collection2.clear();  //collection2.size();

继续优化:思考如果我们每次都想上面方式去创建动态的代理类实在有点重复,那么这个是JDK的Proxy类中提供了简单的方法直接去创建动态代理类,方式如下:

//3.3 采用Proxy 中提供的简单方法创建  Collection collection3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),    new Class[]{Collection.class},    new InvocationHandler()  {   private ArrayList target = new ArrayList();   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    return method.invoke(target, args);   }     });  //System.out.println(collection3);  collection3.add("abc");  collection3.add("def");  collection3.add("hij");  System.out.println(collection3.size());

三、动态代理的原理简单分析

上面我们创建了动态代理类,下面我们分析下代理的原理:

 JAVA如何实现动态代理技术

 下面在来看一个问题:

JAVA如何实现动态代理技术

动态代理的工作原理图:

JAVA如何实现动态代理技术

对上面的这个图,我们简单来说说:客户端动态生成代理类,然后调用代理类的方法,代理类内部调用handler.invoke()方法,在invoke中呢,我们又指向的目标类.这样就实现了代理了.我客户端调用代理的什么方法,invoke就只向目标类的同一个方法.而在指定目标类方法的前后呢,我们还可以做其他的操作,比如记录日志.图中用圈圈出来的部分就是代理类自己实现的功能了.这就是代理类的原理.

JAVA如何实现动态代理技术

我们来做最后一步,将上面的动态生成的代理类,编写可生成代理和插入通告的通用方法:

 JAVA如何实现动态代理技术

test代码:

package study.javaenhance;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;public class ProxyTest{ public static void main(String[] args) throws Exception {  Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);  System.out.println(clazzProxy1.getName());    //上面输出的为一个类,那么一个类肯定有其构造方法和方法,下面我们来列出来    System.out.println("----------begin constructors list----------");  //1.列出构造方法  Constructor[] constructors = clazzProxy1.getConstructors();  for (Constructor constructor : constructors)   {   String name = constructor.getName();   StringBuilder sBuilder = new StringBuilder(name);   sBuilder.append('(');   Class[] clazzParams = constructor.getParameterTypes();   for (Class clazzParam : clazzParams) {    sBuilder.append(clazzParam.getName()).append(',');   }   if(clazzParams!=null && clazzParams.length != 0)    sBuilder.deleteCharAt(sBuilder.length()-1);   sBuilder.append(')');   System.out.println(sBuilder.toString());   }    //2.列出这个类字节码中的所有方法  System.out.println("----------begin methods list----------");  Method[] methods = clazzProxy1.getMethods();  for(Method method : methods){   String name = method.getName();   StringBuilder sBuilder = new StringBuilder(name);   sBuilder.append('(');   Class[] clazzParams = method.getParameterTypes();   for(Class clazzParam : clazzParams){    sBuilder.append(clazzParam.getName()).append(',');   }   if(clazzParams!=null && clazzParams.length != 0)    sBuilder.deleteCharAt(sBuilder.length()-1);   sBuilder.append(')');   System.out.println(sBuilder.toString());     }    //3.创建实例对象  System.out.println("----------begin create instance object----------");  Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);  MyInvocationHandler myInvocationHandler = new MyInvocationHandler();  Collection collection = (Collection) constructor.newInstance(myInvocationHandler);  System.out.println(collection);  collection.clear();  //collection.size(); //报错,异常会发生,产生异常的原因在于其返回值为int类型,但是每一次调用一个方法都会调用到invoke方法,我们此时的invoke返回的为null,所以是没有办法转换为int类型的。    //3.1 采用匿名内部类方式进行创建  Collection collection2 = (Collection) constructor.newInstance(new InvocationHandler()  {   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    // TODO Auto-generated method stub    return null;   }     });  System.out.println(collection2);  collection2.clear();  //collection2.size();    //3.3 采用Proxy 中提供的简单方法创建  Collection collection3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),    new Class[]{Collection.class},    new InvocationHandler()  {   private ArrayList target = new ArrayList();   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    return method.invoke(target, args);   }     });  //System.out.println(collection3);  collection3.add("abc");  collection3.add("def");  collection3.add("hij");  System.out.println(collection3.size());  System.out.println(collection3.getClass().getName());//这个返回的是    System.out.println("----------begin create instance object 抽化----------");  //3.4抽出动态代理让目标对象和切面对象都是传入进去的    final ArrayList target = new ArrayList();  Collection collection4 = (Collection)getProxy(target,new MyAdvice());  collection4.add("test1");  collection4.add("test2");  System.out.println(collection4.size()); } private static Object getProxy(final Object target,final Advice advice) {  Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),    target.getClass().getInterfaces(),    new InvocationHandler()  {   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    advice.beforeMethod(method);    Object retValue = method.invoke(target, args);    advice.afterMethod(method);    return retValue;   }     });  return object; }}

四、实现类似Spring的可配置的AOP框架

首先,我们要完成的要求如下:

JAVA如何实现动态代理技术

我们来模拟Spring的工厂模式读取从配置文件传递过来的类。如果这个类是一个普通类则直接返回。如果是一个代理类,则创建代理对象,返回代理类。

具体的理论知识可以看上面的图片

首先创建一个BeanFactory.java类。这是一个Bean工厂,用于读取配置文件中的类

package study.javaenhance.aopframework;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import study.javaenhance.Advice;public class BeanFactory { private Properties properties = new Properties();   public BeanFactory(InputStream inStream) {  try   {   properties.load(inStream);  }  catch (IOException e)   {   e.printStackTrace();  }  finally  {   if(inStream != null)   {    try     {     inStream.close();    }    catch (IOException e)    {     e.printStackTrace();    }   }  } }  public Object getBean(String name) {  String className = properties.getProperty(name);  Object bean = null;  try  {   Class clazz = Class.forName(className);   bean = clazz.newInstance();   if(bean instanceof ProxyFactoryBean)   {    ProxyFactoryBean proxyBean = (ProxyFactoryBean) bean;    Advice advice = (Advice)Class.forName(properties.getProperty(name + ".advice")).newInstance();    Object target = Class.forName(properties.getProperty(name + ".target")).newInstance();    proxyBean.setAdvice(advice);    proxyBean.setTarget(target);    Object proxy = proxyBean.getProxy();    return proxy;   }  }   catch (Exception e)   {   // TODO Auto-generated catch block   e.printStackTrace();  }  return bean; }}

如果这个类中包含ProxyFactoryBean,则调用ProxyFactoryBean中的getProxy方法。动态生成代理类。
ProxyFactoryBean.java

package study.javaenhance.aopframework;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import study.javaenhance.Advice;public class ProxyFactoryBean{ private Object target;  private Advice advice;   public Object getTarget() {  return target; } public void setTarget(Object target) {  this.target = target; } public Advice getAdvice() {  return advice; } public void setAdvice(Advice advice) {  this.advice = advice; } public Object getProxy()  {  Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),    new InvocationHandler()  {   @Override   public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    advice.beforeMethod(method);    Object retValue = method.invoke(target, args);    advice.afterMethod(method);    return retValue;   }     });  return object; }}

接下来创建一个config.Properties文件.config.Properties

xxx=java.util.ArrayList
#xxx=study.javaenhance.aopframework.ProxyFactoryBean
xxx.advice=study.javaenhance.MyAdvice
xxx.target=java.util.ArrayList

最后建立测试类:

package study.javaenhance.aopframework;import java.io.InputStream;import java.util.Collection;public class AopFrameworkTest{ public static void main(String[] args) {  InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");  Object bean = new BeanFactory(ips).getBean("xxx");  System.out.println(bean.getClass().getName());  ((Collection)bean).clear(); }}

以上是“JAVA如何实现动态代理技术”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯