前言
对类的增强有多少种方式?我们首先想一下一个类的生命周期,从编码到程序运行结束:
对类的增强,目的是为了在使用的时候能够在类原有的行为上进行增强,所以编码一直到使用中的几乎每个环节都可以对类进行增强,主要有以下几种方式:
- 静态代理
- 编译期织入:aspectJ
- 加载时织入:1. 自定义classLoader;有容器能力;2. instrumention 动态attach
- 运行时织入:动态代理
1. 静态代理
静态代理是最简单也是最容易理解的一种方式,只需要在编码的时候创建手动创建代理类调用即可,缺点也很明显,灵活性太差,代理的代码很难复用,编码结束之后类的增强就没有补充的可能性了。
// 接口
public interface NameService {
void printName(String name);
}
// 实现类
public class NameServiceImpl implements NameService {
@Override
public void printName(String name) {
System.out.println(name);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 代理类
public class NameServiceProxy implements NameService {
private NameService target;
public NameServiceProxy(NameService nameService){
this.target = nameService;
}
@Override
public void printName(String name) {
long cost = System.currentTimeMillis();
target.printName(name);
cost = System.currentTimeMillis() - cost;
System.out.println("method printName cost is "+ cost);
}
}
// 调用
public class Main {
public static void main(String[] args) throws Exception {
NameService nameService = new NameServiceProxy(new NameServiceImpl());
nameService.printName("tiang");
}
}
// 结果
tiang
method printName cost is 1002
2. 编译期织入
需要特殊的编译器,在将java文件编译成字节码的时候进行增强,编译后的字节码本身就包含了增强的结果。
- 服务编码
public class NameService {
public void printName(String name){
System.out.println(name);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 编写切面文件WasteTime.aj
public aspect WasteTime {
pointcut print():execution(void NameService.printName(..));
void around(): print(){
long cost = System.currentTimeMillis();
proceed();
cost = System.currentTimeMillis() - cost;
System.out.println("method printName cost :" + cost);
}
}
- 采用AspectJ编译器编译后执行
public class Main {
public static void main(String[] args) {
NameService nameService = new NameService();
nameService.printName("tiang");
}
}
运行结果
tiang
method printName cost :1000
- 将编译后的NamService.class文件进行反编译
public class NameService
{
public void printName(final String name) {
printName_aroundBody1$advice(this, name, WasteTime.aspectOf(), null);
}
// 原方法
private static final /* synthetic */ void printName_aroundBody0(final NameService ajc$this, final String name) {
System.out.println(name);
try {
Thread.sleep(1000L);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
// 增强的代码与原方法耦合
private static final /* synthetic */ void printName_aroundBody1$advice(final NameService ajc$this, final String name, final WasteTime ajc$aspectInstance, final AroundClosure ajc$aroundClosure) {
long cost = System.currentTimeMillis();
printName_aroundBody0(ajc$this, name);
cost = S