- 浏览: 547565 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (740)
- css (4)
- jquery (8)
- javascript (23)
- html (0)
- uml (0)
- 设计模式 (1)
- 开发工具 (14)
- json (4)
- struts 1.x (3)
- spring (3)
- hibernate (6)
- struts 2.x (17)
- JFreechart (0)
- j2se (48)
- jsp (9)
- flex (22)
- 找工作 (1)
- 技术杂谈 (18)
- 网络编程 (5)
- io流 (1)
- ORACLE (15)
- 报表 (3)
- extjs (11)
- jpbm (2)
- swing (5)
- jspereports (3)
- sql (1)
- linux (15)
- ps (1)
- storm (4)
- hbase (8)
- li (0)
- python (1)
- hive (3)
- 机器学习 (1)
- hdfs (1)
- elasticsearch (1)
- hadoop 2.2 (5)
- hadoop (1)
最新评论
-
Tristan_S:
这个有点意思
ASM -
starryskydog:
程序修改detail band部分的样式 如内容字体大小 ...
使用jasperReport实现动态表头 -
samwong:
Good, so usefule
使用YUI Compressor压缩CSS/JS -
gc715409742:
能够告诉我怎么在web项目中使用YUI Compressor? ...
使用YUI Compressor压缩CSS/JS -
JsonTeye:
您好! 我看你的代码,我现在也在做动态报表,实现功能由用户自己 ...
使用jasperreport动态生成pdf,excel,html
本文介绍了如何在普通Java程序中应用代码动态生成技术,并测试、比较了各种实现方法
的性能。
提纲:
一、概述 / 二、表达式计算器 / 三、解释法 / 四、解析法 / 五、编译法 / 六、生成法
/ 七、性能和应用
正文:
一、概述
经常有人批评Java的性能,认为Java程序无法与C或C++程序相提并论。为此,Java一直在
性能优化上进行着不懈的努力,特别是运行时的性能优化机制,平息了许多责难。但是,
不管Java把性能提高到了什么程度,人们对代码性能的渴求是没有止境的。
显然,Java在某些操作上的性能确实无法与C/C++相比,这是由Java语言的特点所决定的,
例如为了跨平台而采用了中间语言(字节码)机制。另一方面,由于Java有着许多独特的
特性,它可以利用许多其他语言很难采用的优化技术,动态代码生成就是其中之一。
所谓动态代码生成,就是一种在运行时由程序动态生成代码的过程。动态生成的代码和生
成它的程序在同一个JVM中运行,且访问方式也相似。当然,和其他优化技术相似,动态代
码生成只适用于某些特定类型的任务。
JSP或许就是人们最熟悉的动态代码生成的例子。Servlet引擎能够把客户的请求分发给Se
rvlet处理,但Servlet天生是一种静态的结构。在启动服务器之前,Servlet一般必须先编
译和配置好。虽然Servlet有着许多优点,但在灵活性方面,Servlet略逊一筹。JSP技术突
破了Servlet的限制,允许在运行时以JSP文件为基础动态创建Servlet。
当客户程序发出了对JSP文件的请求,Servlet引擎向JSP引擎发出请求,JSP引擎处理JSP文
件并返回结果。JSP文件是一系列动作的文本描述,这一系列动作的执行结果就是返回给用
户的页面。显然,如果每一个用户的请求到达时都通过解释的方式执行JSP页面,开销肯定
比较大。所以,JSP引擎编译JSP页面动态创建Servlet。一旦JSP页面被改变,JSP引擎就会
动态地创建新的Servlet。
在这里,动态代码生成技术的优势非常明显——既满足了灵活性的要求,又不致于对性能
产生太大的影响。在编译Servlet甚至启动服务器时,系统的行为方式不必完全固定;同时
,由于不必在应答每一个请求时解释执行JSP文件,所以也就减少了响应时间。
二、表达式计算器
下面我们来看看如何在普通Java程序中使用动态代码生成技术。本文的例子是一个简单的
四则运算表达式计算器,它能够计算形如“4 $0 + $1 *”的后缀表达式,其中$0和$1分别
表示变量0、变量1。可能出现在表达式中的符号有三种:变量,常量,操作符。
后缀表达式是一种基于堆栈的计算表达式,处理过程从左到右依次进行,仍以前面的表达
式为例:先把4和变量0压入堆栈,下一个字符是操作符“+”,所以把当时栈顶的两个值(
4和变量0)相加,然后用加法结果取代栈顶的两个值。接着,再把1压入堆栈,由于接下来
的是操作符“*”,所以对这时栈顶的两个值执行乘法操作。如果把这个表达式转换成通常
的代数表达式(即中缀表达式),它就是“(4 + $0) * $1”。如果两个变量分别是“[3,
6]”,则表达式的计算结果是(4+3)*6=42。
为了比较代码动态生成和常规编程方式的性能差异,我们将以各种不同的方式实现表达式
计算器,然后测试各个计算器的性能。
本文的所有表达式计算器都实现(或隐含地实现)calculator接口。calculator接口只有
一个evaluate方法,它的输入参数是一个整数数组,返回值是一个表示计算结果的整数。
//Calculator.java
public interface Calculator {
int evaluate(int[] arguments);
}
三、解释法
首先我们来看一个简单但效率不高的表达式计算器,它利用Stack对象计算表达。每次计算
,表达式都要重新分析一次,因此可以称为解释法。不过,表达式的符号分析只在对象创
建时执行一次,避免StringTokenizer类带来太大的开销。
//SimpleCalculator.java
import java.util.ArrayList;
import java.util.Stack;
import java.util.StringTokenizer;
public class SimpleCalculator implements Calculator {
String[] _toks; // 符号列表
public SimpleCalculator(String expression) {
// 构造符号列表
ArrayList list = new ArrayList();
StringTokenizer tokenizer
= new StringTokenizer(expression);
while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken());
}
_toks = (String[])
list.toArray(new String[list.size()]);
}
// 将变量值代入表达式中的变量,
// 然后返回表达式的计算结果
public int evaluate(int[] args) {
Stack stack = new Stack();
for (int i = 0; i < _toks.length; i++) {
String tok = _toks[i];
// 以‘$’开头的是变量
if (tok.startsWith("$")) {
int varnum = Integer.parseInt(tok.substring(1));
stack.push(new Integer(args[varnum]));
} else {
char opchar = tok.charAt(0);
int op = "+-*/".indexOf(opchar);
if (op == -1) {
// 常量
stack.push(Integer.valueOf(tok));
} else {
// 操作符
int arg2 = ((Integer) stack.pop()).intValue();
int arg1 = ((Integer) stack.pop()).intValue();
switch (op) {
// 对栈顶的两个值执行指定的操作
case 0:
stack.push(new Integer(arg1 + arg2));
break;
case 1:
stack.push(new Integer(arg1 - arg2));
break;
case 2:
stack.push(new Integer(arg1 * arg2));
break;
case 3:
stack.push(new Integer(arg1 / arg2));
break;
default:
throw new RuntimeException
("操作符不合法: " + tok);
}
}
}
}
return ((Integer) stack.pop()).intValue();
}
}
从本文后面的性能测试数据可以看出,这种表达式计算方式的效率相当低。对于偶尔需要
计算表达式的场合,它也许适用,但我们还有更好的处理方式。
四、解析法
如果经常要计算表达式的值,一种更好的办法是先解析表达式,应用Composite设计模式,
构造一棵表达式树。我们称这种表达式计算方式为解析法。如下面的代码所示,树的内部
结构代表了表达式的计算逻辑,因而避免了每次计算表达式时重复分析计算逻辑。
//CalculatorParser.java
import java.util.Stack;
import java.util.StringTokenizer;
public class CalculatorParser {
public Calculator parse(String expression) {
// 分析表达式,构造由表达式各个符号构成的
// 树形结构。
Stack stack = new Stack();
StringTokenizer toks
= new StringTokenizer(expression);
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
// 以‘$’开头的是变量
int varnum
= Integer.parseInt(tok.substring(1));
stack.push(new VariableValue(varnum));
} else {
int op = "+-*/".indexOf(tok.charAt(0));
if (op == -1) {
//常量
int val = Integer.parseInt(tok);
stack.push(new ConstantValue(val));
} else {
//操作符
Calculator node2 = (Calculator) stack.pop();
Calculator node1 = (Calculator) stack.pop();
stack.push(
new Operation(tok.charAt(0), node1, node2));
}
}
}
return (Calculator) stack.pop();
}
// 常量
static class ConstantValue implements Calculator {
private int _value;
ConstantValue(int value) {
_value = value; }
public int evaluate(int[] args) {
return _value; }
}
// 变量
static class VariableValue implements Calculator {
private int _varnum;
VariableValue(int varnum) {
_varnum = varnum; }
public int evaluate(int[] args) {
return args[_varnum]; }
}
// 操作符
static class Operation implements Calculator {
char _op;
Calculator _arg1;
Calculator _arg2;
Operation(char op, Calculator arg1, Calculator arg2) {
_op = op;
_arg1 = arg1;
_arg2 = arg2;
}
public int evaluate(int args[]) {
int val1 = _arg1.evaluate(args);
int val2 = _arg2.evaluate(args);
if (_op == '+') {
return val1 + val2;
} else if (_op == '-') {
return val1 - val2;
} else if (_op == '*') {
return val1 * val2;
} else if (_op == '/') {
return val1 / val2;
} else {
throw new RuntimeException("操作符不合法: " + _op);
}
}
}
}
由于表达式的计算逻辑已经事先解析好,CalculatorParser的性能明显高于第一个通过
解释方式执行的计算器。尽管如此,我们还可以通过代码动态生成技术进一步优化代码。
五、编译法
为了进一步优化表达式计算器的性能,我们要直接编译表达式——先根据表达式的逻辑动
态生成Java代码,然后执行动态生成的Java代码,这种方法可以称之为编译法。
把后缀表达式翻译成Java表达式很简单,例如“$0 $1 $2 * +”可以由Java表达式“args
[0] + (args[1] * args[2]”表示。我们要为动态生成的Java类选择一个唯一的名字,然
后把代码写入临时文件。动态生成的Java类具有如下形式:
public class [类的名称] implements Calculator{
public int evaluate(int[] args) {
return args[0] + (args[1] * args[2]);
}
}
下面是编译法计算器的完整代码。
//CalculatorCompiler.java
import java.util.Stack;
import java.util.StringTokenizer;
import java.io.*;
//定制的类装入器
public class CalculatorCompiler extends ClassLoader {
String _compiler;
String _classpath;
public CalculatorCompiler() {
super(ClassLoader.getSystemClassLoader());
//编译器类型
_compiler = System.getProperty("calc.compiler");
//默认编译器
if (_compiler == null) _compiler = "javac";
_classpath = ".";
String extraclasspath
= System.getProperty("calc.classpath");
if (extraclasspath != null) {
_classpath = _classpath +
System.getProperty("path.separator")
+ extraclasspath;
}
}
public Calculator compile(String expression) {
// A3
String jtext = javaExpression(expression);
String filename = "";
String classname = "";
try {
//创建临时文件
File javafile = File.createTempFile(
"compiled_", ".java", new File("."));
filename = javafile.getName();
classname = filename.substring(
0, filename.lastIndexOf("."));
generateJavaFile(javafile, classname, expression);
//编译文件
invokeCompiler(javafile);
//创建java类
byte[] buf = readBytes(classname + ".class");
Class c = defineClass(buf, 0, buf.length);
try {
// 创建并返回类的实例
return (Calculator) c.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
} catch (InstantiationException e) {
throw new RuntimeException(e.getMessage());
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
//生成java文件
void generateJavaFile(
File javafile, String classname, String expression)
throws IOException {
FileOutputStream out = new FileOutputStream(javafile);
String text = "public class " + classname +
" implements Calculator {" +
" public int evaluate(int[] args) {" +
" " + javaExpression(expression) +
" }" + "}";
out.write(text.getBytes());
out.close();
}
//编译java文件
void invokeCompiler(File javafile) throws IOException {
String[] cmd = {_compiler, "-classpath",
_classpath, javafile.getName()};
//执行编译命令
//A1:
Process process = Runtime.getRuntime().exec(cmd);
try { //等待编译器结束
process.waitFor();
} catch (InterruptedException e) {
}
int val = process.exitValue();
if (val != 0) {
throw new RuntimeException(
"编译错误:" + "错误代码" + val);
}
}
//以byte数组形式读入类文件
byte[] readBytes(String filename) throws IOException {
// A2
File classfile = new File(filename);
byte[] buf = new byte[(int) classfile.length()];
FileInputStream in = new FileInputStream(classfile);
in.read(buf);
in.close();
return buf;
}
String javaExpression(String expression) {
Stack stack = new Stack();
StringTokenizer toks
= new StringTokenizer(expression);
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
stack.push("args[
" + Integer.parseInt(tok.substring(1)) + "]");
} else {
int op = "+-*/".indexOf(tok.charAt(0));
if (op == -1) {
stack.push(tok);
} else {
String arg2 = (String) stack.pop();
String arg1 = (String) stack.pop();
stack.push("(
" + arg1 + " " + tok.charAt(0) + " " + arg2 + ")");
}
}
}
return "return " + (String) stack.pop() + ";";
}
}
有了动态生成的代码之后,还要编译这些代码。我们假定系统使用的是javac编译器,且系
统的PATH环境变量包含了javac编译器的路径。如果javac不在PATH环境变量中,或者要使
用其他的编译器,则可以通过compiler属性指定,例如“-Dcalc.compiler=jikes”。如果
编译器不是javac,一般还要把Java运行时JAR文件(jre/lib目录下的rt.jar)放入编译器
的CLASSPATH。我们通过classpath属性为编译器指示额外的CLASSPATH成员。例如“-Dcal
c.classpath=c:\java\jre\lib\rt.jar”。
编译器可以通过Runtime.exec(String[] cmd)作为一个外部进程调用,Runtime.exec的执
行结果是一个Process对象(参见注释为“A1”的代码,下文以相似的方式引用代码的特定
部分)。cmd数组包含了要执行的系统命令,其中第一个元素必须是待执行程序的名称,其
余元素是传递给执行程序的各个参数。启动编译进程后,我们要等待编译进程运行结束,
然后获取编译器的返回值。编译进程返回0表示编译成功。
最后一个与编译器有关的问题是,由于编译器作为外部进程运行,所以最好能够读取编译
器的输出和错误报告。如果编译器遇到了大量的错误,编译过程可能处于阻塞状态(等待
读取)。本文的例子只是为了测试性能,为简单计,不处理该问题。但是,在正式的Java
工程中,这个问题是必须处理的。
编译成功之后,当前目录下就会有一个class文件,我们要用ClassLoader装入它(注释“
A2”)。ClassLoader读取的是byte数组,所以我们先把class文件的内容读入byte数组,
然后创建一个类。这里的类装入器属于最简单的定制类装入器,不过它已经足以完成我们
这里的任务。成功装入类之后,创建该类的实例,然后返回这个实例(注释“A3”)。
从测试结果可以看出,编译法计算器的性能有了显著的提高。同样是1000000次计算,现在
只需要100-200ms,而不是原来的1-2秒。不过,编译操作也带来了很大的时间开销,调用
javac编译器编译代码大约需要1-2秒,抵消了计算器本身性能的提升。不过,javac并不是
一个高性能的编译器,如果我们改用jikes之类的高速编译器,编译时间大大改善,降低到
了100-200ms。
六、生成法
最理想的方案当然是既有编译法的运行时性能优势,又避免调用外部编译器的开销。下面
我们要通过在内存中直接生成Java字节码避免调用外部编译器的开销,称之为生成法。
Java class文件的格式比较复杂,所以我们要用一个第三方的字节码代码库来生成文件。
本例使用的是BCEL,即Bytecode Engineering Library。BCEL是一个源代码开放的免费代
码库(http://sourceforge.net/projects/bcel/),可以帮助我们分析、创建、处理二进
制的Java字节码。先来看看用BCEL直接生成字节码的计算器代码清单。
//CalculatorGenerator.java
import java.io.*;
import java.util.Stack;
import java.util.StringTokenizer;
//从sourceforge.net/projects/bcel/下载BCEL代码库
import de.fub.bytecode.classfile.*;
import de.fub.bytecode.generic.*;
import de.fub.bytecode.Constants;
public class CalculatorGenerator
extends ClassLoader {
public Calculator generate(String expression) {
String classname =
"Calc_" + System.currentTimeMillis();
// 声明类
// B1
ClassGen classgen
= new ClassGen(classname, "java.lang.Object", "",
Constants.ACC_PUBLIC | Constants.ACC_SUPER,
new String[]{"Calculator"});
// 构造函数
// B2
classgen.addEmptyConstructor(Constants.ACC_PUBLIC);
// 加入计算表达式的方法
// B3
addEvalMethod(classgen, expression);
byte[] data = classgen.getJavaClass().getBytes();
Class c = defineClass(data, 0, data.length);
try {
return (Calculator) c.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
} catch (InstantiationException e) {
throw new RuntimeException(e.getMessage());
}
}
private void addEvalMethod(
ClassGen classgen, String expression) {
// B4
ConstantPoolGen cp = classgen.getConstantPool();
InstructionList il
= new InstructionList();
StringTokenizer toks
= new StringTokenizer(expression);
int stacksize = 0;
int maxstack = 0;
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
int varnum = Integer.parseInt(tok.substring(1));
// 数组引用
il.append(InstructionConstants.ALOAD_1);
// 数组序号
il.append(new PUSH(cp, varnum));
il.append(InstructionConstants.IALOAD);
} else {
int op = "+-*/".indexOf(tok.charAt(0));
// 根据操作符生成操作指令
switch (op) {
case -1:
int val = Integer.parseInt(tok);
il.append(new PUSH(cp, val));
break;
case 0:
il.append(InstructionConstants.IADD);
break;
case 1:
il.append(InstructionConstants.ISUB);
break;
case 2:
il.append(InstructionConstants.IMUL);
break;
case 3:
il.append(InstructionConstants.IDIV);
break;
default:
throw new RuntimeException("操作符非法");
}
}
}
il.append(InstructionConstants.IRETURN);
// 创建方法
// B5
MethodGen method
= new MethodGen(Constants.ACC_PUBLIC, Type.INT,
new Type[]
{
Type.getType("[I")}, new String[]{"args"},
"evaluate", classgen.getClassName(), il, cp);
// B6
method.setMaxStack();
method.setMaxLocals();
// 将方法加入到类
classgen.addMethod(method.getMethod());
}
}
使用BCEL时,首先要创建一个代表Java类的ClassGen对象(注释“B1”)。就象前面的编
译法一样,我们要定义一个唯一的类名字。与普通Java代码不同的是,现在我们要明确声
明超类java.lang.Object。ACC_PUBLIC声明该类是public类型。所有Java 1.0.2或更高版
本的Java类都必须声明ACC_SUPER访问标记。最后,我们指定了该类实现Calculator接口。
其次,我们要保证类有一个默认的构造函数(注释“B2”)。对于一般的Java编译器,如
果Java类没有定义构造函数,则Java编译器会自动插入一个默认的构造函数。现在我们用
BCEL直接生成字节码,必须显式声明构造函数。用BCEL生成默认构造函数的办法很简单,
只须调用ClassGen.addEmptyConstructor即可。
最后,我们要生成计算表达式的evaluate(int[] arguments)方法(注释“B3”和“B4”)
。JVM本身就是以堆栈为基础,所以把表达式转换成字节码的过程很简单,基于堆栈的计算
器几乎可以直接转换成字节码。指令按照执行次序收集到一个InstructionList。另外,我
们还要一个指向常量池的引用ConstantPoolGen。
准备好InstructionList之后,接着我们就可以创建MethodGen对象(注释“B5”)。我们
要创建的是一个public类型的方法,它的返回值是int,输入参数是一个整数数组(注意,
这里我们用到了整数数组的内部表示法“[I”)。另外,我们还提供了参数的名字,不过
这不是必需的。在这里,参数的名字是args,方法的名字是evaluate,最后几个参数包括
一个类的名字,一个InstructionList和一个常量池。
在BCEL中定义Java方法的限制比较严格(注释“B6”)。例如,Java方法必须声明它需要
多少大的操作符栈空间和为局部变量分配的空间。如果这些值错误,JVM将拒绝执行方法。
对于本例来说,手工计算这些值也不是很麻烦,但BCEL提供了几个能够分析字节码的方法
,我们只需简单地调用setMaxStack()和setMaxLocals()方法即可。
至此为止,整个类已经构造完毕。剩下的任务就是将类装入JVM,只要内存中有了byte数组
形式的类,我们就可以象在编译法中那样调用类装入器。
直接生成的代码和编译法生成的代码执行起来一样快,但初始的对象创建时间却大大减少
了。如果调用外部编译器,最好的情况下也需要100ms以上,利用BCEL创建类平均只需4ms
。
七、性能和应用
表一显示了四种方法的平均对象创建时间,其中编译法分两种编译器分别测试。表二是5个
测试用的表达式,表三是计算这些表达式1000000次所需时间。
http://bbs1.nju.edu.cn/file/C/crudeII/performance.jpg
显然,本文的例子完全是出于测试性能的目的,在实际应用中,要计算一个表达式100000
0次的情形是非常罕见的。然而,需要在运行时解析数据(XML、脚本语言、查询语句,等
等)却是经常会遇到的情形。动态代码生成不一定适用于每一类任务,但在下面这类场合
应该比较有用:
·处理过程主要由运行时才有效的定义信息决定。
·处理过程需要多次重复执行。
·如果每次执行处理过程时都重新解析定义信息,需要付出较大的开销。
如果某个问题适合于使用代码动态生成技术,接下来还有一个问题:应该使用编译法,还
是使用生成法?一般而言,首先生成Java代码然后调用外部编译器的方式比较简单。与JV
M指令相比,大多数人更熟悉Java代码;调试有源代码的程序也比直接调试字节码来得方便
。另外,好的编译器会在编译过程中优化代码,而这类优化操作在手工编码时一般是难以
顾及的。另一方面,调用外部编译器是一个开销很大的过程,配置编译器和CLASSPATH也增
加了维护应用的复杂程度。
生成法的性能优势非常明显。但是,它要求开发者深入了解class文件的格式和JVM字节码
指令。编译器在生成代码的过程中实际上完成了许多表面上看不到的工作,手工编写的字
节码不一定能够达到编译器自动编译的效果。如果要生成的代码比较复杂,在选择使用生
成法之前,务必仔细斟酌。
发表评论
-
使用Java调用谷歌搜索
2013-10-19 12:50 903转自:http://yangshangchuan.iteye ... -
走出类加载器迷宫
2013-10-16 14:22 691这是前几天在看类加载器机制时搜到的一篇旧文,网上搜了搜 ... -
Log4j使用
2012-12-05 11:34 784... -
Java的JDBC数据库连接池实现方法
2012-09-14 10:20 743虽然J2EE程序员一般都有现成的应用服务器所带的JDBC数据库 ... -
什么是线程安全和线程不安全
2012-08-23 14:49 796什么是线程安全和线程 ... -
线程运行栈信息的获取
2012-08-23 14:49 834线程运行栈信息的获取 ... -
log4j 获取行号
2012-08-23 14:50 1163log4jjava 今天看log4j的日志,突然注意到log ... -
javassist【动态改字节码】学习三
2012-08-23 14:50 2670这里举个简单的例子,来记录下如何用CtClass创建一个类,并 ... -
javassist【动态改字节码】学习二
2012-08-22 11:53 840写了个例子。 有一个类: Java代码 package ... -
javassist[动态改字节码]学习一
2012-08-22 11:52 906前段时间为了公司里的 ... -
JVM启动参数
2012-08-22 11:51 948一、标准参数 1.-server -client 虚拟机服务器 ... -
使用javassist动态注入代码
2012-08-22 11:33 789关于java字节码的处理,目前有很多工具,如bcel,asm。 ... -
利用javaassist修改Class文件
2012-08-22 11:22 1505我们在开发中有时候会遇到这样的问题,就是使用的某个第三方包中的 ... -
JavaMail
2012-08-16 20:03 952在Java EE应用程序中,经常需要发送E-mail。Java ... -
让java变成脚本语言
2012-08-15 12:42 0今天在弄游戏的GM模块,大部分gm命令很简单,只是单纯改 ... -
JavaAgent
2012-08-13 23:43 1279-javaagent 这个JVM参数是JDK 5引进的. j ... -
在Eclipse RCP中实现控制反转(IoC)
2012-08-13 23:13 973摘要:这篇文章描述了 ... -
aop的几种实现方式
2012-08-05 21:14 9491 AOP各种的实现 AOP就是面向切面编程,我们可以从 ... -
Java编程中“为了性能”尽量要做到的一些地方
2012-07-28 21:36 597http://www.iteye.com/magazines/ ... -
java基础拾遗
2012-06-17 10:05 882. 电梯直达 楼主 发表于 2012-1-28 13: ...
相关推荐
代码动态生成技术是提高Java程序灵活性的重要技术之一,它可以广泛应用于各种Java应用程序和架构中,提高程序的灵活性、可扩展性和可维护性。 此外,代码动态生成技术也可以与其他技术结合使用,例如,与Aspect-...
Java项目代码生成工具是一种高效的开发辅助工具,它利用模板引擎技术来自动化生成常见的Java代码,如控制器(Controller)、服务(Service)以及其实现类(ServiceImpl)。在这个特定的案例中,工具使用了FreeMarker...
使用ZXing,开发者可以轻松地在Java程序中集成二维码生成。首先,需要引入ZXing的依赖库,然后调用其提供的Encoder类进行数据编码,接着通过BitMatrix类构建二维码的图形表示,最后通过Writer类将BitMatrix转换为...
总的来说,Java动态生成代码并编译载入是一种强大的技术,它结合了反射和编译的能力,使得Java程序具备了更强的灵活性和适应性。但是,需要注意的是,这种技术也可能会带来代码复杂度的增加,以及可能的安全风险,...
使用Java Swing版代码生成器,开发者能够快速地搭建Swing应用的基础框架,然后专注于业务逻辑的实现,而不是基础UI代码的编写。这有助于提高开发速度,降低出错概率,同时使得代码更易于维护。 尽管上述信息是基于...
Java动态生成代码并编译载入是一个高级的编程技术,主要用于那些需要在应用程序运行时根据某些模板和数据动态地生成、编译和加载Java代码的场景。这种技术在需要高度定制化处理或是运行时参数化生成业务逻辑的系统中...
面向Java程序包的代码概要生成技术有以下关键点: 1. **包级别的分析**:在Java中,包是组织代码的基本单位,包含相关类和接口。生成代码概要时,不仅要考虑单个类或方法,还需考虑整个包的结构和依赖关系。这涉及...
在Java领域,Freemarker被广泛应用于邮件模板、动态报表生成以及代码生成等场景。它通过定义模板文件,结合数据模型,可以生成格式化的输出文本。 **工具使用流程** 1. **下载与解压**:首先,你需要从指定的源...
【标题】"简单的Java代码生成器"涉及到的是Java编程领域中的自动化工具,它主要用于简化开发者编写重复性代码的过程。在软件开发中,特别是在Java企业级应用开发中,大量的代码是相似或者重复的,如数据库访问层...
在IT行业中,Java是一种广泛应用的编程语言,尤其在构建Web应用程序方面。本示例将探讨如何使用Java生成HTML文件,这是构建动态网站或自动化报告等任务时常见的需求。通过Java生成HTML,我们可以将数据结构转化为...
- **命令行参数**:运行`wsdl2java`时,可以指定各种参数,比如指定输出目录 (`-d`)、选择代码生成模式 (`-frontend`) 或指定特定的绑定配置 (`-b`)。 - **修改路径**:根据实际项目需求,用户可能需要修改Wsdl2...
总结起来,`java动态编译`和`将class生成jar`是Java开发中的重要技能,它们允许我们构建更加灵活和动态的程序。`JavaCompiler` API提供了编译源代码的能力,而打包成JAR则有助于分发和执行这些编译后的类。这两个...
1. **代码生成器框架**: 在Java世界里,有很多成熟的框架和工具可以用于这个目的,如MyBatis-Plus、Apache Velocity、FreeMarker等。这些框架通过解析数据库表结构,能够自动生成Service、Mapper、DAO以及实体类等...
动态代码生成器是一种在程序运行时能够自动生成和执行代码的技术。这种技术广泛应用于各种软件开发场景,如元编程、反射、AOP(面向切面编程)、动态语言实现以及性能优化等。动态代码生成通常涉及以下几个核心概念...
- **主程序或脚本**:用于执行代码生成逻辑,读取配置,解析模板,并将结果输出为Java源文件。 - **依赖库**:如模板引擎库、数据库连接库等,支持代码生成器的运行。 总的来说,Java代码生成器是提升开发效率的...
Java代码生成器(MySQL版)是...总之,Java代码生成器(MySQL版)通过自动化的方式帮助开发者快速构建基于MySQL的Java应用程序,减少重复劳动,提高代码质量,让开发者更专注于业务逻辑的实现,而不是基础架构的搭建。
本项目聚焦于利用Java技术栈进行后端开发,Vue.js作为前端框架,并结合代码生成器来加速开发流程,最终使用Nginx服务器部署前端页面。 Java后端开发是整个系统的核心部分,它处理业务逻辑、数据存储以及与前端交互...
使用Java代码生成器可以提高软件开发的效率,特别是在企业级应用开发中,当涉及到大量的数据库操作时,代码生成器能够快速生成基础的CRUD操作代码,让开发者有更多时间专注于业务逻辑的实现。同时,它还可以减少错误...
在这个特定的"JAVA MVC三层架构 代码生成器"中,它使用了反向工程技术,根据数据库表自动创建MVC架构所需的代码。这包括但不限于以下内容: - 实体类(Entity):表示数据库中的表,通常包含与表字段相对应的属性和...
Java代码生成应用软件工具是一种将Java程序转换为可独立运行的应用程序的解决方案。这使得开发者无需依赖Java开发环境(如Eclipse或IntelliJ IDEA)就能执行他们编写的Java程序。这种工具通常会将Java字节码(.class...