Javac 编译自定义注解及分析 Lombok 的注解实现

`<artifactId>lombok</artifactId>` 是Maven或Gradle构建工具中的依赖标签,用于指定Lombok库的项目ID ZoneAvoidanceRule: 区域感知策略,先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例。AvailabilityFilteringRule: 可用过滤策略,先过滤出故障的或并发请求大于阈值一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个; 阅读详情

本篇就是以我们最常用的 lombok作为主线来引出 javac 注解处理器,Lombok 插件注解功能很多,出了有自动 set、get 方法外,还有链式调用、建造者模式等等,但是我们就讨论最简单的 set、get 方法的生成。

一、用Lombok引出问题

1.1、引入

1、idea 中打开 settings (快捷键:ctrl+alt+s) ,搜索 plugin ,在 plugins 里面搜索 lombok ,安装

2、在项目中引入 lombok 的依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

1.2、优缺

Lombok 是一个 Java 库,能自动插入编辑器并构建工具,简化 Java 开发。通过添加注解的方式,不需要为类编写 getter或 eques 方法,同时可以自动化日志变量。官网链接

优点:简化 Java 开发,减少了许多重复代码。

缺点:

  • 降低了源码的可读性和完整性;
  • 有可能会破坏封装性,因为有些属性并不需要向外暴露;
  • 降低了可调试性;
  • Lombok 会帮我们自动生成很多代码,但这些代码是在编译期生成的,因此在开发和调试阶段这些代码可能是“丢失的”,这就给调试代码带来了很大的不便。

如果不考虑的那么严谨,我觉得还是要用的,因为我懒。

1.3、使用

写一个类来分析一下:

我们自己手写的一个JavaBean

/**
 * @description:
 * @author: Yihui Wang
 * @date: 2022年07月06日 20:23
 */

public class Student {

    private String name;

    private String age;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

用了 lombok 注解的 JavaBean

/**
 * @description:
 * @author: Yihui Wang
 * @date: 2022年07月06日 20:23
 */
@Data
public class StudentLombok {
    private String name;
    private String age;
}

我们编译一下,Idea 中点击顶部菜单 Build ,下拉选择 Recompile 看看他们生成的 class文件是什么样的。

可以明显看出,使用了 @Setter、@Getter 注解后,和我们手动编写的 Java 代码,编译完的结果是一样的。

它直接帮我们生成了这些方法,这些步骤究竟是谁做的勒?我们是否也可以自己编写这样的注解呢?

二、Lombok 原理分析

其实这里面用到了 AOP 编程的编译时织入技术,就是在编译的时候修改最终 class 文件。

大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行:

Javac 的编译过程

归纳起来主要是由以下三个过程组成:

  • 分析和输入到符号表
  • 注解处理
  • 语义分析和生成 class 文件

而Lombok 正是利用注解处理这一步来进行实现的。Lombok 使用的是 JDK 6 实现的 JSR 269: Pluggable Annotation Processing API (编译期的注解处理器) ,它允许在编译期处理注解,读取、修改、添加抽象语法树中的内容。

其实说到这里,我们还只是知道它是在这一步处理的,但如何处理的,我们还是一无所知。

稍后我们会手动实现 Lombok 中的 @Getter、@Setter 注解,这里先事先说明可能会牵扯到的知识。

  • 主要使用到的都是 jdk 源码的 tools.ja 包
  • 使用的 api 主要是com.sun.tools.javac包下的
  • 抽象语法 JCTree 使用

不懂也没关系,我也不是很懂,哈哈,我也只是因为好奇,才来探寻的

其中最主要的就是牵扯到的AbstractProcessor抽象注解处理类,还有就是 JCTree 相关的api,这些的话,我也用的不多,不敢胡乱发言。

要实现注解处理器首先要做的就是继承抽象类 javax.annotation.processing.AbstractProcessor,然后重写它的 process() 方法,process() 方法是 javac 编译器在执行注解处理器代码时要执行的过程。

/**
一个抽象注释处理器,旨在成为大多数具体注释处理器的方便超类。
 */
public abstract class AbstractProcessor implements Processor {
    /**
     * Processing environment providing by the tool framework.
     */
    protected ProcessingEnvironment processingEnv;
    private boolean initialized = false;

