Spring面向切面编程(AOP)的简单实例

超、凢脫俗 2021-08-19 17:46 494阅读 0赞

AOP是Aspect Oriented Programming的简称,意思是面向切面编程。Spring AOP的实现是基于Java的代理机制,从JDK1.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)的性能的不断提高,这两种代理性能的差距会越来越小。

【实例】给一个系统的业务逻辑方法添加业务日志功能。要求在其业务方法调用前记录日志,记录方法调用的时间,调用的业务方法名和调用的参数,运行效果如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70 1

(1)实现模拟业务系统

仅模拟业务逻辑层的两个方法:register(用户注册)、comment(用户评论)。在使用Spring的时候,业务逻辑层也被称作“服务层”。

创建UserService.java用户信息业务逻辑接口。

  1. package com.pjb.aop;
  2. /**
  3. * 用户信息业务逻辑接口
  4. * @author pan_junbiao
  5. **/
  6. public interface UserService
  7. {
  8. /**
  9. * 用户注册
  10. */
  11. public boolean register(String userName, String blogUrl, String sex);
  12. /**
  13. * 用户评论
  14. */
  15. public void comment(String userName,String comments);
  16. }

创建UserServiceImpl.java用户信息业务逻辑实现类。

  1. package com.pjb.aop;
  2. /**
  3. * 用户信息业务逻辑实现类
  4. * @author pan_junbiao
  5. **/
  6. public class UserServiceImpl implements UserService
  7. {
  8. /**
  9. * 用户注册
  10. */
  11. @Override
  12. public boolean register(String userName, String blogUrl, String sex)
  13. {
  14. System.out.println("业务方法register开始执行:");
  15. System.out.println("用户名称:"+userName);
  16. System.out.println("博客地址:"+blogUrl);
  17. System.out.println("用户性别:"+sex);
  18. System.out.println("业务方法register执行完成");
  19. return true;
  20. }
  21. /**
  22. * 用户评论
  23. */
  24. @Override
  25. public void comment(String userName, String comments)
  26. {
  27. System.out.println("业务方法comment开始执行:");
  28. System.out.println("用户名称:"+userName);
  29. System.out.println("评论内容:"+comments);
  30. System.out.println("业务方法comment执行完成");
  31. }
  32. }

(2)编写切面代码

实现特定功能的切面代码在AOP概念中又称为“通知(Advice)”。通知分为前置通知、后置通知、环绕通知和异常通知。

这个分类是根据通知织入到业务代码时执行的时间划分的。前置通知是在方法执行前自动执行的通知;后置通知是在方法执行后自动执行的通知;环绕通知能力最强,它可以在方法调用前执行通知代码,可以决定是否还调用目标方法;异常通知是方法抛出异常时自动执行的切面代码。

前置通知:org.springframework.aop.MethodBeforeAdvice

后置通知:org.springframework.aop.AfterReturningAdvice

环绕通知:org.aopalliance.intercept.MethodInterceptor

异常通知:org.springframework.aop.ThrowsAdvice

这里使用前置通知。

  1. package com.pjb.aop;
  2. import org.springframework.aop.MethodBeforeAdvice;
  3. import org.springframework.lang.Nullable;
  4. import java.lang.reflect.Method;
  5. import java.text.DateFormat;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Arrays;
  8. import java.util.Date;
  9. /**
  10. * 日志通知
  11. * @author pan_junbiao
  12. **/
  13. public class LogAdvice implements MethodBeforeAdvice
  14. {
  15. @Override
  16. public void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable
  17. {
  18. DateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
  19. System.out.println("\n[系统日志]");
  20. System.out.println("执行时间:" + sdf.format(new Date()));
  21. System.out.println("方法名称:" + var1.getName());
  22. System.out.println("执行参数:" + Arrays.toString(var2));
  23. System.out.println("====================================================================");
  24. }
  25. }

编写前置通知需要实现MethodBeforeAdvice接口,这个接口要求实现的方法是:

  1. public void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;

参数Method var1是被通知目标方法对象;参数Object[] var2是传入被调用方法的参数;参数Object var3是被调用方法所属的对象实例。通过这些参数,我们几乎可以在方法代码中完成很多工作。

(3)将切面代码织入到业务对象中

如果直接访问原来的Bean,通知代码肯定不会被执行。Spring采用“代理”的方法将通知织入到原Bean中。Spring将原Bean和通知都封装到org.springframework.aop.framework.ProxyFactoryBean类别中。用户通过访问代理类访问原Bean,这样就能保证在目标方法调用前先执行前置通知的代码了。

无需编写一行程序代码,只需要通过配置完成织入的过程即可,配置工作仍然是在Spring配置文件中完成的。

在项目中添加Spring的jar包。

如果使用Maven,则pom.xml文件配置如下:

  1. <!-- Spring框架 -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-core</artifactId>
  5. <version>5.2.3.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-context</artifactId>
  10. <version>5.2.3.RELEASE</version>
  11. </dependency>

在src目录下创建applicationContext.xml配置文件。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
  3. <!-- Spring配置文件 -->
  4. <beans>
  5. <bean id="userServiceTarget" class="com.pjb.aop.UserServiceImpl"/>
  6. <bean id="logAdvice" class="com.pjb.aop.LogAdvice"/>
  7. <!-- 定义代理类 -->
  8. <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
  9. <!-- 被代理的接口 -->
  10. <property name="proxyInterfaces">
  11. <value>com.pjb.aop.UserService</value>
  12. </property>
  13. <!-- 织入的通知列表 -->
  14. <property name="interceptorNames">
  15. <list>
  16. <value>logAdvice</value>
  17. </list>
  18. </property>
  19. <!-- 被代理的原Bean -->
  20. <property name="target" ref="userServiceTarget"/>
  21. </bean>
  22. </beans>

首先定义了原Bean“userServiceTarget”和“logAdvice”。然后定义代理类,名称为userService,我们将通过这个Bean访问业务方法。代理类有3个必须设置的属性:proxyInterfaces,表示被代理的接口;interceptorNames表示织入的通知列表;target表示被代理的原Bean。

(4)运行测试

创建AopTest.java类,编写测试代码。

  1. package com.pjb.aop;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. /**
  5. * 运行测试
  6. * @author pan_junbiao
  7. **/
  8. public class AopTest
  9. {
  10. public static void main(String[] args)
  11. {
  12. //装载配置文件
  13. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  14. //获取UserService的代理类
  15. UserService userService = (UserService)context.getBean("userService");
  16. //调用注册方法
  17. userService.register("pan_junbiao的博客","https://blog.csdn.net/pan_junbiao","男");
  18. //调用用户评论方法
  19. userService.comment("pan_junbiao的博客","您好,欢迎访问 pan_junbiao的博客!");
  20. }
  21. }

发表评论

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

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

相关阅读

    相关 Spring aop面向切面编程

    aop面向切面编程: 本质: 是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面.它所面对的是处理过程中某个步骤或阶段,以获得逻辑过程中各部分之前低耦合的