在本文中,我将向你展示如何生成的代码JAnnocessor通过创建框架Nikolche Mihajlovski 。 在Nikolche的演讲中,我第一次在GeeCON 2012大会上遇到JAnnocessor: “创新和实用的Java源代码生成” (幻灯片) 。 之后,我在一个项目中成功使用了它。 这个框架几乎没有资源,所以我希望我的文章对那些对使用它感兴趣或正在为项目寻找全新玩具的人有所帮助。

背景

每个Java开发人员每天都使用某种代码生成工具。 设置器,获取器,琐碎的构造函数,toString –所有这些只是样板代码。 通常,我们会在我们最喜欢的IDE的帮助下生成它。 我真的无法想象手动编码它,并且由于Java是一种静态语言,我们将永远无法跳过此过程。

所有现代IDE都提供的那些琐碎的代码生成示例并不是唯一有用的代码生成情况。 有许多现代框架生成一些代码来帮助我们编写更可靠的代码并更快地执行。 我认为最著名的示例是QueryDSL和JPA2元模型生成器 ,它们创建用于执行类型安全的数据库查询的对象。

还有其他情况-IDE不能很好地支持-我们可以使用代码生成。 通常,它可能有助于生成:

  • 建造者
  • 正面
  • 从域对象到DTO的DTO和映射器

这些仅是示例。 对于某些项目,可能存在某些特定于项目的项目,我们不能使用任何现有的代码生成工具,而必须编写自己的代码。 怎么做? 使用Java APT –注释处理工具 。

关于Java APT的几句话

Java 1.5中引入了注释处理工具,它提供了用于处理注释类的低级API。 它是大多数(也许是全部?)现有代码生成框架的基础,并且由于API的级别很低,如果您只想生成一些类,我不建议您使用纯Java APT。 使用apt-jelly(另一个代码生成工具)页面上的示例 ,可以很清楚地了解使用APT进行代码生成的情况。

您可以使用现有框架之一,而不必使用普通的Java APT。 最近,我参加了有关有趣的JAnnocessor的会议讨论,该JAnnocessor似乎做得很好。

您好JAnnocessor

JAnnocessor是Nikolche Mihajlovski制作的一个相当新的框架。 与APT有何不同? 这是作者怎么说的作者:

JAnnocessor建立在Java APT的基础上,将Java源代码模型封装在丰富而方便的高级域模型中,该模型是表达匹配和转换的良好目标。

JAnnocessor带有几个内置处理器: builderdtofacademapper 。 如果您需要自定义功能,则可以轻松地自己编写。

有一个很大的缺点–非常差的文档。 实际上,几乎没有任何文档。 在Wiki中,您找不到太多甚至更糟的东西–在框架类中也找不到Javadocs。 尽管有作者编写的入门指南 ,但我还是有一些遗漏之处,因此我将逐步指导您完成基础知识。  

Maven设置

我们将仅在编译阶段使用JAnnocessor,因此无需将其添加到我们的应用程序包中-我们将范围设置为

<dependency>
    <groupId>com.googlecode.jannocessor</groupId>
    <artifactId>jannocessor</artifactId>
    <version>0.7.2</version>
    <scope>provided</scope>
</dependency>

下一部分是注释处理插件。 尽管《 入门指南》建议使用jannocessor-maven-plugin,但由于缺少一些配置选项,我被迫使用maven-processor-plugin“过时”。

在构建/插件部分,我们添加:

<plugin>
	<groupId>org.bsc.maven</groupId>
	<artifactId>maven-processor-plugin</artifactId>
	<version>2.0.4</version>
	<executions>
		<execution>
			<id>generate-code</id>
			<goals>
				<goal>process</goal>
			</goals>
			<phase>compile</phase>

			<configuration>
				<processors>
					<processor>org.jannocessor.processor.JannocessorProcessor</processor>
				</processors>

				<systemProperties>
					<logback.configurationFile>${project.basedir}/etc/jannocessor-logback.xml</logback.configurationFile>
				</systemProperties>
				<options>
					<templates.path>${project.basedir}/src/main/resources</templates.path>
				</options>
				<defaultOutputDirectory>${project.basedir}/target/generated-sources/</defaultOutputDirectory>
			</configuration>
		</execution>
	</executions>
</plugin>
  • 处理器 -告诉maven-processor-plugin哪个类执行注释处理-请勿更改
  • configuration / logback.configurationFile –是可选的。 JAnnocessor使用内部lo​​gback进行日志记录,如果您在项目中也这样做,并且在classpath中有logback.xml,它将由JAnnocessor使用。 我建议为JAnnocessor编写单独的日志记录配置,以避免可能的问题(例如,如果您使用janino conditionals )。 jannocessor-logback.xml可以很简单:
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    
    	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    		<encoder>
    			<pattern>%-30(%date) %-5level %logger{0} %msg%n</pattern>
    		</encoder>
    	</appender>
    
    	<root level="error">
    		<appender-ref ref="console"/>
    	</root>
    
    </configuration>
  • options / templates.path –可能您将使用自定义JAnnocessor模板。 如果您不愿意,可以删除该行
  • defaultOutputDirectory –很重要–默认情况下,将生成的类添加到src / main / java中-在我看来,这是一个非常糟糕的主意。 请记住, 每次Maven构建都会重新创建所有生成的类,并且所有手工修改都将丢失 。 这就是为什么生成的类应该放在/target/generated-sources//target/generated-test-sources/

