spring自定义注解

1.定义在字段上,很简单 到时候反射获取后check是否有注解再进行下一步行动

package jsontest;

import anno.Name;
import anno.Sex;

import java.util.Date;



/**
 * User
 *
 * @author keriezhang
 * @date 2016/10/31
 */

public class User {
    private String id;
    @Name(value = "xqy")  //value默认可以直接写的
    private String name;
    @anno.Sex(gender = anno.Sex.GenderType.Female)
    private String Sex;
    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    private Date birthday;
    public User(){
        super();
    }
    public User(String id, String name, Date birthday) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
    }

    public User(String id, String name, String sex, Date birthday) {
        this.id = id;
        this.name = name;
        Sex = sex;
        this.birthday = birthday;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

package anno;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Name {
    public String value() default "";
}

package anno;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sex {
    public enum GenderType {
        Male("男"),
        Female("女");
        private String genderStr;
        private GenderType(String arg0) {
            this.genderStr = arg0;
        }
        @Override
        public String toString() {
            return genderStr;
        }
    }
    GenderType gender() default GenderType.Male;
}

package anno;

import jsontest.User;

import java.lang.reflect.Field;

/**
 * annoUtil
 *
 * @author xieqinyu
 * @description
 * @date 15:40 2022/7/11
 */
public class annoUtil {
    public static String getInfo(Class<?> cs){
        String result = "";
        //反射获得所有属性
        Field[] declaredFields = cs.getDeclaredFields();
        for (Field field : declaredFields){
            //查注解
            if(field.isAnnotationPresent(Name.class)){
                Name annotation = field.getAnnotation(Name.class);
                String value = annotation.value();
                result += (field.getName() + ":" + value + "\n");
            }
            if(field.isAnnotationPresent(Sex.class)){
                Sex annotation = field.getAnnotation(Sex.class);
                String value = annotation.gender().name();
                result += (field.getName() + ":" + value + "\n");
            }
        }
        return result;
    }

    public static void main(String[] args){
        String info = getInfo(User.class);
        System.out.println(info);
    }

}

2. 注解用于方法上

转载自https://programminghunter.com/article/91412364284
2.1 第一个实例
接下来,我们先看一个极简的例子:所有的get请求在这里插入代码片被调用前在控制台输出一句"get请求的advice触发了"。

具体实现如下:

在这里插入代码片

1、创建一个AOP切面类,只要在类上加个 @Aspect 注解即可。@Aspect 注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解将该类交给 Spring 来管理。在这个类里实现advice:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Description:
 * @author: 张重虎
 * @Date: 2022/2/8 17:02
 * @Version 1.0
 */
@Aspect
@Component
public class LogAdvice {
    /**
     * 定义一个切点:所有被 GetMapping 注解修饰的方法都会被织入 advice
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void logAdvicePointcut(){}

    /**
     * 表示 logAdvice 将在目标方法执行前执行
     */
    @Before("logAdvicePointcut()")
    public void logAdvice(){
        //这里只是一个示例,你可以写任何处理逻辑
        System.out.println("切面 @Before 执行了");
    }
}

2、创建一个接口类,内部创建一个get请求:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @author: 张重虎
 * @Date: 2022/2/8 17:12
 * @Version 1.0
 */
@RestController
@RequestMapping(value = "/aop")
public class AopController {

    @GetMapping("/getTest")
    public JSONObject aopTest() {
        System.out.println("Get 方法代码执行中");
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
   @PostMapping("/postTest")
    public JSONObject aopTestTwo(){
       System.out.println("Post 方法代码执行中");
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
}

项目启动后,Get 方式请求http://localhost:8080/aop/getTest接口:
image

Post 方式请求http://localhost:8080/aop/postTest接口,控制台无输出,证明切点确实是只针对被GetMapping修饰的方法。

2.2 第二个实例
下面我们将问题复杂化一些,该例的场景是:

1、 自定义一个注解PermissionsAnnotation

2、 创建一个切面类,切点设置为拦截所有标注PermissionsAnnotation的方法,截取到接口的参数,进行简单的权限校验

3、 将PermissionsAnnotation标注在测试接口类的测试接口test上

具体的实现步骤:

1、 使用@Target、@Retention、@Documented自定义一个注解:

import java.lang.annotation.*;

/**
* @Description: 自定义注解
* @author: 张重虎
* @Date: 2022/2/8 17:29
* @Version 1.0
*/


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionAnnotation {
}

2、创建第一个AOP切面类,,只要在类上加个 @Aspect 注解即可。@Aspect 注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解将该类交给 Spring 来管理。在这个类里实现第一步权限校验逻辑:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
* @Description:
* @author: 张重虎
* @Date: 2022/2/8 17:53
* @Copyright: Xi'an Dian Tong Software Co., Ltd. All Rights Reserved.
* @Version 1.0
*/
@Aspect
@Component
@Order(1)
public class PermissionFirstAdvice {

 @Pointcut("@annotation(com.example.zhangchonghu.demo.controller.aop3.PermissionAnnotation)")
 private void permissionCheck(){}

 @Around("permissionCheck()")
 public Object permissionCheckFirst(ProceedingJoinPoint joinPoint) throws Throwable {
     System.out.println("===================第一个切面===================:" + System.currentTimeMillis());

     //获取请求参数,详见接口类
     Object[] objects = joinPoint.getArgs();
     Long id = ((JSONObject) objects[0]).getLong("id");
     String name = ((JSONObject) objects[0]).getString("name");
     System.out.println("id1->>>>>>>>>>>>>>>>>>>>>>" + id);
     System.out.println("name1->>>>>>>>>>>>>>>>>>>>>>" + name);

     // id小于0则抛出非法id的异常
     if (id < 0) {
         return JSON.parseObject("{\"message\":\"illegal id\",\"code\":403}");
     }
     return joinPoint.proceed();
 }
}

3、创建接口类,并在目标方法上标注自定义注解 PermissionsAnnotation:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
* @Description:
* @author: 张重虎
* @Date: 2022/2/8 17:58
* @Copyright: Xi'an Dian Tong Software Co., Ltd. All Rights Reserved.
* @Version 1.0
*/
@RestController
@RequestMapping("/permission")
public class TestController {
 @RequestMapping(value = "/check", method = RequestMethod.POST)
 // 添加自定义注解
 @PermissionAnnotation()
 public JSONObject getGroupList(@RequestBody JSONObject request) {
     System.out.println("方法中打印请求参数"+request);
     return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
 }
}

有人会问,如果我一个接口想设置多个切面类进行校验怎么办?这些切面的执行顺序如何管理?

很简单,一个自定义的AOP注解可以对应多个切面类,这些切面类执行顺序由@Order注解管理,该注解后的数字越小,所在切面类越先执行。

下面在实例中进行演示:

创建第二个AOP切面类,在这个类里实现第二步权限校验:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Description:
 * @author: 张重虎
 * @Date: 2022/2/8 18:07
 * @Version 1.0
 */
@Aspect
@Component
@Order(0)
public class PermissionSecondAdvice {
    /**
     * 自定义注解作为切面,凡是被这个注解定义的方法,都会被切面拦截
     */
    @Pointcut("@annotation(com.example.zhangchonghu.demo.controller.aop3.PermissionAnnotation)")
    private void permissionCheck(){}

    @Around("permissionCheck()")
    public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("===================第二个切面===================:" + System.currentTimeMillis());

        //获取请求参数,详见接口类
        Object[] objects = joinPoint.getArgs();
        Long id = ((JSONObject) objects[0]).getLong("id");
        String name = ((JSONObject) objects[0]).getString("name");
        System.out.println("id->>>>>>>>>>>>>>>>>>>>>>" + id);
        System.out.println("name->>>>>>>>>>>>>>>>>>>>>>" + name);

        // name不是管理员则抛出异常
        if (!"admin".equals(name)) {
            return JSON.parseObject("{\"message\":\"not admin\",\"code\":403}");
        }
        return joinPoint.proceed();
    }
}

重启项目,继续测试,构造两个参数都异常的情况:
image

响应结果,表面第二个切面类执行顺序更靠前:
image

2.3 总结
@Pointcut 注解,用来定义一个切面,即上文中所关注的某件事情的入口,切入点定义了事件触发时机。
@Pointcut 注解指定一个切面,定义需要拦截的东西,这里介绍两个常用的表达式:一个是使用 execution(),另一个是使用 annotation()。

execution表达式:

@Aspect
@Component
public class LogAspectHandler {

    /**
     * 定义一个切面,拦截 com.itcodai.course09.controller 包和子包下的所有方法
     */
    @Pointcut("execution(* com.mutest.controller..*.*(..))")
    public void pointCut() {}
}

以 execution(* * com.mutest.controller…(…))) 表达式为例:

第一个 * 号的位置:表示返回值类型,* 表示所有类型。

包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller包、子包下所有类的方法。

第二个 * 号的位置:表示类名,* 表示所有类。

(…):这个星号表示方法名, 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

annotation() 表达式:

annotation() 方式是针对某个注解来定义切面,比如我们对具有 @PostMapping 注解的方法做切面,可以如下定义切面:

@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void annotationPointcut() {}

然后使用该切面的话,就会切入注解是 @PostMapping 的所有方法。这种方式很适合处理 @GetMapping、@PostMapping、@DeleteMapping不同注解有各种特定处理逻辑的场景。

还有就是如上面案例所示,针对自定义注解来定义切面:

@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
private void permissionCheck() {}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值