之前的aop是通过手动创建代理类来进行通知的,但是在日常开发中,我们并不愿意在代码中硬编码这些代理类,我们更愿意使用DI和IOC来管理aop代理类。Spring为我们提供了以下方式来使用aop框架

ProxyFactoryBean类是FactoryBean的一个实现类,它允许指定一个bean作为目标,并且为该bean提供一组通知和顾问(这些通知和顾问最终会被合并到一个AOP代理中)它和我们之前的ProxyFactory都是Advised的实现。

以下是一个简单的例子:一个学生和一个老师,老师会告诉学生应该做什么。

  1. public class Student {
  2. public void talk() {
  3. System.out.println("I am a boy");
  4. }
  5. public void walk() {
  6. System.out.println("I am walking");
  7. }
  8. public void sleep() {
  9. System.out.println("I want to sleep");
  10. }
  11. }

老师类

  1. public class Teacher {
  2. private Student student;
  3. public void tellStudent(){
  4. student.sleep();
  5. student.talk();
  6. }
  7. public Student getStudent() {
  8. return student;
  9. }
  10. public void setStudent(Student student) {
  11. this.student = student;
  12. }
  13. }
  1. package cn.lyn4ever.aop;
  2. import org.aspectj.lang.JoinPoint;
  3. public class AuditAdvice implements MethodBeforeAdvice {
  4. @Override
  5. public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
  6. System.out.println("这个方法被通知了" + method.getName());
  7. }
  8. }
  • 然后就使用spring的IOC来管理这个通知类,在xml配置文件中声明如下:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/util
  8. https://www.springframework.org/schema/util/spring-util.xsd">
  9. <!--注入student-->
  10. <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student">
  11. </bean>
  12. <!--注入teacher-->
  13. <bean name="teacher" class="cn.lyn4ever.aop.aopconfig.Teacher">
  14. <!--注意,这个student的属性要是上边的代理类,而不是能student-->
  15. <!--<property name="student" ref="student"/>-->
  16. <property name="student" ref="proxyOne"/>
  17. </bean>
  18. <!--注入我们创建的通知类-->
  19. <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean>
  20. <!--创建代理类,使用前边写的通知进行通知,这样会使这个类上的所有方法都被通知-->
  21. <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student"
  22. p:interceptorNames-ref="interceptorNames">
  23. <!--因为interceptorNames的属性是一个可变参数,也就是一个list-->
  24. </bean>
  25. <!--在上边引入了util的名称空间,简化了书写-->
  26. <util:list id="interceptorNames">
  27. <value>advice</value>
  28. </util:list>
  29. </beans>
  • 测试类
  1. public static void main(String[] args) {
  2. GenericXmlApplicationContext context = new GenericXmlApplicationContext();
  3. context.load("application1.xml");
  4. context.refresh();
  5. Teacher teacher = (Teacher) context.getBean("teacherOne");
  6. teacher.tellStudent();
  7. }
  • 运行结果没有问题
    SpringAop在web应用中的使用

  • 以上是通过直接创建通知的方式,接下来我们试一个创建一个切入点(因为以上是对类中所有方法都进行通知,这时我们使用切入点只对其中部分方法进行通知),在xml配置文件中添加如下。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/util
  8. https://www.springframework.org/schema/util/spring-util.xsd">
  9. <!--注入student-->
  10. <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student">
  11. </bean>
  12. <!--注入teacher-->
  13. <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher">
  14. <!--注意,这个student的属性要是上边的代理类,而不是能student-->
  15. <!--<property name="student" ref="student"/>-->
  16. <property name="student" ref="proxyOne"/>
  17. </bean>
  18. <!--注入我们创建的通知类-->
  19. <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean>
  20. <!--创建代理类,使用前边写的通知进行通知,这样会使这个类上的所有方法都被通知-->
  21. <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student"
  22. p:interceptorNames-ref="interceptorNames">
  23. <!--因为interceptorNames的属性是一个可变参数,也就是一个list-->
  24. </bean>
  25. <!--在上边引入了util的名称空间,简化了书写-->
  26. <util:list id="interceptorNames">
  27. <value>advice</value>
  28. </util:list>
  29. <!--以下是使用切入点的方式来进行通知,上边的代码和上一个配置文件一样,没有修改-->
  30. <!--sutdent基本bean,我们继续使用-->
  31. <bean name="teacherTwo" p:student-ref="proxyTwo" class="cn.lyn4ever.aop.aopconfig.Teacher"/>
  32. <bean id="proxyTwo" class="org.springframework.aop.framework.ProxyFactoryBean"
  33. p:target-ref="student" p:interceptorNames-ref="interceptorAdvisorNames">
  34. </bean>
  35. <util:list id="interceptorAdvisorNames">
  36. <value>advisor</value>
  37. </util:list>
  38. <!--配置切入点bean-->
  39. <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
  40. p:advice-ref="advice">
  41. <property name="pointcut">
  42. <!--这个切入点我们用了一个匿名bean来写aspectJ的表达式,当然也可以用其他的类型切入点,这个在上边链接中能看到-->
  43. <bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut"
  44. p:expression="execution(* talk*(..))"/>
  45. </property>
  46. </bean>
  47. </beans>