建造者一代

在此示例中,我将使用内置生成器为我的简单POJO生成生成器类。

首先,我们需要新的注释来标记要为其生成构建器的类。 它可以很简单:

package pl.maciejwalkowiak.jannocessor.domain;

public @interface GenerateBuilder {
}

我们的样本POJO:

package pl.maciejwalkowiak.jannocessor.domain;

@GenerateBuilder
public class Person {
	private String firstName;
	private String lastName;
	private Integer age;

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Integer getAge() {
		return age;
	}

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

接下来的重要部分是告诉JAnnocessor必须生成什么。 为此,我们需要在特定的包中创建特定的类: org.jannocessor.config.Processors 。 我找不到使它可配置的方法,这样配置就可以在我们项目的程序包中。

在配置中,我们指定应在何处放置创建的类: pl.maciejwalkowiak.jannocessor.domain.builder以及基本bean类在哪里: pl.maciejwalkowiak.jannocessor.domain 。 最后一个参数表示我们是否要使用调试模式–目前并不重要。

package org.jannocessor.config;

import pl.maciejwalkowiak.jannocessor.domain.GenerateBuilder;

import org.jannocessor.extra.processor.BuilderGenerator;
import org.jannocessor.model.structure.JavaClass;
import org.jannocessor.processor.annotation.Annotated;
import org.jannocessor.processor.annotation.Types;

public class Processors {

	@Annotated(GenerateBuilder.class)
	@Types(JavaClass.class)
	public BuilderGenerator generateBuilder() {
		return new BuilderGenerator("pl.maciejwalkowiak.jannocessor.domain.builder", "pl.maciejwalkowiak.jannocessor.domain", false);
	}
}

运行它

为了运行它,我们只需要执行mvn compile 。 在target/generated-sources/pl/maciejwalkowiak/jannocessor/domain/builder/我们将找到PersonBuilder类:

package pl.maciejwalkowiak.jannocessor.domain.builder;

import pl.maciejwalkowiak.jannocessor.domain.Person;
import javax.annotation.Generated;

/**
 * Generated by JAnnocessor
 */
@Generated("Easily with JAnnocessor")
public class PersonBuilder {

    private String firstName;

    private String lastName;

    private Integer age;

    public PersonBuilder firstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public PersonBuilder lastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    public PersonBuilder age(Integer age) {
        this.age = age;
        return this;
    }

    public Person build() {
        Person instance = new Person();

        instance.setFirstName(firstName);
        instance.setLastName(lastName);
        instance.setAge(age);

        return instance;
    }

}

多亏了builder类,我们有了用于创建Person对象的流畅接口:

Person person = new PersonBuilder().firstName("John").lastName("Doe").age(25).build();

创建我们自己的发电机

为了创建自定义生成器,JAnnocessor提供了丰富的API和几个示例。 不幸的是,没有可用的指南或教程。 对于本文,我想快速编写FEST Assert 2.x的生成器,但是在研究JAnnocessor源代码一段时间后,我放弃了。 相反,我将仅展示概念。

为了编写自定义生成器代码,您只需要创建从org.jannocessor.extra.processor.AbstractGenerator继承的类。

public class MyCustomGenerator extends AbstractGenerator<AbstractJavaClass> {

	public MyCustomGenerator(String destPackage, boolean inDebugMode) {
		super(destPackage, inDebugMode);
	}

	@Override
	protected void generateCodeFrom(PowerList<AbstractJavaClass> models, ProcessingContext context) {
		// ....
	}
}

PowerList<AbstractJavaClass> models表示所有带有自定义注释的类的集合。 然后,您可以访问所有类的字段,方法,已实现的接口等。我唯一想念的就是对类超类的丰富访问。 为了获得超类的字段,我不得不使用Java Reflection API。

如果您想编写自定义生成器,我鼓励您看一下诸如BuilderGenerator之类的示例 。 数量不多,但绝对有帮助。  

摘要

在这篇文章中,我展示了如何设置和使用JAnnocessor。 尽管我认为它很好而且可能非常有用的库,但是缺少文档使其无法在真正的严肃项目中使用。 我希望Nikolche能够撰写出色的文档或围绕将要实现的项目建立社区。 我也希望该项目将移至github。 它以某种方式成为标准,因此如果作者希望围绕它建立社区–我认为这是唯一正确的举动。 尽管如此,我还是问了他这个问题,至少到目前为止,他没有任何计划。

参考: Software Development Journey博客上的JCG合作伙伴 Maciej Walkowiak 使用JAnnocessor进行Java代码生成 。


翻译自: https://www.javacodegeeks/2012/08/java-code-generation-with-jannocessor.html

更多推荐

使用JAnnocessor生成Java代码