Spring AOP概念下注解的使用

笔者前几天看代码的时候,看到了之前的一位大佬用注解写的事务,出于好奇之下,查了些资料后也尝试去写了一个,在这里记录一下。


一、使用自定义注解的前提

  • 1、项目必须是一个spring项目
  • 2、需要导入一个额外的依赖aspectjweaver,通过该依赖中的某些注解实现自定义注解
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
</dependency>

二、如何实现自定义注解

1、第一步:自定义一个注解类

注解类中没有额外的具体实现,只需要设置一些必要的参数和注解的作用域、生命周期。

package com.muyichen.demo.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 校验账号是否有权限
 * @author muyichen
 * @date 2021-04-15
 */
@Target(ElementType.METHOD) //表明该注解的目标是方法
@Retention(RetentionPolicy.RUNTIME) //指定注解的生命周期(作用域) source < class < runtime
public @interface CheckAcct {

    /**
     * 用户名
     * @return
     */
    String name() default "";

    /**
     * 权限
     * @return
     */
    String[] authorities() default "";

    /**
     * 是否开启校验
     * @return
     */
    boolean isActive() default true;

}

2、第二步:创建自定义注解类的实现类

之前的注解类只相当于一个标签,并没有对应的具体实现,需要通过@Aspect注解创建一个切片,为对应的标签加上对应的实现方法。具体如下:

package com.muyichen.demo.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 校验切面类
 */
@Aspect //把当前类标识为一个切面供容器读取
@Component  //把当前类标记为一个组件加入容器
public class CheckAcctAop {

    /**
     * 在所有标记了@CheckAcct注解的方法之前执行
     * @param joinPoint aspectj内置参数
     */
    @Before("@annotation(com.muyichen.demo.aop.CheckAcct)")
    public void checkAcct(JoinPoint joinPoint) throws Exception {
        //JoinPoint是aspectj内置参数,同HttpServletRequest类似,通过它可获取注解中的参数信息
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        //获取配置参数isActive,判断是否开启校验,默认开启
        boolean active = method.getAnnotation(CheckAcct.class).isActive();
        if (!active) {
            return;
        }
        String name = method.getAnnotation(CheckAcct.class).name();
        System.out.println("注解中的配置参数name为:" + name);
        String[] authorities = method.getAnnotation(CheckAcct.class).authorities();
        System.out.println("注解中的配置参数authorities为:" + Arrays.asList(authorities));

        //这里填写业务逻辑
        if (name.equals("Smith") && Arrays.asList(authorities).contains("sys:delete")) {
            System.out.println("校验成功");
        } else {
            System.out.println("校验失败");
        }

    }

}
  • @Aspect: 作用是把当前类标识为一个切面供容器读取;
  • @Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码;
  • @Around:环绕增强,相当于MethodInterceptor;
  • @AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行;
  • @Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有;
  • @AfterThrowing:异常抛出增强,相当于ThrowsAdvice;
  • @After: final增强,不管是抛出异常或者正常退出都会执行;

通过上述两步之后就已经完成了一个注解的创建,笔者接下来要说一下,自定义注解使用过程中的一些注意事项。

三、自定义注解注意事项

1、注解一定要在spring组件中使用

  • 注解一定要在spring组件中使用,也就是必须要在spring容器启动成功后才能使用。如果容器没有启动,是无法找到注解的具体实现的。笔者试过用main方法调用一个加了注解,但不在Spring组件中的方法,发现注解的方法根本没有触发,代码如下:
package com.muyichen.demo.aop;

import org.springframework.stereotype.Component;

/**
 * 注解测试类
 */
//@Component
public class TestAop {

    @CheckAcct(name = "Smith", authorities = "sys:delete")
    public static void getUserInfo() {
        System.out.println("当前用户的信息是xxxxxx!");
    }

    public static void main(String[] args) {
        getUserInfo();
    }
}

结果:

当前用户的信息是xxxxxx!

笔者正常启用,是通过spring test组件,通过单元测试的方式来完成对注解的校验的:

package com.muyichen.demo;

import com.muyichen.demo.aop.TestAop;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

	@Autowired
	private TestAop testAop;

	@Test
	public void test(){
		testAop.getUserInfo();
	}

}

正常的结果为:

注解中的配置参数name为:Smith
注解中的配置参数authorities为:[sys:delete]
校验成功
当前用户的信息是xxxxxx!

2、使用注解时需要注意它的生命周期

注解按生命周期来划分可分为3类:

  • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  • RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
  • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

3、使用注解时需要注意它的作用域

注解的作用域主要都在ElementType这个枚举类型中

public enum ElementType {
    /**标明该注解可以用于类、接口(包括注解类型)或enum声明*/
    TYPE,

    /** 标明该注解可以用于字段(域)声明,包括enum实例 */
    FIELD,

    /** 标明该注解可以用于方法声明 */
    METHOD,

    /** 标明该注解可以用于参数声明 */
    PARAMETER,

    /** 标明注解可以用于构造函数声明 */
    CONSTRUCTOR,

    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,

    /** 标明注解可以用于注解声明(应用于另一个注解上)*/
    ANNOTATION_TYPE,

    /** 标明注解可以用于包声明 */
    PACKAGE,

    /**
     * 标明注解可以用于类型参数声明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 类型使用声明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缘丶沐逸尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值