Maven中使用APT

APT(Annotation Processing Tool)是一个java注解处理工具,主要运行在JDK编译阶段,我们可以简单理解为预编译的处理工具类, 就是我们在执行我们传统的编译前,做一些预处理,可以是资源文件的,也可以是JAVA类的,把一部分注解在编译时解释为传统的类,以提高运行时效率,在运行时,这些注解就不要再执行,即使我们使用字节码优化也不能达到这种类似于原生代码的效率.

如果使用APT,建议使用JDK的最小版本为1.6. APT从在JDK 1.5开始支持,但是从1.6版本开始才稳定和可用性有所提高,简化开发部署的难度.
以下所有的阐述都是针对1.6版本以后.

JDK 1.6版本后添加了几个javac参数:

  -proc:{none,only}          Control whether annotation processing and/or compilation is done.
  -processor <class1>[,<class2>,<class3>...]
							 Names of the annotation processors to run; 
							 bypasses default discovery process
  -processorpath <path>      Specify where to find annotation processors
  -s <directory>             Specify where to place generated source files
  -implicit:{none,class}     Specify whether or not to generate class files 
							 for implicitly referenced files
  -Akey[=value]              Options to pass to annotation processors

那么我们在maven工程中只要添加javac参数就可以了.如下示例:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>2.3.2</version>
	<configuration>
		<proc>only</proc>
		<annotationProcessors>
			<annotationProcessor>com.annotation.processor.AnnotationProcessor</annotationProcessor>
		</annotationProcessors>
		<source>1.6</source>
		<target>1.6</target>
	</configuration>
</plugin>

除了以上方法,也有一些插件来处理,如apt-maven-plugin,不过主要是针对eclipse的m2e插件的,具体代码如下:

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

Dubbo使用总结

Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

其核心部分包含:

  • 远程通讯 : 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
  • 集群容错 : 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
  • 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

Dubbo能做什么?

  • 透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
  • 软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
  • 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

上面是官方对于Dubbo一些描述, 对于稳定性是经过阿里巴巴验证过的,所以稳定是能够得到保证的.
对于不同公司和不同的团队,有着各种需求和情况,那么我们就要探索如何使用Dubbo,使Dubbo能够很好的和我们的目标一致.

应该怎么使用dubbo?

  • 从零开始
    从头开始使用dubbo是比较简单的,我们可以完全按照dubbo官方指导实现. 具体项目示例请参照https://github.com/alibaba/dubbo/tree/master/dubbo-demo
  • 已有项目改造
    如果我们是spring的项目,那么我们可以平滑的过度到dubbo, 具体的实现思路是我们在编译阶段提取我们spring的service,生成consumer和provider文件配置,然后通过启动dubbo来加载我们的服务

接下来我们重点讲述如何对于现有spring项目改造成dubbo
首先如果我们使用dubbo远程服务调用,那么我们如何保证事务? 如果要保证事务,我们需要把所要运新的服务在一台机器执行, 即官方所谓Best Efforts 1PC模式.那我们就要保证暴露的服务尽量是一个事务.
改造的思路是通过约定开发,达到开发和部署分离.开发采用传统的开发方式,在服务器发布时自动提取service,具体实现通过使用java的APT来实现.具体实现已经实现,后面有时间把具体的实现放到github上.

dubbo如何启动?
java com.alibaba.dubbo.container.Main

dubbo如何停止?
优雅停机,Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用"kill -9 PID"等强制关闭指令,是不会执行优雅停机的,只有通过"kill PID"时,才会执行。

POI根据Excel模板导出大数据

在一些项目中,需要导出数据, 如果编程实现POI样式等比较麻烦,所以一般根据模板来导出数据,之前使用过JXLS来实现,对于小数据比较好用,但是对于数据量比较大就比较慢,甚至死掉,不能导出.

