最后
我还为大家准备了一套体系化的架构师学习资料包以及BAT面试资料,供大家参考及学习
已经将知识体系整理好(源码,笔记,PPT,学习视频)
这种代理模式的设计方式,我们一般称之为静态代理:由编码人员创建完成或由特定工具生成源代码,在编译时就已经将接口、被代理类、代理类等确定类下来,在程序运行之前,代理类的字节码文件已经生成了。如果由其他的代理内容,可能需要新建很多的代码来实现。
4|**0**动态代理
=======================
与静态代理最大的区别在于,动态代理类是在程序运行时创建的代理。例如在上面的例子中DataServiceProxy
代理类是我们自己定义的,在程序运行之前就已经编译完成。在动态代理中,代理类不是在代码中定义,而是在程序运行时根据我们的需要在Java
代码中动态生成的。
在Java
中我们提到动态代理,一般绕不开JDK
动态代理和CGLIB
动态代理。
4|**1**JDK动态代理
利用JDK
自带的代理类来完成,相当于利用一个拦截器(需实现接口InvocationHanlder
)配合反射机制生成一个实现代理类的匿名接口,在调用具体的方法前调用InvocationHanlder
来处理。
我们依旧使用DataService
接口和DataServiceImpl
业务类来完成一个动态代理的案例。
-
创建被代理类的接口和业务类(已经有了)
-
创建
InvocationHanlder
接口的实现类,在invoke
方法中实现代理的逻辑 -
通过
Proxy
的静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
创建一个代理对象。
public class JDKProxy implements InvocationHandler { // 被代理对象 private Object object; // 缓存 Map<Integer,String> cacheMap = new HashMap<>(); public JDKProxy(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 只代理其中的查询方法 if (method.getName().equals(“getById”)){ // 参数 Integer id = (Integer) args[0]; // 记录访问的开始时间 final long start = System.currentTimeMillis(); String result = null; // 优先从缓存获取 String cache = getCache(id); if (cache == null){ // 代理执行 result =(String) method.invoke(object,args); // 放入缓存中 putCache(id,result); }else { result = cache; } final long end = System.currentTimeMillis(); System.out.println(“耗时:” + (end - start) + “ms”); return result; }else { return method.invoke(object,args); } } // 缓存信息 private void putCache(Integer id,String value){ cacheMap.put(id,value); } // 获取缓存信息 private String getCache(Integer id){ return cacheMap.get(id); } }
InvocationHandler接口详解
InvocationHandler
接口是proxy
代理实例的调用处理程序实现的一个接口,每一个proxy
代理实例都有一个关联的调用处理程序;在代理实例调用方法(Method
)时,方法调用被编码分派到调用处理程序的invoke
方法。
每一个动态代理类的调用处理程序都必须实现InvocationHandler
接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler
接口类的invoke
方法来调用,看如下invoke
方法:
/** * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0(按次序进行,每生成一个 +1) * method:我们所要调用某个对象真实的方法的Method对象 * args:指代代理对象方法传递的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Client
客户端在调用时的方式也和静态代理不一样,最终是使用代理类$Proxy
来进行方法的调用
@Test public void JDKProxyTest() { DataService dataService = new DataServiceImpl(); JDKProxy jdkProxy = new JDKProxy(dataService); // 获取代理对象 DataService dataServiceProxy = (DataService) Proxy.newProxyInstance(DataService.class.getClassLoader(), new Class[]{DataService.class}, jdkProxy); dataServiceProxy.getById(1); dataServiceProxy.getById(1); }
其运行的结果是一样的,都完成了代理内容。
Proxy类详解
Proxy
类就是用来创建一个代理对象的类,它提供了很多方法,我们最常用的是newProxyInstance
方法。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance
就是创建一个代理类对象,它接收三个参数:
-
loader
:指定代理类的类加载器(我们传入当前测试类的类加载器) -
interfaces
:一个interface
对象数组,代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口) -
h
:一个InvocationHandler
对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler
对象上,用来处理方法的调用。这里传入我们自己实现的handler
4|**2**CGLIB动态代理
利用asm
开源包,对代理对象类的class
文件加载进来,通过修改其字节码生成子类来处理。
-
导入
cglib-xxx.jar
包,这里包含了asm
和cglib
-
创建
MethodInterceptor
接口的实现类,在intercept
方法中实现代理的逻辑 -
编写
getCglibProxy
方法(自定义)返回代理类对象
Pom导入cglb
cglib cglib 3.3.0
重写MethodInterceptor
public class CglibProxy implements MethodInterceptor { // 被代理对象,便于通用,可以写成Object private Object object; // 缓存 Map<Integer,String> cacheMap = new HashMap<>(); @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 只代理其中的查询方法 if (method.getName().equals(“getById”)) { // 参数 Integer id = (Integer) args[0]; // 记录访问的开始时间 final long start = System.currentTimeMillis(); String result = null; // 优先从缓存获取 String cache = getCache(id); if (cache == null) { result = (String)method.invoke(object,args); // 放入缓存中 putCache(id, result); } else { result = cache; } final long end = System.currentTimeMillis(); System.out.println(“耗时:” + (end - start) + “ms”); return result; } else { return method.invoke(object, args); } } // 获取代理对象 这里采用了范型的写法,更直观的传入被代理类,然后返回代理对象 public T getCglibProxy(T t){ this.object = t;//为目标对象target赋值 Enhancer enhancer = new Enhancer(); //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类 enhancer.setSuperclass(object.getClass()); //设置回调 enhancer.setCallback(this); //创建并返回代理对象 Object result = enhancer.create(); return (T) result; } // 缓存信息 private void putCache(Integer id,String value){ cacheMap.put(id,value); } // 获取缓存信息 private String getCache(Integer id){ return cacheMap.get(id); } }
Client
@Test public void CGLBProxyTest(){ // 被代理类 这里可以不用接口声明哦 DataService dataService = new DataServiceImpl(); CglibProxy cglibProxy = new CglibProxy(); // 获取代理对象 DataService proxy = cglibProxy.getCglibProxy(dataService); proxy.getById(1); proxy.getById(1); }
可以发现两种动态代理的写法基本差不多,基本的思路都是生成代理类,拦截,反射,获取真正的代理类方法,执行。那么两种方式有什么区别和用法呢?
4|**3**JDK代理和CGLIB代理的区别
JDK
动态代理只能对实现了接口的类生成代理,而不能针对类 ,使用的是Java
反射技术实现,生成类的过程比较高效。
总结
一般像这样的大企业都有好几轮面试,所以自己一定要花点时间去收集整理一下公司的背景,公司的企业文化,俗话说「知己知彼百战不殆」,不要盲目的去面试,还有很多人关心怎么去跟HR谈薪资。
这边给大家一个建议,如果你的理想薪资是30K,你完全可以跟HR谈33~35K,而不是一下子就把自己的底牌暴露了出来,不过肯定不能说的这么直接,比如原来你的公司是25K,你可以跟HR讲原来的薪资是多少,你们这边能给到我的是多少?你说我这边希望可以有一个20%涨薪。
最后再说几句关于招聘平台的,总之,简历投递给公司之前,请确认下这家公司到底咋样,先去百度了解下,别被坑了,每个平台都有一些居心不良的广告党等着你上钩,千万别上当!!!
Java架构学习资料,学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书。
析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**