java代理

来源:互联网 发布:西门子系统圆弧编程 编辑:程序博客网 时间:2024/05/22 03:53

代理

1.       程序中的代理:要为已存的多个具有相同功能接口的目标类的各个方法增加一些系统功能,例如:异常处理,日志,计算方法和运行时间。

2.       编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用功能上增加系统功能代码。

3.       系统中存在交叉业务,一个交叉业务就是要切入到系统的一个方面。

a)       交叉业务的编程问题即为面向方面的编程问题(Aspectoriented program)简称AOP,AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。

4.       动态代理技术:

a)       要为系统中的各种借口类增加代理功能,那就需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事,JVM可以再运行时期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理。

b)      JVM生成的的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能作用于具有相同接口的目标类的代理。

c)       CGLIB库可以动态生成一个类的子类,一个类的子类也可以作该类的代理,所以,要为一个没有实现接口的类生成动态代理,那么可以使用CGLIB库。

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

l         在调用方法之前

l         在调用方法之后

l         在调用方法前后

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

5.       代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

6.       要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:

a)       java.lang.reflect.Proxy:这是 Java动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象

l         1: 该方法用于获取指定代理对象所关联的调用处理器

l         static InvocationHandler getInvocationHandler(Objectproxy)

l          

l         2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

l         static Class getProxyClass(ClassLoader loader, Class[]interfaces)

l          

l         3:该方法用于判断指定类对象是否是一个动态代理类

l         static boolean isProxyClass(Class cl)

l          

l         4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

l         static Object newProxyInstance(ClassLoader loader,Class[] interfaces,

l             InvocationHandler h)

b)       java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。 

l         // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象

l         // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

l         Object invoke(Object proxy, Method method, Object[] args)

c)        java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象

7.        Java 动态代理。具体有如下四步骤:

a)       通过实现 InvocationHandler接口创建自己的调用处理器;

b)       通过为 Proxy类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

c)        通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

d)       通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

8动态代理对象创建过程

a)       //InvocationHandlerImpl 实现了InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

b)       // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

c)        InvocationHandlerhandler = new InvocationHandlerImpl(..);

d)        

e)       // 通过 Proxy 为包括Interface 接口在内的一组接口动态创建代理类的类对象

f)        Classclazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

g)        

h)       // 通过反射从生成的类对象获得构造函数对象

i)         Constructorconstructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

j)          

k)       // 通过构造函数对象创建动态代理类实例

l)         InterfaceProxy = (Interface)constructor.newInstance(new Object[] { handler });

9     分析JVM动态生成的类

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

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Collection;

 

 

public class ProxyText {

    public static void main(String[] args)throws Exception {

       Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

       //获取Proxy这份字节码,实现了Collection接口,类加载器为对应的接口类的类加载器

       System.out.println(clazzProxy1.getName()+"-------");

      

       System.out.println("-----------BeginConstructor List-------------");

       Constructor[] constructors =clazzProxy1.getConstructors();

       //获取字节码的所有构造函数,存到数组中

       for(Constructor constructor : constructors){

           //高级for循环遍历constructors

           String name = constructor.getName();

           StringBuilder sBuilder = new StringBuilder(name);

           //定义容器存储名称

           sBuilder.append('(');

           Class[] clazzParams = constructor.getParameterTypes();

           //clazzParams储存参数类型,

           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);

       }

       System.out.println("-----------BeginMethod 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);

       }

总结:动态代理类$Proxy0内部有带一个参数(InvocationHandler)的构造方法,

Proxy0实现了COnection接口,不仅得到了Connection的方法还获取了ObjectHashCodetoString方法

System.out.println("-----------Begin create List-------------");

Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);

       //获取动态代理的构造参数,接收的类型是InvocationHandler

       class MyInvocationHandlerimplements InvocationHandler{

           //因为InvocationHandler为接口类型不可以new也就是实例化出来,

       //所以定义自己的MyInvocationHandler类去是实现InvocationHandler接口,必须覆盖invoke方法。

           @Override

           public Object invoke(Object proxy, Method method, Object[]args)

                  throws Throwable {

              // TODO Auto-generatedmethod stub

              return null;

           }

       }

       Collection proxy1 = (Collection)constructor.newInstance(newMyInvocationHandler());

       System.out.println(proxy1);//打印出null

       proxy1.clear();//打印出null

       proxy1.size();//运行空指针异常,发现returnnull;而size返回的是int

 

---------------------------------------------------------------

//讲前面的两步简化成一步实现,Proxy提供了newProxyInstance

//Foo f = (Foo)Proxy.newProxyInstance(

//Foo.class.getClassLoader(),

//new Class[] { Foo.class },

//handler);

       Collection proxy3 = (Collection)Proxy.newProxyInstance(

       Collection.class.getClassLoader(),//类加载器

       new Class[]{Collection.class},//加载的类,写在大括号,为多个实现类。此处为Collection

       new InvocationHandler(){//实现InvocationHandler接口的子类,匿名内部类方法

       ArrayList target =newArrayList();

       public Object invoke(Object proxy, Method method, Object[]args)

              throws Throwable {

              //ArrayListtarget = new ArrayList();

              long beginTime = System.currentTimeMillis();

              Object retVal = method.invoke(target, args);

              long endTime = System.currentTimeMillis();

              System.out.println(method.getName()+"running time of "+(endTime-beginTime));

              return retVal;

           }});

       proxy3.add("xxx");//每调用一次add方法,就走了invoke方法一次

       proxy3.add("dddd");

       proxy3.add("ffff");

       System.out.println(proxy3.size());//打印出3

System.out.println(proxy3.getClass().getName());//为什么返回的不是ArrayList呢?不是调用了invoke方法了吗?

//通过查询文档了解,getClass()继承与Object,这个上帝只允许hashCodetoStringequals方法访问Handler,其他方法不交给 Handler

       System.out.println(proxy3.toString());//可以验证了以上说法

  1. 猜想分析动态生成的类的内部代码

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

l        接受是为了创建一个对象记住它,方便以后使用。所以构造函数的代码应该是这样写的

InvocationHandler handler;

Public $Proxy0(InvocationHandler handler){

this. handler = handler ;

}

l          

b)     

c)      客户端(client)调用动态代理$Proxy1的构造方法,构造方法传入InvocationHandler,然后在去找到invoke方法,内部加入日志功能,然后在去找目标的方法。



原创粉丝点击