Java的JDK动态代理实现
- 动态代理在自己的记忆里模模糊糊,所以我决定把这个实现的过程写下来,记录一下。
动态代理有什么用?
- 我的理解很粗暴:就是对原用功能的增强或者修改。
- 想想我们平时使用的梯子,那也是一种代理,对我们的请求进行一顿操作,让我们能够访问外网,代理就是干的这样一个火。
- java的动态代理也一样,原来的类功能已经固定下来了,我在不修改源码的情况下在使用类的方法时进行日志记录?动态代理就是一个方法,在调用源码的方法的时候,在调用这个方法的前后可以增加一点自己想要的操作。
实现开始咯,JDK动态代理是基于接口的,所以我们先把接口,和类创建出来
-
这是文件的结构
-
接口
public interface manDo {
public void manDo();
public void NotManDo();
}
- 接口的实现类
public class Man implements manDo{
@Override
public void manDo() {
System.out.println("重写 mando");
}
@Override
public void NotManDo() {
System.out.println("重写 notMando");
}
}
上面这里就像我们拿到的源码,我们如何在不修改Man这个源码的情况下进行方法的增强?
- 首先我们要创建代理类,这个代理类需要换入的参数是
实现了接口的类
,即实现了manDo接口的Man这个类。这个代理一定要实现InvocationHandler
接口,并且通常定义一个Object对象,通过构造方法传入实现了接口的类
。然后我们在里面重写invoke方法进行代理。可以看到我这里还通过method.getName
判断了要执行的方法名,如果是manDo
则执行对应的增强
public class ManProxy implements InvocationHandler {
public Object obj;
public ManProxy(Object target) {
this.obj = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
if (method.getName() != "NotManDo") {
System.out.println("before : 这是man do的事情");
object = method.invoke(obj, args);
System.out.println("after : 这是man do的事情");
} else if (method.getName() != "manDo") {
System.out.println("before : 这^-^不是man do的事情");
object = method.invoke(obj, args);
System.out.println("after : 这^-^不是man do的事情");
}
return object;
}
}
- 接下来是创建我们jdk动态代理的类实例,然后进行调用了
public class ClassHandler {
public static void main(String[] args) throws ClassNotFoundException {
Man man = new Man();
Class aClass = Class.forName("originClass.Man");
ClassLoader classLoader = aClass.getClassLoader();
Class[] interfaces = aClass.getInterfaces();
// ClassLoader classLoader = man.getClass().getClassLoader();
// Class[] interfaces = man.getClass().getInterfaces();
InvocationHandler manProxy = new ManProxy(man);
manDo man_proxy = (manDo)Proxy.newProxyInstance(classLoader, interfaces, manProxy);
man_proxy.manDo();
System.out.println("--------");
man_proxy.NotManDo();
}
}
- 创建动态代理使用的是Proxy.newProxyInstance方法,传入一个类加载器,方法接口,然后就是代理类。
- 值得注意的是我这里的类加载器使用的是通过Man类的得到的,实际上这个类加载器使用程序的任一个类的得到的都可以。例如通过
ClassHandler.class.getClassLoader()
一样可以获得,一样可以执行。 - 另外,可以看到我中间有注释两行为
ClassLoader classLoader = man.getClass().getClassLoader();
和Class[] interfaces = man.getClass().getInterfaces();
这是我一开始获取方法和加载器的方法,后面我使用通过自己主动写类名originClass.Man
来获得这些。 - 还有一点我卡bug了的是,
manDo man_proxy = (manDo)Proxy.newProxyInstance(classLoader, interfaces, manProxy);
这里的强转类型是转为接口manDo,不能转为这个接口的实现类Man
。一开始我就是选择转为Man
,然后咔咔报错。理由的话我认为是这种代理方式是基于接口的,因此实现的代理类其实就是对接口的方法进行增强,因此我们转型的时候转成接口类型,也没毛病! - 发现神奇的地方了吗,只要我知道这个类的全类名,我就可以通过反射去实现这个类动态代理类。
当然这是JDK动态代理的方法,意思是说这一切都要基于我们想代理的类它是实现了接口的。如果没有实现接口的类的动态代理,是另一种代理方式了。