Java的两种动态代理机制(JDK和CGLIB)

Java有两种代理方式,一种静态代理,另一种是动态代理。
Java的静态代理事先知道要代理什么,而动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。静态代理类由程序员创建或者第三方,再进行编译,在程序运行之前,代理类的.class的文件已经存在;而动态代理类在程序运行时通过反射机制动态生成。***
下面我主要介绍一下两大动态代理机制(JDK和CGLIB)

1.JDK代理

Java JDK模式的代理:
1.被代理的类(目标类),必须要有接口;
2.产生的代理对象,其类型是接口类型的派生类型;
3.代理对象只能调用目标类的接口方法,不能调用目标类自身的方法。

下面我们结合具体的代码来看一下:
先定义一个接口:

package com.mec.proxy;

public interface ISubject {
	public String learn(String str);
}

再定义一个接口的实现类(也就是目标类),其中不仅有该接口方法,还有自身方法。

public class SubjectImpl implements ISubject {
	
	public SubjectImpl() {
	}

	@Override
	public String learn(String str) {
		System.out.println("正在执行learn方法!");
		return "我想要学习" + str;
	}
	
	public void doSomnthing() {
		System.out.println("执行doSometning方法!");
	}

}

然后再来看一下JDK代理的具体部分:

public class JDKProxy {

	@SuppressWarnings("unchecked")
	public static <T> T getProxy(T target) {
		Class<?> klass = target.getClass();
		ClassLoader classLoader = klass.getClassLoader();
		Class<?>[] interfaces = klass.getInterfaces();
		ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader, interfaces, 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) 
							throws Throwable {
						System.out.println("前置拦截");
						Object result = method.invoke(target, args);
						System.out.println("后置拦截");
						return result;
					}
				});
		
		return (T) proxy;
	}

}

JDK代理机制中,最重要的是一个接口(java.lang.reflect.InvocationHandler)和一个类(java.lang.reflect.Proxy)。Proxy类最常用的是newProxyInstance,其中有三个参数,第一个是目标类的类加载器,第二个是目标类的所有接口,第三个是InvocationHandler的对象。InvocationHandler接口中定义了一个方法invoke,其中有三个参数Object proxy(所需代理的真实对象)、== Method method==(所需代理对象中的某个方法)以及Object[] args(该方法所接收到的参数)。invoke方法用来执行所需代理对象中的方法,它的返回值是Object类型,因为返回值是所执行方法的返回值。
现在我们来测试一下
测试类:

public class JDKTest {

	public static void main(String[] args) {
		SubjectImpl subjectImpl = new SubjectImpl();
		ISubject proxy = JDKProxy.getProxy(subjectImpl);
		
		System.out.println("proxy instanceof SubjectImpl:" + (proxy instanceof SubjectImpl));
		System.out.println("proxy instanceof ISubject:" + (proxy instanceof ISubject));
		System.out.println(proxy.getClass());
		String str = proxy.learn("英语");
		System.out.println(str);
	}

}

测试结果如下:

proxy instanceof SubjectImpl:false
proxy instanceof ISubject:true
class com.sun.proxy.$Proxy0
前置拦截
正在执行learn方法!
后置拦截
我想要学习英语

我们可以看到代理对象的类类型是class com.sun.proxy.$Proxy0,它是在JVM运行时动态生成的一个对象。

2.CGLIB代理

与JDK代理相比,CGLIB代理:
1、被代理的类,不必须实现接口;
2、由于CGLib代理的原理是,创建一个被代理类的子类对象,因此,如果被代理的类存在不能被继承的方法,则,这个代理类对象当然就无法调用!即,被代理类中的final方法是不能被代理的;当然,若被代理类本身是final类,则,不能被代理!(代理对象是目标类的子类的对象)
3、代理对象可以调用除了final修饰的其它所有方法。(代理对象不仅可以调用目标类的接口方法,还可以调用目标类的自身方法)

继续使用上面的接口和接口的实现类(目标类)。
我们看一下CGLIB代理的具体部分:

public class CGLIBProxy {

	@SuppressWarnings("unchecked")
	public static <T> T getProxy(T target) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object object, Method method, Object[] args,
					MethodProxy proxy) throws Throwable {
				System.out.println("前置拦截");
				Object result = method.invoke(target, args);
				System.out.println("后置拦截");
				return result;
			}
		});
		
		return (T) enhancer.create();
	}

}

CGLIB模式的代理和JDK模式代理一样,都是通过反射机制来执行方法的。不同的是CGLIB代理的代理对象是目标类的子类的对象。

测试类:

	public class CGLIBTest {
	public static void main(String[] args) {
		SubjectImpl subject = new SubjectImpl();
		
		SubjectImpl proxy = CGLIBProxy.getProxy(subject);
		String str = proxy.learn("语文");
		System.out.println(str);
		System.out.println("=====================");
		proxy.doSomnthing();
	}
}

测试结果如下:

前置拦截
正在执行learn方法!
后置拦截
我想要学习语文
=====================
前置拦截
执行doSometning方法!
后置拦截

我们可以看到CGLIB代理不仅可以调用目标类的接口方法,还可以调目标类的自身方法。以上就是我对Java的两大动态代理的理解,如有不足,敬请指出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值