设计模式-代理模式

定义

  • 为其他对象提供一种代理,以控制对这个对象的访问。
  • 代理对象在客户端和目标对象之间起到中介的作用。

优点

  • 将代理对象与真实被调用的目标对象分离。
  • 一定程度上降低了系统的耦合度,扩展性好。
  • 保护了目标对象。
  • 增强目标对象。

缺点

  • 增加系统类的数目。
  • 增加代理对象,会一定程度上影响处理速度。
  • 增加系统复杂度。

主要实现

  • 动态代理
    • JDK代理(接口): JAVA提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例。
      • 主要涉及到java.lang.reflect中的两个类:ProxyInvocationHandler 其中InvocationHandler是一个接口,可以通过实现该接口定义逻辑,并通过反射机制调用该目标类的代码,动态的将逻辑和业务编制在一起。
    • CGLIB代理(继承,ASM字节码):使用底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。(注意 final关键字的继承关系)
  • 静态代理

JDK代理比CGLIB代理大约快20%左右。

代码

我们就简单的用两种代理实现一个方法前后调用的拦截。

JDK代理

在使用JDK动态代理的时候,最主要的就是这个方法

java.lang.reflect.Proxy#newProxyInstance:

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler invocationHandler:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

目标接口类(HelloWorld

我们都知道JDK代理核心是创建接口的代理实例,因此必须有一个代理目标的接口。

/**
 * 目标接口类
 */
public interface HelloWorld {

    void sayHello();
}

简单的sayHello方法。

目标类实现HelloWorldImpl

public class HelloWorldImpl implements HelloWorld {
    @Override
    public void sayHello() {
        System.out.println("Hello World~");
    }
}

这里就没什么可说的 ,实现接口并实现方法。

创建代理类

public class HelloWorldProxy implements InvocationHandler {
    //代理的目标类
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeMethod(proxy.getClass().getName(),method.getName());
        method.invoke(target,args);
        afterMethod(proxy.getClass().getName(),method.getName());
        return null;
    }

    /**
     * 调用前执行的方法
     * @param clazz
     * @param methodName
     */
    private void beforeMethod(String clazz,String methodName) {
        System.out.println("在 类: "+ clazz +"的方法:"+methodName+"前执行");
    }

    /**
     * 调用后执行的方法
     * @param clazz
     * @param methodName
     */
    private void afterMethod(String clazz,String methodName) {
        System.out.println("在 类: "+ clazz +"的方法:"+methodName+"后执行");
    }
}

JDK代理需要实现InvocationHandler接口,同时重写invoke方法,我们在该代理类中,增加了 方法调用前以及调用后的逻辑实现,简单的打印输出。

测试类JdkProxyTest

public class JdkProxyTest {
    public static void main(String[] args) {
        //目标类声明
        HelloWorld helloWorld = new HelloWorldImpl();

        //代理类声明
        HelloWorldProxy helloWorldProxy = new HelloWorldProxy(helloWorld);
        //通过接口实现代理目标类的实例
        HelloWorld proxyHelloWorld = (HelloWorld) Proxy.newProxyInstance(
                helloWorld.getClass().getClassLoader(),
                helloWorld.getClass().getInterfaces(),
                helloWorldProxy
        );
        //调用方法
        proxyHelloWorld.sayHello();
    }
}

UML

jdkproxyuml.jpg

我们可以看到使用动态代理,代理类与目标类对象并没有直接的关联,这就体现了动态代理低耦合灵活的特征。

测试结果

jdkproxyresult.jpg

CGLIB代理

首先使用CGLIB需要引入相关的包,这里我使用的MAVEN工程,直接引入依赖即可。


    cglib
    cglib
    2.2.2

目标类对象(CGlibHelloWorld

public class CGlibHelloWorld {

    public void sayHello() {
        System.out.println("Hello World CGLIB ");
    }
}

简单的sayHello方法。并且没有实现任何接口。

代理对象(CGlibHelloWorldPoxy)

首先我们需要先了解一个基础的类:Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。

具体细节可以参考一下 Cglib之Enhancer创建动态代理

public class CGlibHelloWorldPoxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeMethod(o.getClass().getSuperclass().getName(),method.getName());
        methodProxy.invokeSuper(o,objects);
        afterMethod(o.getClass().getSuperclass().getName(),method.getName());
        return null;
    }

    /**
     * 调用前执行的方法
     * @param clazz
     * @param methodName
     */
    private void beforeMethod(String clazz,String methodName) {
        System.out.println("在 类: "+ clazz +"的方法:"+methodName+"前执行");
    }

    /**
     * 调用后执行的方法
     * @param clazz
     * @param methodName
     */
    private void afterMethod(String clazz,String methodName) {
        System.out.println("在 类: "+ clazz +"的方法:"+methodName+"后执行");
    }

}

