设计模式-代理模式

2019-08-20  本文已影响0人  NealLemon

定义

优点

缺点

主要实现

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

代码

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

JDK代理

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

java.lang.reflect.Proxy#newProxyInstance:

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

目标接口类(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工程,直接引入依赖即可。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

目标类对象(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.
    * <p>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.
    * <p>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.
    * <p>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.
    * <p>Can be called repeatedly. Effect will vary if we've added
    * or removed interfaces. Can add and remove interceptors.
    * <p>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.
    * <p>Can be called repeatedly. Effect will vary if we've added
    * or removed interfaces. Can add and remove interceptors.
    * <p>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.
    * <p>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> T getProxy(Class<T> 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> T getProxy(Class<T> 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包下的源码。

上一篇 下一篇

猜你喜欢

热点阅读