淺談JDK動態代理<3轉載>

熟悉的陌生人

面試官若是問「請你談談你對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對象

注入的是UserServiceImpl類型編程

  • 注入userServiceImpl的代理對象(CGLib動態代理)

注入的是CGLib動態代理生成的userServiceImpl的代理對象ide

爲何兩次注入的對象不一樣?模塊化

由於第二次我給UserServiceImpl加了@Transactional 註解。工具

此時Spring讀取到這個註解,便知道咱們要使用事務。而咱們編寫的UserService類中並無包含任何事務相關的代碼。若是給你,你會怎麼作?動態代理嘛!

可是要用動態代理完成事務管理,還須要本身編寫一個通知類,並把通知對象傳入代理對象,通知負責事務的開啓和提交,並在代理對象內部調用目標對象同名方法完成業務功能。

咱們能想到的方案,Spring確定也知道。一樣地,Spring爲了實現事務,也編寫了一個通知類,TransactionManager。利用動態代理建立代理對象時,Spring會把transactionManager織入代理對象,而後將代理對象注入到UserController。

因此咱們在UserController中使用的userService實際上是代理對象,而代理對象才支持事務。


山寨AOP事務需求分析

瞭解了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提供的代理對象

這就是事務的實現原理。


AOP事務具體代碼實現

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註解,獲得代理對象:

相關文章
相關標籤/搜索