这里调用方法(intercept)与JDK代理有明显的不同。我们使用的是父类的调用。

UML

这里由于就简单的类,并且没有任何关联,因此就不上图了。

测试类

public class CGlibTest {

    public static void main(String[] args) {
        CGlibHelloWorldPoxy poxy = new CGlibHelloWorldPoxy();
        CGlibHelloWorld helloWorld = (CGlibHelloWorld) poxy.getProxy(CGlibHelloWorld.class);
        helloWorld.sayHello();
    }
}

测试结果

cglibresult.jpg

其他开源

Spring

org.springframework.aop.framework.ProxyFactory

/**
 * Factory for AOP proxies for programmatic use, rather than via declarative
 * setup in a bean factory. This class provides a simple way of obtaining
 * and configuring AOP proxy instances in custom user code.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 14.03.2003
 */
@SuppressWarnings("serial")
public class ProxyFactory extends ProxyCreatorSupport {

   /**
    * Create a new ProxyFactory.
    */
   public ProxyFactory() {
   }

   /**
    * Create a new ProxyFactory.
    * 

Will proxy all interfaces that the given target implements. * @param target the target object to be proxied */ public ProxyFactory(Object target) { setTarget(target); setInterfaces(ClassUtils.getAllInterfaces(target)); } /** * Create a new ProxyFactory. *

No target, only interfaces. Must add interceptors. * @param proxyInterfaces the interfaces that the proxy should implement */ public ProxyFactory(Class... proxyInterfaces) { setInterfaces(proxyInterfaces); } /** * Create a new ProxyFactory for the given interface and interceptor. *

Convenience method for creating a proxy for a single interceptor, * assuming that the interceptor handles all calls itself rather than * delegating to a target, like in the case of remoting proxies. * @param proxyInterface the interface that the proxy should implement * @param interceptor the interceptor that the proxy should invoke */ public ProxyFactory(Class proxyInterface, Interceptor interceptor) { addInterface(proxyInterface); addAdvice(interceptor); } /** * Create a ProxyFactory for the specified {@code TargetSource}, * making the proxy implement the specified interface. * @param proxyInterface the interface that the proxy should implement * @param targetSource the TargetSource that the proxy should invoke */ public ProxyFactory(Class proxyInterface, TargetSource targetSource) { addInterface(proxyInterface); setTargetSource(targetSource); } /** * Create a new proxy according to the settings in this factory. *

Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. *

Uses a default class loader: Usually, the thread context class loader * (if necessary for proxy creation). * @return the proxy object */ public Object getProxy() { return createAopProxy().getProxy(); } /** * Create a new proxy according to the settings in this factory. *

Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. *

Uses the given class loader (if necessary for proxy creation). * @param classLoader the class loader to create the proxy with * (or {@code null} for the low-level proxy facility's default) * @return the proxy object */ public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } /** * Create a new proxy for the given interface and interceptor. *

Convenience method for creating a proxy for a single interceptor, * assuming that the interceptor handles all calls itself rather than * delegating to a target, like in the case of remoting proxies. * @param proxyInterface the interface that the proxy should implement * @param interceptor the interceptor that the proxy should invoke * @return the proxy object * @see #ProxyFactory(Class, org.aopalliance.intercept.Interceptor) */ @SuppressWarnings("unchecked") public static T getProxy(Class proxyInterface, Interceptor interceptor) { return (T) new ProxyFactory(proxyInterface, interceptor).getProxy(); } /** * Create a proxy for the specified {@code TargetSource}, * implementing the specified interface. * @param proxyInterface the interface that the proxy should implement * @param targetSource the TargetSource that the proxy should invoke * @return the proxy object * @see #ProxyFactory(Class, org.springframework.aop.TargetSource) */ @SuppressWarnings("unchecked") public static T getProxy(Class proxyInterface, TargetSource targetSource) { return (T) new ProxyFactory(proxyInterface, targetSource).getProxy(); } /** * Create a proxy for the specified {@code TargetSource} that extends * the target class of the {@code TargetSource}. * @param targetSource the TargetSource that the proxy should invoke * @return the proxy object */ public static Object getProxy(TargetSource targetSource) { if (targetSource.getTargetClass() == null) { throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class"); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTargetSource(targetSource); proxyFactory.setProxyTargetClass(true); return proxyFactory.getProxy(); } }

这个代理工厂,实现了 JDK代理以及CGLIB的代理实现的工厂。

通过这个工厂类,来选择使用某个代理技术来创建代理对象。

小结

代理模式最突出的就是AOP的应用,如果感兴趣,可以深入的阅读以下spring的core包下的源码。

你可能感兴趣的:(设计模式-代理模式)