java代理通俗简单解析
1 代理
1.1 代理的概念和作用
代理的概念很好理解,就像黄牛代替票务公司给你提供票,经纪人代理艺人和别人谈合作。Java的代理是指实现类作为代理类的属性对象,代理类提供方法给外部调用,代理类内部再去调用实现类的方法,实现具体的业务。也就是代理类作为对外接口人,实现类不直接对外。这就是java代理的概念。
代理的作用是当你需要增加一些而外的操作,而又不想去修改实现类的。可以通过代理来实现,在代理类中增加附件的操作。例如需要增加权限过滤,但是业务类已经开发好,不想将权限和业务混在一起,想让每个类的功能尽可能单一,各司其职。,此时我们就可以做一个该类的代理类,由该代理类做权限判断,如果安全则调用实际类的业务开始处理。
代理分为静态代理和动态代理,动态代理有分为jdk动态代理和cglib动态代理。
1.2 静态代理
先定义一个接口Subject,然后实现类RealSubject和代理类Proxy都继承这个接口,实现接口的方法。代理类构造函数的形参是接口引用Subject subject,实现类对象作为代理类构造函数的实参传入,保存到代理类的接口类型的Subject属性中。用户调用代理类的方法,代理类方法内部通过接口引用再去调用实现类的方法。
//定义接口
interface Subject {
void request();
}
//实现类实现接口
class RealSubject implements Subject {
public void request(){
System.out.println("RealSubject");
}
}
//代理类实现接口
class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){//代理构造函数接口形参,以实现类对象为实参
this.subject = subject;
}
public void request(){
System.out.println("begin");
subject.request();//调用实现类的方法
System.out.println("end");
}
}
//调用实例
public class ProxyTest {
public static void main(String args[]) {
RealSubject subject = new RealSubject();//创建实现类对象
Proxy p = new Proxy(subject);//创建代理类对象
p.request();//调用代理类的方法,内部再去调用实现类的request方法
}
}
1.3 Jdk动态代理
1.3.1 Jdk动态代理实现
Jdk动态代理实际上是利用了java的反射机制。利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。将实现类的接口信息传入到代理类的静态构造函数Proxy.newProxyInstance中,代理类的构造函数内部通过反射的机制,获取实现类的方法,代理类的代理方法对实现类的方法进行了包装。调用代理类的方法,内部调用实现类的方法。实际上静态代理和动态代理本质上是一样的,只是动态代理利用的反射的机制获取实现类的方法。
(1)//定义实现类继承的接口
public interface Service {
//目标方法
public abstract void add();
}
(2)//实现类并继承接口,实现方法add
public class UserServiceImpl implements Service {
public void add() {
System.out.println("This is add service");
}
}
(3)利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。重写InvocationHandler的invoke方法。
class MyInvocatioHandler implements InvocationHandler {
private Object target;//接口属性
public MyInvocatioHandler(Object target) {//构造函数,实参实现类对象
this.target = target;
}
//重写代理类InvocationHandler的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before-----");
Object result = method.invoke(target, args);
System.out.println("-----end-----");
return result;
}
// 生成代理对象方法
public Object getProxy() {
//获取类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//获取实现类的方法接口,提供反射机制的方法名称
Class<?>[] interfaces = target.getClass().getInterfaces();
//调用Proxy的静态方法创建代理对象
return Proxy.newProxyInstance(loader, interfaces, this);
}
}
(4)使用动态代理实例
public class ProxyTest {
public static void main(String[] args) {
//创建实现类对象
Service service = new UserServiceImpl();
//实现类对象以实参形式传入代理类,保存在tartget属性中
MyInvocatioHandler handler = new MyInvocatioHandler(service);
//获取代理对象
Service serviceProxy = (Service)handler.getProxy();
//通过代理对象调用方法
serviceProxy.add();//执行方法
}
}
执行结果:
-----before-----
This is add service
-----end-----
1.3.2 Jdk动态代理反推过程
从步骤(4)开始在逆向推到一遍。先创建一个实现类对象service,作为入参传给 new MyInvocatioHandler(service);构造函数,保存在target属性中。通过getProxy()方法创建代理对象。Proxy.newProxyInstance(loader, interfaces, this);入参分别是loader加载类对象,interfaces实现类对象target的方法信息,this是MyInvocatioHandler指针。在newProxyInstance函数内部通过反射机制,根据interfaces传入的方法名称等信息,获取实现类的方法method。同时代理类内部对实现类的每个方法都对应实现了代理方法。serviceProxy.add();调用了代理类的代理方法,代理方法内部通过调用MyInvocatioHandler的invoke方法,返回invoke(Object proxy, Method method, Object[] args)去调用实现类的Method。在invoke方法里面就可以增加其他的操作,比如说日志打印;其中Method参数是通过反射机制,根据interfaces信息获取到的实现类的方法。和静态映射的原理差不多,只是将代理类进行了包装。
1.3.3 jdk代理类源码解析
为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。
byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());
FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");
out.write(classFile);
out.flush();
使用 反编译工具 jad jad com.sun.proxy.$Proxy.1
看看代理类如何实现,反编译出来的java代码如下:
//代理类继承extends类,和实现类一样也实现了Service接口
public final class $proxy1 extends Proxy implements Service {
//构造函数
public $proxy1(InvocationHandler invocationhandler) {
super(invocationhandler);
}
//代理类的代理方法
public final boolean equals(Object obj) {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//这里调用的是实现类add代理方法
public final void add() {
try {
//内部调用的是MyInvocatioHandler的invoke方法
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//定义静态Method变量
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//通过反射机制和interfaces提供的信息获取实现类的方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
//通过反射机制获取实现类的方法
m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
1.4 不用实现接口的Cglib代理
1.4.1 Cglib代理的简单实现
静态代理和jdk动态代理都需要通过实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性。使用cglib[Code Generation Library]实现动态代理,并不要求实现类必须实现接口。
使用步骤
(1)先定义实现类,实现方法add
public class UserServiceImpl {
public void add() {
System.out.println("This is add service");
}
}
(2)实现MyMethodInterceptor实现MethodInterceptor接口,定义方法的拦截器intercept方法,该对象会被传入代理类中,相当于回调函数的作用,代理类内部调用intercept方法,intercept方法的四个参数:1)代理对象;2)实现类方法;3)方法参数;4)代理方法的MethodProxy对象。
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("Before:" + method);
Object object = proxy.invokeSuper(obj, arg);
System.out.println("After:" + method);
return object;
}
}
(3)利用Enhancer类生成代理类;创建Enhancer enhancer对象,将实现类作为入参传入enhancer,作为代理类的父类。设置拦截器MyMethodInterceptor 到enhancer内部,作为回调。
Enhancer enhancer = new Enhancer();//创建Enhancer类对象
enhancer.setSuperclass(UserServiceImpl.class); //传入实现类的class,为反射提供信息 ,同时实现类是Enhancer的父类
enhancer.setCallback(new MyMethodInterceptor()); //设置拦截器对象,通过拦截器回调。Enhancer是CGLib的字节码增强器,可以方便的对类进行扩展,内部调用GeneratorStrategy.generate方法生成代理类。传入了实现类作为父类,传入MyMethodInterceptor对象,作为拦截回调。
UserServiceImpl userService = (UserServiceImpl)enhancer.create();//创建代理类对象,因为代理类继承了实现类,所以可以用父类引用接收子类对象。
(4)userService.add();方法的执行输出结果。
Before: add
This is add service
After: add
1.4.2 代理类反向编译源码解析
对(UserServiceImpl)enhancer.create();返回的代理类进行反向编译,得到代理类的源码如下。
这样可能还是不能理解,代理类内部是怎么实现的;
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
//
// Decompiled by Procyon v0.5.30
//
//(1)enhancer代码生成器自动生成了类UserService$$EnhancerByCGLIB$$394dddeb,继承了实现类UserService,实现了接口Factory。
public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;//这里保存步骤(4)中通过enhancer.setCallback(new MyMethodInterceptor());传入的对象
private static final Method CGLIB$add$0$Method;
private static final MethodProxy CGLIB$add$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
static void CGLIB$STATICHOOK2() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//通过反射机制获取代理类
final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb");
final Class<?> forName3;
//查找到实现类的add方法
CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0];
//创建MethodProxy对象,每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法
CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");//将代理类的两个代理方法传入
}
//最终调用的方法是父类,也就是实现类的方法add
final void CGLIB$add$0() {
super.add();
}
public final void add() {
MethodInterceptor cglib$CALLBACK_0;
cglib$CALLBACK_0=this. CGLIB$CALLBACK_0;
//先判断拦截器对象MethodInterceptor是否为空,不为空则执行回调intercept;
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_0.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy);
return;
}
super.add();
}
static {
CGLIB$STATICHOOK2();
}
}
1.4.3 Cglib代理的整体流程
详细步骤如下
(1) 定义实现类UserServiceImpl和拦截器MyMethodInterceptor,拦截器实现intercept方法;
(2) 创建Enhancer enhancer对象,通过enhancer.setSuperclass(UserServiceImpl.class); 传入实现类的class,作为代理类的父类。通过enhancer.setCallback(new MyMethodInterceptor()); //设置拦截器对象,保存在代理类的cglib$CALLBACK_0属性中。提供回调代理类会调用MyMethodInterceptor的函数intercept函数返回调用;
(3) Enhancer是CGLib的字节码增强器,生成代理类的代码,代理类继承了实现类UserServiceImpl,代理类实现了两个代理方法final void CGLIB$add$0()和public final void add()方法;这里不知道为什么要这样设计。CGLIB$add$0()是最终调用的方法;
(4) 创建代理类对象UserServiceImpl userService = (UserServiceImpl)enhancer.create();userService.add();调用代理类的在步骤三中的public final void add();方法;先判断cglib$CALLBACK_0对象是否为空。不为空,则执行MethodInterceptor的回调函数cglib$CALLBACK_0.intercept(Object obj, Method method, Object[] arg, MethodProxy proxy);
(5) Intercept回调函数实现实际上调用了参数MethodProxy proxy的proxy.invokeSuper方法;
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("Before:" + method);
Object object = proxy.invokeSuper(obj, arg);
System.out.println("After:" + method);
return object;
}
(6) proxy又是从哪里来的呢,步骤(4)中传入的实参是这样实现的
//创建MethodProxy对象,每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法
CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");//将代理类的两个代理方法传入
(7) 每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法proxy.invokeSuper的内部的实现如下
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
实现方法中创建了一个FastClassInfo fci对象,然后调用了fci.f2.invoke(fci.i2, obj, args);
(8)单看invokeSuper方法的实现,似乎看不出实现类add方法调用,在MethodProxy实现中,通过FastClassInfo维护了实现类和代理类的FastClass。
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}
以add方法的methodProxy为例,f1指向实现类对象,f2指向代理类对象,i1和i2分别是方法add和CGLIB$add$0在对象中索引位置。那么fci.f2.invoke(fci.i2, obj, args);的含义就是调用代理对象的invoke方法,传入参数是代理类的实现方法CGLIB$add$0、代理对象、函数参数;
(9)这样有回到了代理类中的方法CGLIB$add$0()
final void CGLIB$add$0() {
super.add();
}
(10)super是父类的意思,指向的是实现类,然后执行实现类的add方法。绕了一个好大的圈终于绕回来了。不知道什么原因要这样搞。
1.4.4 FastClass实现机制
FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。
1、定义原类
class Test {
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
2、定义Fast类
class FastTest {
public int getIndex(String signature){
switch(signature.hashCode()){
case 3078479:
return 1;
case 3108270:
return 2;
}
return -1;
}
public Object invoke(int index, Object o, Object[] ol){
Test t = (Test) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
}
在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB$add$0方法,CGLIB$add$0直接调用了实现类的add方法。
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
https://www.cnblogs.com/bclshuai/p/11380657.html
百度云盘下载地址:
链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg
提取码:mc8l
微信公众号获取最新的软件和视频介绍
QStockView
java代理通俗简单解析的更多相关文章
- java正则API简单解析
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 1.简介: java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. ...
- java枚举通俗实例解析
1 枚举 1.1 枚举的作用 我们经常要定义一些常量值,例如日期(MONDAY,TUESDAY……)或者错误码等.可以将常量组织起来,统一进行管理.常量比较只是值 ...
- Java使用Jsoup简单解析页面
jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址.HTML 文本内容.它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出 ...
- 使用 java 实现一个简单的 markdown 语法解析器
1. 什么是 markdown Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的 ...
- [ 转载 ] Java基础10--关于Object类下所有方法的简单解析
关于Object类下所有方法的简单解析 类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直 ...
- java代理的深入浅出(一)-Proxy
java代理的深入浅出(一)-Proxy 1.什么是代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事 ...
- Java代理:静态代理、动态代理
要理解动态代理,需要先理解反射(http://www.cnblogs.com/Donnnnnn/p/7729443.html) 通俗理解: 在很多底层框架中都会用得到,比如struts,Spring等 ...
- 沉淀再出发:java中线程池解析
沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...
- java代理,手把手交你写java代理
一:常用的java代理模式 一般经常做java开发的知道java的代理模式一共有三种,第一种也就是静态代理,这种用法比较简单,没有什么魔法棒,比较好理解,另外两种分别是JDK代理和cglib代理,他们 ...
随机推荐
- Signalr实时通讯
我们直接来干货~~~~~~觉得好推荐一下哈 研究不易 参考--https://www.jb51.net/article/133202.htm 这是基本教程 下面是重点: 如果你想允许跨域 具体代码 ...
- 关于asp.net中链接数据库的问题
学习了asp.net 有web服务器控件和C#代码两部分 那么在做页面时候,需要用到数据库和asp.net的链接 课本上只是说明了和SQL server的链接,本文介绍如何在.net中链接 Acces ...
- mac下mysql安装及配置启动
---恢复内容开始--- 原始链接:https://segmentfault.com/q/1010000000475470 按照如下方法成功安装并启动: mysql.server start//启动服 ...
- 微信中H5网页如何唤醒打开外部浏览器打开指定链接
最近遇到一个需求.朋友找我制作一个在微信中的聊天框,或者公众号菜单发布一条链接或者二维码.跳出微信打开一个指定的我们自己的页面, 拿到这个需求后我们团队分开去找资料研究方案,通过微信的开发文档.腾讯的 ...
- XML文件的读取
<?xml version="1.0" encoding="gbk"?> <!--设置编码格式为gbk--> <!DOCTYPE ...
- MacBook Pro App Store无法下载和更新软件解决方案
环境:MacBook Pro 2017 系统:MacOS 10.14 问题:在App Store中无法下载和更新软件,输入App Store密码后,弹出如下对话框 咨询苹果官方技术支持电话:400-6 ...
- 淘宝客订单api处理优化
首选我们看看api定义: http://open.taobao.com/api.htm?docId=38078&docType=2&scopeId=14474 注意下span这个参数 ...
- postgresql 自定义函数
CREATE OR REPLACE FUNCTION "public"."pm25_aqi"("pm25_nd" numeric) RETU ...
- 用vue脚手架创建bootstrap-vue项目
用vue脚手架创建bootstrap-vue项目 框架的地址:https://bootstrap-vue.js.org/docs/ 第一步 vue init webpack demo第二步 cd de ...
- 画多边形form并填充背景色(可以实现圆角边框 有锯齿)
public Form1() { InitializeComponent(); this.BackColor = ColorTranslator.FromHtml("#F7F1F1" ...