三种代理模式:静态代理, JDK动态代理,CGLIB动态代理

        代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。  代理的作用,消除硬编码。

 静态代理:将代理类的对象引用设为自己的成员变量。然后调用增强实现的委托方法。

public interface BuyHouse {
    void buyHosue();
}
public class BuyHouseImpl implements BuyHouse {

    @Override
    public void buyHosue() {
        System.out.println("我要买房");
    }
}
public class BuyHouseProxy implements BuyHouse {

    private BuyHouse buyHouse;

    public BuyHouseProxy(final BuyHouse buyHouse) {
        this.buyHouse = buyHouse;
    }

    @Override
    public void buyHosue() {
        System.out.println("买房前准备");
        buyHouse.buyHosue();
        System.out.println("买房后装修");

    }
}
public class ProxyTest {
    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();
        buyHouse.buyHosue();
        BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
        buyHouseProxy.buyHosue();
    }
}

 JDK动态代理:

public class DynamicProxyHandler implements InvocationHandler {

    private Object object;

    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("买房前准备");
        Object result = method.invoke(object, args);
        System.out.println("买房后装修");
        return result;
    }
}
public class DynamicProxyTest {
    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();
        BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
                Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
        proxyBuyHouse.buyHosue();
    }
}

        JDK动态代理,利用反射机制,调用invoke方法。此时动态代理不再像静态代理一样把代理类设置为自己的成员变量,然后调用带有增强实现的代理方法。而缺点是被代理的类必须实现接口(BuyHouse),有很强的局限性。动态代理返回一个代理对象Object,把它强转为BuyHouse接口的对象,然后该对象通过反射来调用带有增强实现的代理方法。invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。

        CGLIB动态代理不要求代理类实现接口,简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;原理就是当被代理类调用代理方法时拦截下来。

图1.1 被代理类

图1.2 实现MethodInterceptor接口生成方法拦截器

图1.3 生成代理类对象并打印在代理类对象调用方法之后的执行结果

CGLIB(Code Generation Library)是一个开源项目!

是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展 

CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。                  

这些只是原理,但这样做会节省大量的代码。在实际的开发中通过AspecJ来基于XML和注解的方式设置通知和切面类。底层用的是ProxyFactoryBean来生成代理对象。

latex-table


JDK动态代理本质上跟静态代理是一样的,只是JDK动态代理自动生成了代理类,省去了编写代理类的时间,另外,只需要传入一个被代理对象就可以自动识别实现的接口类,即不需要知道被代理类实现了什么接口。可以理解为自动编写了静态代理中的代理类的代码。 

静态代理的缺点:

1、静态代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2、静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

JDK动态代理本质上就是自动编写了静态代理的代理类方法,因此没有克服上面的缺点1,但是因为JDK动态代理只需要传入被代理对象即可,实现不同接口的对象对于JDK动态代理都是一样的,因为会自动实现接口。因此克服了上面的缺点2.

CGLIB和Java动态代理的区别

  1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

cglib proxy区别于jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect。

cglib代码包结构

猜你喜欢

转载自blog.csdn.net/raylrnd/article/details/82950341