ASM
我们知道Java是静态语言,而python、ruby是动态语言,Java程序一旦写好很难在运行时更改类的行为,而python、ruby可以。
不过基于bytecode层面上我们可以做一些手脚,来使Java程序多一些灵活性和Magic,ASM就是这样一个应用广泛的开源库。
ASM is a Java bytecode manipulation framework. It can be used to dynamically generate stub classes or other proxy classes,
directly in binary form, or to dynamically modify classes at load time, i.e., just before they are loaded into the Java
Virtual Machine.
ASM完成了BCEL
和SERP
同样的功能,但ASM
只有30多k,而后两者分别是350k和150k。apache真是越来越过气了。
让我们来看一个ASM的简单例子Helloworld.java,它生成一个Example类和一个main方法,main方法打印"Hello world!"语句:
-
import
java.io.FileOutputStream;
-
import
java.io.PrintStream;
-
-
import
org.objectweb.asm.ClassWriter;
-
import
org.objectweb.asm.MethodVisitor;
-
import
org.objectweb.asm.Opcodes;
-
import
org.objectweb.asm.Type;
-
import
org.objectweb.asm.commons.GeneratorAdapter;
-
import
org.objectweb.asm.commons.Method;
-
-
public
class
Helloworld
extends
ClassLoader
implements
Opcodes {
-
-
public
static
void
main(
final
String args[])
throws
Exception {
-
-
-
-
-
ClassWriter cw = new
ClassWriter(
0
);
-
cw.visit(V1_1, ACC_PUBLIC, "Example"
,
null
,
"java/lang/Object"
,
null
);
-
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>"
,
"()V"
,
null
,
-
null
);
-
mw.visitVarInsn(ALOAD, 0
);
-
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object"
,
"<init>"
,
"()V"
);
-
mw.visitInsn(RETURN);
-
mw.visitMaxs(1
,
1
);
-
mw.visitEnd();
-
mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main"
,
-
"([Ljava/lang/String;)V"
,
null
,
null
);
-
mw.visitFieldInsn(GETSTATIC, "java/lang/System"
,
"out"
,
-
"Ljava/io/PrintStream;"
);
-
mw.visitLdcInsn("Hello world!"
);
-
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream"
,
"println"
,
-
"(Ljava/lang/String;)V"
);
-
mw.visitInsn(RETURN);
-
mw.visitMaxs(2
,
2
);
-
mw.visitEnd();
-
byte
[] code = cw.toByteArray();
-
FileOutputStream fos = new
FileOutputStream(
"Example.class"
);
-
fos.write(code);
-
fos.close();
-
Helloworld loader = new
Helloworld();
-
Class exampleClass = loader
-
.defineClass("Example"
, code,
0
, code.length);
-
exampleClass.getMethods()[0
].invoke(
null
,
new
Object[] {
null
});
-
-
-
-
-
-
cw = new
ClassWriter(ClassWriter.COMPUTE_MAXS);
-
cw.visit(V1_1, ACC_PUBLIC, "Example"
,
null
,
"java/lang/Object"
,
null
);
-
Method m = Method.getMethod("void <init> ()"
);
-
GeneratorAdapter mg = new
GeneratorAdapter(ACC_PUBLIC, m,
null
,
null
,
-
cw);
-
mg.loadThis();
-
mg.invokeConstructor(Type.getType(Object.class
), m);
-
mg.returnValue();
-
mg.endMethod();
-
m = Method.getMethod("void main (String[])"
);
-
mg = new
GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m,
null
,
null
, cw);
-
mg.getStatic(Type.getType(System.class
),
"out"
, Type
-
.getType(PrintStream.class
));
-
mg.push("Hello world!"
);
-
mg.invokeVirtual(Type.getType(PrintStream.class
), Method
-
.getMethod("void println (String)"
));
-
mg.returnValue();
-
mg.endMethod();
-
cw.visitEnd();
-
code = cw.toByteArray();
-
loader = new
Helloworld();
-
exampleClass = loader.defineClass("Example"
, code,
0
, code.length);
-
exampleClass.getMethods()[0
].invoke(
null
,
new
Object[] {
null
});
-
}
-
}
我们看到上面的例子分别使用ASM的MethodVisitor和GeneratorAdapter两种方式来动态生成Example类并调用打印语句。
cglib
cglib is a powerful, high performance and quality Code Generation
Library, It is used to extend JAVA classes and implements interfaces at
runtime.
cglib是Code Generation Library的缩写。
cglib依赖于ASM库。
Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。
而JDK的动态代理机制要求有接口才行,这样就强制我们的pojo实现某个接口。
这里还是提供一个cglib的入门级的示例:
MyClass.java:
-
public
class
MyClass {
-
-
public
void
print() {
-
System.out.println("I'm in MyClass.print!"
);
-
}
-
-
}
Main.java:
-
import
java.lang.reflect.Method;
-
import
net.sf.cglib.proxy.Enhancer;
-
import
net.sf.cglib.proxy.MethodInterceptor;
-
import
net.sf.cglib.proxy.MethodProxy;
-
-
public
class
Main {
-
-
public
static
void
main(String[] args) {
-
-
Enhancer enhancer = new
Enhancer();
-
enhancer.setSuperclass(MyClass.class
);
-
enhancer.setCallback(new
MethodInterceptorImpl());
-
MyClass my = (MyClass) enhancer.create();
-
my.print();
-
}
-
-
private
static
class
MethodInterceptorImpl
implements
MethodInterceptor {
-
public
Object intercept(Object obj, Method method, Object[] args,
-
MethodProxy proxy) throws
Throwable {
-
-
System.out.println(method + " intercepted!"
);
-
-
proxy.invokeSuper(obj, args);
-
return
null
;
-
}
-
}
-
}
打印结果为:
-
public
void
MyClass.print() intercepted!
-
I'm in MyClass.print!
这个示例就基本上实现了日志AOP的功能,很简单吧。
反射、Proxy和元数据是Java最强的三个特征,再加上CGLib (Code Generation Library)
和ASM,使得Java虽然没有Ruby,Python般后生可畏,一样能做出强悍的框架。
Proxy
可以看作是微型的AOP,明白提供了在继承和委托之外的第三个代码封装途径,只要有足够的想象力,可以做得非常好玩,Spring的源码里用Proxy就
用得很随便,看得我非常眼红。可惜Proxy必须基于接口。因此Spring的做法,基于接口的用proxy,否则就用cglib。AOP么,一般小事非
compoent一级的就不麻烦AspectJ出手了。
cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。
它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
public
Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
在
intercept()函数里,你可以在执行Object
result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全
干别的。说白了,就是AOP中的around advice。
AOP没有出现以前,该领域经典的设计模式是Decorator,像Java IO Stream的设计就是如此.不过,如果为每个DAO, 每个方法的写Decorator函数会写死人的,所以用上cglib的好处是一次过拦截所有方法。
另外,cglib除了Enhancer之外,还有BulkBean和Transform,都是Hibernate持久化的基础,但文档贫乏,一时还没去看怎么用。
1.AOP里讲了一百遍阿一百遍的log aspect在cglib是这样做的:
public
class
LogDAOProxy
implements
MethodInterceptor
{
private
Logger log
=
Logger.getLogger(LogDAOProxy.
class
);
private
Enhancer enhancer
=
new
Enhancer();
//
返回DAO的子类
public
Object getDAO(Class clz)
{
enhancer.setSuperclass(clz);
enhancer.setCallback(
this
);
return
enhancer.create();
}
//默认
的拦截方法
public
Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
throws
Throwable
{
log.info(
"
调用日志方法
"
+
method.getName());
Object result
=
proxy.invokeSuper(o,args);
return
result;
}
}
应用的代码:
LogDAOProxy proxy
=
new
LogDAOProxy();
GoodsDAO dao
=
(GoodsDAO)proxy.getDAO(GoodsDAO.
class
);
dao.insert(goods);
2.而在Spring的管理下应该略加修改的高级Decorator
上面的例子用return
enhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分
public
class
LogDAOProxy
implements
MethodInterceptor
{
private
Logger log
=
Logger.getLogger(LogDAOProxy.
class
);
private
Object dao
=
null
;
private
Enhancer enhancer
=
new
Enhancer();
//
返回DAO的子类
public
Object getDAO(Class clz,Object dao)
{
this
.dao
=
dao;
enhancer.setSuperclass(clz);
enhancer.setCallback(
this
);
return
enhancer.create();
}
//
默认的拦截方法
public
Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
throws
Throwable
{
log.info(
"
调用日志方法
"
+
method.getName());
Object result
=
proxy.invoke(dao, args);
return
result;
}
}
可见,原来模式里在getDao()时由enhancer创建dao,而 调用intercept时则将enhancer创建的dao以Object o参数传回。
而新模式里,dao在getDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy.
有点遗憾, intercept函数里MethodProxy的Signature是固定的 , 即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String);
分享到:
相关推荐
Asm和cglibjar包Asm和cglibjar包Asm和cglibjar包Asm和cglibjar包
cglib.jar和asm.jar .
java操作Excel环境easyExcel-2.0.5最新版本环境(自动适配属性),解决asm及cglib冲突等。亲测可用。asm.jar cglib-2.2.jar cglib-3.1.jar cglib-nodep-2.2.jar commons-collections4-4.1.jar easyexcel-2.0.5.jar ...
asm 字节码操作工具库的代码demo 以及cglib实现简单aop功能的代码demo
cglib.jar 和 asm.jar 对应版本
cglib包及依赖汉cglib3.1和asm4.2,主要作用是用户代理,代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。...
里面包含完整的cglib-nodep-2.2.jar和asm-2.2.3.jar,放心下载
asm-3.3.1.jar cglib-nodep-3.1.jar cglib动态代理所需jar包
NULL 博文链接:https://bijian1013.iteye.com/blog/2382393
jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,...
cglib需要用到的动态代理jar包。贡献出来.
SSH2环境搭建asm和cglib的包冲突,使用cglib-2.2.3.jar
在练习代理模式的时候,使用cglib时总是会出现cglib和asm的jar包不匹配的问题。这时候控制台会报错。如果单独找包,不容易匹配。这套jar包是自己试验过的。
asm3.3相关jar包集合(asm-commons-3.3,asm-3.3.1,asm-tree-3.3)和CJLIB2.2.jar。asm3.3相关jar包集合(asm-commons-3.3,asm-3.3.1,asm-tree-3.3)和CJLIB2.2.jar
cglib2.2.2.jar和asm3.3.1.jar
cglib-2.2.2.jar和asm-3.3.jar,使用Cglib需要的jar包。
cglib.jar2.2.2和 asm.jar 3.3.1,用来实现cglib代理模式
免费下载Cglib.jar 和asm.jar,杜绝赚分的人
cglib-3.3.0.jar,asm-7.0.jar