代理模式详解

       
        代理模式为对象提供一个代理以控制对这个对象的访问。所谓代理,是指与代理元(即:被代理的对象)具有相同接口的类,客户端必须通过代理与代理元进行交互。我们可以将代理理解成另一个对象的代表
        代理(proxy)模式本质上就是通过一个代理对象访问目标对象,而不直接访问目标对象;代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。这么做的好处是,可以让目标对象只关注业务逻辑,而非业务逻辑(比如日志输出等)可以在代理对象中实现,实现业务分离。

 

代理模式要点:
  • 代理模式包含三个要素:1)接口/Interface; 2)代理元/Target:即被代理的对象; 3)代理类/Proxy:代理元的代表,对外提供访问;
  • 代理元与代理类都是接口的实现类;
  • 客户端只能访问代理类,无法直接访问代理元;

 

代理分为静态代理和动态代理。

 


静态代理


 
        静态代理就是在编译时生成代理类。即:通过编码手动创建代理类,并在代理类中调用代理元。
 
编码实现
 
1.接口

package effectiveJava.proxy;

public interface HelloService {
    void sayHello();
}

2,代理元

package effectiveJava.proxy;

public class HelloServiceImpl implements HelloService{
    @Override
    public void sayHello() {
        System.out.println("Hello Proxy.");
    }
}

3,代理类

package effectiveJava.proxy.staticProxy;

import effectiveJava.proxy.HelloService;

public class LogProxy implements HelloService {

    private HelloService service;

    public LogProxy(HelloService service) {
        this.service = service;
    }

    @Override
    public void sayHello() {
        System.out.println("Static Proxy : Before Hello....");
        service.sayHello();
        System.out.println("Static Proxy : After Hello....");
    }
}

4,测试类

package effectiveJava.proxy.staticProxy;

import effectiveJava.proxy.HelloServiceImpl;

public class LogProxyDemo {
    public static void main(String[] args) {
        LogProxy logProxy = new LogProxy(new HelloServiceImpl());
        logProxy.sayHello();
    }
}

5,测试结果

Static Proxy : Before Hello....
Hello Proxy.
Static Proxy : After Hello....

 


动态代理


 

        动态代理地实现有两种方式:1)基于JDK的动态代理;2)基于CGLIB的动态代理;

 

1)基于JDK的动态代理

 

API

public class Proxy implements java.io.Serializable {
     /**
   * 创建代理实例
      *  @param loader 代理元的类加载器
      *  @param interfaces 代理元的接口
      *  h 一个 InvocationHandler 对象
      */
     
   public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

}
/**
 *
 * 每个代理的实例都有一个与之关联的 InvocationHandler 实现类,
 * 如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
 */
public interface InvocationHandler {

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

 

编码实现

1,接口、代理元(编码与静态代理一致,不再赘述)
2,代理类(必须实现InvocationHandler接口)

package effectiveJava.proxy.v0;

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

public class HelloInvocation implements InvocationHandler {
    /**
     * 代理元
     */
    private Object target;

    public HelloInvocation(Object target) {
        this.target = target;
    }

    /**
     *
     * @param proxy 代理类实例
     * @param method 实际要调用的方法
     * @param args  实际要调用方法的参数类型
     * @return 结果值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("HelloInvocation : Before Hello....");
        Object reslut = method.invoke(target, args);
        System.out.println("HelloInvocation : After Hello....");
        return reslut;
    }

}

3,测试类

package effectiveJava.proxy.v0;

import effectiveJava.proxy.HelloService;
import effectiveJava.proxy.HelloServiceImpl;

import java.lang.reflect.Proxy;

/**
* 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建代理对象的实例
*/
public class HelloInvocationDemo {
    public static void main(String[] args) {
        HelloServiceImpl helloService = new HelloServiceImpl();
        HelloInvocation helloInvocation = new HelloInvocation(helloService);
        HelloService impl = (HelloService)Proxy.newProxyInstance(
                helloService.getClass().getClassLoader(),
                helloService.getClass().getInterfaces(),
                helloInvocation);
        impl.sayHello();
    }
}

4,测试结果

HelloInvocation : Before Hello....
Hello Proxy.
HelloInvocation : After Hello....

 

2)基于CGLIB的动态代理

   Cglib是基于继承的方式进行代理,代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑。因此,基于CGLIB的动态代理不需要接口。

 

 

编码实现

1,引入架包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
2,代理类(HelloServiceImpl.java,编码与静态代理一致)
 3, 方法拦截器

package effectiveJava.proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib : Before hello..."); 
        //调用父委方法
        Object result = methodProxy.invokeSuper(object, args);
        System.out.println("Cglib : After hello...");
        return result;
    }
}

4,测试类

package effectiveJava.proxy.cglib;

import effectiveJava.proxy.HelloService;
import effectiveJava.proxy.HelloServiceImpl;
import net.sf.cglib.proxy.Enhancer;

public class CglibDemo {
    public static void main(String[] args) {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(HelloServiceImpl.class);
        //设置回调函数(拦截器)
        enhancer.setCallback(new LogInterceptor());
        //创建代理类实例
        HelloService service = (HelloService)enhancer.create();
        service.sayHello();
    }
}

5,测试结果

Cglib : Before hello...
Hello Proxy.
Cglib : After hello...
总结:
  • 静态代理是在编译时创建代理,动态代理是在运行时创建代理;
  • JDK动态代理是基于接口的方式,CGLib动态代理是基于继承;