學習內容
Spring AOP 切面的定義
Spring AOP定義切面有多種方式,例如
1、使用@AspectJ註解
2、使用<aop:aspect>標籤
3、使用<aop:advisor>標籤
4、使用Advisor類
一、使用@AspectJ註解
a、在配置文件中加<aop:aspectj-autoproxy>
b、編寫切面類
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*;
import java.util.Arrays; @Component @Aspect public class LogAspect { private final String POINT_CUT = "execution(public * com.congrong.service.*.*(..))";
@Pointcut(POINT_CUT) public void pointCut(){}
/** * 切點方法之前執行 * @param joinPoint */ @Before(value = "pointCut()") public void before(JoinPoint joinPoint){ System.out.println("@Before通知執行"); }
/** * 切點方法之後執行 * @param joinPoint */ @After(value = POINT_CUT) public void doAfterAdvice(JoinPoint joinPoint){ System.out.println("After通知執行"); }
/** * 切點方法返回後執行 * 如果第一個參數爲JoinPoint,則第二個參數爲返回值的信息 * 如果第一個參數不爲JoinPoint,則第一個參數爲returning中對應的參數 * returning:限定了只有目標方法返回值與通知方法參數類型匹配時才能執行後置返回通知,否則不執行, * 參數爲Object類型將匹配任何目標返回值 * @param joinPoint * @param result */ @AfterReturning(value = POINT_CUT,returning = "result") public void doAfterReturningAdvice(JoinPoint joinPoint,Object result){ System.out.println("AfterReturning通知執行"); }
/** * 切點方法拋異常執行 * 定義一個名字,該名字用於匹配通知實現方法的一個參數名,當目標方法拋出異常返回後,將把目標方法拋出的異常傳給通知方法; * throwing:限定了只有目標方法拋出的異常與通知方法相應參數異常類型時才能執行後置異常通知,否則不執行, * 對於throwing對應的通知方法參數爲Throwable類型將匹配任何異常。 * @param joinPoint * @param exception */ @AfterThrowing(value = POINT_CUT,throwing = "exception") public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){ System.out.println("AfterThrowing通知執行"); if(exception instanceof NullPointerException){ System.out.println("NullPointerException!"); } }
/** * 環繞通知: * Spring AOP的環繞通知會影響到AfterThrowing通知的運行,不要同時使用 * 環繞通知非常強大,可以決定目標方法是否執行,什麼時候執行,執行時是否需要替換方法參數,執行完畢是否需要替換返回值。 * 環繞通知第一個參數必須是org.aspectj.lang.ProceedingJoinPoint類型 * @param proceedingJoinPoint * @return */ @Around(value = POINT_CUT) public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ System.out.println("@Around環繞通知開始執行"); Object obj = null; try { obj = proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("@Around環繞通知結束執行"); return obj; }
} |
二、使用<aop:aspect>標籤
<!-- 定義目標對象 --> <bean name="userDao" class="com.congrong.dao.UserDaoImpl" />
<!-- 定義切面 --> <bean name="logAspect" class="com.congrong.LogAspect" />
<!-- 配置AOP --> <aop:config> <!-- 定義切點函數 --> <aop:pointcut id="pointcut" expression="execution(* com.congrong.dao.*.*(..))" />
<!-- 配置切面--> <aop:aspect ref="logAspect"> <!-- 前置通知 method 指定通知方法名,必須與logAspect中的相同 pointcut 指定切點函數--> <aop:before method="before" pointcut-ref="pointcut" />
<!-- 返回通知 returning="returnVal" 定義返回值 必須與類中聲明的名稱一樣--> <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" />
<!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pointcut" />
<!--異常通知 throwing="throwable" 指定異常通知錯誤信息變量,必須與類中聲明的名稱一樣--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
<!--後置/最終通知 --> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> |
三、使用<aop:advisor>標籤
與< aop:aspect>定義切面時不同的是,< aop:aspect>的通知只需要定義一般的bean就行,而
< aop:advisor>中引用的通知,必須實現Advice接口。
定義通知
//定義通知 public class LogAdvice implements MethodBeforeAdvice,AfterReturningAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("開始打印日誌!"); }
@Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("結束打印日誌!"); } } |
配置
<bean id="logAdvice" class="com.congrong.LogAdvice"></bean>
<aop:config> <aop:pointcut id="pointcut" expression="execution(* com.congrong.*.*(..))"/> <aop:advisor advice-ref="logAdvice" pointcut-ref="pointcut"/> </aop:config> |
四、使用Advisor類
//定義一個接口 public interface UserDao { void addUser(); } |
//目標對象實現接口並重寫方法 public class UserDaoImpl implements UserDao { @Override public void addUser() { System.out.println("新增用戶"); } } |
//實現增強類的接口 public class LogBefore implements MethodBeforeAdvice { //arg0 目標類的方法 arg1 目標類的入參數 arg2 目標類 @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("添加日誌"); } } |
ProxtyFactory以編程形式
public class Test { public static void main(String[] args) throws Exception{ LogBefore log = new LogBefore(); UserDao userDao = new UserDaoImpl(); ProxyFactory proxyf = new ProxyFactory(); //設置目標對象 proxyf.setTarget(userDao); //設置增強類對象 proxyf.addAdvice(log); //獲得代理對象 UserDao up = proxyf.getProxy(); up.addUser(); } } |
ProxyFactoryBean通過xml配置代理。
<bean id="logBefore" class="com.cong.LogBefore"/>
<bean id="target" class="com.cong.UserDaoImpl"/>
<bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean" p:proxyInterfaces="com.cong.UserDao" p:interceptorNames="logBefore" p:target-ref="target" /> |