设计模式:动态代理

静态代理

//代理设计模型
interface Subject    //核心主题   宠幸
{
    public void make();
}
class RealSubject implements Subject  //真实主题  皇上正在宠幸
{
    public void make()
    {System.out.println("核心主题正在操作");}
}
//这是代理类  
class ProxySubject implements Subject  ///代理主题  内务人员帮忙
{
    private Subject subject;
    //要接收一个真实主题的操作对象
    public ProxySubject(Subject subject) //要操作也要有个对象啊
    {this.subject=subject;}

    public void prepare()   //内务人员做的
    {System.out.println("操作前的准备");}
    //具体事情
    public void make()          //内务人员什么都知道
    {
        this.prepare();
        this.subject.make();
        this.destory();
    }
    public void destory()     //内务人员做的
    {System.out.println("操作后的收尾");}
}

public class JieKouSj2
{
    public static void main(String args[])
    {
    Subject sub=new ProxySubject(new RealSubject()); //内务人员是爱妃和皇上的间接人
    sub.make();
    }
}

代理(给我的感觉就是):别人能看到只是一个大概,具体里面做了什么就不知道,在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
另一个例子:比如写一个接口,TakeOne和TakeTwo实现这个接口,这两个类都有一个构造方法,参数是接口类,这样TakeOne就可以对TakeTwo里面的方法进行包装,TakeTwo也可以对TakeOne里面的方法进行包装;

动态代理

AOP就是动态代理的一个应用

动态代理:感觉就像代理的总代理,一个类动态生成代理类
想在总代理前后加上自己写的代码,就又要写一个代理,把总代理当作构造参数带进去

动态代理类的创建

  • java.lang.reflect.InvocationHandler: 这是调用处理器接口,它自定义了一个 invoke()方法,我们就在这个方法里触发代理对象自己的方法,你可以在它的前后增加我们自己的增强方法

  • java.lang.reflect.Proxy: 这是 Java动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象,也就是动态生成代理对象的方法。这就是我上面说的代理的总代理

每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke()方法。在 invoke()方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke()方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。熟悉AOP的人对这种使用模式应该不陌生。但是这种方式不需要依赖AspectJ等AOP框架。

创建一个代理

我们可以通过Proxy.newProxyInstance()方法来动态的创建一个代理。这个方法有3个参数:

1. ClassLoader :负责加载动态代理类
2. 接口数组
3. InvocationHandler:把要增强的方法调用转到代理上

Proxy类动态创建代理类:

InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                        MyInterface.class.getClassLoader(),
                        new Class[] { MyInterface.class },
                        handler);

在执行完这段代码之后,变量proxy 包含一个 MyInterface 接口的的动态实现。所有对 proxy 的调用都被转向到实现了 InvocationHandler 接口的 handler 上。有关 InvocationHandler 的内容会在下一段介绍。

关于InvocationHandler接口

在前面提到了当你调用Proxy.newProxyInstance()方法时,你必须要传入一个InvocationHandler接口的实现。所有对动态代理对象的方法调用都会被转向到InvocationHandler接口的实现上,下面是 InvocationHandler 接口的定义:

public interface InvocationHandler{
  Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable;
}

传入invoke()方法中的proxy参数是实现要代理接口的动态代理对象。通常你是不需要他的。invoke()方法中的Method对象参数代表了被动态代理的接口中要调用的方法,从这个method对象中你可以获取到这个方法名字,方法的参数,参数类型等等信息。Object数组参数包含了被动态代理的方法需要的方法参数。注意:原生数据类型(如int,long等等)方法参数传入等价的包装对象(如Integer, Long等等)。

说再多不如举个实例

比如我们有两个业务,要为这两个业务添加日志打印功能。如果是静态代理,那么就需要分别为每个业务类写一个代理类,而如果用动态代理,只需要实现一个日志打印功能的handler即可,完全不需要自己再单独写代理类,下面我们具体看一下这个例子。

1 准备两个业务接口及其实现

接口A和接口B:

public interface SubjectA {
   public void setUser(String name,String password);
}

public interface SubjectB {
   public void sayHello(String name);
}

接口A和接口B的实现:

public class RealSubjectA implements SubjectA {
   public void setUser(String name,String password){
      System.out.println("-------------set user,name:"+name+" password:"+password+"-------------");
   }
}

public class RealSubjectB implements SubjectB{
   public void sayHello(String name) {
      System.out.println("--------------say hello:"+name+"-------------");
   }
}

2 写一个日志打印的handler

/**
 * 日志打印handler,打印调用代理对象的方法及其参数值
 * **/
public class LogHandler implements InvocationHandler{
   private Object proxied;
   LogHandler(Object proxied){
      this.proxied=proxied;
   }
   //实现接口的invoke方法
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("begin to invoke method:"+method.getName()+" params:"+ Arrays.toString(args));
      Object result=method.invoke(proxied,args);
      System.out.println("invoke "+method.getName()+" end");
      return result;
   }
}

3 最后用Proxy类生产动态代理对象

public class TestDynamicProxy {
   public static void main(String[] args) {
  RealSubjectA realA = new RealSubjectA();
  SubjectA proxySubjectA = (SubjectA) Proxy.newProxyInstance(SubjectA.class.getClassLoader(),
        new Class[]{SubjectA.class},
        new LogHandler(realA));//生成一个业务A的动态代理对象
  RealSubjectB realB = new RealSubjectB();
  SubjectB proxySubjectB = (SubjectB) Proxy.newProxyInstance(SubjectB.class.getClassLoader(),
        new Class[]{SubjectB.class},
        new LogHandler(realB));//生成一个业务B的动态代理对象
  proxySubjectA.setUser("heaven","123456");
  proxySubjectB.sayHello("heaven");
   }
}

4 运行结果

begin to invoke method:setUser params:[heaven, 123456]
-------------set user,name:heaven password:123456-------------
invoke setUser end
begin to invoke method:sayHello params:[heaven]
--------------say hello:heaven-------------
invoke sayHello end

5 结果说明

1. 通过动态代理,我们的业务逻辑没有做任何修改便实现了日志打印功能,实现了解耦(这其实就是一个aop编程的例子)
2. 我们没有为每个业务单独去写代理类,代理的代码量不会因为业务增加而庞大

数据库连接以及事物管理

Spring 框架中有一个事物代理可以让你提交/回滚一个事物,如果用动态代理的话,其方法调用序列如下:

web controller --> proxy.execute(...);
proxy --> connection.setAutoCommit(false);
proxy --> realAction.execute();
realAction does database work
proxy --> connection.commit();

转载:https://blog.csdn.net/suifeng3051/article/details/51507475
视频:http://study.163.com/course/introduction/1062009.htm

猜你喜欢

转载自blog.csdn.net/guo_binglo/article/details/80549429