AutoValue: 自动生成equals,hashCode和toString方法

背景

自己编写一个并重写equals,hashCode方法,很容易出错.而使用IDE工具生成的方法又显得十分”臃肿”.于是AutoValue应运而生.AutoValue最低支持Java 8.

官方仓库地址: https://github.com/google/auto/tree/master/value

java标志
image-2977

引入依赖

Maven

引入注解包:

<dependencies>
  <dependency>
    <groupId>com.google.auto.value</groupId>
    <artifactId>auto-value-annotations</artifactId>
    <version>${auto-value.version}</version>
  </dependency>
</dependencies>

处理注解:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <annotationProcessorPaths>
          <path>
            <groupId>com.google.auto.value</groupId>
            <artifactId>auto-value</artifactId>
            <version>${auto-value.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

上面的这个,也可以像下面这样定义(注意: 您可以将处理器本身包含在编译时的类路径中。这样做可能会将不必要的类拉到您的运行时类路径中。):

<dependencies>
  <dependency>
    <groupId>com.google.auto.value</groupId>
    <artifactId>auto-value</artifactId>
    <version>${auto-value.version}</version>
    <optional>true</optional>
  </dependency>
</dependencies>

Gradle

dependencies {
  // Use 'api' rather than 'compile' for Android or java-library projects.
  compile             "com.google.auto.value:auto-value-annotations:${autoValueVersion}"
  annotationProcessor "com.google.auto.value:auto-value:${autoValueVersion}"
}

发生了什么?

AutoValue在javac内作为标准注释处理器运行。它读取您的抽象类并推断实现类的外观。它在您的程序包中生成一个具体实现类的源代码,该类扩展了您的抽象类,具有:

  • 包装可见性(非公开)
  • 每个抽象访问器方法一个字段
  • 设置这些字段的构造函数
  • 返回相关字段值的每个访问器方法的具体实现
  • 一个以常规方式比较这些值的equals实现
  • 适当的对应hashCode
  • 返回实例的有用(但未指定)字符串表示形式的toString实现

如上所示,您的手写代码将其工厂方法调用委派给了生成的构造函数和方法!

请注意,您类的调用者不需要了解任何这些。 它们只是调用您提供的工厂方法,并返回行为良好的实例。

警告

注意不要将参数以错误的顺序意外传递给生成的构造函数。 您必须确保测试足以捕获任何现场订购问题。 在大多数情况下,这应该是测试创建此值类别的任何实际目的的自然结果! 在其他情况下,只需执行上面显示的非常简单的测试即可。 考虑切换到使用builder选项来避免此问题。

我们保留随时更改hashCode实现的权利。 永远不要保留hashCode的结果或将其用于任何其他意外目的,也要小心不要依赖于您的值在HashSet之类的无序集合中出现的顺序。

示例

pom.xml

<dependencies>
	<dependency>
		<groupId>com.google.auto.value</groupId>
		<artifactId>auto-value</artifactId>
		<version>1.7</version>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>com.google.auto.value</groupId>
		<artifactId>auto-value-annotations</artifactId>
		<version>1.7</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>compile</scope>
	</dependency>
</dependencies>

AutoValueTest.java

Animal在IDE中可能会提示错误,忽略即可.

import com.google.auto.value.AutoValue;

import static org.junit.Assert.*;


@AutoValue
abstract class Animal {
    static Animal create(String name, int numberOfLegs) {
        return new AutoValue_Animal(name, numberOfLegs);
    }

    abstract String name();

    abstract int numberOfLegs();


}


public class AutoValueTest {

    public static void main(String[] args) {
        Animal dog = Animal.create("dog", 4);
        assertEquals("dog", dog.name());
        assertEquals(4, dog.numberOfLegs());

        // You probably don't need to write assertions like these; just illustrating.
        assertTrue(Animal.create("dog", 4).equals(dog));
        assertFalse(Animal.create("cat", 4).equals(dog));
        assertFalse(Animal.create("dog", 2).equals(dog));

        assertEquals("Animal{name=dog, numberOfLegs=4}", dog.toString());
    }
}

运行AutoValueTest.java,如果没有输出错误,则正常.

Animal抽象类,经过AutoValue生成的代码如下:

import javax.annotation.Generated;

@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_Animal extends Animal {
  private final String name;
  private final int numberOfLegs;

  AutoValue_Animal(String name, int numberOfLegs) {
    if (name == null) {
      throw new NullPointerException("Null name");
    }
    this.name = name;
    this.numberOfLegs = numberOfLegs;
  }

  @Override
  String name() {
    return name;
  }

  @Override
  int numberOfLegs() {
    return numberOfLegs;
  }

  @Override
  public String toString() {
    return "Animal{"
        + "name=" + name + ", "
        + "numberOfLegs=" + numberOfLegs + "}";
  }

  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (o instanceof Animal) {
      Animal that = (Animal) o;
      return this.name.equals(that.name())
          && this.numberOfLegs == that.numberOfLegs();
    }
    return false;
  }

  @Override
  public int hashCode() {
    int h = 1;
    h *= 1000003;
    h ^= this.name.hashCode();
    h *= 1000003;
    h ^= this.numberOfLegs;
    return h;
  }
}