SpringAop在web应用中的使用

  • 上图中的那个aspectj表达式写错了,在代码中有正确的
    title

在xml中引入如下的名称空间,为了不被影响,我册了其他多余的名称空间。然后很普通地注入我们之前那三个bean

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd
  9. ">
  10. <!--通过普通的方式来注入三个bean-->
  11. <!--注入student-->
  12. <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"/>
  13. <!--注入teacher-->
  14. <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher">
  15. <property name="student" ref="student"/>
  16. </bean>
  17. <!--注入我们创建的通知类-->
  18. <bean id="advice" class="cn.lyn4ever.aop.proxyfactory.BeforeAdvice"/>
  19. <aop:config>
  20. <aop:pointcut id="talkExecution" expression="execution(* talk*(..))"/>
  21. <aop:aspect ref="advice">
  22. <!--这个方法就是我们在自定义通知类中之写的方法-->
  23. <aop:before method="beforeSaySomething" pointcut-ref="talkExecution"/>
  24. <!--当然,还可以配置其他通知类型-->
  25. </aop:aspect>
  26. </aop:config>
  27. </beans>

在这个配置中,我们还可以配置其他类型的通知,但是这个method属性一定要写我们自定义的那个通知类中的方法

SpringAop在web应用中的使用

  • 在aop:pointcut中写expression时还支持如下语法:
  1. <aop:pointcut id="talkExecution" expression="execution(* talk*(..)) and args(String) and bean(stu*)"/>
  2. <!--
  3. 中间的这个and表示和,也可以用or来表示或
  4. args(String) 意思是参数类型是string,也可是自定义的类,这个后边有例子
  5. bean(stu*) 意思是bean的id是以stu开头的,常用的就是用bean(*Service*)来表示服务层的bean
  6. -->

