面試官若是問「請你談談你對Spring的理解」,估計不少人會脫口而出:IOC和AOP。IOC大概是你們對Spring最直接的印象,就是個大容器,裝了不少bean,還會幫你作依賴注入。java
IOCmysql
可是對於AOP,不少人其實沒有太多概念,一時不知道Spring哪裏用了AOP。好像事務用了切面,但具體又不瞭解。這樣吧,我問你一個問題,我本身寫了一個UserController,以及UserServiceImpl implements UserService,而且在UserController中注入Service層對象:面試
@Autowired private UserService userService;
那麼,這個userService必定是咱們寫的UserServiceImpl的實例嗎?sql
若是你聽不懂我要問什麼,說明你自己對Spring的瞭解仍是太侷限於IOC。數據庫
實際上,Spring依賴注入的對象並不必定是咱們本身寫的類的實例,也多是userServiceImpl的代理對象。下面分別演示這兩種狀況:apache
注入的是UserServiceImpl類型編程
注入的是CGLib動態代理生成的userServiceImpl的代理對象ide
爲何兩次注入的對象不一樣?模塊化
由於第二次我給UserServiceImpl加了@Transactional 註解。工具
此時Spring讀取到這個註解,便知道咱們要使用事務。而咱們編寫的UserService類中並無包含任何事務相關的代碼。若是給你,你會怎麼作?動態代理嘛!
可是要用動態代理完成事務管理,還須要本身編寫一個通知類,並把通知對象傳入代理對象,通知負責事務的開啓和提交,並在代理對象內部調用目標對象同名方法完成業務功能。
咱們能想到的方案,Spring確定也知道。一樣地,Spring爲了實現事務,也編寫了一個通知類,TransactionManager。利用動態代理建立代理對象時,Spring會把transactionManager織入代理對象,而後將代理對象注入到UserController。
因此咱們在UserController中使用的userService實際上是代理對象,而代理對象才支持事務。
瞭解了Spring事務的大體流程後,咱們再來分析一下本身如何編寫一個山寨的AOP事務。
AOP事務,有兩個概念:AOP和事務。
事務,你們已經很熟悉,這裏主要講講什麼是AOP。AOP,它是Aspect-Oriented Programming(面向切面編程)的英文縮寫。什麼是面向切面編程?有時直接介紹一個東西是什麼,可能比較難。可是一說到它是幹嗎的,你們就當即心照不宣了。
咱們的系統中,經常存在交叉業務,好比事務、日誌等。UserService的method1要用到它,BrandService的method2也要用到它。一個交叉業務就是要切入系統的一個方面。具體用代碼展現就是:
這個切面,能夠是日誌,也能夠是事務
交叉業務的編程問題即爲面向切面編程。AOP的目標就是使交叉業務模塊化。能夠將切面代碼移動到原始方法的周圍:
原先不用AOP時,交叉業務直接寫在方法內部的先後,用了AOP交叉業務寫在方法調用先後。這與AOP的底層實現方式有關:動態代理其實就是代理對象調用目標對象的同名方法,並在調用先後加加強代碼。不過這兩種最終運行效果是同樣的。
而所謂的模塊化,我我的的理解是將切面代碼作成一個可管理的狀態。好比日誌打印,再也不是直接硬編碼在方法中的零散語句,而是作成一個通知類,經過通知去執行切面代碼。
因此,如今需求已經很明確,咱們須要一個通知類(TransactionManager)執行事務,一個代理工廠幫助生成代理對象,而後利用動態代理將事務代碼織入代理對象的各個方法中。
就比如下面三個Service,原先是沒有開啓事務的:
咱們但願最終達到的效果是,我加了個@MyTransactional後,代理工廠給我返回一個代理對象:
代理工廠使用動態代理,爲每個目標對象建立一個代理對象
細節分析:
txManager實際上是在目標對象test()方法的先後執行事務,而不是方法內部的先後
也就是說,代理對象方法 = 事務 + 目標對象方法。
另外,還有個棘手的問題:事務操做,必須使用同一個Connection對象。如何保證?第一次從數據源獲取Connection對象並開啓事務後,將它存入當前線程的ThreadLocal中,等到了DAO層,仍是從ThreadLocal中取,這樣就能保證開啓事務和操做數據庫使用的Connection對象是同一個。
開啓事務後,Controller並非直接調用咱們本身寫的Service,而是Spring提供的代理對象
這就是事務的實現原理。
ConnectionUtils工具類
package com.demo.myaopframework.utils; import org.apache.commons.dbcp.BasicDataSource; import java.sql.Connection; /** * 鏈接的工具類,它用於從數據源中獲取一個鏈接,而且實現和線程的綁定 */ public class ConnectionUtils { private ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private static BasicDataSource dataSource = new BasicDataSource(); //靜態代碼塊,設置鏈接數據庫的參數 static{ dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("123456"); } /** * 獲取當前線程上的鏈接 * @return */ public Connection getThreadConnection() { try{ //1.先從ThreadLocal上獲取 Connection conn = tl.get(); //2.判斷當前線程上是否有鏈接 if (conn == null) { //3.從數據源中獲取一個鏈接,而且存入ThreadLocal中 conn = dataSource.getConnection(); tl.set(conn); } //4.返回當前線程上的鏈接 return conn; }catch (Exception e){ throw new RuntimeException(e); } } /** * 把鏈接和線程解綁 */ public void removeConnection(){ tl.remove(); } }
AOP通知(事務管理器)
package com.demo.myaopframework.utils; /** * 和事務管理相關的工具類,它包含了,開啓事務,提交事務,回滾事務和釋放鏈接 */ public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * 開啓事務 */ public void beginTransaction(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); }catch (Exception e){ e.printStackTrace(); } } /** * 提交事務 */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); }catch (Exception e){ e.printStackTrace(); } } /** * 回滾事務 */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); }catch (Exception e){ e.printStackTrace(); } } /** * 釋放鏈接 */ public void release(){ try { connectionUtils.getThreadConnection().close();//還回鏈接池中 connectionUtils.removeConnection(); }catch (Exception e){ e.printStackTrace(); } } }
自定義註解
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyTransactional { }
Service
public interface UserService { void getUser(); } public class UserServiceImpl implements UserService { @Override public void getUser() { System.out.println("service執行..."); } }
實例工廠
public class BeanFactory { public Object getBean(String name) throws Exception { //獲得目標類的Class對象 Class<?> clazz = Class.forName(name); //獲得目標對象 Object bean = clazz.newInstance(); //獲得目標類上的@MyTransactional註解 MyTransactional myTransactional = clazz.getAnnotation(MyTransactional.class); //若是打了@MyTransactional註解,返回代理對象,不然返回目標對象 if (null != myTransactional) { ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); TransactionManager txManager = new TransactionManager(); txManager.setConnectionUtils(new ConnectionUtils()); //裝配通知和目標對象 proxyFactoryBean.setTxManager(txManager); proxyFactoryBean.setTarget(bean); Object proxyBean = proxyFactoryBean.getProxy(); //返回代理對象 return proxyBean; } //返回目標對象 return bean; } }
代理工廠
public class ProxyFactoryBean { //通知 private TransactionManager txManager; //目標對象 private Object target; public void setTxManager(TransactionManager txManager) { this.txManager = txManager; } public void setTarget(Object target) { this.target = target; } //傳入目標對象target,爲它裝配好通知,返回代理對象 public Object getProxy() { Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(),/*1.類加載器*/ target.getClass().getInterfaces(), /*2.目標對象實現的接口*/ new InvocationHandler() {/*3.InvocationHandler*/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //1.開啓事務 txManager.beginTransaction(); //2.執行操做 Object retVal = method.invoke(target, args); //3.提交事務 txManager.commit(); //4.返回結果 return retVal; } catch (Exception e) { //5.回滾事務 txManager.rollback(); throw new RuntimeException(e); } finally { //6.釋放鏈接 txManager.release(); } } } ); return proxy; } }
代碼結構
獲得普通UserService:
給UserServiceImpl添加@MyTransactional註解,獲得代理對象: