面向切面编程相信大家都熟悉这个词汇了,废话我就不说了。
1、java.lang.reflect.Proxy
用动态代理来生成一个接口对象,然后通过InvocationHandler来实现切面织入。
这个动态代理是利用ProxyGenerator.generateProxyClass根据接口生成二进制流(byte数组的形式),然后调用本地方法defineClass0加载这个byte数组,从而得到这个代理对象的Class对象。这个代理Class对象有一个构造方法,这个构造方法的参数是InvocationHandler。
然后再通过反射生成代理对象。代码示例如下
public static void main(String[] args) {
final Monitor m=new Monitor("测试");
Logging log=(Logging) Proxy.newProxyInstance(m.getClass().getClassLoader(), new Class[]{Logging.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("do xxxxx");
Object o=method.invoke(m, args);
System.out.println("do xxxxx end");
return o;
}
});
log.log();
}
interface Logging{
public void log();
}
class Monitor implements Logging{
public Monitor(String name){
this.name=name;
}
private String name;
@Override
public void log() {
System.out.println(" logging:"+name);
}
}
结果:
do xxxxx
logging:测试
do xxxxx end
优点:使用简单。仅用jdk就能完成不需要其他jar
缺点:从Proxy的代码来看,他的aop编程是基于代码运行时生成代理,然后在这个代理上做切面织入的。而且每次调用方法,都会使用反射,所以性能上不太好。
2、AspectJ
AspectJ是基于在编译期将切面织入字节码文件的。所以对于AspectJ的aop来说,不存在大量反射影响性能的情况。
切面代码
public aspect BoundPerson {
pointcut log(Person p) : call(public void Person.*(*))&&target(p);
after(Person p) returning() : log(p){
System.out.println(thisJoinPoint.getSourceLocation()+" "+thisJoinPoint.getSignature().getName());
System.out.println("logging:"+p.getName());
}
}
被织入的对象
public class Person {
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public static void main(String a[]){
new Person().setName("haha");
}
}
运行结果
Person.java:12 setName
logging:haha
AspectJ的编程有自己的一套规范,对于java开发者来说不难掌握。AspectJ的编译器实际上就是运行一个java程序,将aspect文件加载后并解析。然后根据这个文件内容,去修改切面的字节码文件,来达到织入的效果。
优点:织入语言比较清晰容易掌握。因为是编译期织入,所以运行速度比较快。
缺点:需要AspectJ编译器,加入aspectj的相关jar包才能使用。
3、CGlLib(asm包装)
这个在spring、hibernate等开源框架都有使用。
public static void main(String[] args) {
final Dog dog=new Dog();
Enhancer en=new Enhancer();
en.setSuperclass(Dog.class);
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("begin xxxx");
Object o=method.invoke(dog, args);
return o;
}
});
Dog d=(Dog)en.create();
d.say();
}
class Dog {
public void say(){
System.out.println("dog say: 汪汪汪");
}
}
运行结果
begin xxxx
dog say: 汪汪汪
CGlib是对asm的一个包装。asm是一个优秀的字节码框架。CGlib在生成代理时,会利用asm来生成一个被代理对象的子类字节码流,然后通过加载这个字节码流,来生成代理类。这么做的好处就是不需要被代理类实现接口。通过反编译CGlib的动态代理字节码文件,会发现代理对象的方法调用不是通过反射来完成的,这样会在性能上有较大提升。
优点:稳定、简单、容易使用。比java.lang.reflect.Proxy性能好。不需要实现接口。
4、其他修改字节码文件框架,asm、javassit、bcel等
这几种使用起来不方便,就不介绍了