什麼是AOP?
基本概念
切面(aspect):橫切關注點被模塊化的特殊對象。
通知(advice):切面必需要完成的工做。切面中的每一個方向稱之爲通知。通知是在切面對象中的。
目標(target):被通知的對象。
代理(proxy):向目標對象應用通知後建立的對象。spring
鏈接點(joinpoint):目標對象的程序執行的某個特定位置。如某個方法調用前,調用後的位置。包括兩個信息:1.目標程序的哪一個方法?2.方法執行
前仍是執行後?
切點(pointcut):每一個類會有多個鏈接點,AOP經過切點定位到特定的邊接點。
類比,鏈接點至關於數所成方圓 中的記錄,而切點至關於查詢條件。一個切點匹配多個鏈接點。express
1.導入 spring包和aspectj的兩個jar包編程
2.目標對象(要切入的對象)ide
首先爲了避免違反開閉原則和更好的可擴展性,目標對象其實是實現了已定義好的某個接口模塊化
接口:函數
package com.itnba.test; public interface IHuman { public void chifan(); public void shuijiao(); }
實現接口的兩個類:ui
package com.itnba.test; import org.springframework.stereotype.Component; public class Chinese implements IHuman { @Override public void chifan() { // TODO 自動生成的方法存根 System.out.println("中國人吃飯"); } @Override public void shuijiao() { // TODO 自動生成的方法存根 System.out.println("中國人睡覺"); } }
package com.itnba.test; import org.springframework.stereotype.Component; public class American implements IHuman { @Override public void chifan() { // TODO 自動生成的方法存根 System.out.println("美國人吃飯"); } @Override public void shuijiao() { // TODO 自動生成的方法存根 System.out.println("美國人睡覺"); } }
3.定義一個切面類spa
package com.itnba.test; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; public class Qiemian { public void chifanqian(){ System.out.println("洗手"); } public void chifanhou(){ System.out.println("漱口"); } public void shuijiaoqian(){ System.out.println("洗澡"); } }
4.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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" default-autowire="byName" > <!-- 首相要實例化目標對象類和切面類 --> <bean id="chinese" class="com.itnba.test.Chinese"></bean> <bean id="american" class="com.itnba.test.American"></bean> <bean id="qiemian" class="com.itnba.test.Qiemian"></bean> <aop:config> <!-- 要切入的對象 --> <aop:pointcut expression="execution(* com.itnba.test.*.chifan(..))" id="chifan"/> <aop:pointcut expression="execution(* com.itnba.test.*.shijiao(..))" id="shijiao"/> <!-- 切入點 --> <aop:aspect id="ha" ref="qiemian"><!-- 切面類 --> <!-- <aop:以前before、以後after... method="切面類中的方法" pointcut-ref="上面的切入的對象"/> --> <aop:before method="chifanqian()" pointcut-ref="chifan"/><!-- 以前通知 --> <aop:after method="chifanhou()()" pointcut-ref="chifan"/><!-- 以後通知 --> <aop:before method="shuijiaoqian()" pointcut-ref="shijiao"/> </aop:aspect> </aop:config> </beans>
5.運行日誌
package com.itnba.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //要用接口接收 IHuman c= (IHuman)context.getBean("chinese"); c.chifan(); c.shuijiao(); System.out.println("*********************************"); //要用接口接收 IHuman a= (IHuman) context.getBean("american"); a.chifan(); a.shuijiao(); } }
結果:
1.導如 spring包和aspectj的兩個jar包
2.配置文件中加入aop命名空間
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > <!-- 自動掃描包下的類,並將其實例化。多個包之間用,隔開 --> <context:component-scan base-package="com.itnba.test"></context:component-scan> <!-- 配置文件中啓動AspectJ的註解功能 ,默認是false,要將其改成true --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans>
3.目標對象(要切入的對象)
首先爲了避免違反開閉原則和更好的可擴展性,目標對象其實是實現了已定義好的某個接口
接口:
package com.itnba.test; public interface IHuman { public void chifan(); public void shuijiao(); }
實現接口的兩個類:
package com.itnba.test; import org.springframework.stereotype.Component; @Component public class Chinese implements IHuman { @Override public void chifan() { // TODO 自動生成的方法存根 System.out.println("中國人吃飯"); } @Override public void shuijiao() { // TODO 自動生成的方法存根 System.out.println("中國人睡覺"); } }
package com.itnba.test; import org.springframework.stereotype.Component; @Component public class American implements IHuman { @Override public void chifan() { // TODO 自動生成的方法存根 System.out.println("美國人吃飯"); } @Override public void shuijiao() { // TODO 自動生成的方法存根 System.out.println("美國人睡覺"); } }
4.定義一個切面類,直接在裏面註解
package com.itnba.test; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect //聲明該類是切面類 @Component//配置文件中啓動自動掃描功能 public class Qiemian { @Before("execution(* com.itnba.test.*.chifan(..))")//在那個方法的以前通知 *表明所有..表明全部形參,無論有多少 public void chifanqian(){ System.out.println("洗手"); } @After("execution(* com.itnba.test.*.chifan(..))")//在那個方法以後通知 public void chifanhou(){ System.out.println("漱口"); } @Before("execution(* com.itnba.test.*.shuijiao(..))") public void shuijiaoqian(){ System.out.println("洗澡"); } }
5.寫好後用main函數來運行一下
package com.itnba.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Chinese c= context.getBean(Chinese.class); c.chifan(); c.shuijiao(); System.out.println("*********************************"); American a= (American) context.getBean("american"); a.chifan(); a.shuijiao(); } }
結果:
這樣,Spring的面向切面編程就簡單介紹完了。
通知分類:
前置通知(@Before) -- 在目標方法執行前執行的通知。
後置通知(@After) -- 在目標方法執行後執行的通知。不管是否發生異常。後置通知中,沒法讀取目標方法返回的結果。
返回通知(@AfterReturnning) --在目標方法執行成功後執行的通知。在返回通知中能夠訪問目標返回結果的。
@AfterReturnning(value="execution(* com.itnba..*(..))",returning="result")
public void afterRun(Object result){
System.out.println(result);
}
異常通知(@AfterThrowing) -- 在目標方法執行出錯後執行的通知。
@AfterThrowing(value="execution(* com.itnba..*(..))",throwing="ex")
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage());
}
環繞通知(@Around) -- 須要切面方法攜帶ProceedingJoinPoion參數,相似於一個完整的動態代理,環繞通知必需要有一個返回值,是目標方法的返
回值。
@Around("execution(* com.itnba..*(..))")
public object aroundAdvice( ProceedingJoinPoint pjp){
object obj = null;
try{
//作前置通知
obj = pjp.proceed();
//作返回通知
}
catch(Exception ex){
//作異常通知
}
//作後置通知
return obj;
}
添加日誌:
切面方法能夠加入參數(JoinPoint) joinPost.getSignature().getXXX()得到相關方法信息切面方法中能夠加入Object參數,用來得到目標方法的返回值(只對返回通知起做用)