一、静态代理
接口:Animal
public interface Animal {
void eat(String food);
String type();
}
实现类:Monkey
public class Monkey implements Animal {
@Override
public String type() {
String type = "哺乳动物";
System.out.println(type);
return type;
}
@Override
public void eat(String food) {
System.out.println("The food is " + food + " !");
}
}
包装类:AnimalWrapper
public class AnimalWrapper implements Animal {
private Animal animal;
// 使用构造方法包装Animal的接口,这样所有的Animal实现类都可以被这个Wrapper包装。
public AnimalWrapper(Animal animal) {
this.animal = animal;
}
@Override
public void eat(String food) {
System.out.println("+++Wrapped Before!+++");
animal.eat(food);
System.out.println("+++Wrapped After!+++");
}
@Override
public String type() {
System.out.println("---Wrapped Before!---");
String type = animal.type();
System.out.println("---Wrapped After!---");
return type;
}
}
**优点:**这里我们完成了对 Animal 所有子类的代理,在代理方法中,你可以加入一些自己的额外的 处理逻辑,就像上面的+++、—输出语句一样。那么 Spring 的前置、后置、环绕方法通知, 通过这种方式可以有限的模拟出来,以 Spring 的声明式事务为例,无非就是在调用包装的 目标方法之前处开启事务,在之后提交事务,这样原有的业务逻辑没有受到任何事务管理代 码的侵入。
**缺点:**这种方式的静态代理,缺点就是当 Animal 接口中增加了新的方法,那么包装类中也必须增 加这些新的方法
二、动态代理
1、基于JDK Proxy的动态代理
接口 1:Mammal(哺乳动物)
public interface Mammal {
void eat(String food);
String type();
}
接口 2:Primate(灵长类动物)
public interface Primate {
void think();
}
public class Monkey implements Mammal, Primate {
@Override
public String type() {
String type = "哺乳动物";
System.out.println(type);
return type;
}
@Override
public void eat(String food) {
System.out.println("The food is " + food + " !");
}
@Override
public void think() {
System.out.println("思考!");
}
}
回调类:MyInvocationHandler
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Invoke method Before!");
Object returnObject = method.invoke(obj, args);
System.out.println("Invoke method After!");
return returnObject;
}
}
// 第一种创建动态代理的方法
Object proxy = Proxy.newProxyInstance(
Monkey.class.getClassLoader(),
Monkey.class.getInterfaces(),
new MyInvocationHandler(new Monkey()));
Mammal mammal = (Mammal) proxy;
mammal.eat("香蕉");
mammal.type();
Primate primate = (Primate) proxy;
primate.think();
// 第二种创建动态代理的方法
Class<?> proxyClass = Proxy.getProxyClass(
Monkey.class.getClassLoader(),
Monkey.class.getInterfaces()
);
Object proxy = proxyClass.getConstructor(new Class[] { InvocationHandler.class })
.newInstance(new MyInvocationHandler(new Monkey()));
Mammal mammal = (Mammal) proxy;
mammal.eat("香蕉");
mammal.type();
Primate primate = (Primate) proxy;
primate.think();
**优点:**解决了静态代理的问题,即接口添加新方法,实现类实现后,代理类无需增加新代码。
**缺点:**该方式只能对接口进行代理,无法对类进行代理。
2、基于 CGLIB 的动态代理
由于 CGLIB 使用 ASM 直接操作字节码,因此效率 要比 Proxy 高,但这里所说的效率是指代理对象的性能,在创建代理对象时,Proxy 是要比 CGLIB 效率高的。
目标类:Monkey
public class Monkey {
public String type() {
String type = "哺乳动物";
System.out.println(type);
return type;
}
public final void eat(String food) {
System.out.println("The food is " + food + " !");
}
public void think() {
System.out.println("思考!");
}
}
代理类:MyMethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(
Object obj,
Method method,
Object[] args,
MethodProxy proxy
) throws Throwable {
System.out.println("******************");
Object o = proxy.invokeSuper(obj, args);
System.out.println("++++++++++++++++++");
return o;
}
}
运行类:Cglib
public class Cglib {
public static void main(String[] args) {
Monkey monkey = (Monkey) Enhancer.create(
Monkey.class,
new MyMethodInterceptor()
);
monkey.eat("香蕉");
monkey.type();
monkey.think();
}
}
你会发现 eat()方法没有被代理,因为在它的前后没有输出 MethodInterceptor 中的打印语句。 这是因为 CGLIB 动态代理的原理是使用 ASM 动态生成目标对象的子类,final 方法不能被 子类覆盖,自然也就不能被动态代理,这也是 CGLIB 的一个缺点。
但是一般情况下,我们会创建 Enhancer 的实例来完成动态代理,而不是使用静态方法
create(),因为使用 Enhancer 的实例,你可以获取更多的功能。使用 Enhancer 实例的代码如
下所示:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Monkey.class);
enhancer.setCallback(new MyMethodInterceptor());
Monkey monkey = (Monkey) enhancer.create();
monkey.eat("香蕉");
monkey.type();
monkey.think();
通过 Enhancer 的实例你可以设置是否使用缓存、生成策略等。