当前位置:   article > 正文

Spring学习日记:Aop五种增强+注解开发Ioc/Aop_java环绕增强

java环绕增强

自述:此篇文章主要记录了本人学习Spring的日常,在CSDN上发表,一是为了方便今后回顾,二也是想分享给有需要的人。

目录

1.Aop五种增强

一.概念

二.前置增强

三.后置增强

四.环绕增强

五.异常增强

六.最终增强

2.P命名空间注入

3.不同数据类型注入 

一.使用list

二.使用set

三.使用Map

四.使用prop

4.使用注解实现Spring Ioc

一.Ioc常用注解

二.实现

5.使用Java标准注解完成装配

6.使用注解实现Spring Aop


1.Aop五种增强

一.概念

以下是AOP的五种增强概念:

Aop五种增强
名称作用
前置增强(Before Advice)在目标方法执行之前执行的横切逻辑。可以用于执行一些预处理操作,如参数验证、权限检查等。
后置增强(After Advice)在目标方法执行之后执行的横切逻辑。可以用于执行一些清理操作,如资源释放、日志记录等。
环绕增强(Around Advice)在目标方法执行前后都可以执行的横切逻辑。环绕增强可以完全控制目标方法的执行过程,可以选择是否调用目标方法以及何时调用。
异常增强(After Throwing Advice)在目标方法抛出异常时执行的横切逻辑。可以用于处理异常、发送通知等
最终增强(After finally)它在目标方法执行结束后(无论是否发生异常)都会执行。最终增强通常用于资源清理或日志记录等必须执行的操作。

二.前置增强

前置增强的特点包括:

  1. 执行时机:前置增强在目标方法执行之前执行,可以在目标方法执行之前干预和处理。

  2. 参数获取:在前置增强中可以获取目标方法的参数信息,并对参数进行校验或者使用参数进行一些预处理。

  3. 控制流程:通过前置增强可以决定是否继续执行目标方法,可以根据具体场景判断是否需要终止目标方法的执行。

使用前置增强可以实现以下功能:

  1. 参数校验:在前置增强中可以对目标方法的参数进行验证,确保参数满足要求,避免不合法的参数进入目标方法。

  2. 权限验证:前置增强可以用于验证用户的权限,检查当前用户是否有权执行目标方法。

  3. 日志记录:在前置增强中可以记录目标方法的调用信息,如方法名、参数值等,用于跟踪代码执行流程和排查问题。

  4. 缓存数据:在前置增强中可以检查缓存中是否存在所需数据,如果存在则直接返回缓存数据,避免执行耗时的数据库查询操作。

前置增强可以与其他增强概念结合使用,例如返回后通知、异常通知等,以实现更复杂的切面逻辑。通过AOP的前置增强,可以在目标方法执行前加入额外的处理逻辑,提高代码的可维护性和重用性。 

  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.Aspect;
  3. import org.aspectj.lang.annotation.Before;
  4. import org.springframework.stereotype.Component;
  5. @Aspect
  6. @Component
  7. public class MyAspect {
  8. @Before("execution(* com.example.MyService.myMethod(..))")
  9. public void beforeMethod(JoinPoint joinPoint) {
  10. // 在目标方法执行之前执行的通知逻辑
  11. // 可以在此处编写需要在目标方法执行之前执行的代码或操作
  12. }
  13. }

三.后置增强

后置增强的特点包括:

  1. 执行时机:后置增强在目标方法成功执行并返回结果后执行,不会在目标方法抛出异常时执行。

  2. 获取返回值:在后置增强中可以获取目标方法的返回值,并进行相应的处理。

  3. 不会改变返回结果:后置增强不能修改目标方法的返回结果,只能对其进行处理或记录。

