spring之AOP学习笔记

迈不过友情╰ 2022-01-22 14:17 330阅读 0赞

目录

一、AOP术语

二、代理模式

三、动态代理

四、spring中xml版实现AOP

五、spring中注解版实现AOP


一、AOP术语

连接点(Joinpoint):程序执行的某一个特定位置,如类初始前后,方法的运行前后。而Spring只支持方法的连接点。(拦截一个方法有多个位置,就是有多种选择)

切点(Pointcut):切点可以定位到相应的连接点,一个切点可以定位多个连接点。(定位到已经选择的位置)

增强(Advice):又被称为通知,完成逻辑的增强。(在指定的位置开始做业务逻辑—-做事情)

目标对象(Target):增强逻辑织入的目标类。(在指定位置堵指定的目标对象)

引介(Introduction):特殊的增强,为类添加一些属性和方法。

织入(Weaving): 将增强添加到目标类的具体连接点上的过程。Spring使用动态代理织入。

代理(Proxy):一个类(原类)被织入增强(逻辑)后,就产生一个结果类,称为代理类。

切面(Aspect):由切点和增强组成

Spring AOP->不是标准的完整的AOP
只能在方法的运行前与运行后执行
Spring实现AOP
->JDK本身支持的动态代理(只支持有接口的类)
->CGLIB的动态代理

二、代理模式

代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

抽象主题角色:

声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题。

代理主题(Proxy**)角色:**

代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;

代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);

代理角色通常在将客户端调用传递给真实的主题之前或之后(前置增强/通知,后置增强/通知),都要执行某个操作,而不是单纯地将调用传递给真实主题对象。

真实主题角色:

定义了代理角色所代表地真实对象

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70

三、动态代理

新建动态代理类:

  1. package cn.mesmile.dynamic.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. /**
  6. * @Created with IDEA
  7. * @author: Super Zheng
  8. * @Description: 动态代理
  9. * @Date:2019/6/6
  10. * @Time:17:26
  11. */
  12. public class DynamicProxy<T> {
  13. /**
  14. * @param t 真实主题角色
  15. * @return 动态代理角色
  16. */
  17. public T getInstance(T t){
  18. // 新建事务对象
  19. TxManager txManager = new TxManager();
  20. /**
  21. * ClassLoader loader,类加载器(要操作class字节码文件)
  22. * Class<?>[] interfaces,接口(数组)---抽象主题代理角色(数组:代理多个)
  23. * InvocationHandler h 处理器
  24. */
  25. T proxy = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
  26. /**
  27. * 确定咋们要做的事是什么
  28. * @param proxy 代理对象
  29. * @param method 执行的方法
  30. * @param args 方法中的参数
  31. * @return
  32. * @throws Throwable
  33. */
  34. @Override
  35. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  36. Object invoke = null;
  37. try {
  38. // 开始事务
  39. txManager.begin();
  40. // 执行真实对象的方法
  41. invoke = method.invoke(t, args);
  42. // 提交事务
  43. txManager.commit();
  44. } catch (Exception e) {
  45. // 回滚事务
  46. txManager.rollback();
  47. e.printStackTrace();
  48. } finally {
  49. // 关闭事务
  50. txManager.close();
  51. }
  52. return invoke;
  53. }
  54. });
  55. return proxy;
  56. }
  57. }

新建接口类:

  1. package cn.mesmile.dynamic.proxy;
  2. /**
  3. * @Created with IDEA
  4. * @author: Super Zheng
  5. * @Description: 接口类
  6. * @Date:2019/6/6
  7. * @Time:17:29
  8. */
  9. public interface IUserService {
  10. /**
  11. * 保存方法
  12. */
  13. void save();
  14. }

新建实现类:

  1. package cn.mesmile.dynamic.proxy;
  2. /**
  3. * @Created with IDEA
  4. * @author: Super Zheng
  5. * @Description: 实现类
  6. * @Date:2019/6/6
  7. * @Time:17:29
  8. */
  9. public class UserServiceImpl implements IUserService {
  10. @Override
  11. public void save() {
  12. System.out.println("执行保存方法");
  13. }
  14. }

