Java中的代理模式

2019/05/26 20:59 下午 posted in  Java

1. 静态代理和动态代理

    本章节参考了https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/

1.1. 定义

    静态代理和动态代理指的是实现代理模式的方式。静态模式意思是所有的代码是静态写好的。而动态代理则相对,部分代码是动态生成的。在动态代理中还分为JDK动态代理和CGLib动态代理。

1.2. 关键实现

1.2.1. 静态代理

静态代理是基于接口实现的,他要求真实类和代理类实现同样的接口。

public interface IDBQuery {
    String request();
}
public class DBQuery implements IDBQuery{
    public DBQuery(){
        try{
            Thread.sleep(1000);//假设数据库连接等耗时操作
        }catch(InterruptedException ex){
            ex.printStackTrace();
        }
    }

    @Override
    public String request() {
// TODO Auto-generated method stub
        return "request string";
    }


}
public class DBQueryProxy implements IDBQuery{
    private DBQuery real = null;

    @Override
    public String request() {
// TODO Auto-generated method stub
//在真正需要的时候才能创建真实对象,创建过程可能很慢
        if(real==null){
            real = new DBQuery();
        }//在多线程环境下,这里返回一个虚假类,类似于 Future 模式
        return real.request();
    }

}
public class Main {
    public static void main(String[] args){
        IDBQuery q = new DBQueryProxy(); //使用代里
        q.request(); //在真正使用时才创建真实对象
    }
}

1.2.2. JDK代理

当使用JDK代理时,一个最直观的变化就是代理类不需要和真实类实现同一个接口了。取而代之的是代理类实现了InvocationHandler,并Override了invoke方法。在方法里可以统一对实现方法做处理(方法调用前,方法调用后)。

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


public class DBQueryHandler implements InvocationHandler{
    IDBQuery realQuery = null;//定义主题接口

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
// TODO Auto-generated method stub
        //如果第一次调用,生成真实主题
        if(realQuery == null){
            realQuery = new DBQuery();
        }
        //method.invoke(target, args); 执行调用的方法。
        //返回真实主题完成实际的操作
        return realQuery.request();
    }
    public static IDBQuery createProxy(){
        IDBQuery proxy = (IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new DBQueryHandler()); // 注意,生成的代理类实例被强转为IDBQuery
        return proxy;
    }
}

1.2.3. CGLib代理

CGLib一个直观的最大的特点就是真实类无需实现接口(当然实现了也没关系)。

接口类

public interface BookProxy {
    public void addBook();
}

实现类

//该类并没有申明 BookProxy 接口
public class BookProxyImpl {
    public void addBook() { 
        System.out.println("增加图书的普通方法..."); 
    } 
}

代理类

import java.lang.reflect.Method; 
import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy;

public class BookProxyLib 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;
    }
}

调用方法

public class TestCglib { 
    public static void main(String[] args) { 
        BookProxyLib cglib=new BookProxyLib(); 
        BookProxyImpl bookCglib=(BookProxyImpl)cglib.getInstance(new BookProxyImpl()); 
        bookCglib.addBook();  //可以看到BookProxyLib并没有声明BookProxy接口,但是仍然可以调用addBook方法
    } 
}

1.3. 区别和共同点

静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

https://blog.csdn.net/neosmith/article/details/51072840

2. 实际应用场景举例

todo:没有理解cglib不用真实类实现接口的意义。因为真实类没有实现接口,但是暴露了public的方法。这和直接调用有啥区别?

另外cglib的试用场景,真实类没有实现的接口意义何在?如果没有接口来规范统一的调用逻辑,例如一堆的实现类实现了A接口,因此必须实现A接口中定义的B方法。这样才有意义吧?

查看spring源码,了解spring中,cglib的使用方法,来解答上述疑问。

代理模式的意义

  1. 有代理,便于解耦
  2. 静态代理太麻烦,每个都要
  3. JDK代理受限于要实现接口
  4. CGLib不需要实现接口,看上去无法统一接口的方法,但是可能是用在一些common的方法,例如Object的方法。用在类创建时刻。
  5. 另外让方法运行只是最基本的,代理模式的最大用途是管理原方法的运行前,后,时(切面,AOP)。

3. Spring AOP和动态代理

4. OC中的动态代理模式浅谈

5. CGLib和JDK代理的性能对比