使用后置增强可以实现以下功能:

  1. 返回结果处理:可以对目标方法的返回结果进行一些后续处理,如数据加工、格式转换等。

  2. 日志记录:在后置增强中可以记录目标方法的执行结果或其他相关信息,用于跟踪代码执行流程和调试。

  3. 缓存数据:在后置增强中可以将目标方法的返回结果缓存起来,以提高后续相同请求的性能。

  4. 资源释放:后置增强可以用于执行一些资源的释放操作,例如关闭数据库连接、释放文件句柄等。 

  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.After;
  3. import org.springframework.stereotype.Component;
  4. @Aspect
  5. @Component
  6. public class MyAspect {
  7. @After("execution(* com.example.MyService.myMethod(..))")
  8. public void afterMethod(JoinPoint joinPoint) {
  9. // 在目标方法执行之后执行的通知逻辑
  10. // 可以在此处编写需要在目标方法执行之后执行的代码或操作
  11. }
  12. }

四.环绕增强

环绕增强的特点包括:

  1. 完全控制:环绕增强可以完全控制目标方法的执行,包括在目标方法执行前、执行后甚至替代目标方法的执行。

  2. 参数获取和修改:在环绕增强中可以获取目标方法的参数信息,并且有能力修改这些参数。

  3. 返回结果处理:环绕增强可以获取目标方法的返回结果,并且可以决定返回什么样的结果或者是否返回结果。

使用环绕增强可以实现以下功能:

  1. 性能监控:通过环绕增强可以统计目标方法的执行时间,收集性能指标,用于系统性能监控和优化。

  2. 事务管理:环绕增强可以在目标方法执行前后开启和提交事务,确保数据操作的一致性和完整性。

  3. 异常处理:通过环绕增强可以捕获目标方法抛出的异常,并根据需要进行处理、记录或者转换。

  4. 权限验证:在环绕增强中可以检查用户的权限,决定是否允许调用目标方法。

  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. import org.aspectj.lang.annotation.Around;
  3. import org.springframework.stereotype.Component;
  4. @Aspect
  5. @Component
  6. public class MyAspect {
  7. @Around("execution(* com.example.MyService.myMethod(..))")
  8. public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
  9. // 在目标方法前后执行的通知逻辑
  10. // 可以在此处编写需要在目标方法前后执行的代码或操作
  11. Object result;
  12. try {
  13. // 调用proceed()方法执行目标方法
  14. result = joinPoint.proceed();
  15. // 目标方法执行后的操作
  16. // 可以获取目标方法的返回值:result
  17. } catch (Exception ex) {
  18. // 异常处理操作
  19. throw ex;
  20. }
  21. return result;
  22. }
  23. }

五.异常增强

异常增强的特点包括:

  1. 执行时机:异常增强在目标方法抛出异常后执行,不会在目标方法正常返回时执行。

  2. 获取异常信息:在异常增强中可以获取目标方法抛出的异常及其相关信息。

  3. 异常处理:异常增强可以对目标方法抛出的异常进行处理,例如记录日志、发送通知、回滚事务等。

使用异常增强可以实现以下功能:

  1. 异常处理与转换:通过异常增强可以捕获目标方法抛出的异常,并根据需要进行处理、记录或者将异常转换成其他类型的异常。

  2. 事务管理:异常增强可以在目标方法抛出异常时进行事务的回滚操作,保证数据的一致性和完整性。

  3. 异常通知:异常增强可以用于向管理员或用户发送异常通知,以便及时处理和修复问题。

  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.AfterThrowing;
  3. import org.springframework.stereotype.Component;
  4. @Aspect
  5. @Component
  6. public class MyAspect {
  7. @AfterThrowing(pointcut = "execution(* com.example.MyService.myMethod(..))", throwing = "ex")
  8. public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {
  9. // 在目标方法抛出异常后执行的通知逻辑
  10. // 可以在此处编写需要在目标方法抛出异常后执行的代码或操作
  11. }
  12. }

六.最终增强

最终增强的特点包括:

  1. 执行时机:最终增强在目标方法执行完毕后执行,无论目标方法是否抛出异常。

  2. 无法获取返回结果:最终增强无法获取目标方法的返回结果,因为该结果在最终增强执行时已经确定或失效。

  3. 能够处理异常:最终增强可以捕获目标方法抛出的异常,并进行相应的处理或记录。

