Java代理详解(静态代理和动态代理)

一、静态代理

接口: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的动态代理

接口 1Mammal(哺乳动物)
public interface Mammal {
	void eat(String food);
	String type();
}

接口 2Primate(灵长类动物)
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 的实例你可以设置是否使用缓存、生成策略等。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值