任何Bean通過實現(xiàn)ProxyableBeanAccessor接口即可獲得動態(tài)靈活的獲取代理對象或原生對象的能力(最新推薦)
如果一個BEAN類上加了@Transactional,則默認的該類及其子類的公開方法均會開啟事務(wù),但有時某些業(yè)務(wù)場景下某些公開的方法可能并不需要事務(wù),那這種情況該如何做呢?
常規(guī)的做法:
針對不同的場景及事務(wù)傳播特性,定義不同的公開方法【哪怕是同一種業(yè)務(wù)】,并在方法上添加@Transactional且指明不同的傳播特性,示例代碼如下:
@Service
@Transactional
public class DemoSerivce {
//SUPPORTED 若無事務(wù)傳播則默認不會有事務(wù),若有事務(wù)傳播則會開啟事務(wù)
@Transactional(propagation = Propagation.SUPPORTED)
public int getValue(){
return 0;
}
//默認開啟事務(wù)(由類上的@Transactional決定的)
public int getValueWithTx(){
return 0;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int getValueWithoutTx(){
return 0;
}
}上述這樣的弊端就是:若是同一個邏輯但如果要精細控制事務(wù),則需要定義3個方法來支持,getValue、getValueWithTx、getValueWithoutTx 3個方法,分別對應(yīng)3種事務(wù)場景,這種代碼就顯得冗余過多,那有沒有簡單一點的方案呢?其實是有的。
聲明式事務(wù)的本質(zhì)是通過AOP切面,在代理執(zhí)行原始方法【即:被標注了@Transactional的公開方法】前開啟DB事務(wù),在執(zhí)行后提交DB事務(wù)(若拋錯則執(zhí)行回滾),如果要想事務(wù)不生效,則讓AOP失效即可,即:調(diào)原生的service Bean公開方法而不是代理類的公開方法,那如何獲得原生的BEAN類呢,答案是:在原生BEAN方法內(nèi)部通過this獲取即可,如果沒理解,下面寫一個手寫代理示例,大家就明白了:
public class DemoService{
public int getValue(){
return 666;
}
public DemoService getReal(){
//這里的this指向的就是當前的自己,并非代理對象
return this;
}
}
public class DemoServiceProxy{
private DemoService target;
public DemoServiceProxy(DemoService target){
this.target=target;
}
public int getValue(){
//增強:開啟事務(wù)
return target.getValue()+222;
//增強:提交事務(wù)
}
public DemoService getReal(){
//這里就會間接的把原生的對象傳遞返回
return target.getReal();
}
}
public static void main(String[] args) {
DemoServiceProxy proxy=new DemoServiceProxy(new DemoService());
System.out.println("proxy class:" +proxy.getClass().getName());
System.out.println("real class:" +proxy.getReal().getClass().getName());
System.out.println("proxy getValue result:" + proxy.getValue() );
System.out.println("real getValue result:" + proxy.getReal().getValue() );
}最終的輸出結(jié)果為:
proxy class:...DemoServiceProxy
real class:...DemoService →原始的對象
proxy getValue result:888
real getValue result:666
通過DEMO證實了通過避開代理的方案是正確的,而且非常簡單,那么有了這個基礎(chǔ),再應(yīng)用到實際的代碼中則很簡單,想控制有事務(wù)則取代理對象,想控制不要事務(wù)則取原生對象即可,就是這么簡單。
下面貼出核心也是全部的ProxyableBeanAccessor代碼:(注意必需擴展自RawTargetAccess,否則即使返回this也會被強制返回代理)
/**
* @author zuowenjun
* @date 2022/12/5 22:03
* @description 可代理BEAN訪問者接口(支持獲取代理的真實對象、獲取代理對象)
*/
public interface ProxyableBeanAccessor<T extends ProxyableBeanAccessor> extends RawTargetAccess {
String CONTEXT_KEY_REAL_GET = "proxyable_bean_accessor_real_get";
/**
* 獲取代理的真實對象(即:未被代理的對象)
* 注:若調(diào)用未被代理的bean的公開方法,則均不會再走AOP切面
*
* @return 未被代理的對象Bean
*/
@SuppressWarnings("unchecked")
@Transactional(propagation = Propagation.NOT_SUPPORTED)
default T getReal() {
return (T) this;
}
/**
* 獲取當前類的代理對象(即:已被代理的對象)
* 注:若調(diào)用已被代理的對象Bean的公開方法,則相關(guān)AOP切面均可正常攔截與執(zhí)行
*
* @return 已被代理的對象Bean
*/
@SuppressWarnings("unchecked")
@Transactional(propagation = Propagation.NOT_SUPPORTED)
default T getProxy() {
return (T) SpringUtils.getBean(this.getClass());
}
/**
* 將當前BEAN轉(zhuǎn)換為代理對象或真實對象
*
* @param realGet 是否轉(zhuǎn)換獲取真實對象
* @return 未被代理的對象Bean OR 已被代理的對象Bean
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
default T selfAs(Supplier<Boolean> realGet) {
Boolean needGetReal = false;
if (realGet == null) {
if (ContextUtils.get() != null) {
needGetReal = (Boolean) ContextUtils.get().getGlobalVariableMap().getOrDefault(CONTEXT_KEY_REAL_GET, false);
}
} else {
needGetReal = realGet.get();
}
return Boolean.TRUE.equals(needGetReal) ? getReal() : getProxy();
}
}其中,SpringUtils是一個獲取BEAN的工具類,代碼如下:
public SpringUtils implements ApplicationContextAware{
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context=applicationContext;
}
public static <T> getBean(Class<T> clazz){
return context.getBean(clazz);
}
}ContextUtils只是一個內(nèi)部定義了一個ThreadLocal的靜態(tài)map字段,用于存放線程上下文要傳遞的對象。
使用方法:只需將原來Service的子類或其它可能被切面代理的類 加上實現(xiàn)自ProxyableBeanAccessor即可,然后在這個類里面或外部調(diào)用均可通過getReal獲得原生對象、getProxy獲得代理對象、selfAs動態(tài)根據(jù)條件來判斷是否需要代理或原生對象,使用示例如下:
//serive BEAN定義
@Service
@Transactional
public class DemoService implements ProxyableBeanAccessor<DemoService> {
... ...
public Demo selectByMergerParam(Demo demo){
return getMapper().selectByMergerParam(demo);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public Demo selectByMergerParam2(Demo demo){
//通過getProxy獲取當前類的代理BEAN,以便可以執(zhí)行事務(wù)切面
return getProxy().doSelectByMergerParam(demo);
}
public Demo doSelectByMergerParam(Demo demo){
return getMapper().selectByMergerParam(demo);
}
}
//具體使用:
@Autowired
private DemoService demoService;
Demo query = new Demo ();
query.setWaybillNumber("123455667");
//示例一:獲取原生對象查詢,由于沒有代理,則無事務(wù)
demoService.getReal().selectByMergerParam(query);
//示例二:根據(jù)LAMBDA表達式的布爾值來決定是否獲取原生或代理(這里演示:如果是TIDB,則獲取原生對象,即:不開事務(wù))
demoService.selfAs(()-> TidbDataSourceSwitcher.isUsingTidbDataSource()).selectByMergerParam(query);
//示例三:根據(jù)線程上下文設(shè)置的布爾值來決定是否獲取原生或代理(這里演示:如果是TIDB,則獲取原生對象,即:不開事務(wù)),這種方式主要是為了簡化大批量的動態(tài)邏輯判斷的場景,
// 一次設(shè)置同線程的所有ProxyableBeanAccessor的子類的selfAs(null)均可自動判斷
ContextUtils.get().addGlobalVariable(ProxyableBeanAccessor.CONTEXT_KEY_REAL_GET,TidbDataSourceSwitcher.isUsingTidbDataSource());
demoService.selfAs(null).selectByMergerParam(query);
//示例四:獲取代理對象,一般用于BEAN內(nèi)部方法之間調(diào)用,外部調(diào)用其實本身就是代理無意義
demoService.getProxy().selectByMergerParam(query);通過上述示例代碼可以看到,借助于ProxyableBeanAccessor接口默認實現(xiàn)的getReal、getProxy、selfAs方法,可以很靈活的實現(xiàn)按需獲取代理或非代理對象。
到此這篇關(guān)于任何Bean通過實現(xiàn)ProxyableBeanAccessor接口即可獲得動態(tài)靈活的獲取代理對象或原生對象的能力 的文章就介紹到這了,更多相關(guān)Bean ProxyableBeanAccessor接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot實現(xiàn)ModbusTCP通信的示例詳解
ModbusTCP協(xié)議是Modbus由MODICON公司于1979年開發(fā),是一種工業(yè)現(xiàn)場總線協(xié)議標準,本文主要介紹了Springboot實現(xiàn)ModbusTCP通信的相關(guān)知識,需要的可以參考下2023-12-12
Java?easyExcel的復(fù)雜表頭多級表頭導(dǎo)入
最近在項目開發(fā)中遇到的一個excel復(fù)雜表頭的導(dǎo)入數(shù)據(jù)庫操作,下面這篇文章主要給大家介紹了關(guān)于Java?easyExcel的復(fù)雜表頭多級表頭導(dǎo)入的相關(guān)資料,需要的朋友可以參考下2022-06-06
Spring Boot實現(xiàn)圖片上傳/加水印一把梭操作實例代碼
這篇文章主要給大家介紹了關(guān)于Spring Boot實現(xiàn)圖片上傳/加水印一把梭操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-11-11

