动态代理

来源:互联网 发布:c语言长整型定义 编辑:程序博客网 时间:2024/05/29 16:29

动态代理

代理Proxy

Proxy代理是一种常见的设计模式,主要解决的问题:直接访问对象时的困难。

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

更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。

按照代理的创建时期,代理类可以分为两种

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理:在程序运行时,运用反射机制动态创建而成。

第一种情况:假如我去租房,没有经过中介,直接找到了房屋的主人。这里抽象一下就是我直接访问了真实对象(房屋的主人),然后我把房屋给租了。

第二种情况:假如我去租房,需要经过中介,找到一家租房的中介公司,然后把房屋租了。这里抽象一下就是我访问了真实对象的代理对象(中介),这里的代理对象(中介)需要持有真实对象(房屋主人)才能把房屋租给我(因为中介手里没有房子,他肯定要有房屋真实主人的房子才行)

静态代理

1.定义一个账户接口
/**   *接口   */public interface Count {      // 查看账户方法      public void queryCount();      //修改账户方法      public void updateCount();}
1.2 创建委托对象
/**   * 委托类(包含业务逻辑) */    public class CountImpl implements Count {        @Override        public void queryCount() {            System.out.println("查看账户方法...");        }    @Override        public void updateCount() {            System.out.println("修改账户方法...");            }} 
1.3 创建代理类
public class CountProxy implements Count {      private CountImpl countImpl;      /**      * 覆盖默认构造器      *       * @param countImpl      */      public CountProxy(CountImpl countImpl) {          this.countImpl = countImpl;      }      @Override      public void queryCount() {          System.out.println("事务处理之前");          // 调用委托类的方法;          countImpl.queryCount();          System.out.println("事务处理之后");      }      @Override      public void updateCount() {          System.out.println("事务处理之前");          // 调用委托类的方法;          countImpl.updateCount();          System.out.println("事务处理之后");      }  }
1.4 测试并运行
public class TestCount {      public static void main(String[] args) {          CountImpl countImpl = new CountImpl();          CountProxy countProxy = new CountProxy(countImpl);          countProxy.updateCount();          countProxy.queryCount();     }  }  

观察代码发现我们的代理类只能为一个接口提供服务,这样在程序开发过程中就会产生很对的代理类,这些代理类处理调用发法不一样,其他操作都是一样的。所以我们想通过一个代理类来完成所有的代理功能,那么就必须使用动态代理。

JDK动态代理

1.1 动态代理使用的类

在java的动态代理中有2个非常重要的类或接口,一个是 InvocationHandler(Interface)、另一个是Proxy(Class),这个类或接口是实现动态代理必须用到的。

每一个动态代理类都必须实现InvocationHandler这个接口,并且每一个代理类的实例都关联到一个handler,当我们通过代理对象来调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwableproxy:  指代我们所代理的那个真实对象method:  指代的是我们所要调用真实对象的某个方法的Method对象args:  指代的是调用真实对象某个方法时接受的参数

Proxy类:

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是`newProxyInstance` 这个方法public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,     InvocationHandler h) throws IllegalArgumentExceptionloader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,    那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
1.2 定义接口
public interface Subject{    public void rent();    public void hello(String str);}
1.3 定义目标类
public class RealSubject implements Subject{    @Override    public void rent()    {        System.out.println("I want to rent my house");    }    @Override    public void hello(String str)    {        System.out.println("hello: " + str);    }}
1.4 定义代理类
public class DynamicProxy implements InvocationHandler{    //这个就是我们要代理的真实对象    private Object targetObject;    public Object newProxyInstance(Object targetObject) {          this.targetObject = targetObject;          return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),                                 targetObject.getClass().getInterfaces(), this);      }      public Object invoke(Object proxy, Method method, Object[] args)              throws Throwable {          Object ret = null;               try {              System.out.println("正在进行操作前的准备工作……");              //调用目标方法              ret = method.invoke(targetObject, args);              System.out.println("操作成功,正在进行确认处理……");          } catch (Exception e) {              e.printStackTrace();              System.out.println("error-->>" + method.getName());              throw e;          }        return ret;      }  }
1.5 测试结果
public static void main(String[] args) {      DynamicProxy dynamicProxy = new DynamicProxy();      Subject subject = (UserManager)dynamicProxy.newProxyInstance(new RealSubject());      subject.rent();      subject.hello(); } 

但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。

2.Cglib动态代理

JDK的动态代理机制只能代理实现了接口的类。cglib是针对类来实现代理的,原理是指定的目标类生成一个子类,并覆盖其中的方法实现增强,因为采用的是继承,所以不能对final类代理。

2.1 业务类
**  * 这个是没有实现接口的实现类   */  public class BookFacadeImpl1 {      public void addBook() {          System.out.println("增加图书的普通方法...");      }  }  
2.2代理类
/**  * 使用cglib动态代理  */  public class BookFacadeCglib implements MethodInterceptor {      private Object target;      /**      * 创建代理对象      *       * @param target      * @return      */      public Object getInstance(Object target) {          this.target = target;          Enhancer enhancer = new Enhancer();          enhancer.setSuperclass(this.target.getClass());          // 回调方法          enhancer.setCallback(this);          // 创建代理对象          return enhancer.create();      }      @Override      // 回调方法      public Object intercept(Object obj, Method method, Object[] args,              MethodProxy proxy) throws Throwable {          System.out.println("事物开始");          proxy.invokeSuper(obj, args);          System.out.println("事物结束");          return null;      }  }  
2.3测试
public class TestCglib {      public static void main(String[] args) {          BookFacadeCglib cglib=new BookFacadeCglib();          BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());          bookCglib.addBook();      }  }

CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

0 0
原创粉丝点击