一、动态代理的由来

    代理是指:给每个具体类写一个代理类,以后要使用某个具体的类时,只要创建它的代理类的对象,然后调用代理类的方法就可以了,可是如果现在有很多具体的类,那就需要创建很多的代理类才可以,这样显然很不合适,那动态代理就应运而生。


二、Java实现动态代理有2种方式:

    1、JDK实现动态代理,但是它需要实现类通过接口定义业务方法,对于没有接口的类是无法实现的;

    2、CGLib,它采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。


    JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。


三、JDK的实现

    此类型的动态代理使用到了一个接口InvocationHandler和一个代理类Proxy,这两个类配合使用即可实现动态代理。

    

//接口
interface InterfaceClass {
    void show() ;
}

//具体实现类A
class ClassA implements InterfaceClass {
    @Override
    public void show(){
        System.out.println("class A") ;
    }
}

//具体实现类B
class ClassB implements InterfaceClass {
    @Override
    public void show(){
        System.out.println("class B") ;
    }
}

//动态代理类,实现InvocationHandler接口

class Invoker implements InvocationHandler {
    InterfaceClass ia ;
    
    public Invoker(InterfaceClass ia) {
        this.ia = ia ;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable {
        method.invoke(ia, arg) ;
        return null ;
    }
}

// Test
class DynamicProxyTest{
    public static void main(String[] args){
        // 创建具体类ClassA的处理对象
        Invoker invoker1 = new Invoker(new ClassA());
        // 获得具体类ClassA的代理
        InterfaceClass ic1 = (InterfaceClass)Proxy.newProxyInstance(InterfaceClass.class.getClassLoader(), new Class[]{InterfaceClass.class}, invoker1);
        ic1.show() ;
        
        // classB也是如此
    }
}


四、CGLib实现


    

public class CGLIBProxy implements MethodInterceptor{
    private Enhancer enhancer = new Enhancer() ;
    
    public Object getProxy(Class clazz) {
        // 设置父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        // 通过字节码技术动态创建子类实力
        return enhancer.create() ;
    }
    
    // 所有的方法都会被这个方法所拦截,该类实现了子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class类创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,args为方法动态入参,proxy为代理类实例。
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodproxy) throws Throwable{
        Object result = methodproxy.invokerSuper(obj, args) ;
        return result ;
    }
}

public class UserServiceImpl{
    public void sayHello() {
        System.out.println("sayHello method") ;
    }
    
    public void sayBye() {
        System.out.println("sayBye method") ;
    }
}
// Test
public class Test{
    public static void main(String[] args) {
        CGLIBProxy proxy = new CGLIBProxy() ;
    
        UserServiceImpl impl = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class) ;
        impl.sayHello() ;
    }

}


六、比较两种方式的优缺点


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