java之代理

来源:互联网 发布:淘宝行业数据哪里看 编辑:程序博客网 时间:2024/06/01 14:52

一、概述

1、  背景:有一个已经开发好的类,但是现在要为其增加异常处理日志统计方法的运行时间事务处理等功能,但是开发好的类不能修改或者没有办法修改,该如何做?

2、  代理类和目标类

a) 目标类

已经开发好不能被修改称为目标类

b) 代理类和目标类的关系

1) 代理类的功能目标类的功能一样

2) 只是在执行目标类相关功能前或者后增加了新的辅助功能

1 代理类每个方法名目标类每个方法名都一样

2 代理类每个方法都要调用目标类每个方法

3 代理类每个方法调用目标类每个方法前或者后都会加上系统的额外功能

c) 客户端不直接使用目标类,而是直接使用代理类,代理类实际上是在调用目标类的功能。

       3、程序中的代理

       编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换。

 

       4AOP

处理学生课程管理三种业务,有各自的CRUD。但是无论这些类的各自的业务如何不同都会涉及到安全事务管理日志三个方面的内容。、

交叉业务:就是不同模块具有的共同业务

      

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

       

用代码描述交叉业务如下所示:

       

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采

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

   

注意:

安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务。

重要原则:不要把供货商暴露给你的客户。

 

1) 交叉业务编程问题就称为AOP(Aspect Oriented Programming)

a) AOP代理联系的纽带

思想:将和切面有关的代码移动到原始方法周围,这和直接在方法中编写切面代码之后的整体运行效果是一致的。

 

b) AOP代理关联

如果将切面的代码移到原始方法的周围原始方法看做目标类的方法,那么移动以后的切面代码+原始代码就是代理类对应的方法

c) 可以采用代理的方式实现AOP

代理实现AOP核心关键技术。只要是AOP,就一定会涉及代理技术。

d) 代理技术的分类

按照是否是在程序运行期间产生代理类可以将代理分为静态代理动态代理

1 静态代理:就是手动为每一个目标类每一个方法增加交叉业务,也就是手动为每一个目标类增加代理类

缺点:如果目标类数量非常多或者目标类中的功能非常多直接使用静态代理的方式来为目标类增加交叉业务会非常的繁琐。

2 动态代理:通过特定的设置,在程序运行期间指示JVM动态地生成类的字节码。这种动态生成的类往往被用作代理类,即动态代理类。也就是运行时做编译的事情并且把生成的字节码加载成这个类的Class对象

 

       5、动态代理

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

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

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

 

CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现

接口的类生成动态代理类,那么可以使用CGLIB

 

操作步骤如下:

(1). 调用目标类的目标方法

(2). 调用目标类的目标方法

(3). 调用目标类的目标方法和之

(4). 调用目标类的目标方法异常的catch块中

 

a)       创建动态类及查看其方法列表信息

如下所示:

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 NoSuchMethodException, Exception {

              Class clazzproxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(

), Collection.class);

              System.out.println(clazzproxy1.getName());

             

//            查看clazzproxy1中的所有构造方法

              System.out.println("-------begain constructor list-------");

              Constructor [] constructors = clazzproxy1.getConstructors();

              for(Constructor constructor:constructors){

                     String name = constructor.getName();

                     StringBuilder sb = new StringBuilder(name);

                     sb.append("(");

                     Class[] clazzPrams=constructor.getParameterTypes();

                     for(Class clazzPram:clazzPrams){

                            sb.append(clazzPram.getName()).append(",");

                     }

                     if(clazzPrams!=null&&clazzPrams.length!=0){

                            sb.deleteCharAt(sb.length()-1);

                     }

                     sb.append(")");

                    

                     System.out.println(sb.toString());

              }

             

