Java 基础【19】代理
java 代理(proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。
在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。
javaweb 中最常见的过滤器、struts 中的拦截器、spring 中的 aop...都有代理的应用。
此篇博客将编写例子描述 java 底层技术和开源类库cglib实现代理的方法,并对比各方法的优缺性。
1.jdk 静态代理
抽象接口:
/** * 用户服务抽象 */ public interface userservice { /** * 用户登录 * * @param username 用户名 * @param pwd 密码 * @return 登陆结果 */ string login(string username, string pwd); }
实现该接口:
/** * 用户服务实现 * * @author rambo 2019-03-01 **/ public class userserviceimpl implements userservice { @override public string login(string username, string pwd) { console.log("进行登陆逻辑........."); return "登陆结果"; } }
编码代理类,实现该接口,代理目标作为私有对象:
/** * 用户服务代理类 * * @author rambo 2019-03-01 **/ public class userserviceproxy implements userservice { private userservice userservice; userserviceproxy(userservice userservice) { this.userservice = userservice; } @override public string login(string username, string pwd) { console.log("登陆前扩展....."); userservice.login(username, pwd); console.log("登陆后扩展....."); return "登陆结果"; } }
编写测试类:
@test public void testlogin() throws exception { userserviceproxy userserviceproxy = new userserviceproxy(new userserviceimpl()); userserviceproxy.login("rambo","111111"); }
这应该是最原始实现代理的样子,缺点也很明显,需要编码代理类,势必导致代理类冗余,且当目标类增加或删除方法时,需要维护代理类。
2.jdk 动态代理(接口代理)
代理核心方法 proxy.newproxyinstance :
/** * jdk 生成代理类 * @param loader 当前目标对象使用类加载器 * @param interfaces 目标对象实现的接口的类型 * @param h 事件处理对象,通过反射执行目标对象的方法 * @return 生成的代理类实例 */ @callersensitive public static object newproxyinstance(classloader loader,class<?>[] interfaces, invocationhandler h)
代理工厂类:
/** * 代理工厂类 * * @author rambo 2019-03-01 **/ public class jdkproxyfactory { private object target; public jdkproxyfactory(object target) { this.target = target; } public object getproxyinstance() { return proxy.newproxyinstance(target.getclass().getclassloader(), target.getclass().getinterfaces(), new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { console.log("执行目标前的扩展......"); object returnvalue = method.invoke(target, args); console.log("执行目标后的扩展......"); return returnvalue; } }); } }
编写测试类:
@test public void testgetproxyinstance() throws exception { userservice proxyinstance = (userservice) new proxyfactory(new userserviceimpl()).getproxyinstance(); proxyinstance.login("rambo", "111111"); }
目标对象需要实现接口,代理对象是可以不用实现接口的。
使用目标对象接口、自定义调用处理类 invocationhandler 实例化代理类,内部通过反射调用目标对象的方法。
3.cglib 代理 (子类代理)
当目标对象是个单独的类,没有实现任何接口,是无法使用上述两种代理方法,这时候怎么办?
可以使用 cglib 代理(需要单独引入 cglib 类库),自定义目标类的子类进行目标对象的扩展,且这种扩展进行在 jvm 运行期。
cglib 底层是通过一个小而快的字节码处理框架 asm 来转换字节码并生成新的类。
不局限目标类存在方式、运行期增强目标类、底层精致的 asm 字节码框架,使 cglib 成为许多 aop 框架生成动态代理的首选。
代理工厂类:
/** * cglib 动态代理工厂 * * @author rambo 2019-03-01 **/ public class cgbproxyfactory implements methodinterceptor { private object target; public cgbproxyfactory(object target) { this.target = target; } public object getproxyinstance() { enhancer en = new enhancer(); en.setsuperclass(target.getclass()); en.setcallback(this); return en.create(); } @override public object intercept(object obj, method method, object[] args, methodproxy proxy) throws throwable { console.log("执行目标前的扩展......"); object returnvalue = method.invoke(target, args); console.log("执行目标后的扩展......"); return returnvalue; } }
编写测试用例:
@test public void testgetproxyinstance() throws exception { userservice proxyinstance = (userservice) new cgbproxyfactory(new userserviceimpl()).getproxyinstance(); proxyinstance.login("rambo","111111"); }
jdk invocationhandler 、cglib methodinterceptor 具体实现的细节,如你有兴趣可翻翻源码,这里就不赘述了。
至此代理的几种方式已经使用都已描述完毕,如有不符,还望斧正。
上一篇: 我的购物车没钱满了
下一篇: C#使用log4net记录日志