代理与动态代理

删除回忆录丶 提交于 2020-03-07 17:42:19

概述

代理

生活中的代理:比如暴雪又新出了一款游戏,作为一个中国用户,没有很好的方式直接从美国拿到游戏产品,就需要等到中国的某个代理商被暴雪授权代理,才可以玩上暴雪新出的这款游戏,那么代理的作用也就显而易见了,就是为了方便的适应一些不同地区的用户而产生的中间媒介,通过代理,可以更有效的将一些产品发行出去,生活中这样代理的例子比比皆是

程序中的代理:如果一些实现了同一接口的类在实现方法的时候,有了新的需求,需要增加一些异常处理、日志、计算方法的运行时间、事务管理等操作,但是我们又不能去修改源代码,这样严重影响了程序的扩展性,这个时候,我们就可以使用代理,代理与目标类实现同一个接口,代表着代理类与目标类具有同名的方法,调用者不需要直接调用目标对象,而是使用代理间接调用目标对象的方法,并在这个过程中加入自己的处理代码,这就是程序中的代理

代理实现图示

proxy

代理可以处理的问题

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

proxy2

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

proxy3

 

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

proxy4

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

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

  1. 在调用目标方法之前
  2. 在调用目标方法之后
  3. 在调用目标方法前后
  4. 在处理目标方法异常的catch块中

动态代理

了解了代理之后,我们知道代理类要实现与目标类相同的接口,那么现存的已知接口与其子类那么多,我们不可能一个个的手动实现他们的代理类,这个时候,java.lang.reflect中的Proxy类为我们提供了可以动态生成代理类的方法,也就是所谓的动态代理技术

动态代理就是不需要手动,而是通过JVM自动生成一份字节码,这份字节码实现了需要被代理的类的父类接口,这样就使得创建代理类变成了一件模块化的事情

利用动态代理实现一个简单代理过程

public class Proxy
extends Object
implements Serializable

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

static InvocationHandler
getInvocationHandler(Object proxy)
          返回指定代理实例的调用处理程序。

static Class<?>
getProxyClass(ClassLoader loader, Class<?>... interfaces)
          返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。

static boolean
isProxyClass(Class<?> cl)
          当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。

static Object
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

以上是Proxy代理类的一些方法,其中,getProxyClass可以获得生成好的代理类字节码,而newProxyInstance可以直接获得一个动态代理类的对象

这里我们考虑,生成一个代理类,具体需要哪些东西?

  • 一份父类接口的字节码
  • 一个用于加载生成的代理类的类加载器
  • 一系列的对目标对象的操作

这也就是newProxyInstance的参数含义,其中对目标对象的操作封装在InvocationHandler类中

public interface InvocationHandler

InvocationHandler 是代理实例的调用处理程序 实现的接口。

Object
invoke(Object proxy, Method method, Object[] args)
          在代理实例上处理方法调用并返回结果。

这个类就是封装了操作目标对象的具体实现代码,也就是说,将功能也模块化

InvocationHandler中只有一个方法,需要实现的其他功能以及对目标对象的操作,需要在这里完成

然后将这个对象作为参数传递给newProxyInstance方法,从而构造代理类

下面的例子通过newProxyInstance方法实现动态代理

首先是一个公共接口

   1: public interface Speak {
   2:     public void sayHello();
   3: }

以及一个该接口的子类

   1: public class MySpeak implements Speak{
   2:     @Override
   3:     public void sayHello() {
   4:         // TODO Auto-generated method stub
   5:         System.out.println("Hello,World!");
   6:     }
   7: }

下面我们可以创建对该子类对象具体操作的InvocationHandler子类了

   1: public class SpeakHandler implements InvocationHandler {
   2:     //需要操作的目标对象
   3:     private Object obj;
   4:     
   5:     public SpeakHandler(Object obj) {
   6:         super();
   7:         this.obj = obj;
   8:     }
   9:  
  10:     /* (non-Javadoc)
  11:      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
  12:      */
  13:     @Override
  14:     public Object invoke(Object proxy, Method method, Object[] args)
  15:             throws Throwable {
  16:         //执行目标方法前的代码
  17:         before();
  18:         
  19:         //利用反射执行目标对象的对应方法
  20:         Object retVal = method.invoke(obj, args);
  21:         
  22:         //执行目标方法后的代码
  23:         after();
  24:         
  25:         return retVal;
  26:     }
  27:     
  28:     private void before(){
  29:         System.out.println("before");
  30:     }
  31:     
  32:     private void after(){
  33:         System.out.println("after");
  34:     }
  35: }