//            查看clazzproxy1中的所有方法

              System.out.println("-------begain method list-------");

              Method [] methods = clazzproxy1.getMethods();

              for(Method method:methods){

                     String name = method.getName();

                     StringBuilder sb = new StringBuilder(name);

                     sb.append("(");

                     Class[] clazzPrams=method.getParameterTypes();

                     for(Class clazzPram:clazzPrams){

                            sb.append(clazzPram.getName()).append(",");

                     }

                     if(clazzPrams!=null&&clazzPrams.length!=0){

                            sb.deleteCharAt(sb.length()-1);

                     }

                     sb.append(")");

                    

                     System.out.println(sb.toString());

              }

       }

}

运行结果如下:

-------begain constructor list-------

com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

-------begain method list-------

add(java.lang.Object)

remove(java.lang.Object)

equals(java.lang.Object)

toString()

hashCode()

clear()

contains(java.lang.Object)

isEmpty()

iterator()

size()

toArray([Ljava.lang.Object;)

toArray()

spliterator()

addAll(java.util.Collection)

stream()

forEach(java.util.function.Consumer)

removeAll(java.util.Collection)

containsAll(java.util.Collection)

retainAll(java.util.Collection)

parallelStream()

removeIf(java.util.function.Predicate)

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

知识点复习:StringBufferStringBuilder的区别及运用。

答:StringBuilder相对于StringBuffer效率高些,StringBuffer线程安全,StringBuilder线程不安全,StringBuffer用于单线程,StringBuildr用于多线程。

 

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

代码如下:

public class proxyTest {

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

              System.out.println("-------begain creat instance--------");

//            创建该构造方法的方法

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

//            InvocationHandler为一个接口,需创建一个类来实现它

              class myInvocationHander1 implements InvocationHandler{

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

                                   throws Throwable {

                           

                            return null;

                     }

              }

             

              Collection proxy1 = (Collection) constructor.newInstance(new myInvocationHander1());

              System.out.println(proxy1);//结果显示为null

              proxy1.clear();//结果无返回值,不出异常

              proxy1.size();//抛出异常

       }

}

 

c)       完成InvocationHandler对象的内部功能

public class proxyTest {

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

 

              Collection proxy3 = (Collection)Proxy.newProxyInstance(

                            Collection.class.getClassLoader(),

                            new Class[]{Collection.class},

                            new InvocationHandler() {

                                   ArrayList target = new ArrayList();

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

                                                 throws Throwable {

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

                                          return retval;

                                   }

                            }

                            );

                            proxy3.add("abc");

                            proxy3.add("bbc");

                            proxy3.add("qqtv");

                            System.out.println(proxy3.size());

       }

}

运行结果为3

 

d) 分析InvocationHandler对象的运行原理

       动态生成的类实现了Collection接口生成的Collection类中的所有方法和一个

InvocationHandler参数的构造方法。

       实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的

三个参数又是什么意思?

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

       class proxy${

              add(Object object){

                     return handler invoker(Object proxy,Method method, Object[]args);

              }

       }

其中objProxy对象对应Object proxyadd方法对应Method method”abc”参数对应Object[]args

代码如下:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.ArrayList;

import java.util.Collection;

public classProxyTest {

@SuppressWarnings("unchecked")

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

@SuppressWarnings("rawtypes")

Collection proxy = (Collection)Proxy.newProxyInstance(

Collection.class.getClassLoader(),

new Class[] {Collection.class },

new InvocationHandler() {

@Override

public Object invoke(Object proxy,Method method,

Object[] args)throws Throwable{

return null;

}

});

System.out.println(proxy.getClass());

System.out.println(proxy);// 结果:null

proxy.clear();// 无返回值的方法,不会报告异常

proxy.size();//有返回值的方法,会报告异常

}

}

小结:

       分析上面打印动态类的实例对象时,结果是null,因为打印动态类的示例对象实际上就是打印proxytoString方法;

       调用有基本类型返回值的方法时出现NullPointerException异常,是因为执行的代码为:

       int size(){

return handler.invoke(this,this.getClass().getMethod("size"),null);

}

       调用invoke方法返回的是nullnull转换为int类型时,则会报异常。

 

总结分析动态代理类的设计原理与结构:

0 0
原创粉丝点击