spring03

基于動(dòng)態(tài)代理改造上限案例

掌握Spring AOP 基于配置文件方式

掌握Spring AOP 基于注解方式

重點(diǎn)掌握:Spring AOP 基于XML和注解方式


全稱是Aspect Oriented Programming即:面向切面編程


1.1.2 AOP的作用及優(yōu)勢(shì)

作用:

在程序運(yùn)行期間七芭,不修改源碼對(duì)已有方法進(jìn)行增強(qiáng)。

優(yōu)勢(shì):

減少重復(fù)代碼

提高開發(fā)效率

維護(hù)方便



我們可以想辦法讓上面4個(gè)鏈接對(duì)象合并成一個(gè)鏈接對(duì)象,然后類似JDBC代碼一樣袖牙,手動(dòng)控制事務(wù)酒觅,實(shí)現(xiàn)讓業(yè)務(wù)層來控制事務(wù)的提交和回滾悯恍。



創(chuàng)建ConnectionUtil,用于實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接管理

public class ConnectionUtil {

? ? //線程本地變量

? ? private? ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

? ? private DataSource dataSource;

? ? //注入DataSource

? ? public void setDataSource(DataSource dataSource) {

? ? ? ? this.dataSource = dataSource;

? ? }

? ? /**

? ? * 獲取當(dāng)前線程上綁定的連接

? ? * @return

? ? */

? ? public Connection getThreadConnection() {

? ? ? ? try {

? ? ? ? ? ? //1.先看看線程上是否綁了

? ? ? ? ? ? Connection conn = tl.get();

? ? ? ? ? ? if(conn == null) {

? ? ? ? ? ? ? ? //2.從數(shù)據(jù)源中獲取一個(gè)連接

? ? ? ? ? ? ? ? conn = dataSource.getConnection();

? ? ? ? ? ? ? ? //3.和線程局部變量綁定

? ? ? ? ? ? ? ? tl.set(conn);

? ? ? ? ? ? }

? ? ? ? ? ? //4.返回線程上的連接

? ? ? ? ? ? return tl.get();

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? throw new RuntimeException(e);

? ? ? ? }

? ? }

? ? /**

? ? * 把連接和當(dāng)前線程解綁

? ? */

? ? public void remove() {

? ? ? ? tl.remove();

? ? }

}


創(chuàng)建TransactionManager奶段,實(shí)現(xiàn)事務(wù)的管理控制

