前置通知:运行目标方法前时,运行的通知;java
后置通知:运行目标方法后时,运行的通知;spring
异常通知:运行目标方法发生异常时,运行的通知;express
环绕通知:在环绕通知中能够定义,前置通知、后置通知、异常通知和最终通知,比较全面;app
最终通知:运行方法后,都会运行的通知;ide
前置通知:继承MethodBeforeAdvice接口,并重写before()方法;函数
后置通知:继承AfterReturningAdvice接口,并重写afterReturning()方法;测试
异常通知:继承ThrowsAdvice接口,无重写方法;this
环绕通知:继承MethodInterceptor接口,并重写invoke()方法;spa
最终通知;指针
aopaliance.jar aspectjweaver.jar
在下面的这个代码中,咱们使用的业务方法是 addStudent(), 咱们使用的通知是 前置通知;
举例:
<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中的关于通知的相关配置大概就是这样,如今还不理解没事,目前只是举一个栗子;
业务类:
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); } }
编写好了相关的类,咱们如今只要将他们配置一下就好了;
<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>
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("这是一个 后置通知!"); } }
<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); }
<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; } }
业务类仍是跟上面同样;
<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); }
结果:
一、当没有发生异常的结果:
二、当发生异常时的结果:
前置通知: 注解为 @Before
后置通知: 注解为 @AfterReturning
异常通知: 注解为 @AfterThrowing
环绕通知: 注解为 @Around
最终通知: 注解为 @After
使用注解进行通知的配置的话,与接口有一点不一样,利用注解的话只须要在applicationContext.xml中开启注解对AOP的支持就行
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect 声明该类是一个通知;
@Component("annotationAdvice") 将AnnotationAdvice归入到SpringIoc容器中。
若是用注解将通知归入到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("<注解---最终通知!!>"); } }
业务类仍是之前的同样的;
<!-- 开启注解对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); }
结果:(大体的内容有了就行!)
一、没有异常:
二、发生异常:
基于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启蒙老师。^.^ !
颜群老师其余的课程也讲的很不错哦!