java代理模式

引言

我们在写一个功能函数的时候,常常要写入一些与功能无关的代码,如日志记录、参数校验、安全和事务支持等。功能无关代码混在函数中,会带来一些麻烦

  • 对OO造成破坏
  • 加深类之间的耦合
  • 代码重用性降低

代理模式可以解决这个问题,关键功能代码放在函数中,枝节性代码放在代理类中

代理模式

1、作用

代理模式的作用是,为其他对象提供一种代理,在不改变接口的前提下,以控制对这个对象的访问

2、角色

代理模式一般涉及到3个角色:抽象角色(接口)、代理角色、真实(被代理)角色

3、类型

静态代理、动态代理(jdk代理、CGLIB代理)

静态代理

代理类、被代理类需要实现相同的接口,或者继承相同的父类

/**
 * Created by danny.yao on 2017/10/9.
 * 静态代理中,代理类和被代理类需要实现此接口
 */
public interface IUser {
    public void doSomething(String thingsToDo);
}
 
/**
 * Created by danny.yao on 2017/10/9.
 * 被代理类,真实业务逻辑
 */
public class Danny implements IUser {

    public void doSomething(String thingsToDo) {
        System.out.println(this.getClass().getName() + "一边唱歌一边" + thingsToDo);
    }
}
 
/**
 * Created by danny.yao on 2017/10/9.
 * 代理类,被代理类作为其属性,增强被代理类功能
 */
public class UserProxy implements IUser {

    private IUser user;

    public UserProxy(IUser user){
        this.user = user;
    }

    // 在不改变danny行为的基础上,增强功能
    public void doSomething(String thingsToDo) {
        doBefore();
        user.doSomething(thingsToDo);
        doAfter();
    }
    private void doBefore(){
        System.out.println("赖床。。。");
        System.out.println("起床洗漱。。。");
        System.out.println("吃早饭。。。");
        System.out.println("然后。。。");
    }
    private void doAfter(){
        System.out.println("那啥之后。。。");
        System.out.println("开始打王者荣耀!");
    }
}

测试类

public class UserProxyTest {
    @Test
    public void testDoSomething(){
        UserProxy dannyProxy = new UserProxy(new Danny());
        dannyProxy.doSomething("上班!!");
    }
}

java代理模式

缺点:

  • 被代理类须已经存在,而且作为代理类的内部属性
  • 每个业务类都要一个代理类,如果业务量庞大,会导致类急剧膨胀

动态代理

动态代理类的源码是在程序运行期间,由JVM根据反射等机制动态生成的

1、jdk代理

被代理类,须实现接口;代理类,不需要实现接口

// 接口和实现类,同静态代理
 
public class DynamicProxy implements InvocationHandler {
    // 把静态代理中的类引用,改为Object类型
    private Object object;

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

    // 把方法调用过程改为反射方式,不依赖接口的具体方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        method.invoke(object, args);
        doAfter();
        return null;
    }

    private void doBefore(){
        System.out.println("那啥之前。。。");
    }
    private void doAfter(){
        System.out.println("那啥之后。。。");
    }

    // 返回动态代理 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(
              object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                this
        );
    }
}

测试类

public class DynamicProxyTest {
    @Test
    public void testInvoke() throws Exception {
        DynamicProxy dynamicProxy = new DynamicProxy(new Danny());
        IUser dannyProxy = dynamicProxy.getProxy();
        dannyProxy.doSomething("上班!!");
    }
}

java代理模式

缺点:

  • 被代理类必须实现接口

2、CGLIB代理

也叫作子类代理,它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展。被代理类不需要实现接口

// 未实现接口的类
public class Pig {
    public void doSomething(String thingsToDo){
        System.out.println("懒洋洋地" + thingsToDo);
    }
}
 
public class CGLibProxy implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        doBefore();
        Object result = methodProxy.invokeSuper(o, args);
        doAfter();
        return result;
    }
    // cglib 中通过加强器来创建代理
    public <T> T getProxy(Class<T> tClass){
        return (T) Enhancer.create(tClass, this);
    }
    private void doBefore(){
        System.out.println("那啥之前。。。");
    }
    private void doAfter(){
        System.out.println("那啥之后。。。");
    }
}

测试类

public class CGLibProxyTest {
    @Test
    public void testIntercept() throws Exception {
        CGLibProxy cgLibProxy = new CGLibProxy();
        Pig pigProxy = cgLibProxy.getProxy(Pig.class);
        pigProxy.doSomething("思考人生。。。");
    }
}

java代理模式

缺点:

  • 会拦截并包装被代理类的所有方法,做不到方法级别的个性化拦截

参考:

https://www.zhihu.com/question/20794107

http://www.cnblogs.com/machine/archive/2013/02/21/2921345.html

http://www.cnblogs.com/cenyu/p/6289209.html