经过一段时间研究POI,发现POI的SXSSF支持大数据导出,经过测试有很好的性能.实现有两种思路:

  • 通过Excel文件copy样式等, 经过长时间验证和测试,有缺陷,会丢掉一些样式,不能完美达到目标
  • 通过修改模板文件,然后直接输出,经过测试验证,这种能够很好的解决大数据导出问题.

下面我们给出第二种思路的具体实现,经过测试导出5W条数据大概需要4S,这个测试结果非常快. 如果使用标准POI XSSF来实现,会卡死不会输出.

/**
     * 根据Excel模板导出数据,支持大数据导出
     *
     * @param templateInputStream
     * @param data
     * @param outputStream
     * @param <T>
     * @throws IOException
     * @throws InvalidFormatException
     */
public static <T> void generateExcel(InputStream templateInputStream, 
Iterator<T> data, OutputStream outputStream) throws IOException, InvalidFormatException {
	// 通过类加载器获取模板
	XSSFWorkbook workbook = new XSSFWorkbook(templateInputStream);
	XSSFSheet sheet = workbook.getSheetAt(0);
	XSSFRow row = sheet.getRow(1);

	List<CellStyle> cellStyles = new ArrayList<CellStyle>();
	List<String> cellValues = new ArrayList<String>();
	Map<Integer, String> cellFormatMap = new HashMap<Integer, String>();
	Pattern pattern = Pattern.compile("^\\$\\{([\\w\\.]+)\\}$");
	for (int i = 0; i < row.getLastCellNum(); i++) {
		XSSFCell cell = row.getCell(i);
		cellStyles.add(cell.getCellStyle());
		String cellValue = cell.getStringCellValue();
		cellValues.add(cellValue);
		if (cellValue != null) {
			Matcher matcher = pattern.matcher(cellValue);
			if (matcher.find()) {
				cellFormatMap.put(i, matcher.group(1));
			}
		}
	}

	sheet.removeRow(row);
	SXSSFWorkbook newWorkbook = new SXSSFWorkbook(workbook);
	Sheet newSheet = newWorkbook.getSheetAt(0);
	int rowNum = 1;
	while (data.hasNext()) {
		T t = data.next();
		JSONObject item = null;
		if (t instanceof JSONObject) {
			item = (JSONObject) t;
		} else {
			String json = JSON.toJSONString(t);
			item = JSON.parseObject(json);
		}

		Row newRow = newSheet.createRow(rowNum);
		for (int i = 0; i < cellValues.size(); i++) {
			Cell newCell = newRow.createCell(i);
			newCell.setCellStyle(cellStyles.get(i));
			String newCellValue = cellValues.get(i);
			if (newCellValue != null && cellFormatMap.containsKey(i)) {
				newCellValue = JsonUtils.getJSONValue(item, cellFormatMap.get(i));
			}
			newCell.setCellValue(newCellValue);
		}

		rowNum++;
	}
	newWorkbook.write(outputStream);
}

Java中HTML转PDF文档实现

之前我们讲述了如果通过html转换成word文档,现在我们讲述如何通过java实现转换HTML为PDF文档.
首先我们把HTML文本通过jsoup整理成xhtml,然后通过flying-saucer-pdf-itext5或者itextpdf xmlworker转换成PDF.
对于flying-saucer和itextpdf xmlworker都可以转换xhtml为PDF,但是在使用中我们发现flying-saucer对于样式的转换更加自然,而itextpdf的转换会丢失部分样式,所以接下来我们会给出两种实现方式,方便根据需要选择其中一种.

itextpdf xmlworker的maven依赖:

<dependency>
	<groupId>com.itextpdf</groupId>
	<artifactId>itextpdf</artifactId>
	<version>5.5.4</version>
</dependency>
<dependency>
	<groupId>com.itextpdf.tool</groupId>
	<artifactId>xmlworker</artifactId>
	<version>5.5.4</version>
</dependency>

flying-saucer的maven依赖:

<dependency>
	<groupId>org.xhtmlrenderer</groupId>
	<artifactId>flying-saucer-pdf-itext5</artifactId>
	<version>9.0.7</version>
</dependency>

