代理模式:
代理模式的定義:代理模式(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”: