@interface 与aop

博客介绍了在业务中使用自定义注解解决提前对方法参数作业务判断的问题。阐述了定义注解所需的元注解,如@Target和@Retention的作用及可选值。还提及业务类代码编写、枚举类作参数与注解值映射,以及具体实现方法和注意点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在业务中有这样的场景:我需要在调某些方法的时候,需要提前拿出它的参数作业务判断,然后判断通过的代码再进行业务操作,此处我们使用自定义注解来解决问题。

 自定义注解

 

import org.springframework.stereotype.Component;

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

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Component
public @interface Note {
     String type() default  "";
}

定义注解时,会需要一些元注解(meta-annotation),如@Target@Retention

@Target 用来定义你的注解将应用于什么地方(例如是一个方法或者一个域)。

@Retention 用来定义该注解在哪一个级别可用,在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)。

 

Java除了内置了三种标准注解,还有四种元注解。此处借鉴(Java中三种标准注解和四种元注解_良月柒-优快云博客_java元注解

@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:

     ElemenetType.CONSTRUCTOR-----------------------------构造器声明 
     ElemenetType.FIELD ----------------------------------域声明(包括 enum 实例) 
     ElemenetType.LOCAL_VARIABLE------------------------- 局部变量声明 
     ElemenetType.METHOD ---------------------------------方法声明 
     ElemenetType.PACKAGE --------------------------------包声明 
     ElemenetType.PARAMETER ------------------------------参数声明 
     ElemenetType.TYPE----------------------------------- 类,接口(包括注解类型)或enum声明 


@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

     RetentionPolicy.SOURCE-------------注解将被编译器丢弃 
     RetentionPolicy.CLASS -------------注解在class文件中可用,但会被VM丢弃 
     RetentionPolicy.RUNTIME ---------VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。


@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。

@Inherited 允许子类继承父类中的注解。

Spring在扫描类信息的使用只会判断被@Component注解的类,所以任何自定义的注解只要带上@Component(当然还要有String value() default "";的方法,因为Spring的Bean都是有beanName唯一标示的),都可以被Spring扫描到,并注入容器内。

业务类

我们编写一个简单的业务代码,如下controller


import com.example.demo.base.sp.dto.ResponseDto;
import com.example.demo.base.sp.dto.UserDto;
import com.example.demo.base.sp.dto.UserEntity;
import com.example.demo.base.sp.service.IUserService;
import com.example.demo.base.utils.aop.Note;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.RequestParam;

/**
 * @author sun.ll
 * @date 2020/12/1
 */
@Controller
@RequestMapping("/v1/user")
public class UserController {
    @Autowired
    private IUserService userService;

    @GetMapping("/login")
    @ResponseBody
    public ResponseDto<String> loginIn(@RequestParam("username") final String username, @RequestParam("password") final String password) {
        final String token = userService.selectByNameAndPwd(username,password);
        return ResponseDto.wrapSuccess(token);
    }

    /**
     * 此Get方法加上了自定义注解
     * @param userId
     * @return
     */
    @GetMapping("/query")
    @Note(type = "LOGIN")
    @ResponseBody
    public ResponseDto<UserEntity> query(@RequestParam("userId") final Long userId) {
        final UserEntity userEntity = userService.selectById(userId);
        return ResponseDto.wrapSuccess(userEntity);
    }
    /**
     * 此Post方法加上了自定义注解
     * @param entity
     * @return
     */
    @PostMapping("/query/post")
    @Note(type = "LOGIN")
    @ResponseBody
    public ResponseDto<UserEntity> query(final UserDto entity ) {
        final UserEntity userEntity = userService.getUserEntity(entity);
        return ResponseDto.wrapSuccess(userEntity);
    }

}

枚举类

接下来我们需要在调用这个方法的时候先进行一系列的操作,比如权限鉴定等等。我们定义一个Enum,来作参数与注解值的映射:


import java.util.HashMap;
import java.util.Map;

/**
 * @author sun,linglei
 * @date 2020/12/1
 */
public enum  BusinessTypeMappingEnum {
    /**
     * GET测试注解
     */
    GET_PARAM("LOGIN","userId"),
    /**
     * POST测试注解
     */
    POST_BEAN("LOGIN","UserDto");

    private String businessType;
    private String paramName;


    BusinessTypeMappingEnum(String businessType, String paramName) {
        this.businessType = businessType;
        this.paramName = paramName;
    }

    private String getBusinessType(){
        return businessType;
    }

    private String getParamName(){
        return paramName;
    }

    //初始化数据
    private static final Map<String,BusinessTypeMappingEnum> BUSINESS_MAP = new HashMap<>();

    static {
        for (BusinessTypeMappingEnum mappingEnum:values()
        ) {
            BUSINESS_MAP.put(mappingEnum.getBusinessType(),mappingEnum);
        }
    }

    //根据businessType 查找参数名
    public static String getValuesByName(final String businessType){
        final BusinessTypeMappingEnum businessTypeMappingEnum = BUSINESS_MAP.get(businessType);
        return businessTypeMappingEnum == null ? null : businessTypeMappingEnum.getParamName();
    }

}

(这个enum当中的 根据type查找参数名的方法也可以替换很多 简单的if-else匹配)

关于Spring aop(细说Spring——AOP详解(AOP概览)_啦啦啦的博客-优快云博客_aop)

具体实现方法


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.base.enums.BusinessTypeMappingEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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;
import java.util.Objects;
/**
 * @author sun,linglei
 * @date 2020/12/1
 */
@Slf4j
@Aspect
@Component
public class AspectsClass
{
    @Pointcut("@annotation(com.example.demo.base.utils.aop.Note)")
    public void points(){

    }

    @Before("points()")
    public void doBefore(final JoinPoint joinPoint){
        //获取注解信息
        final MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        final Method method = methodSignature.getMethod();
        if (Objects.isNull(method)){
            return;
        }
        final Note annotation = method.getAnnotation(Note.class);

        //获取调用的方法
        final HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        if ("POST".equals(request.getMethod().toUpperCase())) {
            //获取post参数
            String str = "";
            final Object[] args = joinPoint.getArgs();
            if (args.length!=0){
                str =  JSONArray.toJSON(args[0]).toString();
            }
            //获取JsonObject
            final JSONObject jsonObject = JSONObject.parseObject(str);

            //获取object参数的类
            final Class object = getObject(BusinessTypeMappingEnum.getValuesByName(annotation.type()));
            final Object javaMap = JSON.toJavaObject(jsonObject,object);
            //可以再根据type写个策略具体业务具体解决
            System.out.println(javaMap);
        }
        else if ("GET".equals(request.getMethod().toUpperCase())) {
            //根据名字获取参数
            final String param = request.getParameter(BusinessTypeMappingEnum.getValuesByName(annotation.type()));
            System.out.println(param);
            //do something
            return;
        }
    }

    /**
     * 根据类型名字获取bean参数
     * @param name
     * @return
     */
    private Class getObject(final String name){
        try {
            final Class clazz = Class.forName("com.example.demo.base.sp.dto."+name);
            return clazz;
        } catch (ClassNotFoundException e) {
            log.info("class not found ");
        }
        return null;
    }

}

几个注意点:(一)Class.forName后面类名前需要拼上包名,此处也可以全数定义在枚举类中。否则会报ClassNotFoundException;

                        (二)  post或者get拿到具体的参数后可以接上 策略模式等,再次根据从注解中获取的type走不同的业务分支(SpringBoot中使用策略模式_吃鱼的宗介的博客-优快云博客

 

 

 

这里提供一个简单的AOP框架实现,包含@Pointcut,@Advice,@Aspect三个注解。 1. 定义@Pointcut注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Pointcut { String value() default ""; } ``` 2. 定义@Advice注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Advice { String value() default ""; } ``` 3. 定义@Aspect注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Aspect { } ``` 4. 定义切面类 ```java @Aspect public class MyAspect { @Pointcut("execution(* com.example.demo.service.*.*(..))") public void pointcut() {} @Advice("before") public void beforeAdvice() { System.out.println("before advice"); } @Advice("after") public void afterAdvice() { System.out.println("after advice"); } } ``` 5. 定义目标类 ```java @Service public class MyService { public void doSomething() { System.out.println("do something"); } } ``` 6. 实现AOP框架 ```java public class AopFramework { public static void run(Class<?> targetClass) { // 获取所有切面类 List<Class<?>> aspectClasses = getAspectClasses(targetClass); // 创建所有切面类的实例 List<Object> aspectInstances = createAspectInstances(aspectClasses); // 获取所有目标类的方法 Method[] targetMethods = targetClass.getDeclaredMethods(); // 遍历所有目标类的方法 for (Method targetMethod : targetMethods) { // 遍历所有切面类 for (Object aspectInstance : aspectInstances) { // 获取切点方法 Method pointcutMethod = getPointcutMethod(aspectInstance); if (pointcutMethod != null) { try { // 调用切点方法 pointcutMethod.invoke(aspectInstance); // 获取前置通知方法 Method beforeAdviceMethod = getBeforeAdviceMethod(aspectInstance); if (beforeAdviceMethod != null) { // 调用前置通知方法 beforeAdviceMethod.invoke(aspectInstance); } // 调用目标方法 targetMethod.invoke(targetClass.newInstance()); // 获取后置通知方法 Method afterAdviceMethod = getAfterAdviceMethod(aspectInstance); if (afterAdviceMethod != null) { // 调用后置通知方法 afterAdviceMethod.invoke(aspectInstance); } } catch (Exception e) { e.printStackTrace(); } } } } } private static List<Class<?>> getAspectClasses(Class<?> targetClass) { List<Class<?>> aspectClasses = new ArrayList<>(); Package targetPackage = targetClass.getPackage(); String targetPackageName = targetPackage.getName(); String aspectPackageName = targetPackageName + ".aspect"; try { Reflections reflections = new Reflections(aspectPackageName); Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(Aspect.class); aspectClasses.addAll(annotatedClasses); } catch (Exception e) { e.printStackTrace(); } return aspectClasses; } private static List<Object> createAspectInstances(List<Class<?>> aspectClasses) { List<Object> aspectInstances = new ArrayList<>(); for (Class<?> aspectClass : aspectClasses) { try { Object aspectInstance = aspectClass.newInstance(); aspectInstances.add(aspectInstance); } catch (Exception e) { e.printStackTrace(); } } return aspectInstances; } private static Method getPointcutMethod(Object aspectInstance) { Method[] methods = aspectInstance.getClass().getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Pointcut.class)) { return method; } } return null; } private static Method getBeforeAdviceMethod(Object aspectInstance) { return getAdviceMethod(aspectInstance, "before"); } private static Method getAfterAdviceMethod(Object aspectInstance) { return getAdviceMethod(aspectInstance, "after"); } private static Method getAdviceMethod(Object aspectInstance, String adviceType) { Method[] methods = aspectInstance.getClass().getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Advice.class)) { Advice advice = method.getAnnotation(Advice.class); if (advice.value().equals(adviceType)) { return method; } } } return null; } } ``` 7. 测试AOP框架 ```java public class AopTest { public static void main(String[] args) { AopFramework.run(MyService.class); } } ``` 输出结果: ``` before advice do something after advice ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值