新建模拟事务类:

  1. package cn.mesmile.dynamic.proxy;
  2. /**
  3. * @Created with IDEA
  4. * @author: Super Zheng
  5. * @Description: 模拟事务类
  6. * @Date:2019/6/6
  7. * @Time:17:30
  8. */
  9. public class TxManager {
  10. public void begin(){
  11. System.out.println("开始事务");
  12. }
  13. public void commit(){
  14. System.out.println("提交事务");
  15. }
  16. public void rollback(){
  17. System.out.println("回滚事务");
  18. }
  19. public void close(){
  20. System.out.println("关闭事务");
  21. }
  22. }

新建测试类:

  1. package cn.mesmile.dynamic.proxy;
  2. import org.junit.Test;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Proxy;
  7. /**
  8. * @Created with IDEA
  9. * @author: Super Zheng
  10. * @Description: 测试类
  11. * @Date:2019/6/6
  12. * @Time:17:52
  13. */
  14. public class ProxyTest {
  15. @Test
  16. public void test() throws Exception{
  17. // 新建真实对象
  18. IUserService userService = new UserServiceImpl();
  19. DynamicProxy<IUserService> dynamicProxy = new DynamicProxy<>();
  20. // 传入真实对象,获得代理对象
  21. IUserService proxy = dynamicProxy.getInstance(userService);
  22. System.out.println(proxy);
  23. System.out.println(proxy.getClass());
  24. proxy.save();
  25. }
  26. }

动态代理测试结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70 1

四、spring中xml版实现AOP

使用springAOP,配置的时候一定要记住三个要素:何时,何地,做什么事

何时:在执行方法之前/之后/有异常…

何地:在哪些包中的哪些类的哪些方法上面执行

做什么事: 在UserServie中执行update方法之前添加日志

1.导入jar包

  1. <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-context</artifactId>
  5. <version>5.1.1.RELEASE</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
  8. <dependency>
  9. <groupId>org.springframework</groupId>
  10. <artifactId>spring-test</artifactId>
  11. <version>5.1.1.RELEASE</version>
  12. <scope>test</scope>
  13. </dependency>
  14. <dependency>
  15. <groupId>junit</groupId>
  16. <artifactId>junit</artifactId>
  17. <version>4.12</version>
  18. </dependency>
  19. <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
  20. <dependency>
  21. <groupId>org.aspectj</groupId>
  22. <artifactId>aspectjweaver</artifactId>
  23. <version>1.9.1</version>
  24. </dependency>

2.创建一个接口类

20190606233054998.png

3.创建接口的实现类

20190606233142748.png

4.创建一个模拟的事务类

20190606234048314.png

5.在recourses资源文件夹下创建xml文件,springContext-aop.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="
  6. 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"
  10. >
  11. <bean id="studentService" class="cn.mesmile.xml.aop.StudentServiceImpl">
  12. </bean>
  13. <bean id="txManager" class="cn.mesmile.xml.aop.TxManager">
  14. </bean>
  15. <!--
  16. 何时,何地,做什么事
  17. -->
  18. <aop:config>
  19. <!--
  20. 配置:切点
  21. 确定是哪些类的哪些方法
  22. execution(* cn.mesmile.xml.aop.I*Service.*(..))
  23. execution:表达式
  24. *:任意返回值
  25. cn.mesmile.xml.aop 包名
  26. *:所有类
  27. *:所有方法
  28. (..):传入方法的任意参数
  29. -->
  30. <aop:pointcut id="pointcut" expression="execution(* cn.mesmile.xml.aop.I*Service.*(..))"/>
  31. <!--切面(由切点和增强组成),同时引入需要在切面调用方法的对象-->
  32. <aop:aspect ref="txManager" >
  33. <!--在调用方法之前,调用begin这个方法-->
  34. <aop:before method="begin" pointcut-ref="pointcut"/>
  35. <!--在正常执行方法后调用commit方法-->
  36. <aop:after-returning method="commit" pointcut-ref="pointcut"/>
  37. <!--在调用方法出现异常后,调用rollback方法 若需要显示异常,则配置throwing,值必须和方法传入的参数一致-->
  38. <aop:after-throwing method="rollback" pointcut-ref="pointcut" throwing="e"/>
  39. <!--在调用方法之后,调用close这个方法-->
  40. <aop:after method="close" pointcut-ref="pointcut"/>
  41. </aop:aspect>
  42. </aop:config>
  43. </beans>

6.测试

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70 2

xml中aop通知方式:环绕通知

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70 3

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70 4

最终测试结果和单独配置增强的效果是一致的;

五、spring中注解版实现AOP

1.新建模拟事务类

  1. @Component
  2. @Aspect
  3. public class TxManager {
  4. @Pointcut("execution(* cn.mesmile.aop.I*Service.*(..))")
  5. public void pointcut(){}
  6. @Before("pointcut()")
  7. public void begin(){
  8. System.out.println("开始事务");
  9. }
  10. @AfterReturning("pointcut()")
  11. public void commit(){
  12. System.out.println("提交事务");
  13. }
  14. @AfterThrowing(value = "pointcut()",throwing = "e")
  15. public void rollback(Throwable e){
  16. System.out.println("回滚事务"+e.getMessage());
  17. }
  18. @After("pointcut()")
  19. public void close(){
  20. System.out.println("关闭事务");
  21. }
  22. }

2.新建接口类

  1. public interface IStudentService {
  2. /**
  3. * 保存方法
  4. */
  5. void save();
  6. }

3.新建实现类

  1. @Service
  2. public class StudentServiceImpl implements IStudentService {
  3. @Override
  4. public void save() {
  5. System.out.println("调用了保存的方法");
  6. }
  7. }

4.新建一个spring配置文件springContext-aop.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  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 http://www.springframework.org/schema/context/spring-context.xsd"
  10. >
  11. <!--扫描包-->
  12. <context:component-scan base-package="cn.mesmile.aop"/>
  13. <!--让spring aop 支持注解-->
  14. <aop:aspectj-autoproxy/>
  15. </beans>

5.测试

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1cHJlemhlbmc_size_16_color_FFFFFF_t_70 5

注解的方式二:环绕注解配置

1.修改模拟的事务类,添加一个环绕方法,加上环绕注解,同时将其它通知注解注释

  1. package cn.mesmile.aop;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.springframework.stereotype.Component;
  5. /**
  6. * @Created with IDEA
  7. * @author: Super Zheng
  8. * @Description: java类作用描述
  9. * @Date:2019/6/6
  10. * @Time:22:34
  11. */
  12. @Component
  13. @Aspect
  14. public class TxManager {
  15. @Pointcut("execution(* cn.mesmile.aop.I*Service.*(..))")
  16. public void pointcut(){}
  17. // @Before("pointcut()")
  18. public void begin(){
  19. System.out.println("开始事务");
  20. }
  21. // @AfterReturning("pointcut()")
  22. public void commit(){
  23. System.out.println("提交事务");
  24. }
  25. // @AfterThrowing(value = "pointcut()",throwing = "e")
  26. public void rollback(Throwable e){
  27. System.out.println("回滚事务"+e.getMessage());
  28. }
  29. // @After("pointcut()")
  30. public void close(){
  31. System.out.println("关闭事务");
  32. }
  33. @Around("pointcut()")
  34. public void around(ProceedingJoinPoint joinPoint){
  35. try {
  36. begin();
  37. // 执行方法
  38. joinPoint.proceed();
  39. commit();
  40. } catch (Throwable e) {
  41. rollback(e);
  42. } finally {
  43. close();
  44. }
  45. }
  46. }

2.最终测试环绕通知注解结果和单独配置注解增强通知的效果是一致的;

发表评论

表情:
评论列表 (有 0 条评论,330人围观)

还没有评论,来说两句吧...

相关阅读

    相关 spring学习笔记: Spring AOP

    AOP的概念不好解释, 有一大堆的术语都很拗口,还是先看一个代码例子,在src根目录下面新建一个package叫做aop,把这个单元所有的代码都放在这个包里面,有一个接口Boo

    相关 Spring学习笔记 AOP

    AOP,也就是面向方面编程或者说面向面编程,是一种很重要的思想。在企业级系统中经常需要打印日志、事务管理这样针对某一方面的需求,但是传统的面向对象编程无法很好的满足这些需求。因

    相关 Spring学习AOP

    什么是AOP AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“