23中设计模式总结八追加:代理模式之动态代理

静态代理与动态代理的区别

静态代理与动态代理的主要区别在于:静态代理通常只负责代理一个指定的类,而动态代理可以用于代理一系列类,这一系列类通常都实现了同一个接口
可以将静态代理看成是java中定义的常量,动态代理可以看成是java中定义的变量,在编译期可以确定常量的值,而变量的值需要在运行期才可以确定。对应的代理也是一样,静态代理事先知道要代理的是什么,而动态代理不知道要代理的是什么东西,只有在运行时才知道


动态代理的两种实现

java中的动态代理主要可以通过两种方式来实现:JDK代理和CGLIB代理,下面将分别通过一个例子来详细介绍这两个代理


JDK代理的实现

JDK代理是jdk提供的一种动态代理实现,下面将通过一个实例来简单介绍一下
一个Hello实体
package org.smart4j.chapter2.acproxy;

/**
 * 静态代理类
 * @author chengxi
 */
public interface Hello {

    /**
     * 简单的问候
     * @param str
     */
    public void say(String str);
}
Hello接口的一个实现类
package org.smart4j.chapter2.acproxy;

/**
 * 被代理类
 * @author chengxi
 */
public class HelloImpl implements Hello {

    @Override
    public void say(String str){
        System.out.println("hello  " + str);
    }
}
JDK代理方式实现的一个代理类
package org.smart4j.chapter2.ajproxy;

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

/**
 * JDK动态代理类
 * 使用JDK代理有一个条件:被代理的类必须有父接口,否则无法Proxy.newProxyInstance()动态创建代理
 * @author chengxi
 */
public class DynamicProxy implements InvocationHandler{

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }

    public void before(){
        System.out.println("before say hello");
    }

    public void after(){
        System.out.println("after say hello");
    }
}
JDK代理测试类
package org.smart4j.chapter2.ajproxy;

import java.lang.reflect.Proxy;

/**
 * 静态代理测试main类
 * @author chengxi
 */
public class Main {

    public static void main(String[] args){

        Hello hello = (Hello) new HelloImpl();

        DynamicProxy dynamicProxy = new DynamicProxy(hello);

        /**
         * 调用jdk提供的该方法动态创建一个Hello接口代理类
         */
        Hello helloProxy = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),
                dynamicProxy
        );

        helloProxy.say("chengxi");
    }
}
从上面的代码中可以看到,想要动态创建一个对应的代理类需要使用JDK提供的Proxy类的newProxyInstance方法,该方法需要提供三个参数:ClassLoader、Interfaces和Proxy
使用JDK代理的一个缺点在于:它代理的对象是接口,所以需要代理的类必须有实现父接口,否则将无法动态创建对应的代理类。那么有没有一种代理方式不需要我们来为每一个代理类提供一个父接口呢?有的别急,接下来就要介绍这种方式了


CGLIB代理

同样使用上面的Hello和HelloImpl,接下来创建CGLIB实现的代理类
package org.smart4j.chapter2.acproxy;

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

import java.lang.reflect.Method;

/**
 * CGLIB动态代理类
 * 使用该代理方式可以无需被代理类有父接口,面向的是方法代理
 * @author chengxi
 */
public class DynamicProxy implements MethodInterceptor {

    /**
     * 实现单例模式
     */
    private volatile static DynamicProxy dynamicProxy;

    private DynamicProxy(){
    }

    public static DynamicProxy getInstance(){
        if(dynamicProxy == null){
            synchronized (DynamicProxy.class){
                if(dynamicProxy == null){
                    dynamicProxy = new DynamicProxy();
                }
            }
        }
        return dynamicProxy;
    }

    /**
     * 动态获取对应的代理类
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getProxy(Class<T> cls){

        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                          MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(obj, args);
        after();
        return result;
    }

    public void before(){
        System.out.println("before say hello");
    }

    public void after(){
        System.out.println("after say hello");
    }

}
CGLIB代理测试类
package org.smart4j.chapter2.acproxy;

/**
 * CGLIB动态代理测试类
 * @author chengxi
 */
public class Main {

    public static void main(String[] args){

        Hello helloProxy = DynamicProxy.getInstance().getProxy(HelloImpl.class);
        helloProxy.say("chengxi");
    }
}
CGLIB代理相对于JDK代理的好处在于:它的代理对象是面向方法的,所以无需代理类实现任何的接口
这里需要注意的是:CGLIB代理和JDK代理不一样,在java中没有提供它的jar包,所以需要我们自己导入,而这里我使用的是maven,所以我直接使用pom.xml来导入它的依赖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章