Spring系列教程六:AOP詳細講解
阿新 • • 發佈:2019-05-20
AOP 概述
什麼是 AOP
AOP:全稱是 Aspect Oriented Programming 即:面向切面程式設計。
AOP技術是對OOP技術的一種延伸,AOP是面向縱向,OOP是面向橫向。簡單的說它就是把我們程式重複的程式碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改原始碼的
基礎上,對我們的已有方法進行增強。
AOP 的作用以及優勢
作用:在程式執行期間,不修改原始碼對已有方法前面和後面進行增強。
優勢:減少重複程式碼、提高開發效率、維護方便
AOP實現方式
使用動態代理技術
基於xml方式的aop配置
實現步驟如下
匯入spring-aop jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.6.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> 下面幾個是必須要導的包 <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency>
需要增強方法的StudentDao類,和寫增強方法的Userlogger類
package com.ithema.jdbc.dao; public class StudentDao { //在執行add方法之前執行日誌 public int add(int a,int b){ System.out.println("執行了add方法"); return a+b; } public int jian(int a,int b){ System.out.println("執行了減法"); return a-b; } /* public void testexe(){ int i=10/0; }*/ }
package com.ithema.jdbc.aop; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import java.util.Arrays; /** * 切面: * 前置增強、後置增強、異常增強、最終增強、環繞增強 * * Spring aop完全是用動態代理實現的 * 但是動態代理又有兩種,jdkk代理,cglib代理(更強大一些) * spirng aop:預設使用jdk代理,但是你的目標類沒介面,會自動切換到cglib代理 */ public class UserLogger { //在目標方法呼叫之前執行該方法 Logger logger=Logger.getLogger(UserLogger.class); public void before(JoinPoint jp){ logger.info("前置增強,目標類名"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+ "引數為:"+ Arrays.toString(jp.getArgs())); } //在目標方法呼叫之後執行該方法 //當方法丟擲異常就不會執行了,所有該方法不適合做日誌收集,因為發生了異常不執行該方法無法將日誌儲存下來 //所有後置增強用的很少 public void after(JoinPoint jp,Object result){ logger.info("後置增強,執行完類"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+ "方法,結果為:"+ result); } //在方法丟擲異常後就會增強、沒有異常就不會執行、如果使用者try-catch,也不會增強 //執行錯誤的話,非常適合該增強(異常增強) //Java異常體系,根類是Throwable public void afterThorwing(JoinPoint jp,Throwable e){ logger.info("異常增強,執行類"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+ "方法丟擲異常:"+e.getMessage()); } //最終增強:無論是否有異常,都會執行,類似與finally,可以或者是認為是後置增強的升級版 public void finalafter(JoinPoint jp){ logger.info("最終增強:執行完類"+jp.getTarget()+"裡面的"+jp.getSignature().getName()); } //環繞增強,集合了前置新增後置,並且更加強大、當執行方法發生異常後面的就不執行了 //1、可以修改方法的引數 2、也可以修改方法的返回值( return result) public Object around(ProceedingJoinPoint jp)throws Throwable{ Object [] args=jp.getArgs();//獲取方法引數 //可以修改方法的引數 //args = new Object[]{20,1}; logger.info("環繞增強:方法為:"+jp.getArgs()); Object result=jp.proceed();//執行目標方法 //也可以修改方法的返回值 //return 100; return result; } }
配置aop約束到配置檔案中,並實現ioc配置
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--一個bean就是一個物件>
<bean id="studentDao" class="com.ithema.jdbc.dao.StudentDao">
</bean>
<bean id="userLogger" class="com.ithema.jdbc.aop.UserLogger">
</bean>
使用 aop:aspect 配置切面,使用 aop:pointcut 配置切入點表示式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="studentDao" class="com.ithema.jdbc.dao.StudentDao">
</bean>
<bean id="userLogger" class="com.ithema.jdbc.aop.UserLogger">
</bean>
<!--aop配置,實現dao裡面的方法前後增強-->
<aop:config>
<!--定義切入點-->
<!--標準寫法格式:(返回值:全類名(包名.類名.方法名(引數型別列表))-->
<aop:pointcut id="pointcut" expression="execution(* com.ithema.jdbc..*.*(..))"></aop:pointcut>
<!--組織增強-->
<!--切面ref:應用其他bean物件-->
<aop:aspect ref="userLogger">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="after" pointcut-ref="pointcut" returning="result"></aop:after-returning>
<aop:after-throwing method="afterThorwing" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
<aop:after method="finalafter" pointcut-ref="pointcut"></aop:after>
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
基於註解方式的aop配置
匯入spring-aop jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
下面幾個是必須要導的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
配置aop約束到配置檔案中,在配置檔案中指定 spring 要掃描的包,開啟 spring 對註解 AOP 的支援
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--告訴Spring容器在建立的時候要掃描包,配置所需要的標籤不在bean約束中,而是
在一個叫做context的空降名稱和約束中-->
<context:component-scan base-package="com.bdqn"></context:component-scan>
<!-- 開啟註解aop-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
使用註解配置增強方法
package com.bdqn.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
public class UserLogger {
//在目標方法呼叫之前執行該方法
Logger logger=Logger.getLogger(UserLogger.class);
//定義切點
@Pointcut("execution(* com.bdqn.aop..*.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint jp){
logger.info("前置增強,目標類名"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+
"引數為:"+ Arrays.toString(jp.getArgs()));
}
//在目標方法呼叫之後執行該方法
//當方法丟擲異常就不會執行了,所有該方法不適合做日誌收集,因為發生了異常不執行該方法無法將日誌儲存下來
//所有後置增強用的很少
public void after(JoinPoint jp,Object result){
logger.info("後置增強,執行完類"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+
"方法,結果為:"+ result);
}
//在方法丟擲異常後就會增強、沒有異常就不會執行、如果使用者try-catch,也不會增強
//執行錯誤的話,非常適合該增強(異常增強)
//Java異常體系,根類是Throwable
@AfterThrowing(pointcut = "execution(* com.bdqn.aop..*.*(..))",throwing = "e")
public void afterThorwing(JoinPoint jp,Throwable e){
logger.info("異常增強,執行類"+jp.getTarget()+"裡面的"+jp.getSignature().getName()+
"方法丟擲異常:"+e.getMessage());
}
//最終增強:無論是否有異常,都會執行,類似與finally,可以或者是認為是後置增強的升級版
@After("execution(* com.bdqn.aop..*.*(..))")
public void finalafter(JoinPoint jp){
logger.info("最終增強:執行完類"+jp.getTarget()+"裡面的"+jp.getSignature().getName());
}
//環繞增強,集合了前置新增後置,並且更加強大、當執行方法發生異常後面的就不執行了
//1、可以修改方法的引數 2、也可以修改方法的返回值( return result)
@Around("execution(* com.bdqn.aop..*.*(..))")
public Object around(ProceedingJoinPoint jp)throws Throwable{
Object [] args=jp.getArgs();//獲取方法引數
//可以修改方法的引數
//args = new Object[]{20,1};
logger.info("環繞增強:方法為:"+jp.getArgs());
Object result=jp.proceed();//執行目標方法
//也可以修改方法的返回值
//return 100;
return result;
}
}
使用spring整合junit4的方式寫測試類
package com.bdqn.test;
import com.bdqn.dao.StudentDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:ApplicationContext.xml")
public class Testaop {
@Autowired
private StudentDao studentDao;
@Test
public void testaop(){
studentDao.add();
studentDao.jian(3,1);
System.out.println("執行了減法,結果為:");
}
}