第一讲
动态代理讲解
- 动态代理是框架的基础(默认反射机制都清楚啦~),
因为框架是为了应用于不同情境下的,所以不同模块之间的代码需要进行解耦,(低耦合,高内聚,可扩展)。动态代理的出现就是为了解决耦合的问题。
举列:
Spring框架,Mybatis框架都是基于动态代理实现的,这是一个重要思想,后续讲解中会陆续讲解,这些框架的实现原理。
1. Proxy介绍
-
翻译
proxy n.代理人,代表,代理权,代表权(bingDic翻译)
这就是接下来讲解的核心类!!! -
JAVA中API介绍
-
重点是了解
- 构造方法
- getProxyClass
- newProxyInstance
1.1getProxyClass的讲解
getProxyClass(ClassLoader,Interface)
顾名思义,getProxyClass的目的是为了获取代理类
要对代理对象进行操作,那么肯定就需要获得该类是字节码文件。
1、所以在此基础就自然的需要类加载器(ClassLoader)
2、并且为了指定要代理类的类型,那么就需要一个接口(interface),接口的作用,指定了代理对象的方法规则。
//实例用Collection演示
Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class)
了解:显然在此基础上,就会有疑问是否可以对一个类进行代理,不通过接口。答案是可以的,但是java本身不提供,需要第三方提供方法【CGLib】,通过子类实现代理。
在jvm知道了加载该类的加载器,以及字节码中方法加载规则,那么就可以生成一个新的字节码文件,就是动态生成的代理类。
1.2 生成代理对象
在上述操作,已经获取到代理对象的字节码文件【方法区】,但是还未创建出实例【堆】。
- 那么生成了字节码文件,自然就需要生成实例对象使用,自然就想到利用反射中的newInstance()生成实例对象。
- 但是如果直接newInstance()是不行的。根据上面贴图中可见,构造方法只有一种有参构造Proxy(InvocationHandler h)
- 所以需要在newInstance中传入 InvocationHandler的实例参数
//创建InvocationHandler的实例对象
class MyInvocation implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
//获得字节码文件
Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class)
Constructor construtor = clazz.getConstuctor((InvocationHandler.class)
//通过构造器生成实例
Collction collection = (Collection)construtor .newInstance(new MyInvocation());
在Proxy中的部分源码
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
在API中有这样的描述:
1.3简化书写
上述书写是可以成功创建代理类的,但是比较麻烦,java提供了应该更简单的方法
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Colleciton collection = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
Collection.class,
// lambda表达式,更为优雅
(proxy,method,args)->{
//暂时不写,下面讲解。
return null;
}
);
//此时已经生成了collection实例类,操作和原来一样
1.4 InvocationHandler的讲解
- 所有源码
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
修改上面的代码
Collection coll= (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
Collection.class,
//匿名实现类
new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Long beginTime = System.currentTimeMillis();
//method.invoke(对象,参数)--> 执行调用的方法
Object retVal = method.invoke(target, args);
Long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of" + (endTime - beginTime));
return retVal;
}
}
);
coll.add("jack");
coll.add("rose");
coll.add("tom");
此时proxy调用add方法,实际就是将(proxy,add,“jack”)传入了InvocationHandler中的invoke方法。
再通过invoke实现对对象方法的操作,因为现在要通过代理对象进行处理,中间多了一个桥梁(原来是直接调用),这样就可以在中间做其他操作【例如:修改参数等,添加额外“代码”】,改变原本操作。
AOP面向切面编程的核心思想,就是以上的这几句话。
那么下一小结就讲,aop的思想及编程。
版权所有【weixin_41263632 , Limm_666】,禁止转载