好了,准备工作完成了,这样就可以利用Proxy类获取我们的代理类对象了

   1: public class ProxyTest {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         //生成目标对象
   9:         MySpeak ms = new MySpeak();
  10:         
  11:         //生成Handler对象
  12:         SpeakHandler speakHandler = new SpeakHandler(ms);
  13:         
  14:         //构造代理类,获取对象
  15:         Speak speak = (Speak)Proxy.newProxyInstance(
  16:                 Speak.class.getClassLoader(),
  17:                 new Class[]{Speak.class},
  18:                 speakHandler);
  19:         
  20:         //执行代理类实现的接口方法
  21:         speak.sayHello();
  22:     }
  23: }

打印结果

before
Hello,World!
after

推测代理类内部代码实现方式

根据Invocation的invoke方法的三个参数,我们基本可以推测出内部实现的代码应该是这样的:

sayHello(){

handler.invoke(this,this.getClass().getMethod(“sayHello”,null),null);

}

所以整个类的运行过程图示为:

proxy5

模块化的代理

了解上面创建代理类的过程,我们可以抽取一些硬编码进去的功能,提高程序的扩展性

首先,我们要有一个接口用来表示建议执行的额外功能

   1: public interface Advice {
   2:     //目标对象执行前的操作
   3:     void beforeMethod();
   4:     //目标对象执行后的操作
   5:     void afterMethod();
   6: }

然后,就可以完成我们的一步构造代理类对象的方法了

   1: public class ProxyTest2 {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         Collection col = (Collection)getProxy(new ArrayList(),new Advice() {
   9:             
  10:             @Override
  11:             public void beforeMethod() {
  12:                 // TODO Auto-generated method stub
  13:                 System.out.println("before!");
  14:             }
  15:             
  16:             @Override
  17:             public void afterMethod() {
  18:                 // TODO Auto-generated method stub
  19:                 System.out.println("after!");
  20:             }
  21:         });
  22:         
  23:         col.add(1);
  24:         col.add(2);
  25:         System.out.println(col.size());
  26:         System.out.println(col.getClass());
  27:  
  28:     }
  29:     
  30:     public static Object getProxy(final Object obj,final Advice advice){
  31:         //获取代理类对象
  32:         Object retObj = Proxy.newProxyInstance(
  33:                 obj.getClass().getClassLoader(),//根据对象得到类加载器
  34:                 obj.getClass().getInterfaces(),//根据对象得到接口列表
  35:                 new InvocationHandler() {
  36:                     
  37:                     @Override
  38:                     public Object invoke(Object proxy, Method method, Object[] args)
  39:                             throws Throwable {
  40:                         //建议的系统功能
  41:                         advice.beforeMethod();
  42:                         
  43:                         Object retVal = method.invoke(obj, args);
  44:                         
  45:                         //建议的系统功能
  46:                         advice.afterMethod();
  47:                         
  48:                         return retVal;
  49:                         
  50:                     }
  51:                 });
  52:         return retObj;
  53:     }
  54:  
  55: }

这样,我们就将系统建议的功能以及需要操作的目标对象都暴露在外面供调用者使用,其他全部封装起来

注意:

这里col.getClass打印的结果为$Proxy

这是因为代理类在执行继承自Object的方法时,只有执行到hashCode, equals, 或toString,才会将方法传递到目标对象,也就是说只有执行到这三个方法时,才会返回目标对象方法的返回值,而其他的继承自Object的方法目标则是代理类对象本身

实现AOP功能的封装与配置

上面提到了动态代理是解决AOP的核心技术,那么我们来实现一个简单的框架

BeanFactory类通过类中的getBean方法产生需求的对象

配置文件中配置一系列key value值,如下

#ArrayList=java.util.ArrayList
ArrayList=proxy.ProxyFactoryBean
ArrayList.target=java.util.ArrayList
ArrayList.advice=proxy.aopframework.MyAdvice

当value值不是一个ProxyFactoryBean对象时,直接返回读取到的类的对象

当value值为一个ProxyFactoryBean对象时,通过ProxyFactoryBean中的getProxy生成目标类的代理类对象,并返回

这样就实现了一个简单的获取对象的框架

我们需要做的就只是编写自己需要用到的类,并配置到配置文件中,即可。

下面是实现过程

