【Java基础】Java注解简单入门

注解简单来说就是配置,是特别的配置,之前常用的配置文档,可以用注解替换。然后通过反射去获取注解的信息。

如何定义一个注解

你在IDE中新建一个注解定义,是这样的结构的:

package com.nicchagil.exercise.springbootexercise.annotation;

public @interface MyFirstAnnotation {

}

然后大概有4个对上述结构加上一些配置,当然,这配置是以注解的形式添加的=_=!

此注解使用在哪里

此注解会应用的哪里,可通过如下配置:

保留在什么时候

保留到什么时候:

注解体现在文档中

@Documented

子类是否继承父类的注解

@Inherited

用反射获取注解的信息

我们先定义一个注解:

package com.nicchagil.exercise.springbootexercise.annotation;

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;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PojoPostProcessing {
    
    public Class targetClass();
    
    public String[] whiteProperties();

}

使用注解,比如这里我设置到一个方法上:

    @PojoPostProcessing(targetClass=User.class, whiteProperties={"name"})
    public List<User> selectXxx(String id) {
        ......
    }

反射获取注解的信息:

    @Test
    public void test() throws NoSuchMethodException, SecurityException, ClassNotFoundException {
        Class clazz = Class.forName("com.nicchagil.exercise.springbootexercise.service.UserService");
        
        Method method = clazz.getMethod("selectXxx", String.class);
        
        boolean isAnnotationPresent = method.isAnnotationPresent(PojoPostProcessing.class);
        if (!isAnnotationPresent) {
            return;
        }
        
        PojoPostProcessing dpp = (PojoPostProcessing)method.getAnnotation(PojoPostProcessing.class);
        this.logger.info("dpp : {}", dpp);
    }

日志:

dpp : @com.nicchagil.exercise.springbootexercise.annotation.PojoPostProcessing(targetClass=class com.nicchagil.exercise.springbootexercise.mapper.entity.User, whiteProperties=[name])

在Spring AOP中解析注解,然后实体你注解需要做的业务

比如以下就是在Spring AOP中拦截到方法,通过JoinPoint获取反射的Method,然后看是否有指定注解,如果有注解,后面就是关于此注解要做的业务逻辑了:

package com.nicchagil.exercise.springbootexercise.aop;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import com.nicchagil.exercise.springbootexercise.annotation.PojoPostProcessing;

@Aspect
@Configuration
public class ServiceControllerLogAop {
    
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Pointcut("execution(* com.nicchagil.exercise.springbootexercise.service.*Service.*(..))")
    public void myPointcut() {
    }
    
    @AfterReturning("myPointcut()")
    public void myBefore(JoinPoint joinPoint) {
        /* 获取方法对象 */
        Signature signature = joinPoint.getSignature();
        if (signature == null && !(signature instanceof MethodSignature)) {
            return;
        }
        
        MethodSignature methodSignature = (MethodSignature)signature;
        Method method = methodSignature.getMethod();
        
        /* 是否有指定注解 */
        boolean isAnnotationPresent = method.isAnnotationPresent(PojoPostProcessing.class);
        if (!isAnnotationPresent) {
            return;
        }
        
        /* 获取注解信息 */
        PojoPostProcessing dpp = (PojoPostProcessing)method.getAnnotation(PojoPostProcessing.class);
        this.logger.info("dpp : {}", dpp);
    }

}

过滤注解方法的返回值的指定属性

将属性置空的工具类:

package com.nicchagil.exercise.springbootexercise.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;

public class BeanClearUtils {
    
    private static Logger logger = LoggerFactory.getLogger(BeanClearUtils.class);
    
    public static void clearProperty(Object obj, String[] whiteProperty) {
        List<PropertyDescriptor> clearList = BeanClearUtils.getClearProperty(obj.getClass(), whiteProperty);
        
        try {
            for (PropertyDescriptor pd : clearList) {
                MethodParameter mp = BeanUtils.getWriteMethodParameter(pd);
                Method method = mp.getMethod();
                method.invoke(obj, new Object[] {null});
            }
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new RuntimeException("设置空属性失败", e);
        }
    }
    
    /**
     * 获取需要清除的字段列表
     */
    public static List<PropertyDescriptor> getClearProperty(Class<?> clazz, String[] whiteProperty) {
        PropertyDescriptor[] allPropertyDescriptor = BeanUtils.getPropertyDescriptors(clazz);
        
        /* 需保留的字段 */
        Set<String> whitePropertySet = new HashSet<String>(Arrays.asList(whiteProperty));
        whitePropertySet.add("class"); // 此字段无须清除
        
        /* 需清除的字段 */
        List<PropertyDescriptor> clearList = new ArrayList<PropertyDescriptor>();
        for (PropertyDescriptor propertyDescriptor : allPropertyDescriptor) {
            if (!whitePropertySet.contains(propertyDescriptor.getName())) {
                clearList.add(propertyDescriptor);
            }
        }
        return clearList;
    }

}

定义注解:

package com.nicchagil.exercise.springbootexercise.annotation;

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;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PojoPostProcessing {
    
    public String[] whiteProperties();

}

处理注解逻辑:

package com.nicchagil.exercise.springbootexercise.aop;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import com.nicchagil.exercise.springbootexercise.annotation.PojoPostProcessing;
import com.nicchagil.exercise.springbootexercise.util.BeanClearUtils;

@Aspect
@Configuration
public class BeanPropertyFilterAop {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.nicchagil.exercise.springbootexercise.service.*Service.*(..))")
    public void myPojoPostProcessingPointcut() {
    }

    @AfterReturning(value = "myPojoPostProcessingPointcut()", returning = "returnObject")
    public void myAfter(JoinPoint joinPoint, Object returnObject) {
        /* 获取方法对象 */
        Signature signature = joinPoint.getSignature();
        if (signature == null && !(signature instanceof MethodSignature)) {
            return;
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        /* 是否有指定注解 */
        boolean isAnnotationPresent = method.isAnnotationPresent(PojoPostProcessing.class);
        if (!isAnnotationPresent) {
            return;
        }

        /* 获取注解信息 */
        PojoPostProcessing dpp = (PojoPostProcessing) method.getAnnotation(PojoPostProcessing.class);
        this.logger.info("dpp : {}", dpp);

        /* 只保留bean的指定属性值(其它属性值置空) */
        logger.info("before bean : {}", returnObject);
        BeanClearUtils.clearProperty(returnObject, dpp.whiteProperties());
        logger.info("after  bean : {}", returnObject);
    }

}

使用注解:

@PojoPostProcessing(whiteProperties={"name", "createTime"})

关键词:import 注解 lang java annotation org method pojopostprocessing class public

相关推荐:

java自定义注解学习(三)_注解解析及应用

朱晔和你聊Spring系列S1E6:容易犯错的Spring AOP

让 @HystrixCommand 支持Spring EL实现动态commandKey,groupKey,threadPoolKey,fallbackMethod

Spring AOP 实现业务和异常日志记录实战

使用 SpringAOP 获取一次请求流经方法的调用次数和调用耗时

Setting Up MDC Context With AOP in Spring Boot Applications

一起来学SpringBoot | 第二十七篇:优雅解决分布式限流

使用 SpringAOP 获取一次请求流经方法的调用次数和调用耗时

一起来学SpringBoot | 第二十二篇:轻松搞定重复提交(分布式锁)