【Spring】代理模式 與 Spring中的jdk動態代理和cglib動態代理

代理模式:

代理模式的定義:代理模式(Proxy Pattern)是程序設計中的一種設計模式。爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
代理模式的組成

  • 1、抽象角色:通過接口或抽象類聲明真實角色實現的業務方法。
  • 2、真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。
  • 3、代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。

代理的分類

  • 靜態代理:在程序運行之前就已經確定代理類與其代理的真實類。例如,在編寫程序時,指定一個接口A。類B實現A接口完善其邏輯代碼。類C也實現了A接口,但是其並沒有自己的邏輯代碼,而是引用了類B的邏輯代碼,類C也可以在其引用的類B的邏輯代碼的基礎上做一些其他操作。此時,就稱類C是類B的靜態代理。
  • 動態代理:在程序運行時才確定代理類,動態代理目前有兩種常見的實現:jdk動態代理和cglib動態代理。

Spring AOP中隱含的動態代理:

一:Spring AOP中使用jdk動態代理:

想要在spring中使用jdk代理,需要以下幾個條件:
1、定義一個接口,例:

package cn.jingpengchong.math.service;

public interface IMathService {
	int div(int a, int b);
}

2、定義一個實現該接口的類,例:

package cn.jingpengchong.math.service;

import org.springframework.stereotype.Service;
//使用spring自動實例化對象,需添加能夠被掃描到的特定註解
@Service
public class MathService implements IMathService {
	public int div(int a, int b) {
		System.out.println("日誌:The div method begins");
		return a/b;
	}
}

3、在spring的xml配置文件中做如下配置

<!--假如不寫proxy-target-class屬性則默認是false-->
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>

4、定義切面,並且切入點要有與真實類相匹配的增強處理,例:

package cn.jingpengchong.math.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//該註解指定該類爲切面類
@Aspect
//使用spring自動實例化對象,需添加能夠被掃描到的特定註解
@Component
public class MethodAOP {
	
	//前置處理,表達式要與真實類的某個方法匹配,否則無法生成代理類
	@Before("execution(public int cn.jingpengchong.math.service.MathService.*(..))")
	public void before() {
	//此處可以添加附加操作
	}
}

5、通過接口對應的Class類的實例化對象獲得代理類,例:

package cn.jingpengchong.math.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.jingpengchong.math.service.IMathService;
import cn.jingpengchong.math.service.MathService;

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		//此處不能通過真實類對應的Class類的實例化對象獲得代理類
		IMathService mathService = applicationContext.getBean(IMathService.class);
		System.out.println("mathService的類是:");
		System.out.println(mathService.getClass());
		System.out.println("mathService的父類是:");
		System.out.println(mathService.getClass().getSuperclass());
		Class<?>[] interfaces = mathService.getClass().getInterfaces();
		System.out.println("mathService的類實現的接口有:");
		for (Class<?> c : interfaces) {
			System.out.println(c);
		}
		applicationContext.close();
	}
}

運行結果如下,順便可以觀察以下代理類的類、父類及實現的接口
在這裏插入圖片描述
假如沒有第3步所做的配置,則運行結果如下:
在這裏插入圖片描述
假如第4步的切面中沒有一個表達式與真實類中任何一個方法匹配,則結果如下:
在這裏插入圖片描述
假如第5步用真實類對應的Class類的實例化對象獲得代理類,則結果如下:
在這裏插入圖片描述

二、Spring AOP中使用cglib動態代理:

Spring中使用cglib動態代理與jdk代理類似,不同之處在於:
1、將spring的xml配置文件中的aop:aspectj-autoproxy標籤的proxy-target-class屬性值改爲true;
2、既可以通過接口對應的Class類的實例化對象獲得代理類,也可以通過真實類對應的Class類的實例化對象獲得代理類。

  • 通過兩種方式獲得代理類的結果是一樣的:
    在這裏插入圖片描述

三、jdk動態代理和cglib動態代理的區別:

1、jdk動態代理基於接口實現,而cglib動態代理基於真實類實現;
2、jdk動態代理間接實現接口,與真實類沒有繼承關係,而cglib動態代理則直接繼承了真實類。

Spring的事務管理自動使用動態代理:

除了在AOP中我們發現了動態代理的存在,其實在spring的事務管理中也用到了動態代理,Spring會將添加了@Transactional註解的類創建代理類,儘管我們在Spring的xml配置文件中並沒有配置aop:aspectj-autoproxy標籤。例如在下面的例子中:

package cn.jingpengchong.car.service;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.jingpengchong.coupon.service.ICouponService;

@Service
public class CarService implements ICarService {

	@Autowired
	private ICouponService couponService;

	//購物車購買
	@Transactional
	@Override
	public boolean batch(String userId,Map<String,Integer> commodities) {
		Set<Entry<String, Integer>> set = commodities.entrySet();
		for (Entry<String, Integer> commodity : set) {
			String bookId = commodity.getKey();
			int count = commodity.getValue();
			System.out.println(bookId+","+count);
			couponService.insert(userId,bookId, count);
		}
		return true;
	}
}

並且在spring的xml文件中也沒有配置aop:aspectj-autoproxy標籤,但是當我們寫個測試類如下:

package cn.jingpengchong.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.jingpengchong.coupon.service.ICouponService;

public class Test01 {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		ICouponService couponService = applicationContext.getBean(ICouponService.class);
		System.out.println("couponService的類是:");
		System.out.println(couponService.getClass());
		System.out.println("couponService的父類是:");
		System.out.println(couponService.getClass().getSuperclass());
		Class<?>[] interfaces = couponService.getClass().getInterfaces();
		System.out.println("mathService的類實現的接口有:");
		for (Class<?> c : interfaces) {
			System.out.println(c);
		}
		applicationContext.close();
	}
}

發現結果還是使用了動態代理,觀察代理類的類名我們可以清楚的知道這是一個jdk動態代理,因爲其標誌性字眼“Proxy”:
在這裏插入圖片描述

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