什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
image.png

AOP在Spring中的作用

提供声明式事务,允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如:日志、安全、缓存、事务等。
  • 切面(ASPECT):横切关注点被模块化的特殊对象。即它是一个类。
  • 通知(Advice):切面必须要完成的工作。即它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知执行的“地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

image.png
Spring AOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
image.png
即AOP在不改变原有代码的情况下,去增加新的功能。

使用Spring实现AOP

Maven依赖

pom.xml

使用AOP织入,需要导入一个依赖包:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.aspectj</groupId>
  4. <artifactId>aspectjweaver</artifactId>
  5. <version>1.9.5</version>
  6. </dependency>
  7. </dependencies>

方式一、使用Spring的API接口【主要Spring API接口实现】

interface

  1. package com.kuang.service;
  2. public interface UserService {
  3. public void add();
  4. public void delete();
  5. public void update();
  6. public void select();
  7. }

Impl-实现

  1. package com.kuang.service;
  2. public class UserServiceImpl implements UserService {
  3. public void add() {
  4. System.out.println("增加了一个用户");
  5. }
  6. public void delete() {
  7. System.out.println("删除了一个用户");
  8. }
  9. public void update() {
  10. System.out.println("更新了一个用户");
  11. }
  12. public void select() {
  13. System.out.println("查询了一个用户");
  14. }
  15. }

Aop

  1. package com.kuang.log;
  2. import org.springframework.aop.MethodBeforeAdvice;
  3. import java.lang.reflect.Method;
  4. public class Log implements MethodBeforeAdvice {
  5. // method: 要执行的目标对象的方法
  6. // args: 参数
  7. // target: 目标对象
  8. public void before(Method method, Object[] objects, Object target) throws Throwable {
  9. System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
  10. }
  11. }
  1. package com.kuang.log;
  2. import org.springframework.aop.AfterReturningAdvice;
  3. import java.lang.reflect.Method;
  4. public class AfterLog implements AfterReturningAdvice {
  5. // returnValue-返回值
  6. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  7. System.out.println("执行了" + method.getName() + "方法,返回结果为:" + returnValue);
  8. }
  9. }

applicationContext.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. 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. <!-- 注册bean -->
  10. <bean id="userServiceImpl" class="com.kuang.service.UserServiceImpl"/>
  11. <bean id="log" class="com.kuang.log.Log"/>
  12. <bean id="afterLog" class="com.kuang.log.AfterLog"/>
  13. <!-- 方式一、使用原生Spring API接口 -->
  14. <!-- 配置aop:需要导入aop的约束 -->
  15. <aop:config>
  16. <!-- 切入点:expression表达式,expression(要执行的位置) -->
  17. <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.*ServiceImpl.*(..))"/>
  18. <!-- 执行环绕增加 -->
  19. <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
  20. <aop:advisor advice-ref="afterLog" pointcut="execution(* com.kuang.service.*ServiceImpl.*(..))"/>
  21. </aop:config>
  22. </beans>

test

  1. import com.kuang.service.UserService;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class AopTest {
  5. public static void main(String[] args) {
  6. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. // Aop动态代理代理的是接口
  8. UserService userService = context.getBean("userServiceImpl", UserService.class);
  9. userService.add();
  10. }
  11. }

方式二、自定义类实现AOP【主要是切面定义】

自定义切面类

  1. package com.kuang.diy;
  2. public class DiyPointCut {
  3. public void before() {
  4. System.out.println("==方法执行前==");
  5. }
  6. public void after() {
  7. System.out.println("==方法执行后==");
  8. }
  9. }

applicationContext.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. 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. <!-- 注册bean -->
  10. <bean id="userServiceImpl" class="com.kuang.service.UserServiceImpl"/>
  11. <!-- 方式二、自定义类 -->
  12. <bean id="diy" class="com.kuang.diy.DiyPointCut"/>
  13. <aop:config>
  14. <!-- 自定义切面,ref要引用的类 -->
  15. <aop:aspect ref="diy">
  16. <!-- 切入点 -->
  17. <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
  18. <aop:before method="before" pointcut-ref="point"/>
  19. <aop:after method="after" pointcut-ref="point"/>
  20. </aop:aspect>
  21. </aop:config>
  22. </beans>

