通知:
前置通知:運行目標方法前時,運行的通知;
后置通知:運行目標方法后時,運行的通知;
異常通知:運行目標方法發生異常時,運行的通知;
環繞通知:在環繞通知中可以定義,前置通知、后置通知、異常通知和最終通知,比較全面;
最終通知:運行方法后,都會運行的通知;
讓我們用一張圖來更加好的理解這幾個通知:

一、通過接口實現通知
前置通知:繼承MethodBeforeAdvice接口,並重寫before()方法;
后置通知:繼承AfterReturningAdvice接口,並重寫afterReturning()方法;
異常通知:繼承ThrowsAdvice接口,無重寫方法;
環繞通知:繼承MethodInterceptor接口,並重寫invoke()方法;
最終通知;
如何來寫一個通知?
a. 需要的jar包;
aopaliance.jar aspectjweaver.jar
b. 編寫業務方法和需要的通知;
在下面的這個代碼中,我們使用的業務方法是 addStudent(), 我們使用的通知是 前置通知;
c. 在applicationContext.xml中配置相關內容;
舉例:
<bean id="studentDao" class="org.kay.dao.StudentDaoImpl"></bean> <!-- 將studentDao加入到SpringIoc容器中 --> <bean id="studentService" class="org.kay.service.Impl.StudentServiceImpl"> <!-- 將studentService加入到SpringIoc容器中 --> <property name="stuDao" ref="studentDao"></property> </bean> <bean id="myBeforeAdvice" class="org.kay.advice.MyBeforeAdvice"></bean> <!-- 將通知加入到SpringIoc容器中 --> <aop:config>
<!-- 配置切入點(在哪里執行通知) --> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="myPointcut1"/>
<!-- 配置切入點和切面的連接線 --> <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPointcut1"/> </aop:config>
目前在applicationContext.xml中的關於通知的相關配置大概就是這樣,現在還不理解沒事,目前只是舉一個栗子;
我們來使用繼承接口來實現一個前置通知:
a.jar包導一下;
b.編寫業務類和前置通知類;
業務類:
Student類:
public class Student {
private String stuName;
private int stuAge;
private String stuClass;
private Course course;
public Student() {}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getStuClass() {
return stuClass;
}
public void setStuClass(String stuClass) {
this.stuClass = stuClass;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
@Override
public String toString() {
return "Student [stuName=" + stuName + ", stuAge=" + stuAge + ", stuClass=" + stuClass + ", course=" + course
+ "]";
}
}
StudentDao類:
import org.kay.entity.Student;
public interface StudentDao {
public void removeStudent(Student stu);
public void deleteStudent();
}
StudentDaoImpl類:
import org.kay.entity.Student;
public class StudentDaoImpl implements StudentDao{
public void addStudent(Student student) {
System.out.println("增加學生...");
}
@Override
public void deleteStudent() {
// TODO Auto-generated method stub
System.out.println("刪除學生...");
}
@Override
public void removeStudent(Student stu) {
System.out.println("移動學生...");
}
}
StudentService類:
import org.kay.entity.Student;
public interface StudentService {
public void removeStudent(Student stu);
}
StudentServiceImpl類:
import org.kay.dao.StudentDao;
import org.kay.entity.Student;
import org.kay.service.StudentService;
public class StudentServiceImpl implements StudentService{
StudentDao stuDao;
public void setStuDao(StudentDao stuDao) {
this.stuDao = stuDao;
}
@Override
public void removeStudent(Student stu) {
// TODO Auto-generated method stub
//stuDao = null; // 進行異常通知的測試代碼。空指針異常。
stuDao.removeStudent(stu);
}
}
前置通知類:
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("這是一個前置通知!");
System.out.println("Method:"+arg0+", Object[]:"+arg1+", Object:"+arg2);
}
}
編寫好了相關的類,我們現在只要將他們配置一下就行了;
c.在applicationContext.xml中配置相關內容;
<bean id="studentDao" class="org.kay.dao.StudentDaoImpl"></bean> <bean id="studentService" class="org.kay.service.Impl.StudentServiceImpl"> <property name="stuDao" ref="studentDao"></property> </bean> <bean id="myBeforeAdvice" class="org.kay.advice.MyBeforeAdvice"></bean> <aop:config> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="myPointcut1"/> <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPointcut1"/> </aop:config>
測試: 全部都編寫和配置好了之后,我們寫一個Test類來進行一下測試;
import org.kay.dao.StudentDao;
import org.kay.dao.StudentDaoImpl;
import org.kay.entity.CollectionDemo;
import org.kay.entity.Student;
import org.kay.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void beforeAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
public static void main(String[] args) {
beforeAdvice();
}
}
結果:

