Spring入門—SpringAop
一、springAop術語
springAop:面向切面編程。可插拔式的。即各個方法模塊獨立。需要就添加配置,不需要時就刪除配置。
- 切面(Aspect):橫切關注點被模塊化的特殊對象,即要插入的功能。
- 通知(Advice):店面必須要完成的工作,所謂通知是指攔截到joinpoint之後要做的事情。
- 通知分爲:前置通知、後置通知、異常通知、最終通知、和環繞通知。
- 目標(target):被通知的對象。
- 代理(Proxy):向目標對象應用通知之後創建的對象。
- 連接點(JoinPoint):程序執行的某個特定位置。eg:類的某個方法運行前、調用後、方法拋出異常後等。
- 連接點有兩個信息要確定,一個是方法(程序執行點),即執行什麼樣的方法時需要連接。
- 另一個就是方位(程序相對點),即在什麼時候連接,是方法執行前、執行後還是拋出異常後等。
- 切點(PointCut):每個類都擁有多個連接點,那麼切點就是用來定位到特殊連接點的。
- 類比:連接點相當於數據庫中的記錄,而切點相當於查詢條件。
- 織入(Weaving):是指把切面應用到目標對象來創建新的代理對象的過程,切面怎麼指定的連接點織入到目標對象。
- 引入(Introduction):在不修改代碼的前提下,Introduction可以在運行期爲類動態的添加一些方法或Filed。
二、springAop快速入門—案例(這裏使用註解方案)
1. 導入相關依賴包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2. 編寫計算器接口(Calculator.java)
public interface Calculator {
public int plus(int x, int y, int z);
public double plus(double x, double y);
public int plus(int x, int y);
public int substract(int x, int y);
public int division(int x,int y);
public int multiplication(int x,int y);
}
3. 編寫計算器類繼承上面的接口(HxCalculator.java)
@Component //這裏一定要加上註解,説明這是一個組件
public class HxCalculator implements Calculator{
@Override
public int plus(int x, int y, int z) {
return x + y + z;
}
@Override
public double plus(double x, double y) {
return x + y;
}
@Override
public int plus(int x, int y) {
int result = x + y;
return result;
}
@Override
public int substract(int x, int y) {
int result = x - y;
return result;
}
@Override
public int division(int x, int y) {
int result = x / y;
return result;
}
@Override
public int multiplication(int x, int y) {
int result = x * y;
return result;
}
}
4. 編寫日誌工具類(LogUtil.java)
public class LogUtil {
public static Logger log = Logger.getLogger(LogUtil.class);
}
5. 編寫日誌切面類(LoggingAspect.java)
/**
* 日誌切面
* @author Huathy
* @date 2020年3月5日
*/
@Order(1) //如果同時有多個切面,可以通過@Order註解來解決切面的先後順序,值越小優先級越高
@Component //這個切面我們需要放到ioc容器中,所以需要加上註解@Componet
@Aspect //將這個類聲明爲切面
public class LoggingAspect {
/**
* 前置通知
* @param jp
*/
@Before("execution(public int com.hx.springaop.impl.HxCalculator.plus(int,int))")
public void beforeMethod(JoinPoint jp){
String methodName = jp.getSignature().getName();
LogUtil.log.info( "前置通知-方法" + methodName + "開始執行,參數為" + Arrays.toString(jp.getArgs()) );
}
/**
* 後置通知
* @param jp
*/
@After("execution(public * com.hx.springaop.impl.HxCalculator.plus(..))")
public void afterMethod(JoinPoint jp){
String methodName = jp.getSignature().getName();
LogUtil.log.info( "後置通知-方法" + methodName + "運行結束..." );
}
@Pointcut("execution(public * com.hx.springaop.impl.HxCalculator.*(..))")
public void logging(){}
/**
* 返回通知
* @param jp
* @param result
*/
@AfterReturning(value="logging()", returning="result")
public void afterReturnMethod(JoinPoint jp,Object result){
String methodName = jp.getSignature().getName();
LogUtil.log.info( "返回通知-方法" + methodName + "運行結束,返回結果為:" + result );
}
/**
* 異常通知
* @param jp
* @param e
*/
@AfterThrowing(value="logging()", throwing="e")
public void afterThrowMethod(JoinPoint jp, Exception e){
String methodName = jp.getSignature().getName();
LogUtil.log.info( "異常通知-方法" + methodName + "出現異常信息:" + e );
}
/**
* 環繞通知
* @param pjp
* @return
*/
@Around("logging()")
public Object around(ProceedingJoinPoint pjp){
Object obj = null;
String methodName = pjp.getSignature().getName();
LogUtil.log.info( "環繞通知-方法" + methodName + "開始執行,參數:" + Arrays.toString(pjp.getArgs()) );
try {
obj = pjp.proceed(); //運行方法
LogUtil.log.info( "環繞通知-方法" + methodName + "運行結束,結果:" + obj );
} catch (Throwable e) {
LogUtil.log.info( "環繞通知-方法" + methodName + "運行出現異常信息:" + e );
e.printStackTrace();
}
return obj;
}
}
6. 在資源包下編寫全局配置spring-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置掃描路徑 -->
<context:component-scan base-package="com.hx.springaop" />
<!-- 讓註解起作用 -->
<aop:aspectj-autoproxy />
</beans>
7. 把log4j.properties放到資源包下
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=hx.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
log4j.rootLogger=debug, stdout, file
8. 編寫測試類
public class AppTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
Calculator clt = (Calculator) context.getBean("hxCalculator");
System.out.println( clt.plus(10, 20) );
System.out.println( clt.plus(10, 20, 30) );
System.out.println( clt.plus(10.1, 10.2) );
System.out.println( clt.substract(10, 20) );
System.out.println( clt.division(10, 0) );
}
}
9. 項目目錄結構
三、上述案例(xml配置方案)
1.把上面所有類上的通知組件等註解刪除
2.修改spring-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 計算器類 -->
<bean id="calculator" class="com.hx.springaop.impl.HxCalculator"></bean>
<!-- 切面類 -->
<bean id="logAspect" class="com.hx.springaop.util.LoggingAspect"></bean>
<!-- 配置aop -->
<aop:config>
<!-- 配置切點表達式 -->
<aop:pointcut id="pointcut1" expression="execution(public int com.hx.springaop.impl.HxCalculator.plus(int,int))"></aop:pointcut>
<aop:pointcut id="pointcut2" expression="execution(public * com.hx.springaop.impl.HxCalculator.*(..))"></aop:pointcut>
<!-- 配置切面通知。ref:應用的切面id。 -->
<aop:aspect ref="logAspect" order="1">
<!-- 配置通知類型對應調用的方法以及切點 -->
<aop:before method="beforeMethod" pointcut-ref="pointcut1"></aop:before>
<aop:after method="afterMethod" pointcut-ref="pointcut2"></aop:after>
<aop:after-returning method="afterReturnMethod" pointcut-ref="pointcut2" returning="result"></aop:after-returning>
<aop:after-throwing method="afterThrowMethod" pointcut-ref="pointcut2" throwing="e"></aop:after-throwing>
<aop:around method="around" pointcut-ref="pointcut2"></aop:around>
</aop:aspect>
</aop:config>
</beans>
3. 修改測試類
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
Calculator clt = (Calculator) context.getBean("calculator");
System.out.println( clt.plus(10, 20) );
System.out.println( clt.plus(10, 20, 30) );
System.out.println( clt.plus(10.1, 10.2) );
System.out.println( clt.substract(10, 20) );
System.out.println( clt.division(10, 0) );
}