Java靜態代理與動態代理,JDKProxy和CGLIB

代理模式

代理模式就是隱藏真實對象,而暴露代理對象,而由代理對象去調用真實對象的行爲。

靜態代理

public interface Subject {
    /**
     * 處理方法
     */
    void process();
}

代理類

Subject realSubject;
public Proxy(Subject realSubject){
    this.realSubject=realSubject;
}
/**
 * 處理方法
 */
@Override
public void process() {
    System.out.println("before");
    System.out.println("-------------------------------------------");
    try {
        realSubject.process();
    }catch (Exception e){
        System.out.println("after Throwing");
        System.out.println(e.getMessage());
    }finally {
        System.out.println("-------------------------------------------");
        System.out.println("after");
    }
}

真實類

public class RealSubject implements Subject {
    /**
     * 處理方法
     */
    @Override
    public void process() {
        System.out.println("hello");
    }
}

Main

public class app {
    public static void main(String[] args) {
        Subject subject=new Proxy(new RealSubject());
        subject.process();
    }
}

結果
在這裏插入圖片描述
可以看見在執行打印Hello之前和執行之後多了兩行打印文字,這就是代理帶來的功能,在執行業務操作之前和之後能夠額外的添加其他操作。
那麼在實際中有什麼用途呢?
在web項目中,一般需要對請求和返回進行記錄,同時需要對異常進行記錄,這時如果把Logger放在業務類中,則每個方法都需要寫一大段重複代碼,於是代理類的出現解決了這個問題。
但是想一想,靜態代理如果有100個類需要進行代理也就意味着要給100個類寫上
implement Subject,這是不現實的。於是動態代理應運而生。

動態代理

相信大家在學習Spring的過程中都使用過AOP,其中各種Advice註解能夠方便的讓我們做切面編程,
回到剛纔了例子,如果使用AOP來操作會是怎樣的呢?

	@Around(value = "log()")
    public Object log(ProceedingJoinPoint joinPoint){
        for (Object arg:joinPoint.getArgs()){
            logger.info("請求值:"+arg.toString());
        }
        Object result=null;
        try {
           result = joinPoint.proceed();
        }catch (Throwable e){
            logger.error("error",e);
        }
        logger.info("返回值"+result.toString());
        return result;
    }

在這裏插入圖片描述
可以看見已經非常簡潔了。那麼它底層是如何實現的呢?這裏就要引出主角了,JDKProxy和CGLIB

JDKProxy

使用java.lang.reflec.Proxy.newInstance進行生成代理類

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

裏面的Class<> interfaces就是真實類實現的接口

public class app {
    public static void main(String[] args) {
        //將生成的代理類保存
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        Subject proxySubject= (Subject) Proxy.newProxyInstance(app.class.getClassLoader(),RealSubject.class.getInterfaces(),new SubjectInvocationHandler(new RealSubject()));
        proxySubject.process();
    }
}

需要實現InvocationHandler

public class SubjectInvocationHandler implements InvocationHandler {

    Subject subject;
    public SubjectInvocationHandler(Subject realSubject){
        subject=realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        System.out.println("----------------------------");
        Object result=null;
        try {
            System.out.println("開啓事務");
            result=method.invoke(subject,args);
            System.out.println("事務提交");
        }catch (Exception e){
            System.out.println("事務發現異常進行回滾");
            System.out.println(e.getMessage());
        }finally {
            System.out.println("----------------------------");
            System.out.println("after");
        }
        return result;
    }
}

生成的代理類代碼

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final void process() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("staticproxy.Subject").getMethod("process");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

運行結果
在這裏插入圖片描述
可以看到生成的代理類實現了Subject接口,同時持有實現的SubjectInvocationHandler,那麼執行process的時候就是執行SubjectInvocationHandler的invoke方法。

CGLIB

cglib採用asm動態生成字節碼技術
實際上CGLIB是使用攔截器,把方法的執行過程攔截,給方法做代理

public class SubjectInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before");
        System.out.println("------------------------------");
        Object result=null;
        try {
            result=methodProxy.invokeSuper(o,objects);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            System.out.println("----------------------------------");
            System.out.println("after");
        }
        return result;
    }
}
public class App {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new SubjectInterceptor());
        Subject proxySubject= (Subject) enhancer.create();
        proxySubject.process();
    }
}

查看CGLIB生成的代理類,可以看見MethodInterceptor對象var10000調用了intercept方法。

public class RealSubject$$EnhancerByCGLIB$$13c51f84 extends RealSubject implements Factory {

    public final void process() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$process$0$Method, CGLIB$emptyArgs, CGLIB$process$0$Proxy);
        } else {
            super.process();
        }
    }
}

在這裏插入圖片描述

JDKProxy和CGLIB的區別

JDKProxy裏面只能對實現interface的類進行代理,而CGLIB是基於繼承,從而沒有接口限制。
Spring中
在這裏插入圖片描述
DefaultAopProxyFactory源碼

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

對於Spring來說,@EnableAspectJAutoProxy中proxyTargetClass默認爲false,
如果proxyTargetClass爲false則很可能使用JDKProxy。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章