我們來使用繼承接口來實現一個后置通知:
添加一下后置通知類:
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterRunningAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("這是一個 后置通知!");
}
}
業務類就使用上面的類;
在applicationContext.xml中配置相關內容;
<bean id="MyAfterRunningAdvice" class="org.kay.advice.MyAfterRunningAdvice"> <!-- 將通知納入SpringIoc容器中 --> </bean> <aop:config> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="myPointcut2"/> <aop:advisor advice-ref="MyAfterRunningAdvice" pointcut-ref="myPointcut2"></aop:advisor> </aop:config>
接着就是進行測試:
public static void afterRunningAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
這個后置通知測試的代碼跟前置通知測試的代碼一樣,在SpringIoc容器中拿到studentService。進行studentService方法的調用。
結果:

我們來使用繼承接口來實現一個異常通知:
編寫一個異常通知類:
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowAdvice implements ThrowsAdvice{
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
System.out.println("這是一個異常通知!");
}
}
業務類還是跟上面一樣; 但是我們注意一下就是必須要發生異常才進行異常通知中的內容;
所以需要改變一些代碼,就是將 stuDao == null; 給一個 空指針異常;
@Override
public void removeStudent(Student stu) {
// TODO Auto-generated method stub
stuDao = null; // 進行異常通知的測試代碼。空指針異常。
stuDao.removeStudent(stu);
}
在applicationContext.xml中配置相關內容;
<bean id="MyThrowAdvice" class="org.kay.advice.MyThrowAdvice"></bean> <aop:config> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="myPointcut3"/> <aop:advisor advice-ref="MyThrowAdvice" pointcut-ref="myPointcut3"/> </aop:config>
接着就是進行測試:
public static void throwAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
結果:

我們來使用繼承接口來實現一個環繞通知:
編寫一個環繞通知類:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAroundAdvice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//方法體一
Object result = null;
try {
System.out.println("【環繞通知】中的前置通知!!!");
System.out.println("這個是一個【環繞通知】!!!");
result = invocation.proceed(); // 控制目標方法的執行,XXXStudent();
//result 就是目標方法的返回值。
System.out.println("【環繞通知】中的前后置通知!!!");
}catch(Exception e) {
System.out.println("【環繞通知】中的異常通知!!!");
}
return result;
}
}
業務類還是跟上面一樣;
在applicationContext.xml中配置相關內容;
<bean id="MyAroundAdvice" class="org.kay.advice.MyAroundAdvice"></bean> <aop:config> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="myPointcut4"/> <aop:advisor advice-ref="MyAroundAdvice" pointcut-ref="myPointcut4"/> </aop:config>
接着就是進行測試:
public static void aroundAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
結果:
1、當沒有發生異常的結果:

2、當發生異常時的結果:

