Spring的AOP簡介和Spring中的通知使用方法以及異常
阿新 • • 發佈:2018-12-13
AOP中關鍵性概念
連線點(Joinpoint):程式執行過程中明確的點,如方法的呼叫,或者異常的丟擲.
目標(Target):被通知(被代理)的物件
注1:完成具體的業務邏輯
通知(Advice):在某個特定的連線點上執行的動作,同時Advice也是程式程式碼的具體實現,例如一個實現日誌記錄的程式碼(通知有些書上也稱為處理)
注2:完成切面程式設計
代理(Proxy):將通知應用到目標物件後建立的物件(代理=目標+通知),
例子:外科醫生+護士
注3:只有代理物件才有AOP功能,而AOP的程式碼是寫在通知的方法裡面的
切入點(Pointcut):多個連線點的集合,定義了通知應該應用到那些連線點。
(也將Pointcut理解成一個條件 ,此條件決定了容器在什麼情況下將通知和目標組合成代理返回給外部程式)
介面卡(Advisor):介面卡=通知(Advice)+切入點(Pointcut)
如何實現AOP
目標物件只負責業務邏輯程式碼
通知物件負責AOP程式碼,這二個物件都沒有AOP的功能,只有代理物件才有
接下來貼上案例程式碼來實現通知:
biz
public interface IBookBiz {
// 購書
public boolean buy(String userName, String bookName, Double price);
// 發表書評
public String comment(String userName, String comments);
}
biz實現
public class BookBizImpl implements IBookBiz { public BookBizImpl() { super(); } public boolean buy(String userName, String bookName, Double price) { // 通過控制檯的輸出方式模擬購書 如果價格不對將會觸發異常,導致程式停止,但是觸發前的事件依然會執行 if (null == price || price <= 0) { throw new PriceException("book price exception"); } System.out.println(userName + " buy " + bookName + ", spend ASD " + price); return true; } public String comment(String userName, String comments) { // 通過控制檯的輸出方式模擬發表書評 System.out.println(userName + " say:" + comments); return "123"; } }
然後我們寫一個前後置通知的類
/**
* @program: Maven
* @description: 前置後置通知
* @author: hw
* MethodBeforeAdvice 為前置通知的介面 AfterReturningAdvice為後置通知介面
**/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
/**
* @Description: 前置通知方法
* @Param: [method, objects, o]
* @return: void
* @Author: hw
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
//類名
String name = o.getClass().getName();
//方法名
String name1 = method.getName();
//引數
String s = Arrays.toString(objects);
System.out.println("前置類名" + name + "方法名" + name1 + "引數" + s);
}
/**
* @Description: 後置通知方法
* @Param: [o, method, objects, o1]
* @return: void
* @Author: hw
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
//類名
String name = o1.getClass().getName();
//方法名
String name1 = method.getName();
//引數
String s = Arrays.toString(objects);
System.out.println("後置類名" + name + "方法名" + name1 + "引數" + s+"返回值"+o);
}
}
這是環繞通知的類,也就是即會在方法執行前執行也會在方法執行後執行,概念像struts的攔截器
/**
* @program: Maven
* @description: 環繞通知
* @author: hw
**/
public class MyMethodInterceptor implements MethodInterceptor {
/**
* @Description: 環繞通知, 前後都執行
* @Param: [methodInvocation]
* @return: java.lang.Object
* @Author: hw
* @Date: 2018/12/5
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//執行前
//類名
String name = methodInvocation.getThis().getClass().getName();
//方法名
String name1 = methodInvocation.getMethod().getName();
//引數
String s = Arrays.toString(methodInvocation.getArguments());
System.out.println("環繞前"+ name + "方法名" + name1 + "引數" + s);
//執行後通知以下面這行程式碼分割
Object proceed = methodInvocation.proceed();//返回值
//後置通知
System.out.println("環繞後" + name + "方法名" + name1 + "引數" + s+"返回值"+proceed);
return proceed;
}
}
異常類
/**
* @program: Maven
* @description: 異常
* @author: hw
**/
public class MyThrowsAdvice implements ThrowsAdvice {
/** @Description: 這個方法需要手寫出來,並且方法名必須是這個,但是可以通過方法重構使用多個方法
* @Param: [ex]
* @return: void
* @Author: hw
*/
public void afterThrowing(PriceException ex) {
System.out.println("價格異常");
}
}
/**
* @Description: 異常類
* @Param:
* @return:
* @Author: hw
*/
public class PriceException extends RuntimeException {
public PriceException() {
super();
}
public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public PriceException(String message, Throwable cause) {
super(message, cause);
}
public PriceException(String message) {
super(message);
}
public PriceException(Throwable cause) {
super(cause);
}
}
接下來是我們最後的配置檔案
<!--aop-->
<!--目標-->
<bean class="com.hw.two.biz.impl.BookBizImpl" name="bookBiz"></bean>
<!--前置通知 和後置通知-->
<bean class="com.hw.two.advice.MyMethodBeforeAdvice" name="MymethodBeforeAdvice"></bean>
<!--環繞通知-->
<bean class="com.hw.two.advice.MyMethodInterceptor" name="myinterceptor"></bean>
<!--異常-->
<bean class="com.hw.two.advice.MyThrowsAdvice" name="throwsAdvice"></bean>
<!--過濾通知-->
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
name="pointcutAdvisor">
<!--配置需要過來的通知-->
<property name="advice" ref="MymethodBeforeAdvice"></property>
<!--匹配該正則,符合的時候才使用通知-->
<property name="pattern" value=".*buy"></property>
</bean>
<!--代理工廠-->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" name="bookProxy">
<!--設定目標-->
<property name="target" ref="bookBiz"></property>
<!--配置介面 填入目標實現的介面 因為介面可以多實現,所以標籤為list-->
<property name="proxyInterfaces">
<list>
<value>com.hw.two.biz.IBookBiz</value>
</list>
</property>
<!--目標通知-->
<property name="interceptorNames">
<list>
<!--前後通知-->
<!--<value>MymethodBeforeAdvice</value>-->
<!--過濾通知-->
<value>pointcutAdvisor</value>
<!--環繞通知-->
<value>myinterceptor</value>
<!--異常-->
<value>throwsAdvice</value>
</list>
</property>
</bean>
測試類
/**
* @program: Maven
* @description: 通知測試
* @author: hw
**/
public class AdviceTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Object bookProxy = applicationContext.getBean("bookProxy");
IBookBiz biz= (IBookBiz) bookProxy;
biz.buy("猴子","升序",12.5);
System.out.println();
biz.comment("猴子","sss");
}
}