Spring 切面简记及应用

1,850 阅读2分钟
概念

面向切面编程:把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。

切面(AOP)术语
  1. 连接点(Joinpoint)
  2. 切点(Pointcut)
  3. 增强(Advice)
    • Before advice
    • After returning advice
    • After throwing advice
    • After(finally) advice
    • Around advice
  4. 目标对象(Target)
  5. 引入(Introduction)
  6. 织入(Weaving)
  7. 代理(Proxy)
  8. 切面(Aspect)
优点
  • 更清晰的代码逻辑,业务逻辑只关注自己本身,不用去管琐碎的事情,比如:安全,日志,事务等等。

  • 可以减少代码量

切面配置


    
    
    
    
    
    

    
切面应用:日志

注解定义

package com.aspect.annotation;
import java.lang.annotation.*;
({ElementType.METHOD})
(RetentionPolicy.RUNTIME)
public  ControllerLog {
    String description() default "";

切面定义

package com.aspect.aspect;
import com.aspect.annotation.ControllerLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
public class SystemLogAspect {
    ("@annotation(com.aspect.annotation.ControllerLog)")
    public void controllerAspect(){
    ("controllerAspect()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("------defore------");
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getRemoteAddr();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = null;
        try {
            targetClass = Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(ControllerLog.class).description();
                    break;
        System.out.println(ip+" | "+description);
    ("@annotation(com.aspect.annotation.ControllerLog)")
    public void deAfter(JoinPoint joinPoint){
        System.out.println("------after------");

使用切面注解

("/hello")
public class TestServlet {
    (value = "/user",method = RequestMethod.GET,produces = "application/json;charset=utf-8")
    (description = "welcome user")
    public String getMessage(@RequestParam("name") String value){
        System.out.println(value);
        String output="Hello "+value;
        return output;

测试结果

------defore------
0:0:0:0:0:0:0:1 | welcome user
------after------
切面应用:计算方法调用耗时

注解定义

package com.aspect.annotation;
import java.lang.annotation.*;
({ElementType.METHOD})
(RetentionPolicy.RUNTIME)
public  ExecuteTimeAnnotation {

切面定义

package com.aspect.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
public class ExecuteTimeLogAspect {
    ("@annotation(com.aspect.annotation.ExecuteTimeAnnotation)")
    public void executeTimeAspect(){
    (value = "executeTimeAspect()")
    public Object executeTime(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature=joinPoint.getSignature();
        MethodSignature methodSignature=(MethodSignature)signature;
        StopWatch stopWatch=new StopWatch(String.format("方法名: %s",methodSignature.getName()));
        System.out.println("StopWatch start");
        stopWatch.start();
        Object ret=joinPoint.proceed();
        stopWatch.stop();
        System.out.println(stopWatch.toString());
        return ret;

使用切面注解

("/hello")
public class TestServlet {
    (value = "/user",method = RequestMethod.GET,produces = "application/json;charset=utf-8")
    public String getMessage(@RequestParam("name") String value){
        System.out.println(value);
        String output="Hello "+value;
        return output;

测试结果

StopWatch start
StopWatch '方法名: getMessage': running time (millis) = 14; [] took 14 = 100%

注意点:

当同时以上两个切面时,输出结果为

StopWatch start
------defore------
0:0:0:0:0:0:0:1 | welcome user
------after------
StopWatch '方法名: getMessage': running time (millis) = 12; [] took 12 = 100%

可以发现以@Around的joinPoint.proceed();为分界,进入该方法时调用@Before,退出该方法时调用@After。

参考文献:docs.spring.io/spring/docs…