java的动态代理模式有哪些?
①JDK动态代理
②CGLIB动态代理
③Javassist
④ASM
…
那我们需要学习哪个?
我在本目录下我只讲解JDK动态代理和CGLIB动态代理
1.JDK动态代理
此动态代理是JDK自带的功能。我通俗点讲,里面有名词:调用者,真实对象和代理对象
比如在一个软件公司里,有客户来找此公司设计软件,但是首先接待客户的并不是真正的软件工程师,而是专门负责谈商务业务的人,来问你的需求以及各项事宜,包括讲完需求后需要的薪资、以及签订什么样的合同之类的,也包括失败以后是否终止合作。那么讲完这些,达成协议以后,商务人员才会拿着需求给到相应的软件工程师。然后软件工程师才会进行开发设计,将需求完成以后再将设计好的软件给商务人员拿给客户,然后拿到最后的报酬。
在这个过程里,客户就相当于动态代理里的调用者,商务人员就相当于动态代理里的代理对象,软件工程师相当于动态代理里的真实对象。在以下图中可以明确一点他们的关系
客户并不会直接去和软件工程师去接触,而是通过一个谈判者(商务人员)去进行交涉,最后谈判者(商务人员)再把需要的东西给真实对象(软件工程师)去完成。这个过程就形象的描述出了JDK动态代理的思想。
再通俗点讲就是代理对象虽然不是真实对象,但是代理对象拥有真实对象的方法,我可以用代理对象去完成你调用者的需求,但是你并不用知道我是如何去完成的,我可以去给真实对象完成,然后再回去给你返回值,仅此而已。然后我们用简单的代码去了解一下底层是如何实现的
先建立一个接口类
public interface HelloWorld {
void printHello();
}
再写出接口的实现类
public class HelloWorldImpl implements HelloWorld{
public void printHello() {
System.out.println("heyman");
}
}
这个时候就可以动态代理了。先要建立起代理对象和真实对象的关系,以及代理逻辑。
在JDK动态代理里,一定要实现的接口是InvocationHandle,里面定义了invoke方法
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//实现接口InvocationHandler
public class ProxyJDK implements InvocationHandler {
//声明一个真实对象
private Object realTarget;
//此方法用来使用代理对象绑定真实对象
/*
realTarget为需要绑定的真实对象(比如接口实现类的对象)
return的是一个Object Proxy对象,newProxyInstance方法为实例化
realTarget.getClass().getClassLoader()为获取真实对象的类加载器
realTarget.getClass().getInterfaces()为获取真实对象的下挂接口(下挂接口是所有的接口)
this为当前对象,也就是传入的真实对象
*/
public Object bind(Object realTarget){
this.realTarget = realTarget;
return Proxy.newProxyInstance(realTarget.getClass().getClassLoader() ,
realTarget.getClass().getInterfaces() , this);
}
//此方法是实现InvokationHandler接口里的invoke方法
/*
Object proxy代表在bind方法中返回的Object对象(Proxy)
Method method代表需要调度的方法
Object[] args代表方法需要的方法
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在调度真实方法前");
Object object = method.invoke(realTarget , args);
System.out.println("在调度真实方法后");
return object;
}
//测试方法
/*
声明ProxyJDK对象拿到两个方法,用代理对象helloWorld去调用真实对象的方法
先将代理对象绑定于HelloWorldImpl对象,然后在内部invoke需要调用的printHello方法
*/
@Test
public void testJdkProxy(){
ProxyJDK proxyJDK = new ProxyJDK();
HelloWorld helloWorld = (HelloWorld) proxyJDK.bind(new HelloWorldImpl());
helloWorld.printHello();
}
}
在里面我已经讲的很详细的,全部都写在了注释上。到此JDK动态代理就完成了。
最后测试完的结果:
可以看到invoke方法的执行顺序和输出是一样的。
说明一下,如果看不懂代码的建议回头复习一下基础编程里的反射。其中过程也就是创建接口和实现类,我们并不是直接调用实现类去完成需要的方法,而是通过实现类的接口用绑定的方法与实现类绑定起来,然后用类的接口去调用方法,这是一个很大的区别,希望注意一下。如果第一遍看不懂建议可以看多几遍,我在刚开始也是不能看懂,但是可以先放一放,学习后面的知识,学到一定的程度,返回来再看,就会有一种醍醐灌顶的感觉。最好是跟着我的思路来打一遍,有助于理解。
2.CGLIB动态代理
其实能理解JDK动态代理,对于CGLIB动态代理来说就显得很简单了。首先要知道CGLIB动态代理和JDK动态代理有什么区别?为什么要分离出两个动态代理方法?有时候一些类并没有接口,那么没有接口还怎么用JDK动态代理去生存代理对象呢?那就可以用CGLIB动态代理实现,它是不需要接口去实现动态代理的。CGLIB属于第三方技术,所以需要导入jar包才能使用。现在用代码显示一下
因为是第三方技术,需要在Maven中导入CGLIB的jar包
在dependencies中加入这些配置:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
然后就能使用到CGLIB了
写一个PeopleSayHello类
public class PeopleSayHello {
public void sayHello(String name){
System.out.println(name + ",hello");
}
}
然后建立CGLIB类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
//利用类获取类的CGLIB的代理对象
/*
参数c为传入的类
return为返回生成的CGLIB代理对象
enhancer实例对象是CGLIB的增强类对象
setSuperclass是设置增强类型,比如增强为你传入的类的类型
setCallback为设置当前对象为代理逻辑对象
*/
public Object getProxy(Class c){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(c);
enhancer.setCallback(this);
return enhancer.create();
}
//接口MethodInterceptor要实现的方法intercept
/*
o代表由getProxy生成的代理对象
method代表要调用的方法
objects代表调用方法的参数
methodProxy代表方法的代理
CGLIB调用代理方法的形式:methodProxy.invokeSuper(o , objects);
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用CGLIB前");
Object result = methodProxy.invokeSuper(o , objects);
System.out.println("调用CGLIB后");
return result;
}
//测试方法
/*
先生成CGLIB的实例对象
将真实对象的代理对象实例出来
然后用代理对象调用方法
*/
@Test
public void testCGLIBProxy(){
CglibProxy cglibProxy = new CglibProxy();
PeopleSayHello peopleSayHello = (PeopleSayHello) cglibProxy.getProxy(PeopleSayHello.class);
peopleSayHello.sayHello("帅哥");
}
}
关于里面的参数以及细节,都在注释中解释清楚了。
测试结果: