spring基礎(chǔ)概念A(yù)OP與動(dòng)態(tài)代理理解
一、代理模式
代理模式的英文叫做Proxy或Surrogate,中文都可譯為”代理“,所謂代理,就是一個(gè)人或者一個(gè)機(jī)構(gòu)代表另一個(gè)人或者另一個(gè)機(jī)構(gòu)采取行動(dòng)。在一些情況下,一個(gè)客戶(hù)不想或者不能夠直接引用一個(gè)對(duì)象,而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。
以簡(jiǎn)單模擬事務(wù)的執(zhí)行過(guò)程說(shuō)明各種代理區(qū)別
1.1 靜態(tài)代理
由程序員創(chuàng)建或由特定工具自動(dòng)生成源代碼,再對(duì)其編譯。在程序運(yùn)行前,代理類(lèi)的.class文件就已經(jīng)存在了。
public interface PersonDao {
void savePerson();
}
public class PersonDaoImpl implements PersonDao {
@Override
public void savePerson() {
System.out.println("save person");
}
}
public class Transaction {
void beginTransaction(){
System.out.println("begin Transaction");
}
void commit(){
System.out.println("commit");
}
}
接下來(lái)編寫(xiě)靜態(tài)代理類(lèi)---實(shí)現(xiàn)PersonDao接口
/**
* 靜態(tài)代理類(lèi)
* @author qjc
*/
public class PersonDaoProxy implements PersonDao{
PersonDao personDao;
Transaction transaction;
public PersonDaoProxy(PersonDao personDao, Transaction transaction) {
this.personDao = personDao;
this.transaction = transaction;
}
@Override
public void savePerson() {
this.transaction.beginTransaction();
this.personDao.savePerson();
this.transaction.commit();
}
}
測(cè)試
/**
* 測(cè)試靜態(tài)代理
* @author qjc
*/
public class TestPersonProxy {
@Test
public void testSave(){
PersonDao personDao = new PersonDaoImpl();
Transaction transaction = new Transaction();
PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction);
proxy.savePerson();
}
}
總結(jié):
1、靜態(tài)代理模式并沒(méi)有做到事務(wù)的重用
2、假設(shè)dao有100個(gè)類(lèi),100個(gè)proxy,接口中有多少方法,在proxy層就得實(shí)現(xiàn)多少方法,有多少方法就要開(kāi)啟和提交多少事務(wù)
3、如果一個(gè)proxy實(shí)現(xiàn)了多個(gè)接口,如果其中的一個(gè)接口發(fā)生變化(添加了一個(gè)方法),那么proxy也要做相應(yīng)改變
1.2 JDK動(dòng)態(tài)代理
動(dòng)態(tài)代理類(lèi):在程序運(yùn)行時(shí),運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。
JDK的動(dòng)態(tài)代理必須具備四個(gè)條件:1、目標(biāo)接口 2、目標(biāo)類(lèi) 3、攔截器 4、代理類(lèi)
使用上個(gè)例子的PersonDao接口、PersonDaoImpl類(lèi)及Transaction類(lèi)
編寫(xiě)攔截器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 攔截器
* 1、目標(biāo)類(lèi)導(dǎo)入進(jìn)來(lái)
* 2、事物導(dǎo)入進(jìn)來(lái)
* 3、invoke完成:開(kāi)啟事務(wù)、調(diào)用目標(biāo)對(duì)象的方法、事務(wù)提交
*
* @author qjc
*/
public class Interceptor implements InvocationHandler {
private Object target; // 目標(biāo)類(lèi)
private Transaction transaction;
public Interceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
}
/**
* @param proxy 目標(biāo)對(duì)象的代理類(lèi)實(shí)例
* @param method 對(duì)應(yīng)于在代理實(shí)例上調(diào)用接口方法的Method實(shí)例
* @param args 傳入到代理實(shí)例上方法參數(shù)值的對(duì)象數(shù)組
* @return 方法的返回值,沒(méi)有返回值是null
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if ("savePerson".equals(methodName)
|| "deletePerson".equals(methodName)
|| "updatePerson".equals(methodName)) {
this.transaction.beginTransaction(); // 開(kāi)啟事務(wù)
method.invoke(target); // 調(diào)用目標(biāo)方法
this.transaction.commit(); // 提交事務(wù)
} else {
method.invoke(target);
}
return null;
}
}
測(cè)試
/**
* 測(cè)試jdk動(dòng)態(tài)代理
* @author qjc
*/
public class TestJDKProxy {
@Test
public void testSave(){
/**
* 1、創(chuàng)建一個(gè)目標(biāo)對(duì)象
* 2、創(chuàng)建一個(gè)事務(wù)
* 3、創(chuàng)建一個(gè)攔截器
* 4、動(dòng)態(tài)產(chǎn)生一個(gè)代理對(duì)象
*/
Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
/**
* 參數(shù)一:設(shè)置代碼使用的類(lèi)加載器,一般采用跟目標(biāo)類(lèi)相同的類(lèi)加載器
* 參數(shù)二:設(shè)置代理類(lèi)實(shí)現(xiàn)的接口,跟目標(biāo)類(lèi)使用相同的接口
* 參數(shù)三:設(shè)置回調(diào)對(duì)象,當(dāng)代理對(duì)象的方法被調(diào)用時(shí),會(huì)調(diào)用該參數(shù)指定對(duì)象的invoke方法
*/
PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
interceptor);
personDao.savePerson();
}
}
總結(jié):
1、因?yàn)槔肑DKProxy生成的代理類(lèi)實(shí)現(xiàn)了接口,所以目標(biāo)類(lèi)中所有的方法在代理類(lèi)中都有。
2、生成的代理類(lèi)的所有的方法都攔截了目標(biāo)類(lèi)的所有的方法。而攔截器中invoke方法的內(nèi)容正好就是代理類(lèi)的各個(gè)方法的組成體。
3、利用JDKProxy方式必須有接口的存在。
4、invoke方法中的三個(gè)參數(shù)可以訪(fǎng)問(wèn)目標(biāo)類(lèi)的被調(diào)用方法的API、被調(diào)用方法的參數(shù)、被調(diào)用方法的返回類(lèi)型。
缺點(diǎn):
1、在攔截器中除了能調(diào)用目標(biāo)對(duì)象的目標(biāo)方法以外,功能是比較單一的,在這個(gè)例子中只能處理事務(wù)
2、攔截器中的invoke方法的if判斷語(yǔ)句在真實(shí)的開(kāi)發(fā)環(huán)境下是不靠譜的,因?yàn)橐坏┓椒ê芏鄆f語(yǔ)句需要寫(xiě)很多。
1.3 CGLIB動(dòng)態(tài)代理
使用上個(gè)例子的PersonDaoImpl類(lèi)和Transaction類(lèi)(不用接口)
編寫(xiě)攔截器類(lèi)
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* CGLIB代理 攔截器
* @author qjc
*/
public class Interceptor implements MethodInterceptor {
private Object target; // 代理的目標(biāo)類(lèi)
private Transaction transaction;
public Interceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
}
/**
* 創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象
*
* @return
*/
public Object createProxy() {
// 代碼增強(qiáng)
Enhancer enhancer = new Enhancer(); // 該類(lèi)用于生成代理對(duì)象
enhancer.setCallback(this); // 參數(shù)為攔截器
enhancer.setSuperclass(target.getClass());// 設(shè)置父類(lèi)
return enhancer.create(); // 創(chuàng)建代理對(duì)象
}
/**
* @param obj 目標(biāo)對(duì)象代理類(lèi)的實(shí)例
* @param method 代理實(shí)例上 調(diào)用父類(lèi)方法的Method實(shí)例
* @param args 傳入到代理實(shí)例上方法參數(shù)值的對(duì)象數(shù)組
* @param methodProxy 使用它調(diào)用父類(lèi)的方法
* @return
* @throws Throwable
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
this.transaction.beginTransaction();
method.invoke(target);
this.transaction.commit();
return null;
}
}
測(cè)試
/**
* 測(cè)試cglib動(dòng)態(tài)代理
* 通過(guò)cglib產(chǎn)生的代理對(duì)象,代理類(lèi)是目標(biāo)類(lèi)的子類(lèi)
* @author qjc
*/
public class TestCglibProxy {
@Test
public void testSave(){
Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
PersonDaoImpl personDaoImpl = (PersonDaoImpl) interceptor.createProxy();
personDaoImpl.savePerson();
}
}
總結(jié):
1、CGlib是一個(gè)強(qiáng)大的,高性能,高質(zhì)量的Code生成類(lèi)庫(kù)。它可以在運(yùn)行期擴(kuò)展Java類(lèi)與實(shí)現(xiàn)Java接口。
2、用CGlib生成代理類(lèi)是目標(biāo)類(lèi)的子類(lèi)。
3、用CGlib生成 代理類(lèi)不需要接口
4、用CGLib生成的代理類(lèi)重寫(xiě)了父類(lèi)的各個(gè)方法。
5、攔截器中的intercept方法內(nèi)容正好就是代理類(lèi)中的方法體 CGLIB和JDK動(dòng)態(tài)代理區(qū)別:
JDK:
目標(biāo)類(lèi)和代理類(lèi)實(shí)現(xiàn)了共同的接口
攔截器必須實(shí)現(xiàn)InvocationHandler接口,而這個(gè)接口中invoke方法體的內(nèi)容就是代理對(duì)象方法體的內(nèi)容
CGLIB:
目標(biāo)類(lèi) 是代理類(lèi)的父類(lèi)
攔截器必須實(shí)現(xiàn)MethodInterceptor接口,而接口中的intercept方法就是代理類(lèi)的方法體,使用字節(jié)碼增強(qiáng)機(jī)制創(chuàng)建代理對(duì)象的.
二、面向切面編程
OOP(面向?qū)ο缶幊?:封裝、繼承、多態(tài)、抽象
封裝,對(duì)代碼進(jìn)行基本的管理、模塊化的管理。每個(gè)類(lèi)可能都有自己的職能,出了問(wèn)題就是論事找人就行了。從修改角度講,直接修改代碼可能有風(fēng)險(xiǎn),這不是個(gè)長(zhǎng)遠(yuǎn)之計(jì),最自然的是從類(lèi)型封裝變化。但是新的類(lèi)型和舊的體系之間怎么去融合,所以說(shuō)需要在類(lèi)與類(lèi)之間建立一種血緣關(guān)系。那么這就是繼承的需求,通過(guò)繼承就可以發(fā)現(xiàn)這些類(lèi)之間是有關(guān)聯(lián)的,它們之間是有父子關(guān)系的。然后在繼承基礎(chǔ)之上多態(tài)起決定性的特征。所以說(shuō)一般認(rèn)為面向?qū)ο笞詈诵牡奶卣?,其?shí)是多態(tài)。前面幾個(gè)都是在做鋪墊的。多態(tài)才是它最核心的特征。子類(lèi)中通過(guò)重寫(xiě)方法,代表了擴(kuò)展這個(gè)層面的東西,而它能融入老的體系中能夠正常工作,這是重用這個(gè)層面的東西,新的方法、舊的體系、擴(kuò)展和重用。
AOP(面向切面編程):
面向切面編程,是一種通過(guò)預(yù)編譯方式運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù).
OOP與AOP區(qū)別:
OOP:針對(duì)業(yè)務(wù)處理過(guò)程的實(shí)體及其屬性和行為進(jìn)行抽象封裝,以獲得更加清楚的邏輯單元?jiǎng)澐帧?/p>
AOP:針對(duì)業(yè)務(wù)處理過(guò)程中的橫切邏輯 進(jìn)行提取,它所面對(duì)的是處理過(guò)程中的某個(gè)步驟或者階段,以獲得邏輯過(guò)程中各部分之間低耦合的隔離效果。這兩種設(shè)計(jì)思想在目標(biāo)上有著本質(zhì)的差異。AOP做到了代碼塊的重用。
spring AOP代理機(jī)制:
1、若目標(biāo)對(duì)象實(shí)現(xiàn)了若干接口,spring使用JDK的java.lang.reflect.Proxy類(lèi)代理。
優(yōu)點(diǎn):因?yàn)橛薪涌?,所以使系統(tǒng)更加松耦合
缺點(diǎn):為每一個(gè)目標(biāo)類(lèi)創(chuàng)建接口
2、若目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)任何接口,spring使用CGLIB庫(kù)生成目標(biāo)對(duì)象的子類(lèi)。
優(yōu)點(diǎn):因?yàn)榇眍?lèi)與目標(biāo)類(lèi)是繼承關(guān)系,所以不需要有接口的存在。
缺點(diǎn):因?yàn)闆](méi)有使用接口,所以系統(tǒng)的耦合性沒(méi)有使用JDK的動(dòng)態(tài)代理好。
使用第一個(gè)例子的 PersonDao接口、PersonDaoImpl類(lèi)和Transaction類(lèi)
編寫(xiě)spring配置
<bean id="personDao" class="cn.qjc.aop.xml.PersonDaoImpl"></bean> <bean id="transaction" class="cn.qjc.aop.xml.Transaction"></bean> <aop:config> <!-- 切入點(diǎn)表達(dá)式 確定目標(biāo)類(lèi) --> <aop:pointcut expression="execution(* cn.qjc.aop.xml.PersonDaoImpl.*(..))" id="perform"/> <!-- ref指向?qū)ο缶褪乔忻?--> <aop:aspect ref="transaction"> <aop:before method="beginTransaction" pointcut-ref="perform"/> <aop:after-returning method="commit" pointcut-ref="perform"/> </aop:aspect> </aop:config> </beans>
測(cè)試
/**
* 測(cè)試spring動(dòng)態(tài)代理
* @author qjc
*/
public class TransactionTest {
@Test
public void testSave(){
ApplicationContext context = new ClassPathXmlApplicationContext("cn/qjc/aop/xml/applicationContext.xml");
PersonDao personDao = (PersonDao) context.getBean("personDao");
personDao.savePerson();
}
}
spring AOP原理
1、當(dāng)spring容器啟動(dòng)的時(shí)候,加載兩個(gè)bean,對(duì)像個(gè)bean進(jìn)行實(shí)例化
2、當(dāng)spring容器對(duì)配置文件解析到<aop:config>的時(shí)候,把切入點(diǎn)表達(dá)式解析出來(lái),按照切入點(diǎn)表達(dá)式匹配spring容器內(nèi)容的bean
3、如果匹配成功,則為該bean創(chuàng)建代理對(duì)象
4、當(dāng)客戶(hù)端利用context.getBean獲取一個(gè)對(duì)象時(shí),如果該對(duì)象有代理對(duì)象,則返回代理對(duì)象,如果沒(méi)有代理對(duì)象,則返回對(duì)象本身
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 深入淺出講解Spring框架中AOP及動(dòng)態(tài)代理的應(yīng)用
- Spring中AOP概念與兩種動(dòng)態(tài)代理模式原理詳解
- Spring AOP里的靜態(tài)代理和動(dòng)態(tài)代理用法詳解
- Spring AOP手動(dòng)實(shí)現(xiàn)簡(jiǎn)單動(dòng)態(tài)代理的代碼
- Spring AOP中的JDK和CGLib動(dòng)態(tài)代理哪個(gè)效率更高?
- Spring AOP注解失效的坑及JDK動(dòng)態(tài)代理
- 利用spring aop實(shí)現(xiàn)動(dòng)態(tài)代理
- Spring?AOP原理及動(dòng)態(tài)代理
相關(guān)文章
MyBatis分頁(yè)插件PageHelper的使用與原理
提到插件相信大家都知道,插件的存在主要是用來(lái)改變或者增強(qiáng)原有的功能,MyBatis中也一樣,下面這篇文章主要給大家介紹了關(guān)于Mybatis第三方PageHelper分頁(yè)插件的使用與原理,需要的朋友可以參考下2023-02-02
SpringBoot使用Redisson實(shí)現(xiàn)分布式鎖(秒殺系統(tǒng))
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用Redisson實(shí)現(xiàn)分布式鎖,秒殺系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
spring初始化源碼之關(guān)鍵類(lèi)和擴(kuò)展接口詳解
Spring就是一個(gè)大工廠,可以將所有對(duì)象的創(chuàng)建和依賴(lài)關(guān)系的維護(hù)交給Spring管理,下面這篇文章主要給大家介紹了關(guān)于spring初始化源碼之關(guān)鍵類(lèi)和擴(kuò)展接口的相關(guān)資料,需要的朋友可以參考下2023-04-04
Java反射之Call stack introspection詳解
這篇文章主要介紹了Java反射之Call stack introspection詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
Springboot+jwt實(shí)現(xiàn)在線(xiàn)用戶(hù)功能(示例代碼)
這篇文章主要介紹了Springboot+jwt實(shí)現(xiàn)在線(xiàn)用戶(hù)功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-12-12
Lombok中@Builder和@SuperBuilder注解的用法案例
@Builder?是?lombok?中的注解,可以使用builder()構(gòu)造的Person.PersonBuilder對(duì)象進(jìn)行鏈?zhǔn)秸{(diào)用,給所有屬性依次賦值,這篇文章主要介紹了Lombok中@Builder和@SuperBuilder注解的用法,需要的朋友可以參考下2023-01-01