    /**
   如果处理器类使用SupportedOptions进行注释,则返回一个不可修改的集合,该集合与注释的字符串集相同。
   如果类没有这样注释,则返回一个空集
     */
    public Set<String> getSupportedOptions() {
        SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class);
        if  (so == null)
            return Collections.emptySet();
        else
            return arrayToSet(so.value());
    }

    /**
如果处理器类使用SupportedAnnotationTypes进行注释,则返回一个不可修改的集合,
该集合具有与注释相同的字符串集。如果类没有这样注释,则返回一个空集。
return:
此处理器支持的注释类型的名称,如果没有则为空集
     */
    public Set<String> getSupportedAnnotationTypes() {
            SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
            if  (sat == null) {
                if (isInitialized())
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                             "No SupportedAnnotationTypes annotation " +
                                                             "found on " + this.getClass().getName() +
                                                             ", returning an empty set.");
                return Collections.emptySet();
            }
            else
                return arrayToSet(sat.value());
        }

    /**
如果处理器类使用SupportedSourceVersion进行注解,则在注解中返回源版本。
如果类没有这样注释,则返回SourceVersion.RELEASE_6 
     */
    public SourceVersion getSupportedSourceVersion() {
        SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class);
        SourceVersion sv = null;
        if (ssv == null) {
            sv = SourceVersion.RELEASE_6;
            if (isInitialized())
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                         "No SupportedSourceVersion annotation " +
                                                         "found on " + this.getClass().getName() +
                                                         ", returning " + sv + ".");
        } else
            sv = ssv.value();
        return sv;
    }

    /**
该方法有两个参数,“annotations” 表示此处理器所要处理的注解集合;
“roundEnv” 表示当前这个 Round 中的语法树节点,
每个语法树节点都表示一个 Element(javax.lang.model.element.ElementKind 可以查看到相关 Element)。
     */
    public abstract boolean process(Set<? extends TypeElement> annotations,
                                    RoundEnvironment roundEnv);

}

另外还有这两个用来配合的 注解:

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedAnnotationTypes 表示注解处理器对哪些注解感兴趣,“*” 表示对所有的注解都感兴趣;@SupportedSourceVersion 指出这个注解处理器可以处理最高哪个版本的 Java 代码。

三、简易版 Lombok 实现

简要说明

先说说我们要实现的东西,为了简单的去理解,我这里只讨论get、set 方法,其实里面的实现都差不多,如果偏要说不同的话,就是调用的javac的api不同吧。

写了两个注解 :@MyGetter 和 @MySetter 和他们的处理器 MyAnnotationProcessor

注解处理器,顾名思义就是用来处理注解的啦。

项目结构:

由于是maven项目,这里面引用了com.sun.tools的东西,所以,需要在maven的pom文件里面加上,这样,在使用maven打包的时候,才不会报错。

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>jdk路径/lib/tools.jar</systemPath>
 </dependency>

我们这里利用 Java SPI 加载自定义注解器的方式,生成一个 jar 包,类似于 Lombok ,这样之后其它应用一旦引用了这个 jar 包,自定义注解器就能自动生效了。

SPI是java提供的一种服务发现的标准,具体请看SPI介绍,但每次我们都需要自己创建services目录,以及配置文件,google的autoservice就可以帮我们省去这一步。

    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service</artifactId>
        <version>1.0-rc5</version>
    </dependency>

如果你使用Processor(javax.annotation.processing.Processor),并且你的元数据文件被包含在了一个jar包中,同时这个jar包是在javac(java编译)的classpath路径下时,javac会自动的执行通过该方式注入进去的Processor的实现类,以实现对于该项目内的相关数据的扩展。

使用 AutoService 会自动的生成META-INF./services/javax.annotation.processing.Processor 文件,并且文件中内容就是我们动态注入进去的类。

然后在编译的时候就会执行对应的扩展方法,同时写入文件。

项目代码

