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的两大动态代理的理解,如有不足,敬请指出。