虽然是通过注解的方式来声明注解类,但是还是需要在xml中配置一点点内容(通过注解的方式也可以配置,但是在springboot中要使用的话有更方便的方式)

  • 为了方便,就只写了一个HighStudent,而且直接调用它的方法,不依赖于外部的teacher实例来调用
  1. package cn.lyn4ever.aop.aspectj;
  2. import cn.lyn4ever.aop.aopconfig.Teacher;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * 声明这是一个SpringBean,由Spring来管理它
  6. */
  7. @Component
  8. public class HighStudent {
  9. public void talk() {
  10. System.out.println("I am a boy");
  11. }
  12. public void walk() {
  13. System.out.println("I am walking");
  14. }
  15. /**
  16. * 这个方法添加一个teacher来做为参数,为了配置后边切入点中的args()
  17. * @param teacher
  18. */
  19. public void sleep(Teacher teacher) {
  20. System.out.println("I want to sleep");
  21. }
  22. }
  • 创建切面类
  1. package cn.lyn4ever.aop.aspectj;
  2. import cn.lyn4ever.aop.aopconfig.Teacher;
  3. import org.aspectj.lang.JoinPoint;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. import org.springframework.stereotype.Component;
  10. /**
  11. * 声明切面类,也就是包括切点和通知
  12. */
  13. @Component //声明交由spring管理
  14. @Aspect //表示这是一个切面类
  15. public class AnnotatedAdvice {
  16. /*
  17. 创建切入点,当然也可以是多个
  18. */
  19. @Pointcut("execution(* talk*(..))")
  20. public void talkExecution(){}
  21. @Pointcut("bean(high*)")//这里为什么是high,因为我们这回测试bean是highStudent
  22. public void beanPoint(){}
  23. @Pointcut("args(value)")
  24. public void argsPoint(Teacher value){}
  25. /*
  26. 创建通知,当然也可以是多个
  27. 这个注解的参数就是上边的切入点方法名,注意有的还带参数
  28. 这个通知方法的参数和之前一样,榀加JoinPoint,也可不加
  29. */
  30. @Before("talkExecution()")
  31. public void doSomethingBefore(JoinPoint joinPoint){
  32. System.out.println("before: Do Something"+joinPoint.getSignature().getName()+"()");
  33. }
  34. /**
  35. * 环绕通知请加上ProceedingJoinPoint参数 ,它是joinPoint的子类
  36. * 因为你要放行方法的话,必须要加这个
  37. * @param joinPoint
  38. * @param teacher
  39. */
  40. @Around("argsPoint(teacher) && beanPoint()")
  41. public Object doSomethindAround(ProceedingJoinPoint joinPoint, Teacher teacher) throws Throwable {
  42. System.out.println("Around: Before Do Something"+joinPoint.getSignature().getName()+"()");
  43. Object proceed = joinPoint.proceed();
  44. System.out.println("Around: After Do Something"+joinPoint.getSignature().getName()+"()");
  45. return proceed;
  46. }
  47. }
  • xml中配置开启扫描注解
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/aop
  9. http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
  10. <!--通知Spring扫描@Aspect注解-->
  11. <aop:aspectj-autoproxy/>
  12. <!--配置扫描包,扫描@Component-->
  13. <context:component-scan base-package="cn.lyn4ever.aop.aspectj"/>
  14. </beans>
  • 使用Java注解配置的方式配置扫描注解
  1. @Configuration //声明这是一个配置类
  2. @ComponentScan("cn.lyn4ever.aop.aspectj")
  3. @EnableAspectJAutoProxy(proxyTargetClass = true)//相当于xml中的<aop:aspectj-autoproxy/>
  4. public class BeanConfig {
  5. }
  • 测试方法
  1. package cn.lyn4ever.aop.aspectj;
  2. import cn.lyn4ever.aop.aopconfig.Teacher;
  3. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  4. import org.springframework.context.support.GenericApplicationContext;
  5. import org.springframework.context.support.GenericXmlApplicationContext;
  6. public class AspectMain {
  7. public static void main(String[] args) {
  8. // xmlConfig();
  9. javaConfig();
  10. }
  11. private static void javaConfig() {
  12. GenericApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
  13. HighStudent student = (HighStudent) context.getBean("highStudent");
  14. student.sleep(new Teacher());//应该被环绕通知
  15. System.out.println();
  16. student.talk();//前置通知
  17. System.out.println();
  18. student.walk();//不会被通知
  19. System.out.println();
  20. }
  21. private static void xmlConfig(){
  22. GenericXmlApplicationContext context = new GenericXmlApplicationContext();
  23. context.load("application_aspect.xml");
  24. context.refresh();
  25. HighStudent student = (HighStudent) context.getBean("highStudent");
  26. student.sleep(new Teacher());//应该被环绕通知
  27. System.out.println();
  28. student.talk();//前置通知
  29. System.out.println();
  30. student.walk();//不会被通知
  31. System.out.println();
  32. }
  33. }

SpringAOP在web应用中的使用

项目代码地址,如果觉得还不错的话,给个star吧

版权声明:本文为Lyn4ever原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Lyn4ever/p/12057672.html