如果你去面試Java開發, 那麼spring的AOP和DI幾乎是必問的問題。
那麼AOP是什麼呢?
所謂Aop就是 Aspect-OrientedProgramming, 中文就是面向切面編程。
我們之前聽說面向過程編程, 以及面向對象編程, 而這個面向切面編程我們可以視為是面向對象編程的1個補充(增強);
在一般的方法(函數)中, 為了方便,可能只寫了業務代碼
1. 業務代碼
而我們我們可以將其補充成4個部分。
大家看上面的方法, 就像用刀子把1個方法橫向切成4塊, 我們把上面除業務代碼外任意一個部分就叫做橫切關注點.
我們可以把橫切關注點進行分組, 其中任意一組就叫做切面
例如上面的例子中, 我們可以分成
所謂通知就是切面要完成的工作。
例如對於日志通知來講, 裡面執行日志的方法我們就可以稱為1個通知。
所謂目標就是被通知的對象, 也就是指上面例子中的原方法本身啦。
當目標被通知後產生的對象就叫做代理, 因為AOP的原理就是利用代理來實現的, 如果想了解動態代理的可以參考這裡
所謂Joinpoint就是程序執行到的某個位置,
上面例子中, 業務代碼方法的執行之前, 執行之後, 拋出異常後 都可以視為某個連接點。
每個方法都存在多個連接點, 而Spring AOP利用切點來定位到具體那些連接點。
Joinpoint 和 PointCut 的關系可以作如下比喻,假如Joinpoint的數據裡的記錄, 那麼PointCut就相當於查詢條件
而Spring的AOP 能在不修改具體某個方法的前提下, 利用動態代理技術將通知注入到這個方法的各個連接點中, 令到這個方法得到了必要的補充。
對於上面的例子種, 我們原來的方法只有業務代碼, 但是我們可以利用Spring AOP加入 驗證參數, 日志等功能!
我們首先利用spring創建兩個計算類。
1個加法類, 1個減法類。
bean config 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:aop="http://www.springframework.org/schema/aop"
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-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- auto scan -->
<context:component-scan base-package="com.home.aop"></context:component-scan>
</beans>
注意引入aop命名空間
計算接口 Calculator
package com.home.aop;
public interface Calculator {
public double getResult(double a, double b);
}
加法類 AddCalculator
package com.home.aop;
import org.springframework.stereotype.Component;
@Component
public class AddCalculator implements Calculator {
@Override
public double getResult(double a, double b) {
return a + b;
}
}
減法類 SubCalculator
package com.home.aop;
import org.springframework.stereotype.Component;
@Component
public class SubCalculator implements Calculator {
@Override
public double getResult(double a, double b) {
return a - b;
}
}
Client 代碼
package com.home.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopMain {
public static void f(){
g();
}
public static void g(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-aop.xml");
Calculator addCal = (Calculator) ctx.getBean("addCalculator");
Calculator subCal = (Calculator) ctx.getBean("subCalculator");
System.out.println(addCal.getResult(3, 1));
System.out.println(subCal.getResult(3, 1));
}
}
執行結果
Jul 05, 2016 10:10:00 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5d79a4c9: startup date [Tue Jul 05 22:10:00 CST 2016]; root of context hierarchy
Jul 05, 2016 10:10:00 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
4.0
2.0
上面例子中, 加法類和減法類中只包含了業務代碼, 我們可以將視為兩個Target(目標), 下面我們就利用AOP技術為這兩個Target加入通知。
這個任務的需求很簡單。
首先maven裡要引入這些lib
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
然後在bean-config xml裡加入下面這個句話, enable spring aop 功能
<!-- enable @Aspect -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
然後新建1個類LoggingAspect
package com.home.aop;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Component;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
@Component
public class LoggingAspect {
//@Before("execution(public double com.home.aop.AddCalculator.getResult(double,double))")
@Before("execution(public * com.home.aop.*.*(..))")
public void beforeExecute(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("before Execute! --class name: " + classname + ", method name: " + methodName + " " + args );
}
}
方法beforeExecute的意思就是我們要為目標執行之前 而注入的方法。
上面@Before 注解表示這是1個前置通知。 括號裡面的就是PointCut(切點), 上面說過了, 相當於數據庫裡的查詢條件
然後Spring AOP 會根據PointCut 查找出所有符合條件的目標。
內容就很簡單了, 無非就是輸出被執行的類名方法名和參數…
執行結果:
Jul 06, 2016 12:30:03 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6924181b: startup date [Wed Jul 06 00:30:03 CST 2016]; root of context hierarchy
Jul 06, 2016 12:30:03 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
before Execute! --class name: AddCalculator, method name: getResult [3.0, 1.0]
4.0
before Execute! --class name: SubCalculator, method name: getResult [3.0, 1.0]
2.0
項目中, 如果不用AOP 我們往往要為每個方法添加日志代碼, 十分難於維護, 可讀性也大大下降, 而AOP的出現, 就能解決這些問題。
Spring AOP四種實現方式 http://www.linuxidc.com/Linux/2016-10/135993.htm
Spring AOP自定義注解方式實現日志管理 http://www.linuxidc.com/Linux/2015-11/125019.htm
Spring AOP進行日志記錄 http://www.linuxidc.com/Linux/2015-11/124731.htm
使用Spring AOP進行性能監控 http://www.linuxidc.com/Linux/2012-07/64681.htm
利用Spring AOP 更新Memcached 緩存策略的實現 http://www.linuxidc.com/Linux/2012-03/56503.htm
Spring AOP的兩種代理 http://www.linuxidc.com/Linux/2015-11/125017.htm
Spring AOP的注解實例 http://www.linuxidc.com/Linux/2015-11/125018.htm