public class TransactionManager {

? ? private ConnectionUtil connectionUtil;

? ? //數(shù)據(jù)庫(kù)連接管理注入

? ? public void setConnectionUtil(ConnectionUtil connectionUtil) {

? ? ? ? this.connectionUtil = connectionUtil;

? ? }

? ? //開啟事務(wù)

? ? public void beginTransaction() {

? ? ? ? //從當(dāng)前線程上獲取連接饥瓷,實(shí)現(xiàn)開啟事務(wù)

? ? ? ? try {

? ? ? ? ? ? connectionUtil.getThreadConnection().setAutoCommit(false);

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? //提交事務(wù)

? ? public void commit() {

? ? ? ? try {

? ? ? ? ? ? connectionUtil.getThreadConnection().commit();

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? //回滾事務(wù)

? ? public void rollback() {

? ? ? ? try {

? ? ? ? ? ? connectionUtil.getThreadConnection().rollback();

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? //釋放連接

? ? public void release() {

? ? ? ? try {

? ? ? ? ? ? //關(guān)閉連接(還回池中)

? ? ? ? ? ? connectionUtil.getThreadConnection().close();

? ? ? ? ? ? //解綁線程:把連接和線程解綁

? ? ? ? ? ? connectionUtil.remove();

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

}


public class AccountDaoImpl implements AccountDao {

? ? //注入進(jìn)來

? ? private QueryRunner runner;

? ? //注入數(shù)據(jù)庫(kù)連接對(duì)象

? ? private ConnectionUtil connectionUtil;

? ? //提供注入

? ? public void setRunner(QueryRunner runner) {

? ? ? ? this.runner = runner;

? ? }

? ? public void setConnectionUtil(ConnectionUtil connectionUtil) {

? ? ? ? this.connectionUtil = connectionUtil;

? ? }

? ? /**

? ? * 修改操作

? ? * @param account:賬號(hào)數(shù)據(jù)

? ? */

? ? @Override

? ? public void update(Account account) {

? ? ? ? try {

? ? ? ? ? ? runner.update(connectionUtil.getThreadConnection(),"update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? /***

? ? * 根據(jù)名字查找賬號(hào)信息

? ? * @param numberName:賬號(hào)名字

? ? * @return

? ? */

? ? @Override

? ? public Account findByName(String numberName) {

? ? ? ? try {

? ? ? ? ? ? return runner.query(connectionUtil.getThreadConnection(),"select * from account where name=?",new BeanHandler<Account>(Account.class),numberName);

? ? ? ? } catch (SQLException e) {

? ? ? ? ? ? throw new RuntimeException(e);

? ? ? ? }

? ? }

}


public class AccountServiceImpl implements AccountService {

? ? //注入AccountDao

? ? private AccountDao accountDao;

? ? //注入事務(wù)管理器

? ? private TransactionManager txManager;

? ? //提供注入

? ? public void setAccountDao(AccountDao accountDao) {

? ? ? ? this.accountDao = accountDao;

? ? }

? ? public void setTxManager(TransactionManager txManager) {

? ? ? ? this.txManager = txManager;

? ? }

? ? /***

? ? * 轉(zhuǎn)賬操作

? ? * @param sourceName:轉(zhuǎn)出賬戶名

? ? * @param targetName:轉(zhuǎn)入賬戶名

? ? * @param money:轉(zhuǎn)賬金額

? ? */

? ? @Override

? ? public void transfer(String sourceName, String targetName, Float money) {

? ? ? ? try {

? ? ? ? ? ? //開啟事務(wù)

? ? ? ? ? ? txManager.beginTransaction();

? ? ? ? ? ? //根據(jù)名稱查詢兩個(gè)賬戶信息

? ? ? ? ? ? Account source = accountDao.findByName(sourceName);

? ? ? ? ? ? Account target = accountDao.findByName(targetName);

? ? ? ? ? ? //轉(zhuǎn)出賬戶減錢,轉(zhuǎn)入賬戶加錢

? ? ? ? ? ? source.setMoney(source.getMoney()-money);

? ? ? ? ? ? target.setMoney(target.getMoney()+money);

? ? ? ? ? ? //更新兩個(gè)賬戶

? ? ? ? ? ? accountDao.update(source);

? ? ? ? ? ? accountDao.update(target);

? ? ? ? ? ? //提交事務(wù)

? ? ? ? ? ? txManager.commit();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? //事務(wù)回滾

? ? ? ? ? ? txManager.rollback();

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }finally {

? ? ? ? ? ? //關(guān)閉資源

? ? ? ? ? ? txManager.release();

? ? ? ? }

? ? }

}



<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

? ? ? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

? ? <!--

? ? ? ? Dao

? ? -->

? ? <bean id="accountDao" class="com.oppo.dao.impl.AccountDaoImpl">

? ? ? ? <!--注入QueryRunner對(duì)象? 必須有set方法-->

? ? ? ? <property name="runner" ref="runner" />

? ? ? ? <!--注入數(shù)據(jù)庫(kù)連接對(duì)象-->

? ? ? ? <property name="connectionUtil" ref="connectionUtil" />

? ? </bean>

? ? <!--

? ? ? ? Service

? ? -->

? ? <bean id="accountService" class="com.oppo.service.impl.AccountServiceImpl">

? ? ? ? <!--注入dao? 必須有set方法-->

? ? ? ? <property name="accountDao" ref="accountDao" />

? ? ? ? <!--注入事務(wù)管理器-->

? ? ? ? <property name="txManager" ref="txManager" />

? ? </bean>

? ? <!--

? ? ? ? QueryRunner對(duì)象

? ? -->

? ? <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">

? ? ? ? <!--帶參構(gòu)造函數(shù)注入-->

? ? ? ? <constructor-arg name="ds" ref="dataSource" />

? ? </bean>

? ? <!--

? ? ? ? 創(chuàng)建DataSource

? ? -->

? ? <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

? ? ? ? <property name="driverClass" value="com.mysql.jdbc.Driver" />

? ? ? ? <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/spring5" />

? ? ? ? <property name="user" value="root" />

? ? ? ? <property name="password" value="123456" />

? ? </bean>

? ? <!--數(shù)據(jù)庫(kù)連接對(duì)象-->

? ? <bean id="connectionUtil" class="com.oppo.util.ConnectionUtil">

? ? ? ? <property name="dataSource" ref="dataSource" />

? ? </bean>

? ? <!--事務(wù)管理器-->

? ? <bean id="txManager" class="com.oppo.util.TransactionManager">

? ? ? ? <property name="connectionUtil" ref="connectionUtil" />

? ? </bean>

</beans>



上一小節(jié)的代碼痹籍,通過對(duì)業(yè)務(wù)層改造呢铆,已經(jīng)可以實(shí)現(xiàn)事務(wù)控制了,但是由于我們添加了事務(wù)控制蹲缠,也產(chǎn)生了一個(gè)新的問題:

業(yè)務(wù)層方法變得臃腫了棺克,里面充斥著很多重復(fù)代碼。并且業(yè)務(wù)層方法和事務(wù)控制方法耦合了吼砂。

試想一下逆航,如果我們此時(shí)提交,回滾渔肩,釋放資源中任何一個(gè)方法名變更因俐,都需要修改業(yè)務(wù)層的代碼,況且這還只是一個(gè)業(yè)務(wù)層實(shí)現(xiàn)類周偎,而實(shí)際的項(xiàng)目中這種業(yè)務(wù)層實(shí)現(xiàn)類可能有十幾個(gè)甚至幾十個(gè)抹剩。


?動(dòng)態(tài)代理回顧

1.2.4.1 動(dòng)態(tài)代理的特點(diǎn)

字節(jié)碼隨用隨創(chuàng)建,隨用隨加載蓉坎。

它與靜態(tài)代理的區(qū)別也在于此澳眷。因?yàn)殪o態(tài)代理是字節(jié)碼一上來就創(chuàng)建好,并完成加載蛉艾。

裝飾者模式就是靜態(tài)代理的一種體現(xiàn)钳踊。


動(dòng)態(tài)代理常用的有兩種方式

基于接口的動(dòng)態(tài)代理? ? 提供者:JDK官方的Proxy類。? ? 要求:被代理類最少實(shí)現(xiàn)一個(gè)接口勿侯⊥氐桑基于子類的動(dòng)態(tài)代理? ? 提供者:第三方的CGLib,如果報(bào)asmxxxx異常助琐,需要導(dǎo)入asm.jar祭埂。? ? 要求:被代理類不能用final修飾的類(最終類)。

cglibcglib2.1_3



AOP相關(guān)術(shù)語

Joinpoint(連接點(diǎn)):

所謂連接點(diǎn)是指那些被攔截到的點(diǎn)兵钮。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn)蛆橡。

Pointcut(切入點(diǎn)):

所謂切入點(diǎn)是指我們要對(duì)哪些Joinpoint進(jìn)行攔截的定義。

Advice(通知/增強(qiáng)):

所謂通知是指攔截到Joinpoint之后所要做的事情就是通知掘譬。

通知的類型:前置通知,后置通知,異常通知,最終通知,環(huán)繞通知泰演。

Introduction(引介):

引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運(yùn)行期為類動(dòng)態(tài)地添加一些方法或Field。

Target(目標(biāo)對(duì)象):

代理的目標(biāo)對(duì)象屁药。

Weaving(織入):

是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來創(chuàng)建新的代理對(duì)象的過程粥血。

spring采用動(dòng)態(tài)代理織入柏锄,而AspectJ采用編譯期織入和類裝載期織入酿箭。

Proxy(代理):

一個(gè)類被AOP織入增強(qiáng)后复亏,就產(chǎn)生一個(gè)結(jié)果代理類。

Aspect(切面):

是切入點(diǎn)和通知(引介)的結(jié)合缭嫡。



a缔御、開發(fā)階段(我們做的)

編寫核心業(yè)務(wù)代碼(開發(fā)主線):大部分程序員來做,要求熟悉業(yè)務(wù)需求妇蛀。

把公用代碼抽取出來耕突,制作成通知。(開發(fā)階段最后再做):AOP編程人員來做评架。

在配置文件中眷茁,聲明切入點(diǎn)與通知間的關(guān)系,即切面纵诞。:AOP編程人員來做上祈。

b、運(yùn)行階段(Spring框架完成的)

Spring框架監(jiān)控切入點(diǎn)方法的執(zhí)行浙芙。一旦監(jiān)控到切入點(diǎn)方法被運(yùn)行登刺,使用代理機(jī)制,動(dòng)態(tài)創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象嗡呼,根據(jù)通知類別纸俭,在代理對(duì)象的對(duì)應(yīng)位置,將通知對(duì)應(yīng)的功能織入南窗,完成完整的代碼邏輯運(yùn)行揍很。


在spring中,框架會(huì)根據(jù)目標(biāo)類是否實(shí)現(xiàn)了接口來決定采用哪種動(dòng)態(tài)代理的方式万伤。


示例:我們?cè)趯W(xué)習(xí)spring的aop時(shí)窒悔,采用輸出日志作為示例。在業(yè)務(wù)層方法執(zhí)行的前后壕翩,加入日志的輸出蛉迹。并且把spring的ioc也一起應(yīng)用進(jìn)來。



<dependencies>

? <dependency>

? <groupId>org.springframework</groupId>

? <artifactId>spring-context</artifactId>

? <version>5.0.2.RELEASE</version>

? </dependency>

? <dependency>

? <groupId>org.aspectj</groupId>

? <artifactId>aspectjweaver</artifactId>

? <version>1.8.7</version>

? </dependency>

? </dependencies>



<!--

aop的配置步驟:

第一步:把通知類的創(chuàng)建也交給spring來管理

第二步:使用aop:config標(biāo)簽開始aop的配置

第三步:使用aop:aspect標(biāo)簽開始配置切面放妈,寫在aop:config標(biāo)簽內(nèi)部

id屬性:給切面提供一個(gè)唯一標(biāo)識(shí)

ref屬性:用于引用通知bean的id北救。

第四步:使用對(duì)應(yīng)的標(biāo)簽在aop:aspect標(biāo)簽內(nèi)部配置通知的類型

使用aop:befored標(biāo)簽配置前置通知,寫在aop:aspect標(biāo)簽內(nèi)部

method屬性:用于指定通知類中哪個(gè)方法是前置通知

pointcut屬性:用于指定切入點(diǎn)表達(dá)式芜抒。

切入點(diǎn)表達(dá)式寫法:

關(guān)鍵字:execution(表達(dá)式)

表達(dá)式內(nèi)容:

全匹配標(biāo)準(zhǔn)寫法:

訪問修飾符? 返回值? 包名.包名.包名...類名.方法名(參數(shù)列表)

例如:

public void com.oppo.service.impl.AccountServiceImpl.saveAccount()

-->

<!-- 配置通知類 -->

<bean id="logger" class="com.oppo.utils.Logger"></bean>

<!-- 配置aop -->

<aop:config>

<!-- 配置切面 -->

<aop:aspect id="logAdvice" ref="logger">

<!-- 配置前置通知 -->

<aop:before method="printLog" pointcut="execution( * com.oppo.service.impl.*.*(..))"/>

</aop:aspect>

</aop:config>


<!-- 配置通知類 -->

<bean id="logger" class="com.oppo.utils.Logger"></bean>

<!-- 配置aop -->

<aop:config>

<!-- 配置切面 -->

<aop:aspect id="logAdvice" ref="logger">

<!-- 配置前置通知 -->

<aop:before method="printLog" pointcut="execution( * com.oppo.service.impl.*.*(..))"/>

</aop:aspect>

</aop:config>

execution:匹配方法的執(zhí)行(常用)

execution(表達(dá)式)

表達(dá)式語法:execution([修飾符] 返回值類型 包名.類名.方法名(參數(shù)))

寫法說明:

全匹配方式:

public void com.oppo.service.impl.AccountServiceImpl.saveAccount(com.oppo.domain.Account)

訪問修飾符可以省略

void com.oppo.service.impl.AccountServiceImpl.saveAccount(com.oppo.domain.Account)

返回值可以使用*號(hào)珍策,表示任意返回值

* com.oppo.service.impl.AccountServiceImpl.saveAccount(com.oppo.domain.Account)

包名可以使用*號(hào),表示任意包宅倒,但是有幾級(jí)包攘宙,需要寫幾個(gè)*

* *.*.*.*.AccountServiceImpl.saveAccount(com.oppo.domain.Account)

使用..來表示當(dāng)前包,及其子包

* com..AccountServiceImpl.saveAccount(com.oppo.domain.Account)

類名可以使用*號(hào),表示任意類

* com..*.saveAccount(com.oppo.domain.Account)

方法名可以使用*號(hào)蹭劈,表示任意方法

* com..*.*( com.oppo.domain.Account)

參數(shù)列表可以使用*疗绣,表示參數(shù)可以是任意數(shù)據(jù)類型,但是必須有參數(shù)

* com..*.*(*)

參數(shù)列表可以使用..表示有無參數(shù)均可铺韧,有參數(shù)可以是任意類型

* com..*.*(..)

全通配方式:

* *..*.*(..)

注:

通常情況下多矮,我們都是對(duì)業(yè)務(wù)層的方法進(jìn)行增強(qiáng),所以切入點(diǎn)表達(dá)式都是切到業(yè)務(wù)層實(shí)現(xiàn)類哈打。

execution(* com.oppo.service.impl.*.*(..))



aop:config:

? ? 作用:用于聲明開始aop的配置

<aop:config>

? ? <!-- 配置的代碼都寫在此處 -->? ?

</aop:config>


aop:aspect:

? ? 作用:

? ? ? 用于配置切面塔逃。

? ? 屬性:

? ? ? id:給切面提供一個(gè)唯一標(biāo)識(shí)。

? ? ? ref:引用配置好的通知類bean的id料仗。

<aop:aspect id="logAdvice" ref="logger">

? ? ? <!--配置通知的類型要寫在此處-->

</aop:aspect>



aop:aspect:

? ? 作用:

? ? ? 用于配置切面湾盗。

? ? 屬性:

? ? ? id:給切面提供一個(gè)唯一標(biāo)識(shí)。

? ? ? ref:引用配置好的通知類bean的id立轧。

<aop:aspect id="logAdvice" ref="logger">

? ? ? <!--配置通知的類型要寫在此處-->

</aop:aspect>

aop:pointcut:

作用:

用于配置切入點(diǎn)表達(dá)式格粪。就是指定對(duì)哪些類的哪些方法進(jìn)行增強(qiáng)。

屬性:

expression:用于定義切入點(diǎn)表達(dá)式肺孵。

id:用于給切入點(diǎn)表達(dá)式提供一個(gè)唯一標(biāo)識(shí)

<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>




aop:before

作用:

用于配置前置通知匀借。指定增強(qiáng)的方法在切入點(diǎn)方法之前執(zhí)行

屬性:

method:用于指定通知類中的增強(qiáng)方法名稱

ponitcut-ref:用于指定切入點(diǎn)的表達(dá)式的引用

poinitcut:用于指定切入點(diǎn)表達(dá)式

執(zhí)行時(shí)間點(diǎn):

切入點(diǎn)方法執(zhí)行之前執(zhí)行

<aop:before method="beginPrintLog" pointcut-ref="pt1"/>

aop:after-returning

作用:

用于配置后置通知

屬性:

method:指定通知中方法的名稱。

pointct:定義切入點(diǎn)表達(dá)式

pointcut-ref:指定切入點(diǎn)表達(dá)式的引用

執(zhí)行時(shí)間點(diǎn):

切入點(diǎn)方法正常執(zhí)行之后平窘。它和異常通知只能有一個(gè)執(zhí)行

<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>

aop:after-throwing

作用:

用于配置異常通知

屬性:

method:指定通知中方法的名稱吓肋。

pointct:定義切入點(diǎn)表達(dá)式

pointcut-ref:指定切入點(diǎn)表達(dá)式的引用

執(zhí)行時(shí)間點(diǎn):

切入點(diǎn)方法執(zhí)行產(chǎn)生異常后執(zhí)行。它和后置通知只能執(zhí)行一個(gè)

<aop:after-throwing method="afterThrowingPringLog" pointcut-ref="pt1"/>

aop:after

作用:

用于配置最終通知

屬性:

method:指定通知中方法的名稱瑰艘。

pointct:定義切入點(diǎn)表達(dá)式

pointcut-ref:指定切入點(diǎn)表達(dá)式的引用

執(zhí)行時(shí)間點(diǎn):

無論切入點(diǎn)方法執(zhí)行時(shí)是否有異常是鬼,它都會(huì)在其后面執(zhí)行。

<aop:after method="afterPringLog" pointcut-ref="pt1"/>




環(huán)繞通知

配置方式:

<aop:config>

<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>

<aop:aspect id="txAdvice" ref="txManager">

<!-- 配置環(huán)繞通知 -->

<aop:around method="transactionAround" pointcut-ref="pt1"/>

</aop:aspect>

</aop:config>

aop:around:

作用:

用于配置環(huán)繞通知

屬性:

method:指定通知中方法的名稱紫新。

pointct:定義切入點(diǎn)表達(dá)式

pointcut-ref:指定切入點(diǎn)表達(dá)式的引用

說明:

它是spring框架為我們提供的一種可以在代碼中手動(dòng)控制增強(qiáng)代碼什么時(shí)候執(zhí)行的方式均蜜。

注意:

通常情況下,環(huán)繞通知都是獨(dú)立使用的

/**

* 環(huán)繞通知

* 問題:

* 當(dāng)配置完環(huán)繞通知之后芒率,沒有業(yè)務(wù)層方法執(zhí)行(切入點(diǎn)方法執(zhí)行)

* 分析:

*? 通過動(dòng)態(tài)代理的代碼分析囤耳,我們現(xiàn)在的環(huán)繞通知沒有明確的切入點(diǎn)方法調(diào)用

* 解決:

* spring框架為我們提供了一個(gè)接口,該接口可以作為環(huán)繞通知的方法參數(shù)來使用

* ProceedingJoinPoint偶芍。當(dāng)環(huán)繞通知執(zhí)行時(shí)充择,spring框架會(huì)為我們注入該接口的實(shí)現(xiàn)類。

*? 它有一個(gè)方法proceed()匪蟀,就相當(dāng)于invoke椎麦,明確的業(yè)務(wù)層方法調(diào)用

*?

*? spring的環(huán)繞通知:

*? 它是spring為我們提供的一種可以在代碼中手動(dòng)控制增強(qiáng)方法何時(shí)執(zhí)行的方式。

*/

public void aroundPrintLog(ProceedingJoinPoint pjp) {

try {

System.out.println("前置Logger類中的aroundPrintLog方法開始記錄日志了");

pjp.proceed();//明確的方法調(diào)用

System.out.println("后置Logger類中的aroundPrintLog方法開始記錄日志了");

} catch (Throwable e) {

System.out.println("異常Logger類中的aroundPrintLog方法開始記錄日志了");

e.printStackTrace();

}finally {

System.out.println("最終Logger類中的aroundPrintLog方法開始記錄日志了");

}

}




基于注解的AOP配置


把通知類也使用注解配置

/**

* 模擬一個(gè)用于記錄日志的工具類

*/

@Component("logger")

public class Logger {

}


作用:

把當(dāng)前類聲明為切面類材彪。

/**

* 模擬一個(gè)用于記錄日志的工具類

*/

@Component("logger")

@Aspect//表示當(dāng)前類是一個(gè)切面類

public class Logger {}



使用注解配置通知類型

@Before

作用:

把當(dāng)前方法看成是前置通知观挎。

屬性:

value:用于指定切入點(diǎn)表達(dá)式琴儿,還可以指定切入點(diǎn)表達(dá)式的引用。

/**

* 前置通知

*/

@Before("execution(* com.oppo.service.impl.*.*(..))")

public void beforePrintLog() {

System.out.println("前置通知:Logger類中的beforePrintLog方法開始記錄日志了嘁捷。造成。。");

}

@AfterReturning

作用:

把當(dāng)前方法看成是后置通知普气。

屬性:

value:用于指定切入點(diǎn)表達(dá)式谜疤,還可以指定切入點(diǎn)表達(dá)式的引用

/**

* 后置通知

*/

@AfterReturning("execution(* com.oppo.service.impl.*.*(..))")

public void afterReturningPrintLog() {

System.out.println("后置通知:Logger類中的afterReturningPrintLog方法開始記錄日志了佃延。现诀。。");

}

@AfterThrowing

作用:

把當(dāng)前方法看成是異常通知履肃。

屬性:

value:用于指定切入點(diǎn)表達(dá)式仔沿,還可以指定切入點(diǎn)表達(dá)式的引用

/**

* 異常通知

*/

@AfterThrowing("execution(* com.oppo.service.impl.*.*(..))")

public void afterThrowingPrintLog() {

System.out.println("異常通知:Logger類中的afterThrowingPrintLog方法開始記錄日志了。尺棋。封锉。");

}

@After

作用:

把當(dāng)前方法看成是最終通知。

屬性:

value:用于指定切入點(diǎn)表達(dá)式膘螟,還可以指定切入點(diǎn)表達(dá)式的引用

/**

* 最終通知

*/

@After("execution(* com.oppo.service.impl.*.*(..))")

public void afterPrintLog() {

System.out.println("最終通知:Logger類中的afterPrintLog方法開始記錄日志了成福。。荆残。");

}

2.3.7 第四步:在spring配置文件中開啟spring對(duì)注解AOP的支持

<!-- 開啟spring對(duì)注解AOP的支持 -->

<aop:aspectj-autoproxy/>

2.3.8 環(huán)繞通知注解配置

@Around

作用:

把當(dāng)前方法看成是環(huán)繞通知奴艾。

屬性:

value:用于指定切入點(diǎn)表達(dá)式,還可以指定切入點(diǎn)表達(dá)式的引用内斯。

/**

* 環(huán)繞通知

* 問題:

* 當(dāng)配置完環(huán)繞通知之后蕴潦,沒有業(yè)務(wù)層方法執(zhí)行(切入點(diǎn)方法執(zhí)行)

* 分析:

*? 通過動(dòng)態(tài)代理的代碼分析,我們現(xiàn)在的環(huán)繞通知沒有明確的切入點(diǎn)方法調(diào)用

* 解決:

* spring框架為我們提供了一個(gè)接口俘闯,該接口可以作為環(huán)繞通知的方法參數(shù)來使用

* ProceedingJoinPoint潭苞。當(dāng)環(huán)繞通知執(zhí)行時(shí),spring框架會(huì)為我們注入該接口的實(shí)現(xiàn)類真朗。

*? 它有一個(gè)方法proceed()此疹,就相當(dāng)于invoke,明確的業(yè)務(wù)層方法調(diào)用

*?

*? spring的環(huán)繞通知:

*? 它是spring為我們提供的一種可以在代碼中手動(dòng)控制增強(qiáng)方法何時(shí)執(zhí)行的方式遮婶。

*/

@Around("execution(* com.oppo.service.impl.*.*(..))")

public void aroundPrintLog(ProceedingJoinPoint pjp) {

try {

System.out.println("前置Logger類中的aroundPrintLog方法開始記錄日志了");

pjp.proceed();//明確的方法調(diào)用

System.out.println("后置Logger類中的aroundPrintLog方法開始記錄日志了");

} catch (Throwable e) {

System.out.println("異常Logger類中的aroundPrintLog方法開始記錄日志了");

e.printStackTrace();

}finally {

System.out.println("最終Logger類中的aroundPrintLog方法開始記錄日志了");

}

}

2.3.9 切入點(diǎn)表達(dá)式注解

@Pointcut

作用:

指定切入點(diǎn)表達(dá)式

屬性:

value:指定表達(dá)式的內(nèi)容

@Pointcut("execution(* com.oppo.service.impl.*.*(..))")

private void pt1() {}

引用方式:

/**

* 環(huán)繞通知

* @param pjp

* @return

*/

@Around("pt1()")//注意:千萬別忘了寫括號(hào)

public void aroundPrintLog(ProceedingJoinPoint pjp) {

}

2.3.10 不使用XML的配置方式

@Configuration

@ComponentScan(basePackages="com.oppo")

@EnableAspectJAutoProxy

public class SpringConfiguration {

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝗碎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蹭睡,更是在濱河造成了極大的恐慌衍菱,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肩豁,死亡現(xiàn)場(chǎng)離奇詭異脊串,居然都是意外死亡辫呻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門琼锋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來放闺,“玉大人,你說我怎么就攤上這事缕坎〔勒欤” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵谜叹,是天一觀的道長(zhǎng)匾寝。 經(jīng)常有香客問我,道長(zhǎng)荷腊,這世上最難降的妖魔是什么艳悔? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮女仰,結(jié)果婚禮上猜年,老公的妹妹穿的比我還像新娘。我一直安慰自己疾忍,他們只是感情好乔外,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著一罩,像睡著了一般杨幼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擒抛,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天推汽,我揣著相機(jī)與錄音,去河邊找鬼歧沪。 笑死歹撒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诊胞。 我是一名探鬼主播暖夭,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼撵孤!你這毒婦竟也來了迈着?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤邪码,失蹤者是張志新(化名)和其女友劉穎裕菠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闭专,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奴潘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年旧烧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片画髓。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掘剪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奈虾,到底是詐尸還是另有隱情夺谁,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布肉微,位于F島的核電站匾鸥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浪册。R本人自食惡果不足惜扫腺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望村象。 院中可真熱鬧,春花似錦攒至、人聲如沸厚者。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)库菲。三九已至,卻和暖如春志膀,著一層夾襖步出監(jiān)牢的瞬間熙宇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工溉浙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烫止,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓戳稽,卻偏偏與公主長(zhǎng)得像馆蠕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惊奇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文將為各位帶來 Spring 的另一個(gè)重點(diǎn)知識(shí)點(diǎn) —— Spring AOP互躬。關(guān)注我的公眾號(hào)「Java面典」,每...
    Java面典閱讀 406評(píng)論 0 2
  • 1. AOP 1.1 AOP介紹 1.1.1 什么是AOP 在軟件業(yè)颂郎,AOP為Aspect Oriented Pr...
    itzhouq的筆記閱讀 164評(píng)論 0 3
  • Spring AOP 模塊吼渡,是 Spring 框架體系結(jié)構(gòu)中十分重要的內(nèi)容,該模塊中提供了面向切面編程實(shí)現(xiàn) 乓序。本章...
    遼A丶孫悟空閱讀 1,140評(píng)論 2 14
  • 一寺酪、AOP 簡(jiǎn)介 AOP(Aspect-Oriented Programming, 面向切面編程): 是一種新的方...
    leeqico閱讀 798評(píng)論 0 1
  • 六房维、基于@Aspect注解編程(重點(diǎn)) 1沼瘫、說明 Spring 使用了和AspectJ 一樣的注解并使用Aspec...
    唯老閱讀 565評(píng)論 0 1