html转成xhtml的jsoup参考实现如下:

/**
 * 清理HTML内容,转换成标准xhtml
 * @param html
 * @return
 */
public static String tidyHtml(String html){
	Document doc = Jsoup.parse(html);
	// Clean the document.
	//doc = new Cleaner(Whitelist.basicWithImages()).clean(doc);
	doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
	doc.outputSettings().prettyPrint(true);

	doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
	// Get back the string of the body.
	return doc.html();
}

html转换成PDF的itextpdf xmlworker代码参考实现如下:

/**
 * 使用IText默认实现
 *
 * @param html
 * @param baseURL
 * @param os
 * @throws DocumentException
 * @throws IOException
 */
public static void html2Pdf(String html, String baseURL, OutputStream os) throws DocumentException, IOException {
	String xhtml = HtmlUtils.tidyHtml(html);
	xhtml = HtmlUtils.fillImageURL(xhtml, baseURL, null);
	// step 1
	Document document = new Document();
	// step 2
	PdfWriter writer = PdfWriter.getInstance(document, os);
	// step 3
	document.open();
	// step 4
	XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(xhtml.getBytes("UTF-8")), Charset.forName("UTF-8"));
	//step 5
	document.close();
}

html转换成PDF的flying-saucer代码参考实现如下:

/**
 * 使用flying-saucer-pdf-itext5实现
 *
 * @param html
 * @param baseURL
 * @param os
 * @throws IOException
 * @throws DocumentException
 */
public static void html2Pdf(String html, String baseURL, OutputStream os) throws IOException, DocumentException {
	ITextRenderer renderer = new ITextRenderer();
	String xhtml = HtmlUtils.tidyHtml(html);

	renderer.setDocumentFromString(xhtml, baseURL);

	// 解决中文支持问题
	ITextFontResolver fontResolver = renderer.getFontResolver();
	fontResolver.addFont("C:/Windows/Fonts/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
	//解决图片的相对路径问题
	renderer.getSharedContext().setBaseURL(baseURL);
	renderer.layout();
	renderer.createPDF(os);
}

Java中HTML转Word文档实现

有这样一个需求,我们要把数据库中的HTML格式文本字段导出为word,尝试了很多方法终于找到一种比较理想的方法.
首先我们把HTML文本通过jsoup整理成xhtml,然后通过docx4j转换成word.
HTML转换成xhtml还有一个jtidy,但是因为好久没有更新,并且有bug,所以推荐使用jsoup来整理.

docx4j的maven依赖:

<dependency>
	<groupId>org.docx4j</groupId>
	<artifactId>docx4j-ImportXHTML</artifactId>
	<version>3.2.2</version>
</dependency>
<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.8.1</version>
</dependency>

html转成xhtml的jsoup参考实现如下:

/**
 * 清理HTML内容,转换成标准xhtml
 * @param html
 * @return
 */
public static String tidyHtml(String html){
	Document doc = Jsoup.parse(html);
	// Clean the document.
	//doc = new Cleaner(Whitelist.basicWithImages()).clean(doc);
	doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
	doc.outputSettings().prettyPrint(true);

	doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
	// Get back the string of the body.
	return doc.html();
}

xhtml转换成word的java代码参考实现如下:

/**
 * html文本转换为word
 *
 * @param html
 * @param baseURL
 * @param os
 * @throws Docx4JException
 */
public static void html2Docx(String html, String baseURL, OutputStream os) throws Docx4JException {

	// To docx, with content controls
	WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();

	XHTMLImporterImpl XHTMLImporter = new XHTMLImporterImpl(wordMLPackage);
	//XHTMLImporter.setDivHandler(new DivToSdt());
	wordMLPackage.getMainDocumentPart().getContent().addAll(
			XHTMLImporter.convert(html, baseURL));

	/*System.out.println(XmlUtils.marshaltoString(wordMLPackage
			.getMainDocumentPart().getJaxbElement(), true, true));*/

	wordMLPackage.save(os);
}