javascript
Spring交易可见性
在初始化應(yīng)用程序上下文時,Spring遇到帶有@Transactional標(biāo)記的類時會創(chuàng)建代理。 @Transactional可以應(yīng)用于類級別或方法級別。 在類級別應(yīng)用它意味著該類中定義的所有公共方法都是事務(wù)性的。 Spring創(chuàng)建的代理類型,即Jdk代理或CGLIB代理,取決于將方法標(biāo)記為事務(wù)性的類。 如果該類實現(xiàn)了至少一個接口,則Spring將創(chuàng)建一個Jdk動態(tài)代理。 該代理實現(xiàn)與原始類相同的接口,并使用事務(wù)維護(hù)邏輯攔截接口方法。 它將調(diào)用委托給其中組成的原始對象。 假設(shè)該類未實現(xiàn)任何接口,Spring將創(chuàng)建一個CGLIB代理。 該代理擴(kuò)展了原始類并覆蓋了公共方法。 我們將盡快對此進(jìn)行仔細(xì)研究。 假設(shè)我們有一個這樣定義的類:
public interface BookDao{void buyBook(String isbn) throws BookNotFoundException;Book findByIsbn(String isbn);int deductStock(Book book); }public class JdbcBookDao implements BookDao{void buyBook(String isbn) throws BookNotFoundException{Book book = findByIsbn(isbn);if(book == null){throw new BookNotFoundException();}deductStock(book);}@Transactional(propagation=Propagation.REQUIRES_NEW)Book findByIsbn(String isbn){Book book = getJdbcTemplate().queryForObject("SELECT * FROM BOOK WHERE ISBN=?",ParameterizedBeanPropertyRowMapper.newInstance(Book.class), isbn);return book;}@Transactional(propagation=Propagation.REQUIRES_NEW)int deductStock(Book book){String sql = "UPDATE BOOK_STOCK SET STOCK=STOCK-1 WHERE BOOK_ID=?";return getJdbcTemplate().update(sql, stockIncrement, book.getId());} }現(xiàn)在,Spring是否會通過從main方法調(diào)用bookDao的findByIsbn自動創(chuàng)建交易? 否。我們必須在xml配置中聲明這一點(diǎn):
<tx:annotation-driven>因此,如果它不創(chuàng)建事務(wù),是否會引發(fā)錯誤? 答案還是不是。Spring非事務(wù)地執(zhí)行此語句。
按照我們的預(yù)期,一旦聲明了?tx:annotation-driven?,Spring就會在該類實現(xiàn)接口時為JdbcBookDao創(chuàng)建一個Jdk動態(tài)代理。 現(xiàn)在說,我們調(diào)用JdbcBookDao的buyBook方法,Spring創(chuàng)建了多少個事務(wù)?
答案是(3)。 交易的默認(rèn)交易模式是“代理”。 這意味著Spring僅考慮通過代理進(jìn)行的方法調(diào)用進(jìn)行自動事務(wù)管理。 現(xiàn)在,如果您仔細(xì)觀察,buyBook方法不會標(biāo)記為事務(wù)性的。 因此,當(dāng)創(chuàng)建事務(wù)代理時,此方法不會被事務(wù)管理邏輯攔截,因為它沒有標(biāo)記為@Transactional。 簡而言之,buyBook不會在代理中被覆蓋。 因此,該方法直接在原始對象上調(diào)用。 因此,其他兩個方法也將在原始對象上調(diào)用。 請記住,只有PROXY包含事務(wù)管理代碼。 因此,當(dāng)其他方法也被原始對象調(diào)用時,Spring根本不會創(chuàng)建事務(wù)。 現(xiàn)在,如果我們將buyBook標(biāo)記為@Transactional,是否可以解決問題? Spring是否會為每個findByIsbn和deductStock方法創(chuàng)建兩個單獨(dú)的事務(wù)?
不會。在調(diào)用buyBook()時,Spring僅創(chuàng)建一個事務(wù)。 由于不會在原始對象本身而非代理上調(diào)用各個方法,因此不會進(jìn)一步創(chuàng)建任何新事務(wù)。 那么如何解決這個問題呢?
我們可以要求Spring創(chuàng)建一個CGLIB proxy()嗎? 現(xiàn)在,由于代理是具有重寫的公共事務(wù)方法的子類,因此它為每個方法調(diào)用創(chuàng)建一個新事務(wù)? 再次沒有。 CGLIB代理不會直接在超類上調(diào)用該方法。 這是一個大概的實現(xiàn)方法。
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;public class MyInterceptor implements MethodInterceptor {private Object realObject;public MyInterceptor(Object original) {this.realObject = original;}// This method will be called every time the object proxy calls any of its methodspublic Object intercept(Object o, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {/*** Transaction Management Code*/// Invoke the method on the real object with the given paramsObject res = method.invoke(realObject, args);/*** Transaction Management Code*/return res;} }import net.sf.cglib.proxy.Enhancer;public class ProxyCreator(){public static T createProxy(T original){// Enhancer is CGLIB class which builds a dynamic proxy with new capabilitiesEnhancer e = new Enhancer();e.setSuperclass(original.getClass());// We have to declare the interceptor whose 'intercept' will be called when methods are called on proxy.e.setCallback(new MyInterceptor(original));T proxy = (T) e.create();return proxy;} }因此,如您在此處看到的,代理擴(kuò)展了原始類并由其對象組成。 因此,當(dāng)我們調(diào)用buyBook時,代理會創(chuàng)建一個事務(wù)并將該調(diào)用委托給原始對象。 從原始對象的buyBook中調(diào)用findByIsbn和deductStock不會,因此不會創(chuàng)建新交易。
一個快速的周轉(zhuǎn)解決方案是,因為JdbcBookDao是一個單例,請從應(yīng)用程序上下文中獲取此對象。 現(xiàn)在,與其直接在對象上調(diào)用方法,不如使用引用來調(diào)用它(以確保調(diào)用代理),這就是方法的外觀。
public class JdbcBookDao implements BookDao, ApplicationContextAware{private ApplicationContext context;private BookDao bookDao;public void setApplicationContext(ApplicationContext context){this.context = context;}public BookDao getBookDao(){bookDao = (BookDao)context.getBean("jdbcBookDao");}void buyBook(String isbn) throws BookNotFoundException{Book book = getBookDao().findByIsbn(isbn);if(book == null){throw new BookNotFoundException();}getBookDao().deductStock(book);}..... }剛剛實施了一個粗略的版本來使它正常工作。 我們絕對可以改進(jìn)其設(shè)計方式。 與其直接將應(yīng)用程序上下文注入DAO中,不如說我們可以使用一種輔助類來實現(xiàn)。 或完成此任務(wù)的另一種選擇是使用程序化事務(wù)。
最后要注意的一點(diǎn)是,Spring僅在將公共方法標(biāo)記為事務(wù)性時才管理事務(wù)。 對于私有,受保護(hù)和程序包私有的方法,Spring不提供事務(wù)管理支持。 對于動態(tài)代理,當(dāng)它們實現(xiàn)接口時,所有事務(wù)處理方法都是公共的。 因此,無需擔(dān)心非公開方法。 對于CGLIB代理,在創(chuàng)建子類時僅覆蓋公共方法。 因此,即使在這里,也不會考慮非公開方法。
讓我以一個問題結(jié)束這次討論。 當(dāng)我嘗試使用?tx:annotation-driven proxy-target-class =” true” /?代理目標(biāo)類時,它實際上不起作用,即未創(chuàng)建CGLIB代理 。 為此,我必須進(jìn)行一些小改動。 正如Spring文檔明確指出的那樣,如果在?tx:annotation驅(qū)動的?,?aop:config?或?aop:aspectj-autoproxy?中的任何一個上啟用了代理目標(biāo)類,Spring將在容器上啟用CGLIB代理創(chuàng)建。 因此,我剛剛創(chuàng)建了一個空的?aop:config proxy-target-class =“ true” /?。 不用擔(dān)心,它開始起作用! 不知道這是否是Spring本身的錯誤。 高度贊賞,如果有人可以回答這個問題。
翻譯自: https://www.javacodegeeks.com/2013/11/spring-transactions-visibility.html
總結(jié)
以上是生活随笔為你收集整理的Spring交易可见性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java:本地最小语言
- 下一篇: Spring安全–幕后