基于注解的切面编程

1.基本概念

    面向切面编程也叫Aop。面向对象的特点是继承、封装、多态。封装要求我们将不同的功能分散到不同的类中去实现,每个类有自己的职责,这样的好处是降低了代码的复杂度,使得类可以重用;但是在分散代码的同时,也会增加代码的复杂性,比如一些通用的功能,日志,权限等。在之前进行app后端开发的时候,为了跟踪问题,需要对每个api的请求日志都记录下来,可以在每个方法的入口处都加上log.info.....,但是这样不够灵活,如果以后对记录的日志的方式变化,那么改动可想而知,也许你会说封装起来,每个类中进行调用,但是这样就耦合太严重了。这时候就可以使用Aop,在运行时动态的插入代码;这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

    一般而言,我们将切入到指定类指定方法的代码片段称为切面,将切入到哪些类,或者哪些方法的称为切入点。使用Aop,将不同类中重复的地方抽取出来,形成切面,在需要的地方插入到对象或方法上,从而动态改变原有的行为。

2.一个简单权限校验的例子

基于注解的切面,需要定义注解,定义切面

1>定义注解

package com.example.springbootDemo.service.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})//注解类型,级别
@Retention(RetentionPolicy.RUNTIME)//运行时注解
public @interface PermissionCheck {
    String value() default "";
}

2>根据注解定义一个切面

package com.example.springbootDemo.service.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class PermissionAspect {
    /**
     * 选取切入点为自定义注解
     */
    @Pointcut("@annotation(com.example.springbootDemo.service.aspect.PermissionCheck)")
    public void permissionCheck(){}

    /**
     * 在切入点业务逻辑执行前进行操作
     * @param joinPoint
     */
    @Before(value = "permissionCheck();")
    public void before(JoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature(); //获取连接点的方法签名对象
        if (!(signature instanceof MethodSignature)){
            throw new PermissionCheckException("user permission check fail,stop this request!");
        }
        MethodSignature methodSignature = (MethodSignature) signature;
        Object target = joinPoint.getTarget();
        Method method = target.getClass().getDeclaredMethod(methodSignature.getName(),methodSignature.getParameterTypes());//获取到当前执行的方法
        PermissionCheck annotation = method.getAnnotation(PermissionCheck.class);//获取方法的注解
        System.out.println(annotation);
        System.out.println(annotation.value());
        System.out.println("我是在执行业务逻辑之前");
    }

    /**
     * 在业务逻辑执行后进行的操作
     * @param joinPoint
     */
    @After(value = "permissionCheck();")
    public void after(JoinPoint joinPoint) throws NoSuchMethodException {
        System.out.println("我是在执行业务逻辑后");
    }

    @Around(value = "permissionCheck();")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around方法执行前1");
        Object proceed = joinPoint.proceed();
        System.out.println("around方法执行前2");
        System.out.println("proceed:"+proceed.toString());
        return proceed;
    }
}

3>权限验证不满足的时候需要定义异常,定义一个PermissionCheckException来标识权限不满足的异常信息

package com.example.springbootDemo.service.aspect;

public class PermissionCheckException extends RuntimeException {
    public PermissionCheckException(String message) {
        super(message);
    }
}

4>定义Advice处理该异常

package com.example.springbootDemo.service.aspect;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class PermissionAdvice {
    @ExceptionHandler(value = PermissionCheckException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public String dealWithPermissionCheckException(PermissionCheckException exception){
        System.out.println(exception.getMessage());
        return exception.getMessage();
    }
}

注:PermissionCheck 注解必须声明为@Retention(RetentionPolicy.RUNTIME),否则,默认的是@Retention(RetentionPolicy.CLASS) 注解会在class字节码文件中存在,但运行时无法获得;

3.@Before @Around @After 等 advice 的执行顺序

正常情况的执行顺序

异常情况

注:Around中

Object proceed = joinPoint.proceed();

表示

Proceed with the next advice or target method invocation

表示处理返回的结果对象,必须返回;

猜你喜欢

转载自my.oschina.net/u/2596536/blog/1623673
今日推荐