1.动态代理
1.1什么是动态代理?
动态代理(理解) 基于反射机制
举个例子,生活中一般在打官司的时候都会请代理律师,为什么要请律师呢?是因为开庭的时候大部人对于打官司没有经验,只会说出自己案件的陈述,并不会根据法律等争取自己权益的最大化,此时就可以请律师帮助自己不仅完成对案件的陈述,还能争取权益最大化。那么Java中也是一样,如果要对功能进行增强就可以使用动态代理。
我们知道Spring是通过JDK或者CGLib实现动态代理的,今天我们讨论一下JDK实现动态代理的原理。
1.2动态代理的实现方式有几种?
- JDK动态代理
- CGLB动态代理
1.3动态代理的概念和优点
相比于静态代理(在静态代理中,对于类的每一个接口,我们都要单独写一个代理类),动态代理在创建代理对象上更加的灵活。
有需求,有问题,才会有解决方法和改进创新。那么产生动态代理的问题根源或者需求是什么呢?在静态代理中,由于每个接口都需要我们单独的写一个代理类,比较麻烦,因此我们就想,我们能不能写一个类,我们只需要把委托对象(目标对象),还有全部接口(共同行为,其实委托对象中就已经包含接口了)作为参数传给这个类的方法,然后这个方法就可以给我们返回一个我们想要的代理对象呢,并且这个代理对象可以给我们代理全部的共同行为,像是租房,结婚等?
- 动态代理类的字节码在程序运行时由JAVA反射机制动态产生。会根据需要,通过反射机制,在程序运行期动态的为目标对象(委托对象)创建代理对象,无需程序员手动编写代理对象所属类的代码。
- 动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制的使用使得可以生成任意类型的代理对象。
- 动态代理的实现方式有两种,分别是:JDK动态代理和CGLIB动态代理。
- 动态代理的目标对象是不固定的(也就是说把任何一个目标对象或者说是委托对象作为参数传递给生成代理对象的对象的方法,都会给我们返回一个我们想要的代理对象);使用动态代理以后,会在应用程序执行的时候,动态的创建目标对象代理对象依然会增强目标对象的行为;
2.JDK动态代理的概念和特点
先说一个需要注意的点:JDK动态代理的目标对象必须要有接口实现,也就是说:委托类必须要继承接口。
在JDK中,有一个Proxy类(名词,代理人)。Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类(对这个类的其他方法我了解的也不是很多,我们可以看JDK的在线API文档,百度搜一下好像挺多的)。这个类提供的有一个静态方法:newProxyInstance()方法。这个方法的目的就是给我们的目标对象(委托对象)返回一个代理对象。
newProxyInstance()方法需要有三个参数:
- 类加载器(ClassLoader对象)
- 接口集合(一个Interface对象的数组,就是需要代理对象代理那些共同行为,也是委托对象继承的共同行为接口)
- 一个InvocationHandler接口对象(当然可以是它的一个实现类对象)。这个接口中有一个invoke()方法。invoke()方法起到的作用很大,当代理对象调用共同行为方法的时候,invoke()方法就会被自动调用执行。
下面粘贴一张图片:
2.1动态代理的介绍
- 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。
- 动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。
- 动态代理的实现方式常用的有两种:使用JDK代理,与通过CGLlB动态代理。
2.2动态代理的实现
- jdk动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
- cglib动态代理(了解): cglib是第三方的工具库,创建代理对象
- cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中
重写父类中同名的方法,实现功能的修改。- 因为cglib是继承,重写方法,所以要求目标类不能是fina1的,方法也不能是final的。cglib的要求目标类比较宽松,只要能继承就可以了。cglib在很多的框架中使用,
比如mybatis,spring框架中都有使用
2.3CGLB动态代理
CGLB动态代理即Code Generation Library,是一个开源的第三方工具库,其原理是继承,去生成目标类的子类对象,这样对子类的功能进行增强。但是要求:目标类不能用final修饰,目标类中的方法也不能被final修饰。
2.4动态代理的效率
CGLB动态代理的效率要大于JDK动态代理的效率。
3.为什么使用JDK动态代理?
3.1代码案例:
- 请看如下例子,一个音乐人的本质会唱歌、会跳舞、会rap等,但是观众进入演唱会所时首先要交门票,最基本的实现方法是直接对方法进行改造,添加对应的功能。
package com.zhao.service.impl; import com.zhao.service.Actor; public class CXK implements Actor { @Override public void sing(int money) { System.out.println("听"+money); } @Override public String dance() { System.out.println("吹灰舞"); return "发放签名"; } @Override public void rap() { System.out.println("练习时长两年"); } }
-
这样操作发现出现了大量重复的代码,如果有十处、一百处需要同样的处理那么代码需要重复十次、一百次。当然我们可以把这些功能封装成一个增强方法,然后在功能方法中进行调用,但是也出现了方法的十处、一百处的调用操作,一旦增强方法名字改变,就需要完成所有调用处代码的修改。或者有一天不需要这些增强操作了,就再次需要在这十处、一百处删除方法调用。所以这种操作不适用于大型的项目开发的需求,此时我们就必须使用Java的动态代理机制。
在Java开发中如果一个类中的方法在基本功能之外需要进行功能扩充或者功能增强,如:事务控制、权限判断、日志记录等等操作,此时可以使用动态代理机制。
Java的JDK中Proxy类可以实现基于接口的动态代理,实现步骤示例如下:
- 因为Proxy类必须基于接口进行动态代理,所以首先创建接口,定义接口的规范,即功能方法的定义。
package com.zhao.service; /** * 演员接口 * */ public interface Actor { //唱歌 void sing(int money); //跳舞 String dance(); //rap void rap(); }
- 定义实现接口的子类,实现接口定义的方法,此方法只需要把核心功能实现即可,其他增强的操作可以在代理类中实现。
package com.zhao.service.impl; import com.zhao.service.Actor; public class CXK implements Actor { @Override public void sing(int money) { System.out.println("听"+money); } @Override public String dance() { System.out.println("吹灰舞"); return "发放签名"; } @Override public void rap() { System.out.println("练习时长两年"); } }
- 定义代理类,在代理类中对被代理对象进行方法增强。
package com.zhao.advice; import com.zhao.service.Actor; import com.zhao.service.impl.CXK; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JJGS { public static void main(String[] args) { //1.创建被代理类的对象----具体的人物cxk Actor cxk=new CXK(); //2.创建代理对象--- 具体某人为上面cxk /** * ClassLoader loader:类的加载器---联系方式 * Class<?>[] interfaces:类的接口类型---被代理人的类型 * InvocationHandleer h:处理---我要帮你干什么 */ Actor jjr=(Actor) Proxy.newProxyInstance(CXK.class.getClassLoader(), CXK.class.getInterfaces(), new InvocationHandler() { /** * * Object proxy:被代理对象的引用,系统会自动创建被代理对象的一个映射 * Method method:被代理对象的方法 * @param args *Object[] args:被代理对象方法的参数 * 返回值是被代理对象执行后的返回值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //被代理对象方法的执行,并获得返回值 Object result=null; result=method.invoke(cxk,args); System.out.println("演出后的增强:结算费用并纳税"); return result; } }); //3.执行功能 // jjr.sing(5000); String dance = jjr.dance(); System.out.println(dance); } }
测试结果:
3.2基于子类的CGLib动态代理,可以使用Enhancer类完成直接对某个类进行动态代理。具体操作步骤如下:
- 创建被代理的类,并且定义功能方法,只需要完成核心功能即可。
package com.zhao.service.impl; public class Actor { public void sing(int money) { System.out.println("听" + money); } public String dance(int money) { System.out.println("吹灰舞"+money); return "发放签名"; } public void rap(int money) { System.out.println("练习时长两年"+money); } }
- 定义代理类,使用Enhancer创建代理对象,对被代理对象进行方法增强。
package com.zhao.advice; import com.zhao.service.impl.Actor; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class JJGss { public static void main(String[] args) { //1.被代理的对象 Actor actor=new Actor(); //2.使用CGLIb的Enhancer类创建代理对象 Actor proxyActor = (Actor) Enhancer.create(Actor.class, new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { String methodName=method.getName(); String money=args[0]; Object result=null; if(methodName.equals("danceAct")){ System.out.println("对方法执行前进性代理增强..."); result=method.invoke(actor,money); System.out.println("对方法执行后进性代理增强..."); return result; } return result; } }); // proxyActor.dance(3000); } }