【Spring】面向切面编程AOP(分离业务逻辑与日志等非业务需求)

前言

前文get了IOC容器的Bean的详细配置,也get了生命周期历程。但这还不够,下面我们归纳Spring中的另一个容器AOP

问题由来:①②③④⑤⑥×✔✘☞☜√
①程序的方法在处理业务需求时,还需要日志和验证等非业务需求以便提示,若放在都放在该方法中这势必会导致代码的混乱(同时包含了业务与非业务需求)
②由于方法一般都会有日志和验证,且提示内容形式大体相近。若日志需求发生改变,那势必有些崩溃,要修改的日志太多了

在这里插入图片描述思想:AOP技术利用一种称为“横切”的技术,解剖封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块(切面),这样就能减少系统的重复代码,降低模块间的耦合度。
切面(Aspect):横切关注点被模块化的特殊对象
连接点(Joinpoint):程序执行的某个特定位置。如类某个方法调用前、调用后、方法抛出异常后等。每个类都有多个连接点
切点(pointcut):通过切点定位到特定的连接点,一个切点可匹配多个连接点。

配置

1.Mavan的pom.xml中配置jar包

<!--aop切面-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>4.0.2.RELEASE</version>
</dependency>

2.配置Bean
2.1 自动注解方式

<!-- 配置自动扫描的包 -->
<context:component-scan base-package="xu.day3.aop"></context:component-scan>
<!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

java类

@Order(2) //可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
@Aspect //切面标识
@Component
public class LoggingAspect {
    
    
/**
  * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
  * 使用 @Pointcut 来声明切入点表达式. 切入点表达式支持&&,||,!
  * 如* xu.day3.aop.MathCalculator.add(int,..)) ||* xu.day3.aop.MathCalculator.sub(..))
  * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
  */	
    @Pointcut("execution(* xu.day3.aop.MathCalculator.*(..))") 
    //第一个*是任意修饰符及返回值 第二个*是任意方法  ..指匹配任意数量的参数
    public void declareJoinPointExpression() {
    
    }
    
    //xu.day3.aop.MathCalculator接口的每一个实现类的每一个方法开始之前执行一段代码
    @Before("declareJoinPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
    
    
        String methodName = joinPoint.getSignature().getName(); //方法名
    	Object[] args = joinPoint.getArgs(); //方法的参数
    	System.out.println("The method:"+methodName+" begin with:"+ Arrays.asList(args));
    }
    @After("declareJoinPointExpression()") //在方法执行之后执行的代码. 无论该方法是否出现异常
    public void afterMethod(JoinPoint joinPoint) {
    
    
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method:"+methodName+" end");
    }
    //在方法法正常结束受执行的代码返回通知是可以访问到方法的返回值的!
    @AfterReturning(value = "declareJoinPointExpression()",returning = "result")
    public void returnMethod(JoinPoint joinPoint, Object result) {
    
    
    	String methodName = joinPoint.getSignature().getName();
    	System.out.println("The method:"+methodName+" end with return:"+result);
    }
    // 在目标方法出现异常时会执行的代码.可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
    @AfterThrowing(value = "declareJoinPointExpression()", throwing = "e")
    public void throwingMethod(JoinPoint joinPoint, Exception e) {
    
     //这里的异常类型可以进行限定
    	String methodName = joinPoint.getSignature().getName();
    	System.out.println("The method:"+methodName+" occurs exception:"+e);
    }

//环绕通知,是上述的大全,不常用就不赘述	        
}   
@Order(1)
@Aspect
@Component
public class VlidationAspect {
    
    
...//还是通过切点进行@Before @After等 
//若引用另一个类的切点则@Before("xu.day3.aop.LoggingAspect.declareJoinPointExpression()")
}
public interface MathCalculator {
    
    
...
}

@Component("mathCalculator")
public class MathCalculatorImpl implements MathCalculator {
    
    
...
}

2.2 xml配置文件方式
感觉这种方式还是没有自动注解方式来的直接爽快

<!-- 别的用到的类(包含切面、普通类等)正常手动配置Bean -->
<!-- 配置 AOP -->
 <aop:config>
  <!-- 配置切点表达式 -->
  <aop:pointcut expression="execution(* xu.day3.aop.MathCalculator.*(..))" 
   id="pointcut"/>
  <!-- 配置切面及通知 -->
  <aop:aspect ref="loggingAspect" order="2">
   <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
   <aop:after method="afterMethod" pointcut-ref="pointcut"/>
   <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
   <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
   <!--  
   <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
   -->
  </aop:aspect> 
  <aop:aspect ref="vlidationAspect" order="1">
   <aop:before method="validateArgs" pointcut-ref="pointcut"/>
  </aop:aspect>
 </aop:config>

补充:
可动态代理方式实现日志

猜你喜欢

转载自blog.csdn.net/qq_40265247/article/details/106228721
今日推荐