談談Java中的代理

摘要: 原創出處 https://peijie-sh.gitgub.io 歡迎轉載,保留摘要,謝謝!java

代理是Java經常使用的設計模式,代理類經過調用被代理類的相關方法,實現對相關方法加強。好比加入事務、日誌、報警發郵件等操做。git

java.png

靜態代理

靜態代理,就是由程序員手動編寫代理類或者用工具生成代理類的代碼,再進行編譯生成class文件,實現代理。好比簡單工廠模式。程序員

用法:spring

  • 代理類和目標類都實現相同接口。
  • 代理類持有目標類的引用。

缺點: 靜態代理要爲每一個目標類建立一個代理類,當須要代理的對象太多,那麼代理類也變得不少。代理類違背了可重複代理只寫一次的原則。編程

動態代理

爲了解決靜態代理的缺點,因而引入了動態代理。 它有一個好處,那就是不用寫不少代理類,生成的代理類數量是固定的。 通常動態代理分爲2種:設計模式

JDK動態代理

JDK動態代理是JDK自帶的,不依賴第三方框架。 它的實現原理,就是利用Java的反射機制,建立一個實現接口的代理類。框架

用法:dom

  • 被代理對象必須實現接口。
  • 代理對象由代理工廠自動生成。

下面貼個例子ide

接口類:函數

public interface Subject {   
  public void doSomething();   
}
複製代碼

實現類:

public class RealSubject implements Subject {   
  public void doSomething() {   
    System.out.println("do 了 some thing ...");   
  }   
} 
複製代碼

代理工廠:

import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  

public class ProxyHandler implements InvocationHandler {
    private Object target;

    //綁定委託對象,並返回代理類
    public Object bind(Object target) {
        this.target = target;
        //綁定該類實現的全部接口,取得代理類 
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
	
	@Override
    public Object invoke(Object proxy , Method method , Object[] args)throws Throwable {
        Object result = null;
        //這裏就能夠進行所謂的AOP編程了
        //在調用具體函數方法前,執行功能處理
        result = method.invoke(target, args);
        //在調用具體函數方法後,執行功能處理
        return result;
    }
}
複製代碼

測試類:

public class TestProxy {
    public static void main(String args[]) {
           ProxyHandler proxy = new ProxyHandler();
           //綁定該類實現的全部接口
           Subject sub = (Subject) proxy.bind(new RealSubject());
           sub.doSomething();
    }
}
複製代碼

CGLIB代理

使用CGLIB代理須要引入CGLIB庫,它使用字節碼技術實現代理。

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
      
public class CGLibProxy implements MethodInterceptor {    
    
    private Object targetObject;// CGLib須要代理的目標對象 
    
    public Object createProxyObject(Object obj) {    
        this.targetObject = obj;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(obj.getClass());    
        enhancer.setCallback(this);    
        Object proxyObj = enhancer.create();    
        return proxyObj;// 返回代理對象 
    }    
    
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {    
        Object obj = null;    
        if ("addUser".equals(method.getName())) {// 過濾方法 
            checkPopedom();// 檢查權限 
        }    
        obj = method.invoke(targetObject, args);    
        return obj;    
    }    
    
    private void checkPopedom() {    
        System.out.println("檢查權限 checkPopedom()!");    
    }    
}
複製代碼
public class Test {
    
    public static void main(String[] args) {
    
		Subject sub = (Subject) new CGLibProxy().createProxyObject(new RealSubject());
		sub.doSomething();
}
複製代碼

2種動態代理的區別

JDK動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。 而CGLIB動態代理是利用asm開源包,加載代理對象類的class文件,修改其字節碼生成子類來處理。

在 Spring 中,

  • 若是目標對象實現了接口,默認狀況下會採用JDK動態代理實現AOP
  • 若是目標對象實現了接口,能夠強制使用CGLIB實現AOP
  • 若是目標對象沒有實現了接口,必須採用CGLIB庫,Spring會自動在JDK動態代理和CGLIB之間轉換

如何強制使用CGLIB實現AOP?

  • 添加CGLIB依賴
  • 在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
  • 若是是SpringBoot,在配置文件設置spring.aop.proxy-target-class=true

JDK動態代理和CGLIB字節碼生成的區別?

  • JDK動態代理只能對實現了接口的類生成代理,而不能針對未實現接口的類
  • CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
  • 由於是繼承,因此該類或方法最好不要聲明成final
相關文章
相關標籤/搜索