静态代理模式

因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式,达到以上目的。
interface Hosee{
String sayhi();
}
class Hoseeimpl implements Hosee{
@Override
public String sayhi()
{
return "Welcome oschina hosee's blog";
}
}
class HoseeProxy implements Hosee{
Hosee h;
public HoseeProxy(Hosee h)
{
this.h = h;
}
@Override
public String sayhi()
{
System.out.println("I'm proxy!");
return h.sayhi();
}
}
public class StaticProxy
{
public static void main(String[] args)
{
Hoseeimpl h = new Hoseeimpl();
HoseeProxy hp = new HoseeProxy(h);
System.out.println(hp.sayhi());
}
}
1.1 静态代理的弊端
如果要想为多个类进行代理,则需要建立多个代理类,维护难度加大。
仔细想想,为什么静态代理会有这些问题,是因为代理在编译期就已经决定,如果代理哪个发生在运行期,这些问题解决起来就比较简单,所以动态代理的存在就很有必要了。
2.动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface HoseeDynamic
{
String sayhi();
}
class HoseeDynamicimpl implements HoseeDynamic
{
@Override
public String sayhi()
{
return "Welcome oschina hosee's blog";
}
}
class MyProxy implements InvocationHandler
{
Object obj;
public Object bind(Object obj)
{
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("I'm proxy!");
Object res = method.invoke(obj, args);
return res;
}
}
public class DynamicProxy
{
public static void main(String[] args)
{
MyProxy myproxy = new MyProxy();
HoseeDynamicimpl dynamicimpl = new HoseeDynamicimpl();
HoseeDynamic proxy = (HoseeDynamic)myproxy.bind(dynamicimpl);
System.out.println(proxy.sayhi());
}
}
类比静态代理,可以发现,代理类不需要实现原接口了,而是实现InvocationHandler。通过
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
来动态生成一个代理类,该类的类加载器与被代理类相同,实现的接口与被代理类相同。
通过上述方法生成的代理类相当于静态代理中的代理类。
这样就实现了在运行期才决定代理对象是怎么样的,解决了静态代理的弊端。
当动态生成的代理类调用方法时,会触发invoke方法,在invoke方法中可以对被代理类的方法进行增强。
通过动态代理可以很明显的看到它的好处,在使用静态代理时,如果不同接口的某些类想使用代理模式来实现相同的功能,将要实现多个代理类,但在动态代理中,只需要一个代理类就好了。
除了省去了编写代理类的工作量,动态代理实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中。
2.1 动态代理的弊端
代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。
而CGLIB则可以实现对类的动态代理
2.2 回调函数原理
上文说了,当动态生成的代理类调用方法时,会触发invoke方法。
很显然invoke方法并不是显示调用的,它是一个回调函数,那么回调函数是怎么被调用的呢?
上述动态代理的代码中,唯一不清晰的地方只有
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码、显示类加载等操作,前面的步骤并不是我们关注的重点,而最后它调用了
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
该方法用来完成生成字节码的动作,这个方法可以在运行时产生一个描述代理类的字节码byte[]数组。
在main函数中加入
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
加入这句代码后再次运行程序,磁盘中将会产生一个名为”$Proxy().class”的代理类Class文件,反编译(反编译工具我使用的是 JD-GUI )后可以看见如下代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements HoseeDynamic
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String sayhi()
throws
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("HoseeDynamic").getMethod("sayhi", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
可以在上述代码中看到,无论调用哪个方法,都会调用到InvocationHandler的invoke方法,只是参数不同。
2.3 动态代理与静态代理的区别
Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
可以实现AOP编程,这是静态代理无法实现的;
解耦,如果用在web业务下,可以实现数据层和业务层的分离。
动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题
3. CGLIB
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class CGlibHosee
{
public String sayhi()
{
return "Welcome oschina hosee's blog";
}
}
class CGlibHoseeProxy
{
Object obj;
public Object bind(final Object target)
{
this.obj = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(new MethodInterceptor()
{
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable
{
System.out.println("I'm proxy!");
Object res = method.invoke(target, args);
return res;
}
});
return enhancer.create();
}
}
public class CGlibProxy
{
public static void main(String[] args)
{
CGlibHosee cGlibHosee = new CGlibHosee();
CGlibHoseeProxy cGlibHoseeProxy = new CGlibHoseeProxy();
CGlibHosee proxy = (CGlibHosee) cGlibHoseeProxy.bind(cGlibHosee);
System.out.println(proxy.sayhi());
}
}
cglib需要指定父类和回调方法。当然cglib也可以与Java动态代理一样面向接口,因为本质是继承。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Java
# 动态代理与CGLIB
# 动态代理
# Java基础之动态代理Cglib详解
# 详解Java Cglib动态代理
# Java使用JDK与Cglib动态代理技术统一管理日志记录
# JAVA中的静态代理、动态代理以及CGLIB动态代理总结
# Java CGLib动态代理机制(全面解析)
# java 中动态代理(JDK
# cglib)实例代码
# 深入理解java动态代理的两种实现方式(JDK/Cglib)
# 浅谈Java代理(jdk静态代理、动态代理和cglib动态代理)
# java动态代理和cglib动态代理示例分享
# java动态代理(jdk与cglib)详细解析
# Java cglib动态代理原理分析
# 多个
# 回调
# 的是
# 实现了
# 可以使用
# 可以实现
# 来实现
# 这是
# 反编译
# 加载
# 会有
# 是因为
# 给你
# 有个
# 将会
# 子类
# 不需要
# 说了
# 是怎么
# 而来
相关文章:
如何快速搭建FTP站点实现文件共享?
如何在宝塔面板创建新站点?
如何快速搭建支持数据库操作的智能建站平台?
网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
如何零基础在云服务器搭建WordPress站点?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
如何使用Golang table-driven基准测试_多组数据测量函数效率
制作表格网站有哪些,线上表格怎么弄?
建站主机如何安装配置?新手必看操作指南
国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?
如何做静态网页,sublimetext3.0制作静态网页?
建站之星价格显示格式升级,你的预算足够吗?
盘锦网站制作公司,盘锦大洼有多少5G网站?
建站之星安装后如何自定义网站颜色与字体?
制作网站的模板软件,网站怎么建设?
如何在IIS7上新建站点并设置安全权限?
如何在阿里云通过域名搭建网站?
如何选择高性价比服务器搭建个人网站?
如何通过wdcp面板快速创建网站?
h5网站制作工具有哪些,h5页面制作工具有哪些?
建站之星安装步骤有哪些常见问题?
网站制作难吗安全吗,做一个网站需要多久时间?
在线制作视频的网站有哪些,电脑如何制作视频短片?
制作网站的软件免费下载,免费制作app哪个平台好?
制作电商网页,电商供应链怎么做?
家庭建站与云服务器建站,如何选择更优?
深圳网站制作的公司有哪些,dido官方网站?
Android使用GridView实现日历的简单功能
教学论文网站制作软件有哪些,写论文用什么软件
?
如何选购建站域名与空间?自助平台全解析
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
宝塔新建站点报错如何解决?
网站插件制作软件免费下载,网页视频怎么下到本地插件?
利用JavaScript实现拖拽改变元素大小
建站ABC备案流程中有哪些关键注意事项?
如何高效利用亚马逊云主机搭建企业网站?
c# 在高并发下使用反射发射(Reflection.Emit)的性能
如何获取开源自助建站系统免费下载链接?
如何通过服务器快速搭建网站?完整步骤解析
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
如何安全更换建站之星模板并保留数据?
常州自助建站工具推荐:低成本搭建与模板选择技巧
如何用VPS主机快速搭建个人网站?
音响网站制作视频教程,隆霸音响官方网站?
高防服务器:AI智能防御DDoS攻击与数据安全保障
如何在万网开始建站?分步指南解析
成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?
如何快速建站并高效导出源代码?
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
*请认真填写需求信息,我们会在24小时内与您取得联系。