`

java代理

    博客分类:
  • JAVA
 
阅读更多

一、代理类 

代理类的概念与作用:

 

1、 作用:

      要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,

      例如:异常处理,日志,计算方法的运行时间,事务管理等等。

2、 实现方式:

        编写一个与目标类具有相同接口的代理类(容易实现已实现类的切换), 代理类的每个方法调用目标类的相同方法,并在调用方法时加上

        系统功能的代码。

3 、应用:

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

       譬如,想要日志功能时就配置代理类,否则配置目标类,增加系统功能就很容易,去掉也很容易这样提高了灵活性

 

代理架构图

 

二、AOP(Aspect oriented program):面向方面的编程

当系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

 

                                    安全      事务        日志

StudentService   |

CourseService  |

    MiscService    |

 

用具体的程序代码描述交叉业务:

method1         method2          method3

{                      {                       {

------------------------------------------------------切面

....            ....              ......

------------------------------------------------------切面

}                       }                       }

 

交叉业务的编程问题即为面向方面的编程,AOP的目的就是要使交叉业务模块化,

可以采用将切面代码移动到原始方法的周围,这与直接在方面写切面代码的运行效果是一样的,如下所示:

 

------------------------------------------------------切面

func1         func2            func3

{             {                {

....            ....              ......

}             }                }

------------------------------------------------------切面

 

使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

 

三、动态代理技术

       要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情,

       所以JVM可以为我们生成动态代理类。

 

1、动态代理类:

      JVM可以在运行期间动态生成出字节码,这种动态生成的类往往被用作代理类,即动态代理类。

 

2、JVM生成的动态类只能用作具有相同接口的目标类的代理

      JVM生成的动态类必须实现一个或多个接口(因为要委托JVM生成动态代理类,为了明确生成类中的方法,通过接口可直接告诉虚拟机

      要生成类中所含的方法),所以,JVM生成的动态类只能用作具有相同接口的目标类的代理

 

3 、CGLIB库:为没有接口的方法生成代理类

       当目标类没有实现接口,要想生成动态代理类代理,可以使用CGLIB库,它可以动态生成一个没有接口的类的子类,一个类的子类也可

       以用作该类的代理。

 

4、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回结果外,还可以在代理方法中的如下四个位置加上系统的功能代码

       1> 在调用目标方法之前

       2> 在调用目标方法之后

       3> 在调用目标方法前后

       4> 在处理目标方法异常的catch块中

 

3.1、创建动态类及查看其方法列表信息

复制代码
 1 import java.lang.reflect.Constructor;
 2 import java.lang.reflect.InvocationTargetException;
 3 import java.lang.reflect.Method;
 4 import java.lang.reflect.Proxy;
 5 import java.util.Collection;
 6 
 7 public class ProxyTest2 {
 8      public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {  
 9           
10             //创建一个动态类,参数为类加载器和接口,类加载器通常使用和接口相同的类加载器
11             Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);  
12             System.out.println(clazzProxy1.getName());  
13               
14         System.out.println("");      //获取构造函数的信息    
15             Constructor[] constructors = clazzProxy1.getConstructors();  
16            
17             for (Constructor constructor : constructors) {               //遍历构造函数
18                 String name = constructor.getName();  
19                 StringBuilder sBuilder = new StringBuilder(name);          //借助StringBuilder来打印指定格式
20                 sBuilder.append('(');  
21                 Class[] clazzParams = constructor.getParameterTypes();   //获取构造函数的参数类型
22                 for (Class clazzParam : clazzParams) {                  //遍历构造函数的参数类型
23                     sBuilder.append(clazzParam.getName()).append(',');  //获取构造函数参数类型名
24                 }  
25                 if(clazzParams.length!=0)           //如果参数类型数组长度为0,就不删除sBuilder最后的符号
26                // if(clazzParams!=null&&clazzParams.length!=0)  
27                     sBuilder.deleteCharAt(sBuilder.length()-1);     //去掉末尾的','
28                 sBuilder.append(')');  
29                 System.out.println(sBuilder.toString());  //只有一个构造函数:Proxy0(java.lang.reflect.InvocationHandler)
30             }  
31               
32         System.out.println("");   //获取该动态类的一般方法信息
33       
34             Method[] methods = clazzProxy1.getMethods();  
35             for (Method method : methods) {  
36                 String name = method.getName();  
37                 StringBuilder sBuilder = new StringBuilder(name);  
38                 sBuilder.append('(');  
39                 Class[] clazzParams = method.getParameterTypes();  
40                 for (Class clazzParam : clazzParams) {  
41                     sBuilder.append(clazzParam.getName()).append(',');  
42                 }  
43                 if(clazzParams.length!=0)  
44                     sBuilder.deleteCharAt(sBuilder.length()-1);  
45                 sBuilder.append(')');  
46                 System.out.println(sBuilder.toString());  
47          }  
48      }
49 }
复制代码

 

3.2、创建动态类的实例对象及调用其方法

       这里使用了3种方法实现HandlerInvocation接口,其中第1种内部返回null,为了分析handler内部工作原理,一个是匿名内部类

       创建动态类的实例对象的方式有两种:

                 1、直接调用Proxy类的静态方法newProxyInstance( )获取字节码和实例对象

                 2、通过构造函数的方式创建

                          1> 调用Proxy的静态方法getProxyClass( )获取一个动态代理类  

                          2> 通过代理类调用getConstructor( )获取代理类的构造函数  

                          3> 通过构造函数调用newInstance( )方法

复制代码
 1 import java.lang.reflect.Constructor;
 2 import java.lang.reflect.InvocationHandler;
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 import java.util.ArrayList;
 7 import java.util.Collection;
 8 
 9 public class ProxyTest3 {
10     public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {  
11           
12     //创建一个动态类,参数为类加载器和接口,类加载器通常使用和接口相同的类加载器
13     Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);  
14     System.out.println(clazzProxy1.getName());  
15       
16   //创建动态代理类的实例对象  
17   System.out.println("");  
18       Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);  
19       //动态类的构造方法接收一个参数:InvocationHandler,但它是个接口  
20 
21       //三种不同方式的InvocationHandler内部实现方式  
22 
23 //方式一:创建实现类的方式  
24         //这里复写的invoke方法返回null,是为了做测试
25        class MyInvocationHandler1 implements InvocationHandler{  
26           @Override  
27       public Object invoke(Object proxy, Method method, Object[] args)  
28                   throws Throwable {  
29               // TODO Auto-generated method stub  
30               return null;  
31           }  
32        }  
33        
34       Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());  
35       System.out.println(proxy1.toString());  
36       /*打印结果为null,第一种可能是对象没有创建成功,第二种可能是toString()的返回值为null.如果对象为null,当调用toString  
37       时会报告空指针异常,所以是第二种可能,这里的toString方法调用了invoke方法,而返回null,返回的null即为调用toString方法后的值,所以打印null*/  
38     
39       proxy1.clear();  //调用Collection中无返回值的方法,可以通过,同样返回了null,但null相当于返回void,所以可以通过
40       //proxy1.size(); //调用有返回值的方法产生异常空指针异常的原因,是因为其内部会调用handler的invoke方法,由于invoke方法返回null,而size方法要返回一个整数 
41         
42 //方式二:匿名内部类的方式  
43       Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){  
44           @Override  
45           public Object invoke(Object proxy, Method method, Object[] args)  
46                   throws Throwable {  
47               // TODO Auto-generated method stub  
48               return null;  
49           }             
50       });  
51 
52 //方式三:使用Proxy类的newProxyInstance方法直接获取字节码和实例对象
53 //指定一个目标  
54    
55             Collection proxy3 = (Collection)Proxy.newProxyInstance(  
56           Collection.class.getClassLoader(),   
57           new Class[]{Collection.class},
58           new InvocationHandler(){
59             
60       @Override  
61       public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {  
62           
63           ArrayList target = new ArrayList(); 
64           
65           //将系统功能抽取成为一个对象,通过接口方法调用,而不需知道内部的方法
66           long beginTime = System.currentTimeMillis();  
67           //在目标对象身上去执行代理执行的方法,  
68           Object retVal = method.invoke(target, args);                    
69           long endTime = System.currentTimeMillis();  
70           System.out.println("The running time of "+method.getName()+" method is "+(endTime-beginTime));  
71           return retVal;//目标方法将值返回给代理的方法,add方法的返回值就从代理方法的返回值上取  
72         }     
73       }  
74   );
75           proxy3.add("xxx");  /*调用invoke方法,这句话的意思将代理对象proxy3,代理对象的方法add(),参数"xxx",传给invoke方法当使用目标方法的时候,返回一个值,代理类就会接收到这个返回值*/  
76           proxy3.add("yyy");  
77           System.out.println(proxy3.size());  
78           System.out.println(proxy3.getClass().getName());  
79           /*调用invoke方法,去执行目标,目标的getClass().getName()应该返回ArrayList,但结果却为Proxy0.。
80         原因:只有HashCode,equals和toString方法才委托给handler,即自动调用invoke    方法,其他方法有自己的实现。*/  
81   }
82 
83 }
复制代码

3.3、分析InvocationHandler对象的运行原理及上述代码出现的现象

   动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个接受InvocationHandler参数的构造方法。

 

 现象及原因

1、构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?

     接收它,以后用它,内部代码即为:this.handler = handler

 

2、 实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:

 

Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数:

复制代码
Class Proxy$ {
    add(Object object) {
return handler.invoke(Object proxy, Method method, Object[] args);
    }
}
复制代码

 

3、分析先前打印动态类的实例对象时,结果为什么会是null呢?打印实例对象结果为null,原因:

      第一种可能是对象没有创建成功,第二种可能是toString()的返回值为null.如果对象为null,当调用toString时会报告空指针异常,

      所以是第二种可能,这里的toString方法调用了invoke方法,而返回null,返回的null即为调用toString方法后的值,所以打印null

 

4、调用有基本类型返回值的方法时为什么会出现NullPointerException异常?

      因为其内部会调用handler的invoke方法,由于invoke方法返回null,而size方法要返回一个整数

 

5、分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?

      因为调用调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,

      对于其他方法,则不转发调用请求。

 

四、动态代理类的设计原理与结构

      

      客户端调用代理类,代理的构造方法接收一个handler对象,客户端调用代理的各个方法,代理的各个方法会把调用请求转发给handler对象,

      去找handler的invoke方法,在invoke方法内部可以加入要实现的功能,最后handler对象又会把各个请求分发给目标的相应方法。

      每次使用代理类调用目标的各个方法,都会调用handler的invoke方法。

      而invoke方法返回值即为目标方法的最终值,即invoke方法返回什么值,使用代理类调用目标方法后得到的就是什么值,我们也可以在invoke方

      法内部对于返回值进行修改。

 

动态代理的工作原理图

 

五、编写可生成代理和可插入通告的通用方法 

    将要加入的系统功能以对象的形式提供进去,由外部传入,代理对某个目标进行作用也是以对象形式传进去,所以只需把目标和代理的要执行的

    系统功能(通告)传进去即可。  

 

创建一个接口,让代理去实现

 

复制代码
import java.lang.reflect.Method;
public interface Advice {
    void beginMethod(Method method);
    void afterMethod(Method method);
}
复制代码

 

为代理类实现接口的类,到时候直接为代理传进该类的对象即可

复制代码
import java.lang.reflect.Method;
public class MyAdvice implements Advice{

    long beginTime = 0;
    @Override
    public void beginMethod(Method method) {
        // TODO Auto-generated method stub
        System.out.println("轻轻地我来了");
        beginTime = System.currentTimeMillis();  
    }

    @Override
    public void afterMethod(Method method) {
        // TODO Auto-generated method stub
        System.out.println("我挥一挥衣袖,不带走一片云彩……");
        long endTime = System.currentTimeMillis();
        System.out.println("The running time of "+method.getName()+" method is "+(endTime-beginTime));
    }

}
复制代码

 

实现可生成代理和可插入通告的通用方法

复制代码
 1 import java.lang.reflect.Constructor;  
 2 import java.lang.reflect.InvocationHandler;  
 3 import java.lang.reflect.InvocationTargetException;  
 4 import java.lang.reflect.Method;  
 5 import java.lang.reflect.Proxy;  
 6 import java.util.ArrayList;  
 7 import java.util.Collection;  
 8   
 9 public class ProxyTest {  
10     public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {  
11   
12   //使用Proxy类的newProxyInstance方法直接获取字节码和实例对象
13     //指定一个目标  
14       ArrayList target = new ArrayList();  
15       Collection proxy3 = getProxy(target,new MyAdvice());  
16       proxy3.add("xxx"); 
17       proxy3.add("yyy");  
18       System.out.println(proxy3.size());  
19       }
20     
21     //可生成代理和插入通告的通用方法
22     private static Collection getProxy(final Object target,final Advice advice) {
23         Collection proxy3 = (Collection)Proxy.newProxyInstance(  
24                   Collection.class.getClassLoader(),   
25                   /*new Class[]{Collection.class},*/
26                   target.getClass().getInterfaces(),//需要一个和目标相同的接口
27                   new InvocationHandler(){
28                     
29               @Override  
30               public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {  
31                    
32                   /*//将系统功能抽取成为一个对象,通过接口方法调用,而不需知道内部的方法
33                   long beginTime = System.currentTimeMillis();  
34                   //在目标对象身上去执行代理执行的方法,  
35                   Object retVal = method.invoke(target, args);                    
36                   long endTime = System.currentTimeMillis();  
37                   System.out.println(method.getName()+"running time of"+(endTime - beginTime));  
38                   return retVal;//目标方法将值返回给代理的方法,add方法                                                        //的返回值就从代理方法的返回值上取  
39 */                  
40                   advice.beginMethod(method);
41                   Object retVal = method.invoke(target, args);
42                   advice.afterMethod(method);
43                   return retVal;
44               }     
45               }  
46           );
47         return proxy3;
48     }  
49 
50 }
复制代码

 

分享到:
评论

相关推荐

    lotus domino java代理_获取传递值json

    Lotus Domino Java 代理获取传递值 JSON Lotus Domino 是一种基于Notes技术的服务器端应用程序, Lotus Domino Java 代理是指在 Domino 服务器上运行的 Java 代理程序,可以用来处理用户请求、提供数据服务等。今天...

    lotus domnio java代理输出html代理

    Lotus Domino是一个企业级的应用开发平台,而Java代理在Lotus Domino中被用来处理服务器端的任务,例如数据处理、业务逻辑或响应HTTP请求。在这个特定的案例中,`JQueryReportToolJavaAgent`是一个Java代理,它被...

    lotus domnio java代理传值

    在Lotus Domino开发环境中,Java代理是用于执行服务器端任务的重要工具,它们可以自动化许多功能,如处理数据、发送邮件等。"lotus domnio java代理传值"这个主题主要涉及如何在Java代码中创建和使用代理,并在代理...

    JAVA代理

    总结一下,`JAVA代理`涉及到的主要知识点包括: 1. **静态代理**:手动创建代理类,通过代理类对目标对象进行方法调用的拦截和增强。 2. **动态代理**:利用Java的`Proxy`类和`InvocationHandler`接口,动态地创建...

    Java代理模式Java动态代理

    ### Java代理模式与Java动态代理详解 #### 一、代理模式概述 代理模式是一种软件设计模式,它在客户端和目标对象之间提供了一种间接层。这种模式的主要目的是控制客户端对目标对象的访问,并且可以在不修改原有...

    java代理实现webservice接口拦截器功能

    本文根据java代理实现CXF拦截器异常时只能进入fault拦截器而不能继续向下执行的问题。 利用java代理让RMI具有拦截器的功能。

    lotus Domino BS开发 通过java 代理导出excel.doc

    本教程将聚焦于如何利用Java代理来实现Excel文档的导出。 Lotus Domino是一款强大的协作软件,它提供了丰富的开发环境,包括服务器端的LotusScript和客户端的JavaScript,但在此场景下,我们使用的是Java代理,因为...

    lotus domnio java代理获取当前用户名

    在 Lotus Domino 中,Java 代理是可以运行在 Domino 服务器上的小程序,它可以执行特定的任务,如获取当前用户名。 在 Java 代理中,获取当前用户名是一个常见的需求。 Lotus Domino 提供了多种方式来获取当前...

    java代理模式

    Java代理模式是一种设计模式,它在面向对象编程中扮演着重要的角色,主要目的是为了在不修改原有对象的基础上,为对象添加额外的功能或者控制对对象的访问。代理模式的核心思想是通过代理类来间接调用目标类的方法,...

    Java代理模式例子

    Java代理模式 事例很生动,非常容易理解,推荐给大家。

    java 代理服务器源码

    java 代理服务源码 测试通过可以,可以自己修改成为自己的代理类。

    Java代理服务器程序

    Java代理服务器程序是一种用于转发网络请求的应用,它在客户端与目标服务器之间起到中介的作用。这样的设计使得用户可以通过代理服务器访问互联网,从而实现多种功能,如匿名浏览、负载均衡、缓存优化、网络监控等。...

    java代理机制 JDK动态代理和cglib代理 详解

    Java代理机制是Java编程中一个重要的特性,它允许我们在不修改原有代码的基础上,为已有类增加额外的功能。本文将深入探讨两种主要的Java代理实现:JDK动态代理和CGLIB代理。 一、JDK动态代理 JDK动态代理基于接口...

    java代理源码

    Java代理源码主要涉及到的是Java编程中的动态代理和网络代理技术。在Spring MVC框架下,开发者经常使用代理模式来扩展或增强对象的功能,同时,这里的"代理.war"文件表明这是一个Web应用程序,通常用于部署在如...

    Java代理学习

    通过深入理解这两种代理方式,开发者可以更好地利用Java代理机制来优化代码结构,实现更复杂的业务需求。在实际项目中,结合AOP框架如Spring AOP,可以进一步简化代理的使用,提高代码的可维护性和复用性。

    java 代理例子 -javaagent,premain方式实现

    Java代理是一种在运行时增强或拦截对象方法调用的技术,它可以让我们在不修改原有代码的情况下,为类添加新的功能或行为。在Java中,代理主要分为两种:静态代理和动态代理。静态代理是通过手动创建代理类来实现的,...

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 .md

    ### Java 代理模式详解 #### 1. 代理模式概览 代理模式是一种设计模式,主要目的是为了控制或提供对某对象的访问。在代理模式中,代理对象充当客户端与真实对象之间的中介,使得客户端可以通过代理对象间接地访问...

    Lotusscript java代理自动下载指定路径文件

    Lotusscript java代理自动下载指定路径文件。可批量下载指定路径

    Java代理模式模板代码,包含动态代理与静态代理

    Java代理模式是一种设计模式,它允许我们为一个对象创建一个代理,这个代理对象可以在不影响原始对象功能的基础上,增强或扩展其行为。代理模式在软件开发中广泛应用,例如用于权限控制、事务管理、日志记录等场景。...

    java 代理模式详解

    总结一下,Java代理模式的核心在于`Proxy`类和`InvocationHandler`接口,它们共同实现了在运行时动态创建符合特定接口的代理对象。通过代理,我们可以在不修改原始对象代码的情况下,添加额外的功能或者控制对原始...

Global site tag (gtag.js) - Google Analytics