使用最终增强可以实现以下功能:

  1. 资源释放:最终增强可以用于执行一些资源的释放操作,例如关闭数据库连接、释放文件句柄等。

  2. 清理操作:最终增强可以用于执行一些清理操作,如清除临时文件、释放线程相关资源等。

  3. 异常处理:最终增强可以捕获目标方法抛出的异常,并执行相应的处理逻辑,以便进行错误处理或日志记录。

  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.After;
  3. import org.springframework.stereotype.Component;
  4. @Aspect
  5. @Component
  6. public class MyAspect {
  7. @After("execution(* com.example.MyService.myMethod(..))")
  8. public void afterMethod(JoinPoint joinPoint) {
  9. // 在目标方法执行之后执行的通知逻辑
  10. // 可以在此处编写需要在目标方法执行之后执行的代码或操作
  11. }
  12. }

2.P命名空间注入

Spring框架中,p命名空间注入是一种简化XML配置的方式,它允许我们使用更简洁的语法来进行属性注入。这种方式可以减少配置的冗余代码,使配置文件更加清晰易读。

使用p命名空间注入时,我们通过在XML配置文件中使用xmlns:p="http://www.springframework.org/schema/p"来引入命名空间,并使用p:前缀来指定属性值。

  1. <bean id="唯一标识" class="类的全路径"
  2. p:"属性1"="注入的值" p:"属性2"="注入的值" />
  3. <bean id="唯一标识" class="类的全路径"
  4. p:属性-ref="注入的Bean" />

注:配置文件中使用p命名空间时,需要先添加p命名空间的声明 xmlns:p="http://www.springframework.org/schema/p"

3.不同数据类型注入 

一.使用list

  1. package com.cskt.DayTwo;
  2. import lombok.Data;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Properties;
  6. import java.util.Set;
  7. @Data
  8. public class JavaCollection {
  9. //注入 集合 有序 允许重复 有下标
  10. List manList;
  11. }
  1. <!--注入不同数据类型 list-->
  2. <bean id="JavaCollection" class="com.cskt.DayTwo.JavaCollection">
  3. <property name="manList">
  4. <list>
  5. <value>List01</value>
  6. <value>List02</value>
  7. <value>List03</value>
  8. <value>List04</value>
  9. </list>
  10. </property>
  11. </bean>
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. JavaCollection collection = (JavaCollection) context.getBean("JavaCollection");
  3. collection.getManList().stream().forEach(item->{
  4. System.out.println(item);
  5. });

 二.使用set

  1. package com.cskt.DayTwo;
  2. import lombok.Data;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Properties;
  6. import java.util.Set;
  7. @Data
  8. public class JavaCollection {
  9. //注入 set集合 无序 不允许重复
  10. Set manSet;
  11. }
  1. <!--注入不同数据类型 set-->
  2. <bean id="JavaCollection" class="com.cskt.DayTwo.JavaCollection">
  3. <property name="manSet">
  4. <set>
  5. <value>Set01</value>
  6. <value>Set02</value>
  7. <value>Set03</value>
  8. <value>Set04</value>
  9. </set>
  10. </property>
  11. </bean>
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. JavaCollection collection = (JavaCollection) context.getBean("JavaCollection");
  3. collection.getManSet().stream().forEach(item->{
  4. System.out.println(item);
  5. });

三.使用Map

  1. package com.cskt.DayTwo;
  2. import lombok.Data;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Properties;
  6. import java.util.Set;
  7. @Data
  8. public class JavaCollection {
  9. //注入 键值对 key值不能重复 value 可以
  10. Map manMap;
  11. }
  1. <!--注入不同数据类型 map-->
  2. <bean id="JavaCollection" class="com.cskt.DayTwo.JavaCollection">
  3. <property name="manMap">
  4. <map>
  5. <entry key="1" value="Map01" />
  6. <entry key="2" value="Map02" />
  7. <entry key="3" value="Map03" />
  8. <entry key="4" value="Map04" />
  9. </map>
  10. </property>
  11. </bean>
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. JavaCollection collection = (JavaCollection) context.getBean("JavaCollection");
  3. collection.getManMap().forEach((k,v)->{
  4. System.out.println(v);
  5. });

