动态代理是java中非常重要的一种技术,很多框架的底层实现都使用到了动态代理,例如dubbo、spring、jdbc的事务控制等,因此掌握动态代理的使用并深入理解是非常必要的。
动态代理,顾名思义,来自于Java设计模式中的代理模式:
- 代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
- 代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。
代理模式根据字节码文件的创建时机可以分为静态代理和动态代理:
- 静态代理:静态代理就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
- 动态代理:静态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。
静态代理的缺陷如下:
- 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
- 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
动态代理的实现方式:
动态代理技术从其本质上来说与静态代理是一样的,都是通过像操作目标类一样的操作代理类,然后在代理类中去调用执行目标类的对应方法和其它操作。只是在静态代理技术中这个代理类是在编译前就已经定义好的,其字节码文件在运行前已经存在了,而动态代理技术中的代理类的类文件字节码是通过程序动态生成的,然后通过java的反射技术加载生成的字节码文件。
由于目标对象和代理对象的操作需要存在一致性,且代理对象是不能直接强转为目标对象的,因此一般需要代理对象会实现目标对象的接口,或者作为子类继承目标对象,这样就可以和目标对象具有相同的方法了。而这两种分别是jdk和cglib动态代理的思路。
因此动态代理的思路就很明确了,环境如下:
被代理的目标类为Student,该对象是想了对象Person,两者代码如下:
public interface Person {
//上交班费
void giveMoney(int money);
}
public class Student implements Person {
//被代理对象,接口的实现类
private String name;
public Student(String name) {
this.name = name;
}
public void giveMoney(int money) {
System.out.println(name + "上交班费"+money);
}
public void setName(String name) {
this.name = name;
}
}
1、首先是基于实现相同接口的jdk动态代理:
- 先创建一个目标对象处理类stuHandler,传入目标对象student,同时实现invoke方法,使得传入目标类的方法对象和输入参数后可以让目标对象去执行。
public class StuInvocationHandler<T> implements InvocationHandler {
//invocationHandler持有的被代理对象
T target;
public StuInvocationHandler(T target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out