说到动态代理,有时候会在想,既然JDK里面有代理类,直接调用不就完事了吗?为什么还要学动态代理?
但是项目中运行时环境是 JRE,那我们就需要自己去实现动态代理,那需要用什么实现动态代理呢?那就是InvocationHandler类。
之前很苦恼,动态代理到底是个什么鬼玩意,但是了解之后理出来一点思路。
请记住:动态代理只能代理接口!!!
简单的说:如果你是马爸爸,需要找周杰伦做代言,但是又直接接触不到周杰伦,所以只能通过周杰伦的经纪人来谈,那么这就是一个代理机制的实际例子。
马爸爸 ---> 经纪人 ---> 周杰伦
今天讲的是通过JDK的动态代理:
1、在程序运行时,通过反射机制动态生成。
2、通常道理接口下的所有类。
3、事先不知道要代理的是什么,只有在运行的时候才能确定。
4、调用处理程序必须事先Invocation接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
5、只能代理接口,要代理类需要使用第三方的CLIGB等类库。
动态代理的好处:
1、降低代码的冗余
2、增强方法
3、项目的可扩展性强
接下来,上代码!
1、首先我们要有个代理的接口,我们就先定义成Person吧,简单的两个方法
public interface Person {
public void sayHello(String content, int age);
public void sayBye(boolean seeAgin, double time);
}
2、要有个被代理对象(类)Student,实现 Person 接口,重写 Person 接口的所有方法
public class Student implements Person {
@Override
public void sayHello(String content, int age){
System.out.println("student say hello" + content + " "+ age);
}
@Override
public void sayBye(boolean seeAgin, double time){
System.out.println("student sayBye " + time + " "+ seeAgin);
}
}
3、写一个 MyInvocationHandler 实现 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("MyInvocationHandler invoke begining ......");
System.out.println("代理对象proxy: "+ proxy.getClass().getName());
System.out.println("方法method: "+ method.getName());
for(Object o : args){
System.out.println("参数arg: "+ o);
}
// 通过反射调用 被代理类的方法
method.invoke(object, args);
System.out.println("MyInvocationHandler invoke ending ......");
return null;
}
public static void main(String [] args){
// 创建需要被代理的类
Student student = new Student();
// 生成代理类的class文件,前提是你需要在工程根目录下创建com/proxy目录,不然会报找不到路径的io异常
System.getProperties().put("com.proxy.ProxyGenerator","true");
// 获取类加载器,曾经为Java打下了半壁江山
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// 获取被代理类实现的接口
Class<?>[] interfaces = student.getClass().getInterfaces();
// 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(student);
// 生成代理类
Person proxy = (Person) Proxy.newProxyInstance(loader, interfaces, myInvocationHandler);
// 通过代理类调用 被代理类的方法
proxy.sayHello("xiaokang.ma", 20);
proxy.sayBye(true, 110);
System.out.println("end");
}
}
4、接下来我们来运行一下,看看结果
"C:\Program Files\Java\jdk1.8.0_121\bin\java.exe"
MyInvocationHandler invoke begin
proxy: com.sun.proxy.$Proxy0
method: sayHello
arg: xiaokang.ma
arg: 20
student say helloxiaokang.ma 20
MyInvocationHandler invoke end
MyInvocationHandler invoke begin
proxy: com.sun.proxy.$Proxy0
method: sayGoodBye
arg: true
arg: 100.0
student sayGoodBye 100.0 true
MyInvocationHandler invoke end
end
总结:
1、创建被代理的类
2、获取类加载器 ClassLoader(ClassLoader可是为Java打下了半壁江山的男人),ClassLoader将对象加载的进程中
3、获取被代理类实现的接口
4、创建被代理类的委托类 MyInvocationHandler
5、生成 Person 的代理类 proxy
6、proxy 调用 被代理类的方法时,调用的是 MyInvocationHandler 的 invoke()方法,从而实现动态代理。
有兴趣的朋友,可以去看下 Proxy 的源码,主要看 newProxyInstance()方法
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
当然,还有一种方式是 cglib 的动态代理,想要了解的可以进行深究。
我是进阶的球儿,大家一起2019年的爬坑历程。感觉分享很给力的话给个赞,谢谢!!!有问题也可以下方留言沟通。