1. 程式人生 > >靜態代理,動態代理和CGLIB代理模式

靜態代理,動態代理和CGLIB代理模式

代理模式

一、概述

  代理是一種模式,提供了對目標物件的間接訪問方式,即通過代理訪問目標物件。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求,同時代理模式便於擴充套件目標物件功能的特點也為多人所用。

二、圖形描述

三、程式碼實現

靜態代理,由於比較簡單,所有把程式碼都合到一起了: 

// 需要被代理的介面
public interface BussinessInterface {
	void execute();
}

// 基礎實現類
public class Bussiness implements BussinessInterface {

	@Override
	public void execute() {
		System.out.println("柯賢銘在做生意~~~");
	}
}

// 在基礎實現類的基礎之上,封裝一層方法
public class BussinessProxy implements BussinessInterface {
	
	private BussinessInterface bussinessImpl;
	
	public BussinessProxy(BussinessInterface bussinessImpl) {
        this.bussinessImpl = bussinessImpl;
    }
	
	@Override
    public void execute() {
        System.out.println("前攔截...");
        bussinessImpl.execute();
        System.out.println("後攔截...");
    }
}

// 測試類,真正使用的時候,我們採用增強之後的實現類
public class TestAgent {

	public static void main(String[] args) {
		BussinessInterface bussinessInterface = new Bussiness();
		BussinessInterface newBuss = new BussinessProxy(bussinessInterface);
		newBuss.execute();
	}
}

效果截圖:

靜態總結:

優點:可以做到不對目標物件進行修改的前提下,對目標物件進行功能的擴充套件和攔截。

缺點:因為代理物件,需要實現與目標物件一樣的介面,會導致代理類十分繁多,不易維護,同時一旦介面增加方法,則目標物件和代理類都需要維護。

動態代理模式,由於程式碼量較少,我也融合到一起:

// 定義介面
public interface UserService {
	void saveUser();
}

// 定義介面實現類及方法
public class UserServiceImpl implements UserService {
	@Override
    public void saveUser() {
        System.out.println("呼叫 saveUser() 方法");
    }
}

// 定義代理工具類
public class MyProxyUtil {
	public static UserService getProxyByJDK(UserService service) {
         // 引數:目標物件的類載入器,目標物件的介面,代理物件的執行處理器
		UserService userService = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
				service.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("記錄日誌-開始");
						Object obj = method.invoke(service, args);
						System.out.println("記錄日誌-結束");
						return obj;
					}
				});
		return userService;
	}
}


// 測試類
public class Test {

	public static void main(String[] args) {
		// 建立目標物件
		UserService userService = new UserServiceImpl();
		// 生成代理物件
		UserService proxy = MyProxyUtil.getProxyByJDK(userService);
		// 呼叫目標物件方法
		userService.saveUser();
		System.out.println("===================================");
		// 呼叫代理物件方法
		proxy.saveUser();
	}
}

測試截圖:

動態代理的總結

優點:代理物件無需實現介面,免去了編寫很多代理類的煩惱,同時介面增加方法也無需再維護目標物件和代理物件,只需在事件處理器中新增對方法的判斷即可。

缺點:代理物件不需要實現介面,但是目標物件一定要實現介面,否則無法使用JDK動態代理。

CGLib 動態代理

CGLib 動態代理相對於 JDK 動態代理侷限性就小了很多,目標物件不需要實現介面,底層是通過繼承目標物件產生代理子物件

程式碼,只是工具類方法多了一個:

public static UserService getProxyByCglib(UserService service) {
		// 建立增強器
		Enhancer enhancer = new Enhancer();
		// 設定需要增強的類的物件
		enhancer.setSuperclass(UserServiceImpl.class);
		// 設定回撥方法
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				long begin = System.currentTimeMillis();
				System.out.println("記錄程式開始");
				Object object = methodProxy.invokeSuper(o, args);
				long end = System.currentTimeMillis();
				System.out.println("記錄程式結束");
				return object;
			}
		});
		UserService userService = (UserService) enhancer.create();
		return userService;
	}

測試程式碼:

public class TestCglib {
	public static void main(String[] args) {
		// 建立目標物件
		UserService userService = new UserServiceImpl();
		// 生成代理物件
		UserService proxy = MyProxyUtil.getProxyByCglib(userService);
		// 呼叫目標物件方法
		userService.saveUser();
		System.out.println("===================================");
		// 呼叫代理物件方法
		proxy.saveUser();
	}
}

測試結果:

總結:

Cglib代理: 針對類來實現代理,對指定目標 產生一個子類 通過方法攔截技術攔截所有父類方法的呼叫。 我們要使用cglib代理必須引入 cglib的jar包

三種代理模式進行總的分析概括:

代理模式:代理類和被代理類實現共同的介面(或繼承),代理類中存有指向被代理類的索引,實際執行時通過呼叫代理類的方法、實際執行的是被代理類的方法。

而AOP,是通過動態代理實現的。

一、簡單來說:

  JDK動態代理只能對實現了介面的類生成代理,而不能針對類

  CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法(繼承)

二、Spring在選擇用JDK還是CGLiB的依據:

   (1)當Bean實現介面時,Spring就會用JDK的動態代理

   (2)當Bean沒有實現介面時,Spring使用CGlib是實現

   (3)可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)

三、CGlib比JDK快?

  (1)使用CGLib實現動態代理,CGLib底層採用ASM位元組碼生成框架,使用位元組碼技術生成代理類,比使用Java反射效率要高。唯一需要注意的是,CGLib不能對宣告為final的方法進行代理,因為CGLib原理是動態生成被代理類的子類。

  (2)在對JDK動態代理與CGlib動態代理的程式碼實驗中看,1W次執行下,JDK7及8的動態代理效能比CGlib要好20%左右。