四.使用prop

  1. package com.cskt.DayTwo;
  2. import lombok.Data;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Properties;
  6. import java.util.Set;
  7. @Data
  8. public class JavaCollection {
  9. Properties manProp;
  10. }
  1. <!--注入不同数据类型 Prop-->
  2. <bean id="JavaCollection" class="com.cskt.DayTwo.JavaCollection">
  3. <property name="manProp">
  4. <props>
  5. <prop key="one">Prop01</prop>
  6. <prop key="one">Prop04</prop>
  7. <prop key="two">Prop05</prop>
  8. <prop key="three">Prop06</prop>
  9. <prop key="four">Prop07</prop>
  10. </props>
  11. </property>
  12. </bean>
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. JavaCollection collection = (JavaCollection) context.getBean("JavaCollection");
  3. collection.getManProp().forEach((k,v)->{
  4. System.out.println(v);
  5. });

4.使用注解实现Spring Ioc

一.Ioc常用注解

常用注解
名称作用
@Component将类标记为组件,表示它需要由Spring容器进行管理。
@Autowired

自动装配依赖,将相关的依赖注入到标记了该注解的字段、构造方法或者Setter方法中。

@Qualifier@Autowired一起使用,指定具体的依赖Bean。
@Value将配置值注入到属性中。
@Configuration将类标记为配置类,用于定义Bean的创建和依赖关系。
@Bean在配置类中使用该注解来声明一个Bean,并提供其创建和初始化的逻辑。
@Repository用于标注DAO类
@Service用于标注业务类
@Controller用于标注控制器类

二.实现

  1. @Repository("userDao")// 与在XML配置文件中编写
  2. //<bean id="userDao" class="dao.impl.UserDaoImpl" />等效
  3. public class UserDaoImpl implements UserDao {
  4. }

使用@Autowired注解实现Bean的自动装配

默认按类型匹配,可使用@Qualifier指定Bean的名称 

  1. @Service("userService")
  2. public class UserServiceImpl implements UserService {
  3. @Autowired
  4. @Qualifier("userDao")
  5. private UserDao dao;//为dao属性注入名为userDao的Bean
  6. ……
  7. }

修改配置文件使注解生效,完成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:context="http://www.springframework.org/schema/context"
  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/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <!– 扫描包中注解标注的类>
  10. <!--指定需要被Spring扫描的基准包范围(多个包名之间用逗号隔开)-->
  11. <context:component-scan base-package="service,dao" />
  12. </beans>

5.使用Java标准注解完成装配

使用@Resource注解实现组件装配

默认按名称匹配 

  1. @Service("userService")
  2. public class UserServiceImpl implements UserService {
  3. @Resource
  4. private UserDao dao;//查找名为dao的Bean,并注入给dao属性
  5. ……
  6. }
  1. @Service("userService")
  2. public class UserServiceImpl implements UserService {
  3. @Resource(name="userDao")
  4. private UserDao dao;// 为dao属性注入名为userDao的Bean
  5. ……
  6. }
  1. @Service("userService")
  2. public class UserServiceImpl implements UserService {
  3. @Resource(type = UserDaoImpl.class)//用于显示指定依赖的Bean的具体类型
  4. private UserDao dao;
  5. ……
  6. }

 对比@Autowired注解和@Resource注解

1.@Autowired是Spring提供的注解,@Resource是Java提供的

2.在不指定任何参数且不配合其他注解时,@Autowired注解会优先按Bean的类型进行装配,@Resource注解则是优先按Bean的名称进行装配

6.使用注解实现Spring Aop

