水一下:Spring boot AOP 实现身份验证和统一日志管理

基本学过spring的都知道,spring的两大核心特性IOC和AOP

IOC: 英文名称:inversion of controller,中文名:控制反转,大意是在创建对象的过程或者创建对象的权限交给了spring框架来处理,我们不用再通过new的方式来创建JavaBean对象。整个过程就叫IOC.

DI:      英文名称:dependency injection,中文名:依赖注入,spring对javaBean赋值的过程, DI不能单独存在,DI需要在IOC的基础上来完成

最常用的就是    @Autowired这个注解,自动装配

但是今天公司大佬们说新版idea不推荐了,慌得一批。。。。

 

不过不要紧,今天不说这个,只说AOP!

 

基本概念:AOP,面向切面编程。AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

AOP介绍

 

使用aop必须要了解这几个注解:

@Aspect: 定义切入点和通知

@Pointcut: 切入点,即拦截的位置(标识)

@Before: 前置通知, 在方法执行之前执行

@After: 后置通知, 在方法执行之后执行 。

@AfterRunning: 返回通知, 在方法返回结果之后执行

@AfterThrowing: 异常通知, 在方法抛出异常之后

@Around: 环绕通知, 围绕着方法执行

 

执行顺序图:

 

 

在项目中,我用了自定义注解和不用自定义注解两种方式来实现。

好了,开始实战:

引入依赖

 

      <!--  AOP   -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

 

 

1.使用自定义注解

首先创建自定义注解,必加三个:

@Target:注解用在的地方:方法,类,参数,等
@Retention:使用该注解的时机,我理解为生命周期
@Documented:仅表示注解是否将包含在JavaDoc中

可不加一个:

@Inherited: 是否被继承,不加表示不被子类使用

如下:

 


//用于方法,类级别
@Target({ElementType.METHOD,ElementType.TYPE})
//永不丢弃
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface AuthCustomize {
    //用于自定义描述等
    String value() default "";
}

记不住没关系,看源码注解,直接copy: 

 

OK,自定义注解结束,下面开始使用AOP和自定义注解了:

 

AOP+自定义注解:

首先,创建一个类,加上 @Aspect 注解,声明这是个切面

 

然后我们定义个方法作为切入点

 

 /**
     * @description:  类级别切入点,若使用方法级别的注解用@annotation,但要保证自定义注解中@Target包含
     */
    @Pointcut("@within(com.ets.cloudconsumerapi.aop.AuthCustomize)")
    public void cutTest() {
    }

 

创建通知


    /**
     * @description: cutTest()切入点方法,ProceedingJoinPoint:连接点,继承JoinPoint   authCustomize:注解  cutTest() && (@within(authCustomize)||@annotation(authCustomize)) 类和方法同时作用  
     */
    @Around("cutTest() && @within(authCustomize)")
    public Object doAround(ProceedingJoinPoint joinPoint, AuthCustomize authCustomize) throws Throwable {
        System.out.println("进入了 doAround 方法");
        //直接跳转到下一个注解或方法
        return joinPoint.proceed();
    }

    @Before("cutTest()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        //直接跳转到下一个注解或方法
        System.out.println("进入了 doBefore 方法");
    }

    @After("cutTest()")
    public void doAfter(JoinPoint joinPoint) throws Throwable {
        //直接跳转到下一个注解或方法
        System.out.println("进入了 After 方法");
    }

 

上面的   @Around("cutTest() && @within(authCustomize)")   也可以直接写成   @Around("cutTest() ")  因为你在切入点方法定义了,实际上是可加可不加的,但是当你需要用到自定义注解的值的时候必须加上!

另外补充一点,切面方法里面的参数必须要用到,否则启动都有可能启动不了

报错: nested exception is java.lang.IllegalArgumentException: error at ::0 formal

 

创建自定义类,加上注解

@RestController
@RequestMapping("/text")
@Slf4j
@AuthCustomize
public class TestController {
    @RequestMapping("/token")
    public void token(){
        log.info("无权限");
    }


    @RequestMapping("/auto")

    public void auto(){
        log.info("权限");
    }

执行,访问url能直接背拦截进入AOP类,顺序如下:

 

这样,就可以在around里面直接进行身份验证判断,成功直接  return joinPoint.proceed();

before和after根据需求写业务。

 

2.AOP日志管理(和身份验证AOP一起使用,多个AOP执行顺序问题)

 

我们进行日志管理的时候是拦截所有的controller或者server,我们使用自定义注解时,需要每个类多加个注解,

可能不太好(就一个说辞,方便引入下面的话题,不然很尴尬,其实都一样,看个人

所以我们可以使用另外的方式进行管理

 

首先创建一个切面类,并实现Ordered,order(getOrder)越小,优先级越高!

 

@Aspect
@Component
@Slf4j
public class AuthLogAspect implements Ordered{


@Override
    public int getOrder() {
        return 0;
    }
}

 

接着定义切入点

 /**
     * @description: 切入点为整个controller,execution为固定函数
     */
    @Pointcut("execution( * com.ets.cloudconsumerapi.controller.*.*(..))")
    public void logs() {
    }

 

这里说下execution语法

execution可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:方法返回类型, *号表示所有的类型。

3、包名:表示需要拦截的包名。

4、第二个*号:表示类名,*号表示所有的类。

5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面( )里面表示方法的参数,两个句点表示任何参数

execution( * packageName.*.*(..))

 

 

 

通知方法

 @Around("logs()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("先进日志around");
        Object result = proceedingJoinPoint.proceed();
        return result;
    }

 

因为我们定义了全局拦截(拦截所有的controller),就不用再类上加注解,直接执行:

 

日志和身份验证都有了!

 

 

 

总结: 多个AOP,order越小,先进,最后出!

如下图:

 

上图有个问题,优先级低的(内部AOP)的形成了环形,如果不是特别要求,个人认为可以去掉after或before,

如下图:

 

但其实都一样,多个AOP 都会出现内部AOP(优先级低的AOP)与方法形成环流,估计有别的办法,但是,我不会...

 

 

 

 

 

第一次搞,有错误的请大佬指出!

 

 

 

纯学习笔记........

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值