天天看点

java注解是怎么生效的?注解的产生java内置注解自定义注解

注解的产生

简介

注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
注解在一定程度上是在把元数据与源代码文件结合在一起,而不是保存在外部文档中这一大的趋势之下所催生的。

java SE5引入了注解。

其实这也就是,增加了工作的效率,同样加大了代码的耦合度。

也是,工作效率和代码耦合度,二者不可能都能顾及到,找到一个最佳的契合就好。鱼和熊掌不可兼得呀。

java内置注解

java.lang中的标准注解:

  • @Override,表示当前的方法定义将覆盖超类中的方法。
  • @Deprecated,如果程序员使用了注解为它的元素,那么编译器会发出警告信息
  • @SuppressWarnings,关闭不当的警告信息。

另外四种专门负责新注解的创建(元注解):

注解 解释
@Target

表示该注解可以用于生么地方。可能的ElementType参数包括:

CONSTRUCTOR:构造器的声明

FIELD:域声明

LOCAL_VARIABLE:局部变量声明

METHOD:方法声明

PACKAGE:包声明

PARAMETER:参数声明

TYPE:类、接口(包括注解类型)或enum声明

@Retention

表示需要在什么级别保存该注解信息.可选的RetentionPolicy参数包括:

SOURCE:注解将被编译器丢弃

CLASS:注解在class文件中可用,但会被vm丢弃

RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息

@Documented 将此注解包含在Javadoc中
@Inherited 允许子类继承父类中的注解

自定义注解

一般地,注解使用的一套流程是这样的

java注解是怎么生效的?注解的产生java内置注解自定义注解

只进行注解的创建和使用,显然是无效的.必须要进行注解处理,否则就是无根之水.

处理注解

Java SE5 扩展了反射机制的API,同时还提供了一个外部工具apt来处理解析带有注解的Java源代码.

这里我只找到两种处理注解的方式,如果谁有更好的,更多麻烦评论告诉我谢谢.

第一种 spring容器的aop+反射

import com.ql.entity.LoginUser;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author qinlei
 * @email [email protected]
 * @date 2020/11/21 11:23
 */
@Aspect
@Component
public class InjectRealNameAspect {

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.ql.aspect.InjectRealName)")
    public void injectPointCut() {

    }

    /**
     * 环绕模式
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("injectPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //获取调用方法传入的参数
        Object[] args = point.getArgs();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        InjectRealName injectRealName = method.getAnnotation(InjectRealName.class);
        //第一个参数
        //第一个参数必须是需要操作的实体类
        int methodType = injectRealName.method();
        //通过shiro获取当前操作人信息
        // LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        //此处直接给出
        LoginUser sysUser = new LoginUser("qlanto");
        
        //0更新 1修改
        if(methodType == 0){
            InjectRealNameAspect.setPrivateField(args[0],"createBy",sysUser.getRealname());
        }else{
            InjectRealNameAspect.setPrivateField(args[0],"updateBy",sysUser.getRealname());
        }
        //执行方法
        Object result = point.proceed();
        return result;
    }

    /**
     * 设置私有成员的值
     * @param instance
     * @param fieldName
     * @param value
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static void setPrivateField(Object instance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field = instance.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(instance, value);
    }


}
           

第二种 注解处理器+JAVA SPI+反射

java注解是怎么生效的?注解的产生java内置注解自定义注解

spi 扩展processor注解处理机制

javax.annotation.processing.Processor内容

com.ql.processor.InjectProcessor
           

继承AbstractProcessor,重写

import com.google.auto.service.AutoService;
import org.jeecg.modules.demo.test.aspect.InjectRealName;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author qinlei
 * @email [email protected]
 * @date 2020/11/21 14:21
 */

//@AutoService(Processor.class)
public class InjectProcessor extends AbstractProcessor {
    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private Messager messager;
    static{
        System.out.println("InjectProcessor start...");
    }

    /**
     * 注解处理器初始化
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
        elementUtils = processingEnv.getElementUtils();
        messager = processingEnv.getMessager();
    }

    /**
     *  处理注解
     * @param arg0
     * @param arg1
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> arg0,
                           RoundEnvironment arg1) {
        try {
            for (Element element : arg1.getElementsAnnotatedWith(InjectRealName.class)) {
                //做很多的校验...
                
                //进行处理
            }
        }catch (Exception e){

        }
        return false;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotataions = new LinkedHashSet<String>();
        annotataions.add(InjectRealName.class.getCanonicalName());
        return annotataions;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

           

请注意 上面代码有个注解@AutoService,他是谷歌的自动生成META-INF/services/***这个文件的

PS:这个@AutoService这个注解源码也是使用的SPI实现的processor!

maven地址:

<dependency>
			<groupId>com.google.auto.service</groupId>
			<artifactId>auto-service</artifactId>
			<version>1.0-rc1</version>
			<optional>true</optional>
		</dependency>
           

使用这个注解也可以.

PS:做到这里,我在一直提示

java: 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider ****.processor.InjectProcessor not found时抛出异常错误

至今还没解决,如果大家有方法,请评论告知我,谢谢 了!

另外,大家可以参考这个文章,来实现第二种:

https://www.race604.com/annotation-processing

好了,我继续去找为什么报错的原因了…