Java 动态代理模式

来源:互联网 发布:js去掉html所有的属性 编辑:程序博客网 时间:2024/06/05 21:50

            对Java中的动态代理模式得到理解透彻可以帮助我们更好的理解Spring。例如Spring中的AOP,Struts2中的拦截器

           代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模一般涉及到的角色有:

       抽象角色:声明真实对象和代理对象的共同接口。

       代理角色:代理对象角色内部含有对真实对象的引用,从而可以操纵真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

        真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。


 一     下面我们先看一下静态代理模式:


        Subject:代理角色和真实角色共同的接口。

        RealSubject:真实角色类

         ProxySubject:代理角色类

         Client:测试类


         我们先创建接口:

         public interface  Subject
       {
public void request();
        }

       创建真实角色类:


        public class RealSubject implements Subject
       {
public void request()
{
System.out.println("From real subject.");
}
     }

       创建代理角色类:

   public class ProxySubject  inplements Subject
{
private RealSubject realSubject; //

public void request()
{
this.preRequest();//在真实角色操作之前进行的操作

if(null == realSubject)
{
realSubject = new RealSubject();
}

realSubject.request();//调用真实角色的方法

this.postRequest();//在真实角色操作之后进行的操作
}

private void preRequest()
{
System.out.println("pre request");
}

private void postRequest()
{
System.out.println("post request");
}
     }


    创建测试类:

     public class Client
   {
public static void main(String[] args)
{
Subject subject = new ProxySubject();

subject.request();
}
   }


运行结果:

pre request

from realrequest

post request


  我们就用通俗的生活语言描述一下静态代理模式,其实 代理模式与我们生活中租房子的经历很像,首先你想要租房子,你就得找中介,中介背后站着的就是房东,中介会帮你联系房东帮你找到房源,所以一般情况下你自己不会联系房东,你所需要做的仅仅是找到中介就好。然后由 中介在帮你找到房东弄好房源,同时中介本身也会完成一些工作,如向你收取中介费。

由以上代码看出,客户()也就是测试类)实际需要调用的是RealSubject里面的request方法,现在用ProxySubject来代理RealSubject,同样达到了目的,而且还封装了ProxySubject的其他方法,完成其他工作。


   

静态代理,个人理解为自己手写的代理类,或者用工具生成的代理类,总之,就是程序运行前就已经存在的编译好的代理类。

如果使用静态代理模式,那么真实角色必须是事先存在的,并且将其作为代理类的内部属性。但在这样使用时,一个真实角色就必须对应一个代理角色,大量使用就会导致类的 膨胀。如果事先不知道真实角色,该如何使用代理?这时就会用到java我的动态代理模式,在动态代理模式中,开始并不存在代理角色,而是在运行过程中,动态的生成代理角色类,并且我们开始并不会固定哪个真实角色,而是在生成动态代理类的时候赋予其某个真实角色的引用,下面是叙述一下动态代理模式中的jdk动态代理实现。

如何生成的:根据Java的反射机制动态生成。

    

    java动态代理类位于java.lang.reflect.包下,主要涉及到两个类:

   

  Interface InvocationHandler:  该接口中定义了一个方法:
        public Object invoket    (Object object,Method method,Object [ ]args )

         在实际使用时,第一个参数object指的是代理类,method指的是被代理的方法,后面的数组指的是method方法的参数数组。这个抽象方法在代理类中动态实现。

        Proxy:     该类为动态代理类,其功能类似于上面例子中的ProxySubject类

          protected Proxy (InvocationHandler h)构造函数,用于给内部的h赋值。

       staitic Object newProxyInstance(ClassLoader loader  Class[ ]interafce  InvocationHandler h )

       返回代理类的一个实例,返回的代理类可以当做被代理类使用(可使用被代理类在Subject中声明的方法)

       所谓的Dymic Proxy是这样的一种类,他是在运行时生成的类,在生成他时,你必须给他提供一组接口,然后他就会实现这组接口,你可以把这个类的实例当做成这些接口中的任何一个来用。这个Dymic Proxy实际就是一个Proxy,他并不会帮你做任何实际性的工作,而是在生成这个类的实例时,你必须提供一个handler,由他接管实际的工作。


     在使用动态代理类时我们必须实现InvocationHandler这个接口。



   创建接口:public interface Subject
{
public void request();
}

 

创建真实角色类:

我们在这创建两个真实角色类


public class RealSubject1  implements Subject
{
public void request()
{
System.out.println("我是真实角色1");
}


}

public class RealSubject2  implements Subject
{
public void request()
{
System.out.println("我是真实角色2");
}

}


下面以实现真实角色2为例


创建一个动态类去实现动态生成类:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


/**
 *该类实现了Java反射包中的InvocationHandler接口。代理实例调用方法时,将对方法调用指派到它的代理处理器程    * 序的invoke方法中。invoke方法内部实现预处理,对委托类方法调用,事后处理等逻辑。
 *我们可以在执行真实对象的方法的前后,额外加入自己的一些方法。
 *  
 * 
 *
 */
 

public class DynamicSubject implements InvocationHandler
{
private Object sub;

public DynamicSubject(Object obj)
{
this.sub = obj;
}

public Object invoke(Object proxy, Method method, Object[] args)//前面的proxy不用管。后面的method是使我们需要调用的request方法的方法对象,后面的数组是该方法的参数,如没有则为空
throws Throwable
{
System.out.println("before calling: " + method);//在进行真实角色操作之前进行的额外的操作

method.invoke(sub, args);//普通的Java反射代码,通过反射执行某个类的某方法 

System.out.println(args == null);

System.out.println("after calling: " + method);//在进行真实角色操作之后的额外的的操作

return null;
}


  创建测试类:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;


public class Client {
public static void main(String[] args) {
RealSubject2 realSubject2 = new RealSubject2();


InvocationHandler handler = new DynamicSubject(realSubject2);
//调用构造方法,传值,多态,传进去 的是真实角色2


Class<?> classType = handler.getClass();//获得DynamicSubject类的类对象

// 下面代码一次性生成代理


Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(),
realSubject2.getClass().getInterfaces(), handler);
// Proxy.newProxyInstance()方法会返回一个代理类的实例,括号内的class.getclassloader()是生成对应类对象的类装载器,
   // 后面的是动态生成类需要实现的接口,因为他要实现的接口是subject,所以前面可以强制类型转换成subject类型的



subject.request();// 当调用request方法时,他会跳到上面handler指向的invoker方法,invoker方法所在的DynamicSubject类是实现了InvocationHandler接口


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


}


}

}


运行结果:before calling : public abstract void dynamicproxy.Subject.request()

                   我是真实角色2

true

                  after calling : public abstract void dynamicproxy.Subject.request()

          class com.sun.proxy.$Proxy0  

通过这种方式,被代理的对象(RealSubject)也可以再运行时动态改变,需要控制的接口(Subject接口)可以再运行时进行改变,控制的方式(DynamicProxy类)也可以动态的改变,从而实现了非常灵活的动态代理关系。

至于另一种动态代理模式,我就不在描述。


      


  

原创粉丝点击