更多想知道的问题

点: 我怎么?

RTF: 转换为PDF并在线显示

安装及配置Aspose: Java: 将RTF等Word文件转换为PDF文件,Java: 转换RTF文件为txt文本

java标志
image-2971

代码参考:

    @RequestMapping("/toPdf")
    public void toPdf(HttpServletResponse response) {
        long old = System.currentTimeMillis();
        response.setCharacterEncoding("UTF-8");
        String contentType = "application/pdf";
        response.setContentType(contentType);
        response.reset();
        response.setHeader("Content-Disposition", "inline;fileName=report.pdf");
        try {
            // rtf文件路径
            Document doc = new Document("D:\\qqq.rtf");
            doc.save(response.getOutputStream(), SaveFormat.PDF);
            response.getOutputStream().flush();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

Java: 将RTF等Word文件转换为PDF文件

起因

之前介绍过一种RTF文件读取成纯文本的方案,但是缺点太明显了: 不支持格式,文本样式也丢失了.在这里再次推荐另外一种方式: 将RTF文件转换为PDF文件,使用Aspose库.

关于Aspose

Aspose提供了很多种方案,支持Word到PDF的转换.同时还支持很多文件的转换,具体可以参考: https://www.aspose.com/官网.

这么好用的库,是收费的吗? 是的.

官方提供了免费试用,如果使用免费的版本,在转换之后的PDF上会有水印.

安装方式

有两种安装方式:

使用jar文件

访问: Aspose的Maven仓库

选择aspose-words,19.12(表示19年12月发布的版本),下载aspose-words-19.12-jdk17.jar这个jar文件.

使用Maven

更推荐使用这种方式,因为方便很多.

Maven方式需要在pom.xml文件中添加:

<dependencies>
    <dependency>
        <groupId>com.aspose</groupId>
        <artifactId>aspose-words</artifactId>
        <version>19.8</version>
        <classifier>jdk17</classifier>
    </dependency>
    <dependency>
        <groupId>com.aspose</groupId>
        <artifactId>aspose-words</artifactId>
        <version>19.8</version>
        <classifier>javadoc</classifier>
    </dependency>
</dependencies>

然后添加Maven仓库地址:

<repositories>
    <repository>
        <id>AsposeJavaAPI</id>
        <name>Aspose Java API</name>
        <url>https://repository.aspose.com/repo/</url>
    </repository>
</repositories>

之后就可以在项目中使用了.

将RTF文件转换成PDF文件

Java实现代码:

// rtf源文件
Document wpd = new Document( "H:\\test.rtf");
// 保存PDF文件.
wpd.save("H:\\output.pdf", SaveFormat.PDF);
// 保存HTML文件.
wpd.save( "H:\\output.html", SaveFormat.HTML);

效果图

RTF文件:

image-2958

转换之后的PDF文件:

image-2959

转换之后的HTML文件:

image-2960

Java: 转换RTF文件为txt文本

代码整理自网络.

  • 缺点: 这样转换会丢失RTF文件中的样式(比如字体大小,颜色,加粗等).
  • 优点: 没有引用任何外部依赖,仅需JDK即可运行.

java标志
image-2956

DefaultStyledDocument styledDoc = new DefaultStyledDocument();
// D:\\rtf.txt 是RTF文件,用记事本打开后另存的txt文件.
try(InputStream is = new ByteArrayInputStream(Files.readAllBytes(Paths.get("D:\\rtf.txt")))) {
    new RTFEditorKit().read(is, styledDoc, 0);
    // 可能会存在编码问题,只能多试几个编码,直到找到正确的那个.
    String bodyText = new String(styledDoc.getText(0, styledDoc.getLength()).getBytes("UTF-8"));
    // bodyText = new String(styledDoc.getText(0, styledDoc.getLength()).getBytes("GB2312"));
    // bodyText = new String(styledDoc.getText(0, styledDoc.getLength()).getBytes("GBK"));
    System.out.println(bodyText);
}catch(Exception ex){
    System.out.println(ex.getMessage());
}

Docker: 运行Java应用

更方便的运行方式

在以前只能在Linux或者Windows系统中一一搭建所有应用程序的开发环境,包括MySQL,Redis,RabbitMQ等.但自从Docker出现之后,改变了这一切.开发环境和运行环境全都可以直接放到Docker容器中运行,最主要的是,搭建方便.基本只需一条命令就可以安装好.

关于Spring Boot的运行

完整源码: Gitee仓库

其实Spring Boot一般有两种运行方式:

  1. 放在Tomcat中,以war包的方式运行.
  2. 在Java命令行中,以jar包的方式运行.

Docker中,推荐以第二种方式更好,更方便一些.

image-2881
docker

推送方式

把Spring Boot打包之后的jar包,推送到Docker有两种方式:

  1. dockerfile Maven插件
  2. jib Maven插件

这里只介绍第一种用的比较多的dockerfile方式(但是,源码里面提供了两种方式的示例: Gitee仓库),源码如下:

pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>xyz.suancaiyu</groupId>
	<artifactId>custom-spring-boot-docker-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>custom-spring-boot-docker-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<docker.image.prefix>192.168.37.130</docker.image.prefix>
		<java.version>12</java.version>
	</properties>


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<!-- tag::plugin[] -->
			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>dockerfile-maven-plugin</artifactId>
				<version>1.4.9</version>
				<configuration>
					<repository>${docker.image.prefix}/${project.artifactId}</repository>
				</configuration>
			</plugin>
			<!-- end::plugin[] -->

			<!-- tag::unpack[] -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>unpack</id>
						<phase>package</phase>
						<goals>
							<goal>unpack</goal>
						</goals>
						<configuration>
							<artifactItems>
								<artifactItem>
									<groupId>${project.groupId}</groupId>
									<artifactId>${project.artifactId}</artifactId>
									<version>${project.version}</version>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>12</source>
					<target>12</target>
				</configuration>
			</plugin>
			<!-- end::unpack[] -->
		</plugins>
	</build>



	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>



</project>

需要注意: docker.image.prefix 这个值是Docker服务端的地址,需要修改为自己的.

java标志
image-2882

Dockerfile 文件:

定义程序的依赖,运行方式:

FROM openjdk:12-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT [“java”,”-cp”,”app:app/lib/*”,”xyz.suancaiyu.customspringbootdockerdemo.CustomSpringBootDockerDemoApplication”]

编译并运行应用程序:

1. (Windows)在本地机器上配置环境变量:

DOCKER_HOST=192.168.37.130

2. (Windows)在当前目录执行下面的语句编译(推荐从菜单栏单独打开Power Shell,或者cmd.):

mvn install dockerfile:build

3. (Linux)在192.168.37.130的机器上运行(启动应用):

docker run -p 8080:8080 -t 192.168.37.130/custom-spring-boot-docker-demo

4. (Windows)在浏览器中访问: http://192.168.37.130:8080/

更多可以参考: Spring官方示例

完整源码: Gitee仓库