test

  1. import com.kuang.service.UserService;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class AopTest {
  5. public static void main(String[] args) {
  6. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. // Aop动态代理代理的是接口
  8. UserService userService = context.getBean("userServiceImpl", UserService.class);
  9. userService.add();
  10. }
  11. }

方式三、使用注解实现

注解切面类

  1. package com.kuang.diy;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.Signature;
  4. import org.aspectj.lang.annotation.After;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. // 方式三、使用注解方式实现AOP
  9. @Aspect
  10. public class AnnotationPointCut {
  11. @Before("execution(* com.kuang.service.UserServiceImpl.*(..)))")
  12. public void before() {
  13. System.out.println("--方法执行前--");
  14. }
  15. @After("execution(* com.kuang.service.UserServiceImpl.*(..)))")
  16. public void after() {
  17. System.out.println("--方法执行后--");
  18. }
  19. // 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入点的连接点
  20. @Around("execution(* com.kuang.service.UserServiceImpl.*(..)))")
  21. public void around(ProceedingJoinPoint jp) throws Throwable {
  22. System.out.println("--环绕前--");
  23. Signature signature = jp.getSignature();
  24. System.out.println("签名:" + signature);
  25. // 执行方法
  26. Object proceed = jp.proceed();
  27. System.out.println("--环绕后--");
  28. }
  29. }

applicationContext.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. 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. <!-- 注册bean -->
  10. <bean id="userServiceImpl" class="com.kuang.service.UserServiceImpl"/>
  11. <bean id="log" class="com.kuang.log.Log"/>
  12. <bean id="afterLog" class="com.kuang.log.AfterLog"/>
  13. <!-- 方式一、使用原生Spring API接口 -->
  14. <!-- 配置aop:需要导入aop的约束 -->
  15. <!-- <aop:config>-->
  16. <!-- &lt;!&ndash; 切入点:expression表达式,expression(要执行的位置) &ndash;&gt;-->
  17. <!-- <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.*ServiceImpl.*(..))"/>-->
  18. <!-- &lt;!&ndash; 执行环绕增加 &ndash;&gt;-->
  19. <!-- <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->
  20. <!-- <aop:advisor advice-ref="afterLog" pointcut="execution(* com.kuang.service.*ServiceImpl.*(..))"/>-->
  21. <!-- </aop:config>-->
  22. <!-- 方式二、自定义类 -->
  23. <!-- <bean id="diy" class="com.kuang.diy.DiyPointCut"/>-->
  24. <!-- <aop:config>-->
  25. <!-- &lt;!&ndash; 自定义切面,ref要引用的类 &ndash;&gt;-->
  26. <!-- <aop:aspect ref="diy">-->
  27. <!-- &lt;!&ndash; 切入点 &ndash;&gt;-->
  28. <!-- <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>-->
  29. <!-- <aop:before method="before" pointcut-ref="point"/>-->
  30. <!-- <aop:after method="after" pointcut-ref="point"/>-->
  31. <!-- </aop:aspect>-->
  32. <!-- </aop:config>-->
  33. <!-- 方式三、使用注解方式实现AOP -->
  34. <bean id="annotationPointCut" class="com.kuang.diy.AnnotationPointCut"/>
  35. <!--
  36. 开启注解支持
  37. JDK(默认):proxy-target-class="false"
  38. cglib:proxy-target-class="true"
  39. -->
  40. <aop:aspectj-autoproxy proxy-target-class="false"/>
  41. </beans>

test

  1. import com.kuang.service.UserService;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class AopTest {
  5. public static void main(String[] args) {
  6. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. // Aop动态代理代理的是接口
  8. UserService userService = context.getBean("userServiceImpl", UserService.class);
  9. userService.add();
  10. }
  11. }

执行结果

  1. --环绕前--
  2. 签名:void com.kuang.service.UserServiceImpl.add()
  3. --方法执行前--
  4. 增加了一个用户
  5. --方法执行后--
  6. --环绕后--