1. AspectJ

面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译期提供代码的织入

2.@AspectJ

AspectJ 5新增的功能,使用JDK 5.0注解技术和正规的AspectJ切点表达式语言描述切面

3.Spring通过集成AspectJ框架实现了以注解的方式定义切面,使得配置文件的代码大大减少

利用轻量级的字节码处理框架asm处理@AspectJ中所描述的方法参数名

注:使用@AspectJ,首先要保证所用的JDK是5.0或以上版本

  1. <bean id="myAcpect" class="com.cskt.aspect.MyAspect" />
  2. <aop:aspectj-autoproxy/>
  1. // 给那个方法增强 。。。。。,
  2. @Before("execution( * com.cskt.service..*.*(..))")
  3. public void before(){
  4. System.out.println("前置通知.... 在方法之前要執行的公共代码放在这里");
  5. }

Spring在同一个功能上提供了注解和配置文件两种实现方式以供选择

通常情况下,优先选择注解来简化配置工作量

Aop中常用的注解

AOP中常用的注解
名称作用
@AfterThrowing在目标方法抛出异常时执行的通知。可以指定切入点表达式和异常类型,捕获并处理特定异常。
@Before在目标方法执行之前执行的通知。可以指定切入点表达式,在目标方法执行之前执行一些预处理操作。
@AfterReturning在目标方法成功执行后执行的通知。可以指定切入点表达式,并获取目标方法的返回结果进行处理。
@Around在目标方法前后都执行的通知,可控制目标方法的执行过程。需要在通知方法中明确调用proceed()方法来执行目标方法。
@After在目标方法执行之后(无论是否出现异常)执行的通知。通常用于进行资源释放或清理操作。
@Aspect

将类标记为切面类,用于定义通知和切入点。

@Pointcut定义切入点,指定一组满足条件的连接点。通常用于重复使用相同的切入点表达式。
  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.*;
  3. import org.springframework.stereotype.Component;
  4. @Aspect
  5. @Component
  6. public class MyAspect {
  7. @Pointcut("execution(* com.example.MyService.myMethod(..))")
  8. public void myMethodPointcut() {
  9. // 定义切入点
  10. }
  11. @Before("myMethodPointcut()")
  12. public void beforeMethod(JoinPoint joinPoint) {
  13. // 在目标方法执行之前执行的通知逻辑
  14. }
  15. @AfterThrowing(pointcut = "myMethodPointcut()", throwing = "ex")
  16. public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {
  17. // 在目标方法抛出异常后执行的通知逻辑
  18. }
  19. @AfterReturning(pointcut = "myMethodPointcut()", returning = "result")
  20. public void afterReturningMethod(JoinPoint joinPoint, Object result) {
  21. // 在目标方法成功返回结果后执行的通知逻辑
  22. }
  23. @Around("myMethodPointcut()")
  24. public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
  25. // 环绕通知:在目标方法前后执行一些操作,需要手动调用proceed()方法执行目标方法
  26. Object result;
  27. try {
  28. // 目标方法执行前的操作
  29. result = joinPoint.proceed();
  30. // 目标方法执行后的操作
  31. } catch (Exception ex) {
  32. // 异常处理操作
  33. throw ex;
  34. }
  35. return result;
  36. }
  37. @After("myMethodPointcut()")
  38. public void afterMethod(JoinPoint joinPoint) {
  39. // 在目标方法执行之后执行的通知逻辑
  40. }
  41. }

在上述示例中,我们使用@Aspect将类标记为切面类,使用@Pointcut定义了切入点。然后,在不同的通知方法上使用相应的注解来实现不同类型的通知逻辑。

这些注解可以根据需要进行组合和配置,实现对目标方法的前置、后置、环绕、异常等各个时机的增强操作。通过AOP的方式,可以将横切关注点(如日志记录、事务管理等)从业务逻辑中解耦出来,提升代码的可维护性和复用性。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/675015
推荐阅读
相关标签
  

闽ICP备14008679号