头图

目录

静态代理

被代理类和代理类都实现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

eacape
205 声望8 粉丝

JAVA 攻城狮


« 上一篇
原型模式
下一篇 »
适配器模式