代码都很简单,所以除了注解处理器,其他的都没有带啥注释啦哈。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface MyGetter {
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface MySetter {
}

package com.nzc.my_annotation;

import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

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

/**
 * @author nzc
 */
@SupportedAnnotationTypes({"com.nzc.my_annotation.MyGetter","com.nzc.my_annotation.MySetter"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class MyAnnotationProcessor extends AbstractProcessor {

    private JavacTrees javacTrees; // 提供了待处理的抽象语法树
    private TreeMaker treeMaker; // 封装了创建AST节点的一些方法
    private Names names; // 提供了创建标识符的方法

    /**
     * 从Context中初始化JavacTrees,TreeMaker,Names
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        javacTrees = JavacTrees.instance(processingEnv);
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 返回使用给定注释类型注释的元素的集合。
        Set<? extends Element> get = roundEnv.getElementsAnnotatedWith(MyGetter.class);
        for (Element element : get) {
            // 获取当前类的抽象语法树
            JCTree tree = javacTrees.getTree(element);
            // 获取抽象语法树的所有节点
            // Visitor 抽象内部类,内部定义了访问各种语法节点的方法
            tree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    // 在抽象树中找出所有的变量
                    // 过滤,只处理变量类型
                    jcClassDecl.defs.stream()
                            .filter(it -> it.getKind().equals(Tree.Kind.VARIABLE))
                            // 类型强转
                            .map(it -> (JCTree.JCVariableDecl) it)
                            .forEach(it -> {
                                // 对于变量进行生成方法的操作
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genGetterMethod(it));
                            });

                    super.visitClassDef(jcClassDecl);
                }
            });

        }
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(MySetter.class);
        for (Element element : set) {
            JCTree tree = javacTrees.getTree(element);
            tree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    jcClassDecl.defs.stream()
                            .filter(it -> it.getKind().equals(Tree.Kind.VARIABLE))
                            .map(it -> (JCTree.JCVariableDecl) it)
                            .forEach(it -> {
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genSetterMethod(it));
                            });

                    super.visitClassDef(jcClassDecl);
                }
            });

        }

        return true;
    }

    private JCTree.JCMethodDecl genGetterMethod(JCTree.JCVariableDecl jcVariableDecl) {
        // 生成return语句,return this.xxx
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Select(
                        treeMaker.Ident(names.fromString("this")),
                        jcVariableDecl.getName()
                )
        );

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<JCTree.JCStatement>().append(returnStatement);

        // public 方法访问级别修饰
        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
        // 方法名 getXXX ,根据字段名生成首字母大写的get方法
        Name getMethodName = createGetMethodName(jcVariableDecl.getName());
        // 返回值类型,get类型的返回值类型与字段类型一致
        JCTree.JCExpression returnMethodType = jcVariableDecl.vartype;
        // 生成方法体
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // 泛型参数列表
        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();
        // 参数值列表
        List<JCTree.JCVariableDecl> parameterList = List.nil();
        // 异常抛出列表
        List<JCTree.JCExpression> throwCauseList = List.nil();

        // 生成方法定义树节点
        return treeMaker.MethodDef(
                // 方法访问级别修饰符
                modifiers,
                // get 方法名
                getMethodName,
                // 返回值类型
                returnMethodType,
                // 泛型参数列表
                methodGenericParamList,
                //参数值列表
                parameterList,
                // 异常抛出列表
                throwCauseList,
                // 方法默认体
                body,
                // 默认值
                null
        );

    }

    private JCTree.JCMethodDecl genSetterMethod(JCTree.JCVariableDecl jcVariableDecl) {
        // this.xxx=xxx
        JCTree.JCExpressionStatement statement = treeMaker.Exec(
                treeMaker.Assign(
                        treeMaker.Select(
                                treeMaker.Ident(names.fromString("this")),
                                jcVariableDecl.getName()
                        ),
                        treeMaker.Ident(jcVariableDecl.getName())
                )
        );

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<JCTree.JCStatement>().append(statement);

        // set方法参数
        JCTree.JCVariableDecl param = treeMaker.VarDef(
                // 访问修饰符
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),
                // 变量名
                jcVariableDecl.name,
                //变量类型
                jcVariableDecl.vartype,
                // 变量初始值
                null
        );

        // 方法访问修饰符 public
        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
        // 方法名(setXxx),根据字段名生成首选字母大写的set方法
        Name setMethodName = createSetMethodName(jcVariableDecl.getName());
        // 返回值类型void
        JCTree.JCExpression returnMethodType = treeMaker.Type(new Type.JCVoidType());
        // 生成方法体
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // 泛型参数列表
        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();
        // 参数值列表
        List<JCTree.JCVariableDecl> parameterList = List.of(param);
        // 异常抛出列表
        List<JCTree.JCExpression> throwCauseList = List.nil();
        // 生成方法定义语法树节点
        return treeMaker.MethodDef(
                // 方法级别访问修饰符
                modifiers,
                // set 方法名
                setMethodName,
                // 返回值类型
                returnMethodType,
                // 泛型参数列表
                methodGenericParamList,
                // 参数值列表
                parameterList,
                // 异常抛出列表
                throwCauseList,
                // 方法体
                body,
                // 默认值
                null
        );

    }

    private Name createGetMethodName(Name variableName) {
        String fieldName = variableName.toString();
        return names.fromString("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));

    }

    private Name createSetMethodName(Name variableName) {
        String fieldName = variableName.toString();
        return names.fromString("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));

    }
}

@MyGetter
@MySetter
public class School {

    private String name;
    private String address;
}

测试结果:

编译:

编译的话,直接编译根项目就好,我原本想着先编译子项目,再编译另外一个项目,但是会报错,不想纠结了,放出来的,是经过测试的,可以正常编译出来的。

四、思考

相信大家都有使用Lombok的过程,但是不知道大家有没有注意到我上面的demo是存在一些问题的呢?

我们之前分析了 lombok 它是在编译时为我们添加了诸如 set、get 方法的,但实际上我们在开发的时候就已经可以调用对象上的 set、get 方法啦,这又是如何实现的呢?

我个人的测试及思考

这里一定是会牵扯到 idea 中的 lombok 插件的问题,如果你只是引入了 lombok 的依赖,没有安装 lombok 插件的话,那么在 idea 是不会有方法提示的。

我新建了一个空白项目进行测试,如果你引入依赖,没安装插件,idea 只会爆红,但是是可以通过编译的,也不会报错。写一个main 方法也是可以运行的,只不过安装了插件才会提供提示。

按照这个思路,我又返回去测试了上面的那个 Demo,答案是失败的。

测试如下:

  1. 创建了 Demo 类,里面写了 main 方法
  2. 创建了 School 对象,调用了 set、get 方法
  3. 使用 maven 编译,成功过,也失败过(问我也是白问,我都测麻了)
  4. 这个可能跟我的机器环境有关,理论上应该是可以成功的,后来新建的一个项目又是可以的。大家也可以去玩一玩。
  5. 启动 main 方法,是直接报错,起不来,原因不知道,如果我还有时间,我再去找找。
java: java.lang.ClassCastException: class com.sun.proxy.$Proxy26 cannot be cast to class com.sun.tools.javac.processing.JavacProcessingEnvironment (com.sun.proxy.$Proxy26 is in unnamed module of loader java.net.URLClassLoader @4fccd51b; com.sun.tools.javac.processing.JavacProcessingEnvironment is in module jdk.compiler of loader 'app')


lombok 的插件在其中肯定是做了一些事情的,但是我在各大搜索引擎上搜索这方面的知识,也没有找到相关的一些资料,倒是看到有几个小伙伴问出了和我相似的问题。

如下:大家使用过 lombok 的 @Slf4j 注解吧,为什么有了这个注解,我们就可以直接在类里面使用 log 对象,这个对象又是在哪里创建出来的呢?

说到这,其实我还是没说出什么道理,因为我也不明白,所以最后这一小节,我的命名才是直接明了的为思考。

如果有明白的大佬,请不啬赐教,非常感谢!

此外的补充: :

其实自定义注解处理器,给我的感觉就像 SpringMVC 中拦截器一样,SpringMVC是拦截请求,自定义注解是拦截在编译前,而且给我的感觉的话,自定义注解编译器应该更好玩,并竟可以改 class 文件。

VS Code Doxygen插件:自动化代码文档生成与团队协作实践 代码文档是软件工程中保障可维护性与团队协作的关键环节,其核心在于将代码的设计意图、接口规范和使用约束以结构化形式记录下来。传统手动编写文档注释效率低下且易出错,而Doxygen作为一种广泛使用的文档生成系统,通过解析源代码中的特殊注释标签,能自动生成格式统一的API文档。其技术价值在于将文档编写无缝嵌入开发流程,显著提升代码可读性和项目交接效率。在实际应用场景中,开发者常借助编辑器插件来简化Doxygen注释的编写,例如VS Code的Doxygen Documentation Generator插件,它通 阅读详情

相关推荐

JAVA11注解处理器(Annotation Processor)的简单应用

最近在阅读以前的代码时发现Mybatis的映射文件中需要写一个表的列集合,既然PO层已经定义了表对应的实体,不如根据po类的字段自动生成列集合,映射的xml中直接调用,也能保证字段的一致性,注解处理器自然成了第一选择,开撸。 新建一个Person类并添加@TableEntity注解 我希望Person类在编译后新增一个columnNames()方法,返回值是该类的字段名称,并将驼峰转成下划线,如下所示 接下来需要我们自行编写注解处理器来生成columnNames()方法,这需要用到java抽

qwe1505566488的博客 2040

lombok使用

加入 maven 依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> 1. @Getter/@Setter 自动产生 getter/setter 2. @ToString 自动重写 to

qq_38967150的博客 977

自定义Lombok注解

Java 是一门"繁琐"的语言,使用Lombok可以显著地减少样板代码。比如使用@Getter注解可以为你的私有属性创建 get 方法。 源代码 @Getter private int age = 10; 生成后代码 private int age = 10; public int getAge() { return age; } Lombok 自身已经拥有许...

weixin_30896825的博客 2088

Java-详解javac AbstractProcessor

AbstractProcessor 就是注解处理器 ,在java文件编译成class文件之前,对java文件进行操作,生成代码。(可以参考lombok)这些对象必须在init方法里初始化@SupportedAnnotationTypes(“cc.HelloWorld”) ,“cc.HelloWorld” 是HelloWorld注解的类全名。@SupportedSourceVersion(SourceVersion.RELEASE_7) ,annotation支持版本,向前兼容到JDK7。

yueerba126的博客 1821

解决Lombok编译器不兼容:从javac到ECJ的完整排查与配置指南

Java编译时注解处理器是现代开发中提升效率的重要工具,它能在编译阶段自动生成代码,减少样板代码编写。其工作原理是通过编译器API在抽象语法树层面修改代码,最终生成字节码。这项技术的核心价值在于提升开发效率、保持代码简洁性,广泛应用于企业级后端开发、微服务架构和快速原型构建。在实际工程实践中,当项目使用不同编译器如标准javac或Eclipse ECJ时,注解处理器可能因编译器API差异而失效。本文聚焦于Lombok这一流行注解处理器在ECJ编译器环境下的兼容性问题,通过分析Maven构建配置和IDE设置,

weixin_30387799的博客 446

从实践 APT 到深入理解 Lombok

文章目录一、概述1.1 起因1.2 疑问二、前提2.1 Java 编译器的工作流程2.2 关于 Lombok2.3 关于 Javac 的源码三、求证3.1 Lombok 中的注解和 Spring 中的注解有什么区别?3.2 Lombok 到底是生成新的 Java 源文件还是修改已有的 Java 源文件?3.3 如果是修改已有的 Java 源文件那 Lombok 又是怎么实现的呢(源码解析)?3.3...

Coder Yang 1954

【学习总结】自定义lombok注解

​ 官网对他的功能的描述:ProjectLombok是一个java库,可以自动插入编辑器和构建工具,为您的java增添色彩。永远不要再编写另一个getter或equals方法,只要有一个注释,您的类就有一个功能齐全的构建器,自动化您的日志变量,等等。​ 根据官网的解释我们可以把lombok定位为一个构建工具插件。​ Java语言的”编译期“其实是一段”不确定“的操作过程,因为他可能是指一个前端编译器(其实叫”编译器的前端“更准确一些)把 *.java文件转变成 *.class文件;

qq_42563471的博客 2174

【雷达图】R语言绘制雷达图(ggradar),NBA季后赛数据为例

一起用R语言给nba球星画球技雷达图吧!

池塘春草梦的博客 6252

Lombok经常用,但是你知道它的原理是什么吗?(二)

在上一篇Lombok经常用,但是你知道它的原理是什么吗?简单介绍了注解处理器,是用来处理编译期的注解的一个工具,我们只是自己生成了一些代码,但是和Lombok却不一样,因为Lombok是在原有类的基础上增加了一些类,你那么Lombok是如何做到修改原有类的内容呢?接下来我们就再进一步了解Lombok的原理。 Javac原理 既然我们是在编译期对类进行操作了,那么我们就需要了解在Java中Javac...

weixin_43133353的博客 462

手把手教你用STM32实现SPWM逆变器控制(附完整代码)

本文详细介绍了如何使用STM32微控制器实现SPWM逆变器控制,包括硬件设计、PWM生成配置、正弦表生成与DMA传输,以及闭环控制与性能优化。通过完整的代码示例和实测数据,帮助开发者快速掌握SPWM技术,应用于新能源和电力电子领域。

weixin_29296241的博客 318

【Python_PySide6学习笔记(二十四)】菜单栏QMenuBar类的基本用法

此篇文章中介绍 PySide6 中 QMenuBar 类的基本用法,包括"创建菜单栏"、"创建一级菜单"、"创建二级菜单"等,会持续更新,有需要的读者可以点赞收藏,也欢迎在评论区进行讨论。

禾戊之昂的博客 1625

自定义Lombok注解:满足个性化需求

本文介绍了如何开发自定义Lombok注解的完整流程。首先需要创建项目并配置Lombok依赖,然后定义注解接口并实现注解处理器。注解处理器通过继承AbstractProcessor类,重写process方法来自动生成代码逻辑。最后需要注册注解处理器,并通过在类上添加自定义注解来使用。文章以自动生成对象属性打印方法为例,详细演示了从注解定义到处理器实现的完整过程。通过自定义Lombok注解,开发者可以根据项目需求扩展功能,提高代码开发效率。

fykam的博客 1645

手把手教你学simulink(90.1)--直流电动机实例:基于Simulink的基本模型的直流电动机(DC Motor)建模

直流电动机(DC Motor)因其结构简单、控制方便和良好的调速性能,广泛应用于各种工业自动化、机器人技术、家用电器以及航空航天等领域。为了更好地理解和应用直流电动机的工作原理及其控制系统,本项目旨在通过Simulink对直流电动机进行基本模型的建模与仿真。

MHD0815的博客 439

Lombok 的实现原理

我们都用过Lombok 可以快速的进行实体类的setter/getter/toString/hash/construct等等方法的自动编编译字节码生成。但我们只加入了一个注解`@Data`或`@Setter`是怎么生成对应方法的字节码的呢?下面听我慢慢分析...

yh4494的博客 3432

第 9 篇:TMC2240 速度调节实战|STEP 信号生成 + 平滑调速算法(无抖动)

摘要:本文详细介绍了TMC2240电机调速方案,重点解决调速时电机抖动、丢步等问题。核心原理是通过STEP信号频率控制转速,结合平滑加减速算法避免电流冲击。文章从速度与STEP频率的数学关系入手,提供了定时器生成固定频率STEP信号的实现代码,并给出不同转速下的频率参数表。针对42步进电机+256微步细分场景,详细讲解了定时器初始化、中断处理及固定转速设置方法,帮助开发者实现稳定调速。全文包含可直接复用的代码片段,适合嵌入式开发者快速实现电机平滑调速功能。

BackCatK Chen的博客 1035

第4.4节:保护轴承与安全系统

飞轮储能系统的保护轴承是磁悬浮失效时的最后安全屏障,其设计需满足非干涉性、高可靠性和有限寿命承载原则。转子跌落过程包括自由下落、碰撞、进动和滑行减速等复杂非线性阶段,产生远超常规工况的冲击载荷。保护轴承需具备极限冲击容量、高温耐受性和抗卡死能力。系统采用多层级安全架构,结合故障诊断与健康管理,通过监测振动、电流等参数实现预测性维护。安全停机序列有序结合主动制动与被动摩擦制动,确保系统在极端工况下的安全。保护轴承类型多样,需根据系统规模与可靠性要求合理选择。

技术工作者 227

javac 编译拓展,像 lombok 一样通过注解生成 自定义方法

javac 编译拓展,像 lombok 一样通过注解生成 自定义方法 <<深入理解java虚拟机>> 第十章介绍了javac的相关知识,提到可以通过拓展 javax.annotation.processing.Processor 在编译阶段可以自定义一些操作。还提到lombok也是通过这种方式进行代码生成。所以尝试自己来写一个玩玩,根据注解生成一个方法。 参照书上的做法 : 新建一个项目,结构如下 具体内容: CreatePrint注解: package com.rxf113;

我是个农民的博客 1226

vue3 - 最新详细实现高德地图绘制动态热力图详细教程,在某区域或城市地图上做“热力图层“展示人流量、旅游人口密度、热量图、全国地区地理热力图,利用颜色的深浅表示密度或强度等场景(详细示例代码教程)

vue3,nuxt3,高德地图,热力图,绘制热力图层,人流量,vue3使用高德地图实现某个区域热力图,vue3高德地图api加载热力图及关闭,接收热力图数据并渲染热力图,关掉热力图,Vue使用高德API制作热力图,热力图加载不出来,电子围栏围住热力图,vue3中使用高德地图渲染热力图组件,高德地图使用热力图详细教程,报错,卡顿,出不来怎么办,无法关闭,怎么解决这个问题,出错,城市名称,城市某个街道,城区,省份,市区,县城,乡镇,村,各省份的热力图,交通流量,人口流量,密度,旅游出行,人口数量,颜色介绍,深浅

✅ 精选推荐文章 5205

如何把方法的入参传给注解属性_自定义Lombok注解

Java 是一门"繁琐"的语言,使用 Lombok 可以显著地减少样板代码。比如使用@Getter注解可以为你的私有属性创建 get 方法。源代码@Getter private int age = 10;生成后代码private int age = 10;public int getAge() { return age;}Lombok 自身已经拥有许多非常实用的注解,例如@Get...

weixin_39601929的博客 1300

NX二次开发常用函数——从一个坐标系到另一个坐标系的转换(UF_MTX4_csys_to_csys )相同体坐标转化

本文介绍了在项目开发中实现坐标转换的方法,重点解析了两个关键函数UF_MTX4_csys_to_csys和UF_MTX4_vec3_multiply的使用。通过4个变换矩阵(transform1-4)实现工作坐标系与绝对坐标系之间的相互转换,并以圆柱体为例详细说明了坐标转换的具体实现过程,包括原点、X/Y轴方向的转换处理。相比之前采用的if-else判断方法,这种矩阵转换方式在代码复杂度和运行效率上都更具优势。

weixin_47753171的博客 600

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【Lombok】onX | 为Lombok注解添加自定义注解

这个功能被认为是 "变通状态"--它的存在是为了让那些没有这个功能就无法工作的`lombok`用户无论如何都能使用这个功能。如果我们找到一个更好的方法来实现这个功能,或者未来的某个java版本引入了一个替代策略,这个功能可以在没有一个合理的废弃期的情况下消失。另外,这个功能可能在未来的javac版本中无法使用。使用时请自行斟酌。 大多数使`lombok`生成方法或构造函数的注解可以被配置为使`lombok`在生成的代码中的元素上添加自定义注解。

猫巳的博客 2224

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Lombok 原理与实现

本文主要包含以下内容: Lombok 的实现机制分析。 插入式注解处理器的说明及使用。 动手实现 lombok 的 @Getter 和 @Setter 注解。 配置 IDEA 以调试 Java 编译过程。

Sumkor的博客 1470

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【MedusaSTears】IDEA使用eclipse compiler(ecj)后编译出错:lombok注解失效?--用javac编译器就没事,但是项目必须用eclipse编译器!

关键词 错误:java: Internal compiler error: java.util.ServiceConfigurationError: javax.annotation.processing.Processor: Provider lombok.launch.AnnotationProcessorHider$ClaimingProcessor not found at java.ut...

MedusaSTears的博客 2891

ant编辑java忽略注释_自定义Lombok注解

Java 是一门"繁琐"的语言,使用Lombok可以显著地减少样板代码。比如使用@Getter注解可以为你的私有属性创建 get 方法。源代码@Getter private int age = 10;生成后代码private int age = 10;public int getAge() {return age;}Lombok 自身已经拥有许多非常实用的注解,例如@Getter/@...

weixin_36071371的博客 604
上一篇: 简历没东西写?看看这份价值55亿的“网约车”项目资料,丰富你的简历
下一篇: Java内存模型(JMM)与volatile详解
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值