二、通過注解實現通知
前置通知: 注解為 @Before
后置通知: 注解為 @AfterReturning
異常通知: 注解為 @AfterThrowing
環繞通知: 注解為 @Around
最終通知: 注解為 @After
使用注解進行通知的配置的話,與接口有一點不同,利用注解的話只需要在applicationContext.xml中開啟注解對AOP的支持就行
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
兩個注意點:
@Aspect 聲明該類是一個通知;
@Component("annotationAdvice") 將AnnotationAdvice納入到SpringIoc容器中。
其實 @Component("annotationAdvice") 等價於 <bean id="annotationAdvice" class="org.kay.advice.AnnotationAdvice"></bean>
如果用注解將通知納入到SpringIoc容器中去的話,需要在applicationContext.xml文件中設置掃描器;
<context:component-scan base-package="org.kay.advice"> <!-- 里面放包的名字,可以放多個包。放在里面之后運行會在相關包中找相關的注解,找到了就將他們納入到SpringIoc容器中 --> </context:component-scan>
我們來使用注解來實現前置通知通知、后置、異常通知、環繞通知和最終通知:
編寫一個普通的類:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("annotationAdvice") // 將AnnotationAdvice納入到SpringIoc容器中。
@Aspect // 此類是一個通知類
public class AnnotationAdvice {
@Before("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
public void myBefore() {
System.out.println("<注解---前置通知!!>");
}
@AfterReturning(pointcut="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))", returning="returningValue")
public void myAfterReturning(JoinPoint jp, Object returningValue) {
System.out.println("<注解---后置通知!!>, 目標對象:" + jp.getTarget()+"\n" + jp.getArgs() + "\n" + jp.getKind()+" 返回值:" + returningValue);
}
@AfterThrowing("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
public void myThrows() {
System.out.println("<注解---異常通知!!>");
}
//環繞通知
@Around("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
public void myAround(ProceedingJoinPoint jp) {
try {
//前置
System.out.println("<注解---環繞通知---前置通知>");
jp.proceed();
//后置
System.out.println("<注解---環繞通知---后置通知>");
}catch(Throwable e) {
//異常
System.out.println("<注解---環繞通知---異常通知>");
}finally{
//最終
System.out.println("<注解---環繞通知---最終通知>");
}
}
//最終通知
@After("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
public void myAfter() {
System.out.println("<注解---最終通知!!>");
}
}
業務類還是以前的一樣的;
在applicationContext.xml中配置
<!-- 開啟注解對AOP的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
測試:
public static void annotationAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
結果:(大致的內容有了就行!)
1、沒有異常:

2、發生異常:

三、通過配置實現通知
基於Schema配置;類似於接口的方式在applicationContext.xml中進行配置。
先編寫一個普通的類:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class ConfigSchemaAdvice {
public void myConfigBefore() {
System.out.println("Schema---前置通知...");
}
public void myConfigAfterReturning(JoinPoint jp, Object returningValue) {
System.out.println("Schema---后置通知..." + "返回值: " + returningValue);
}
public void myConfigThrows(JoinPoint jp, NullPointerException e) {
System.out.println("Schema---異常通知..." + e.getMessage());
}
public void myConfigFinally() {
System.out.println("Schema---最終通知...");
}
public Object myConfigAround(ProceedingJoinPoint jp) {
Object result = null;
try {
System.out.println("Schema《環繞》 --- 前置通知¥¥¥");
result = jp.proceed();//執行方法。
System.out.println("目標函數: " + jp.getTarget());
System.out.println("Schema《環繞》 --- 后置通知¥¥¥");
}catch(Throwable e) {
System.out.println("Schema《環繞》 --- 異常通知¥¥¥");
}finally {
System.out.println("Schema《環繞》 --- 最終通知¥¥¥");
}
return result;
}
}
因為這個是一個普通的類,而且沒有加注解;所以我們需要在applicationContext.xml中進行配置,使它先納入SpringIoc容器中,在將他變成一個通知類;
<bean id="configSchemaAdvice" class="org.kay.advice.ConfigSchemaAdvice"></bean>
<aop:config> <aop:pointcut expression="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))" id="mySchema"/> <aop:aspect ref="configSchemaAdvice"> <aop:before method="myConfigBefore" pointcut-ref="mySchema"/> <aop:after-returning method="myConfigAfterReturning" returning="returningValue" pointcut-ref="mySchema"/> <aop:after-throwing method="myConfigThrows" pointcut-ref="mySchema" throwing="e" /> <aop:after method="myConfigFinally" pointcut-ref="mySchema"/> <aop:around method="myConfigAround" pointcut-ref="mySchema" /> </aop:aspect> </aop:config>
測試:
public static void schemaAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stuService = (StudentService)context.getBean("studentService");
Student stu = new Student();
stuService.removeStudent(stu);
}
結果:

其他:
如果需要獲取目標對象信息:
注解 和 Schema : JoinPoint ;
接口: Method method, Object[] args, Object target ;
注解和Schema的不同之處:
注解使用的是在普通類進行注解使它變成通知類;
而Schema是在applicationContext.xml中進行了配置,使普通類變成了通知類。
其中關於獲取目標對象信息的參數都沒有介紹感興趣的可以看一下網易雲 顏群老師的Spring課程,是我的Spring啟蒙老師。^.^ !
顏群老師其他的課程也講的很不錯哦!