BeanFactory类

   1: /**
   2:  * @author Shawn
   3:  * Bean工厂
   4:  */
   5: public class BeanFactory {
   6:     Properties props = new Properties();
   7:     
   8:     public BeanFactory(InputStream ips){
   9:         try {
  10:             //载入配置文件读取流
  11:             props.load(ips);
  12:         } catch (IOException e) {
  13:             // TODO Auto-generated catch block
  14:             e.printStackTrace();
  15:         }
  16:     }
  17:     
  18:     //主要方法,根据key值生成对象
  19:     public Object getBean(String name){
  20:         Object obj = null;
  21:         String className = props.getProperty(name);
  22:         try {
  23:             Class beanClass = Class.forName(className);
  24:             obj = beanClass.newInstance();
  25:         } catch (Exception e1) {
  26:             // TODO Auto-generated catch block
  27:             e1.printStackTrace();
  28:         }
  29:         //如果需要生成的是代理类对象
  30:         if(obj instanceof ProxyFactoryBean){
  31:             Object retObj = null;
  32:             try {
  33:                 //读取对应的Advice系统功能类,并创建对象
  34:                 Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance();
  35:                 //读取对应的target目标类,并生成对象
  36:                 Object target = Class.forName(props.getProperty(name+".target")).newInstance();
  37:                 ProxyFactoryBean proxyObj = (ProxyFactoryBean)obj;
  38:                 //为ProxyFactoryBean对象set相关属性
  39:                 proxyObj.setAdvice(advice);
  40:                 proxyObj.setTarget(target);
  41:                 //通过getProxy方法获得代理类对象
  42:                 retObj = proxyObj.getProxy();
  43:             } catch (Exception e) {
  44:                 // TODO Auto-generated catch block
  45:                 e.printStackTrace();
  46:             }
  47:             return retObj;
  48:         }
  49:         else
  50:             return obj;
  51:     }
  52: }

ProxyFactoryBean类

   1: /**
   2:  * @author Shawn
   3:  * 代理工厂
   4:  */
   5: public class ProxyFactoryBean {
   6:     //目标类
   7:     private Object target;
   8:     //系统建议的方法
   9:     private Advice advice;
  10:     
  11:     
  12:     public Object getTarget() {
  13:         return target;
  14:     }
  15:  
  16:     public void setTarget(Object target) {
  17:         this.target = target;
  18:     }
  19:  
  20:  
  21:     public Advice getAdvice() {
  22:         return advice;
  23:     }
  24:  
  25:  
  26:     public void setAdvice(Advice advice) {
  27:         this.advice = advice;
  28:     }
  29:  
  30:     //获取代理类对象
  31:     public Object getProxy() {
  32:         
  33:         Object retObj = Proxy.newProxyInstance(
  34:                 target.getClass().getClassLoader(),//根据对象得到类加载器
  35:                 target.getClass().getInterfaces(),//根据对象得到接口列表
  36:                 new InvocationHandler() {
  37:                     
  38:                     @Override
  39:                     public Object invoke(Object proxy, Method method, Object[] args)
  40:                             throws Throwable {
  41:                         //建议的系统功能
  42:                         advice.beforeMethod();
  43:                         
  44:                         Object retVal = method.invoke(target, args);
  45:                         
  46:                         //建议的系统功能
  47:                         advice.afterMethod();
  48:                         
  49:                         return retVal;
  50:                         
  51:                     }
  52:                 });
  53:         return retObj;
  54:     }
  55:  
  56: }

下面进行测试,编写自己的MyAdvice类,实现Advice接口,表示系统建议的功能

   1: /**
   2:  * @author Shawn
   3:  *
   4:  */
   5: public class MyAdvice implements Advice {
   6:  
   7:     /* (non-Javadoc)
   8:      * @see proxy.Advice#afterMethod()
   9:      */
  10:     @Override
  11:     public void afterMethod() {
  12:         // TODO Auto-generated method stub
  13:         System.out.println("after");
  14:  
  15:     }
  16:  
  17:     /* (non-Javadoc)
  18:      * @see proxy.Advice#beforeMethod()
  19:      */
  20:     @Override
  21:     public void beforeMethod() {
  22:         // TODO Auto-generated method stub
  23:         System.out.println("before");
  24:  
  25:     }
  26: }

配置文件

   1: #ArrayList=java.util.ArrayList
   2: ArrayList=proxy.aopframework.ProxyFactoryBean
   3: ArrayList.target=java.util.ArrayList
   4: ArrayList.advice=proxy.aopframework.MyAdvice

测试类

   1: public class AopFrameworkTest {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         //载入配置文件
   9:         InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
  10:         
  11:         //创建Bean工厂
  12:         BeanFactory factory = new BeanFactory(ips);
  13:         
  14:         //获得对象
  15:         Collection list = (Collection)factory.getBean("ArrayList");
  16:         
  17:         list.add(1);
  18:         list.add(2);
  19:         System.out.println(list);
  20:     }
  21: }

这样就完成了一个简单的框架

配置文件中

ArrayList=java.util.ArrayList时,得到的是目标类对象

ArrayList=proxy.aopframework.ProxyFactoryBean时,得到的是目标类的代理类对象

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!