关于java:代理模式

6次阅读

共计 3670 个字符,预计需要花费 10 分钟才能阅读完成。

目录

  • 动态代理
  • 动静代理

    • JDK 动静代理
    • cglib 动静代理

动态代理

被代理类和代理类都实现 Apple 接口

public interface Apple {Integer buyApple();
}

创立一个“烟台”苹果的实例作为被代理类,外面次要实现一个购买苹果的办法,也是前面次要用来被代理加强的办法。

public class YanTaiApple implements Apple{
    private Integer price;

    public YanTaiApple(Integer price) {this.price = price;}

    @Override
    public Integer buyApple(){return this.price;}
}

创立一个“烟台”苹果的代理类,其次要充当中间商的角色,次要的加强就是价格更高。

public class YanTaiAppleProxy implements Apple{
    private Apple apple;

    public YanTaiAppleProxy(Apple apple) {this.apple = apple;}

    @Override
    public Integer buyApple() {Integer price = apple.buyApple()+2;
        System.out.println("苹果收购价格"+apple.buyApple()+"元, 出售价"+price+"元");
        return price;
    }
}

上面再办法中,咱们通过代理商去买苹果 ……


public class StaticProxyTest {
    @Test
    public void test(){Apple apple = new YanTaiAppleProxy(new YanTaiApple(3));
        apple.buyApple();}
}
====== 后果 ======
苹果收购价格 3 元, 出售价 5 元

动静代理

仔细分析下面的动态代理,咱们会发现,对每个类进行代理,都要创立不同的代理类,如果像当初有河南苹果、新疆苹果、昭通苹果 …. 那么咱们须要为每种苹果都创立一个代理类,难道咱们不能够应用一个代理商代理多种苹果吗?显然动态代理是有很大局限性的。

动静代理是通过反射的形式能够动态创建原始类的代理类,而后在零碎种用代理类替换掉原始类,在 java 语言种动静代理次要是通过反射机制来实现的。

JDK 动静代理

jdk 动静代理次要是基于接口和 java 反射包中的 Proxy 类实现的,也就是被代理类和代理类实现雷同的接口,而后通过 Proxy 代理接口进行动静扩大(emm…. 次要看上面的实现)。

接口和原始类咱们都持续应用下面的 Apple 和 YanTaiApple,而后代理类实现如下,咱们须要实现接口 InvocationHandler 而后在 invoke 办法中对办法调用进行加强(代理商的价格减少两元),当原始类关联这个代理类后,执行接口的办法会主动进入 invoke 这个办法进行加强。

public class AppleProxyHandler implements InvocationHandler {
    private Apple apple;

    public AppleProxyHandler(Apple apple) {this.apple = apple;}

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取 price 字段
        Field field = apple.getClass().getDeclaredField("price");
        // 放开权限 - 针对 private
        field.setAccessible(true);
        Integer price = (Integer) field.get(apple);
        // 从新设置值
        field.set(apple,price+2);
        return method.invoke(apple,args);
    }
}

通过 Proxy 关联 YanTaiApple 和 AppleProxyHandler 实例, 当然 AppleProxyHandler 的结构参数中不仅能够传入 YanTaiApple 实例,也可传入其余实现 Apple 接口的类的实例, 也就是说 AppleProxyHandler 能够代理所有 Apple 接口的实现类。

public class JdkProxyTest {
    @Test
    public void test(){
        // 关联
        Apple apple = (Apple) Proxy.newProxyInstance(this.getClass().getClassLoader(), 
                new Class[]{Apple.class}, 
                new AppleProxyHandler(new YanTaiApple(2)));
        System.out.println(apple.buyApple());
    }
}
===== 后果 =====
4

认真思考下面的设计是略微有些不合理的,因为 Apple 接口中可能不只是有 buyApple 办法也可能有其它办法,那么咱们代理类明想代理的是由 buyApple 办法,那么能够做上面优化。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("buyApple".equals(method.getName())) {
        // 获取 price 字段
        Field field = apple.getClass().getDeclaredField("price");
        // 放开权限 - 针对 private
        field.setAccessible(true);
        Integer price = (Integer) field.get(apple);
        // 从新设置值
        field.set(apple, price + 2);
    }
    return method.invoke(apple,args);
}

cglib 动静代理

cglib 代理和 jdk 代理的次要区别是,jdk 动静代理只反对接口代理不反对类代理,Proxy.newProxyInstance的第二参数只能传入接口的类对象汇合,如果传入类的话会报 java.lang.IllegalArgumentException: java.lang.Object is not an interface 异样。而 cglib 是通过继承的形式来实现动静代理,会在运行的时候动静生成被代理类的一个子类对象,因而如果这个类被 final 润饰的话是无奈被 cglib 代理的。

上面持续应用苹果的实例,然而应用 cglib 动静代理,咱们就不须要对立的接口类了。

public class YanTaiApple {
    public Integer price;

    public YanTaiApple(Integer price) {this.price = price;}

    public Integer buyApple(){return this.price;}
}

代理类是通过实现 MethodInterceptor 接口,而后在 intercept 办法实现代理逻辑的

public class AppleProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object apple, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //apple 是动静生成 YanTaiApple 的一个子类
        Field field = apple.getClass().getSuperclass().getDeclaredField("price");
        field.setAccessible(true);
        Integer price = (Integer) field.get(apple);
        field.set(apple,price+2);
        return methodProxy.invokeSuper(apple,objects);
    }
}

测试类须要应用 Enhancer 类将被代理类设置为 SuperClass, 代理类设置为 Callback

public class CglibProxyTest {
    @Test
    public void test(){Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(YanTaiApple.class);
        enhancer.setCallback(new AppleProxy());
        // 参数类型、参数值 - create 相当于构造函数
        YanTaiApple yanTaiApple = (YanTaiApple) enhancer.create(new Class[]{Integer.class}, new Integer[]{3});
        System.out.println(yanTaiApple.buyApple());
    }
}
===== 后果 =====
5
正文完
 0