代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。属于结构型模式。
在客户和目标对象间起中介作用,保护、增强目标对象,解耦,易扩展;但增加了系统复杂度,延长了处理时间。
在面向对象的编程之中,如果我们想要代理类和目标类可以实现相同的功能,有两种方式:
方式:目标类和代理类同时实现一个目标接口,在代理类对象中持有一个目标接口类型的对象的引用,在代理类的方法中调用该目标接口的方法。
目标接口 Persion.java
public interface Person {
void findHouse();
}
目标类 Myself.java
public class Myself implements Person {
public void findHouse() {
System.out.println("想租房");
}
}
代理类 Friend.java
public class Friend implements Person {
private Person person;
public Friend(Person person) {
this.person = person;
}
public void findHouse() {
System.out.println("我是朋友");
this.person.findHouse();
System.out.println("帮忙找到了");
}
}
测试类 FriendProxyTest.java
public class FriendProxyTest {
public static void main(String[] args) {
Friend friend = new Friend(new Myself());
friend.findHouse();
}
}
缺点:采用硬编码的方式,一个代理类只能为一个目标类服务,造成系统臃肿,且不符合开闭原则。
机制:利用反射在运行时创建代理类。
1. 目标类实现一个目标接口(必需)。
目标类 租客 Tenant.java
public class Tenant implements Person {
public void findHouse() {
System.out.println("租房要求");
}
}
2. 实现调用处理器接口InvocationHandler,声明目标接口类型的成员变量,重写invoke方法,在invoke方法中,反射调用成员变量的方法。
调用处理器类 房屋中介 JDKHouseAgent.java
public class JDKHouseAgent implements InvocationHandler {
private Person target;
public JDKHouseAgent (Person person) {
this.target = person;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target, args);
after();
return obj;
}
private void before() {
System.out.println("我是中介");
}
private void after() {
System.out.println("这是找到的房子,中介费X元");
}
}
3. 运行时,调用Proxy.newProxyInstance方法,指定目标类、目标接口数组、调用处理器,动态地创建一个代理类$proxy0的对象,它继承了Proxy类,实现了目标接口。
在调用$Proxy0代理类对象中的每一个方法时,在代码内部,都是直接调用了Proxy类持有的InvocationHandler成员的invoke方法,(此处InvocationHandler成员实际被指定为JDKHouseAgent类型),而invoke方法被我们在JDKHouseAgent类中重写了,它会根据传入的方法参数,反射调用目标类的目标方法。具体逻辑参见更下方的$Proxy0.class。?
测试类 JdkProxyTest.java
public class JdkProxyTest {
public static void main(String[] args) {
try {
Person target = new Tenant();
InvocationHandler handler = new JDKHouseAgent(target);
//生成代理类的实例,这一步骤也可写在JDKHouseAgent类中,包装为一个getInstance方法
Person obj = (Person) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //指定目标类加载器
target.getClass().getInterfaces(), //指定目标接口数组
handler); //指派调用处理器
System.out.println("obj的类型 :" + obj.getClass());
obj.findHouse(); //类型强转,才能调用到findHouse方法
//将运行时生成的代理类字节码输出到磁盘
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream fos = new FileOutputStream("$Proxy0.class");
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行时生成的字节码文件 $Proxy.class
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void findHouse() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.tmr.proxy.Person").getMethod("findHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m2 = Class.forName("java.lang.Object").getMethod("toString");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法生成代理类实例的逻辑:
Proxy类中的静态成员proxyClassCache,定义如下:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
它的两个构造参数,KeyFactory和ProxyClassFactory是Proxy类的静态内部类。
真正执行代理类生成的是ProxyClassFactory的apply方法,其生成字节码的位置是:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
CGLib(Code Generation Library)为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
通常可以使用JDK动态代理,但要代理的类没有实现接口或者为了更好的性能时,使用CGLib。
CGLib与JDK动态代理的区别:
(ASM是一个Java字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM可以直接产生二进制class文件,也可以在类被加载入JVM之前动态改变类行为。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。)
备注:当Bean实现了接口时,spring使用JDK动态代理,Bean未实现接口时,spring使用CGLib。可在配置文件中,强制使用CGLib。
<aop:aspectj-autoproxy proxy-target-class=true/>
CGLib的注意点:不能代理被final或者非public修饰的方法。
###FastClass实现机制
FastClass其实就是对Class对象进行特殊处理,根据方法签名(方法名+参数类型)得到索引值,根据索引值保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。
定义原类 Test.java
class Test {
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
生成Fast类 FastTest.java
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方法根据建好的索引,直接调用目标方法,避免了反射调用,从而提高效率。
###举例
1.定义业务逻辑。目标类 Customer.java
public class Customer {
public void findHouse() {
System.out.println("顾客想租房");
}
}
2.定义方法拦截器类,实现MethodInterceptor接口 CGLibHouseAgent.java
public class CGLibHouseAgent implements MethodInterceptor {
//利用Enhancer类生成代理类,相当于Proxy.newProxyInstance操作
public static Object getInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new CGLibHouseAgent());
return enhancer.create();
}
//Object为由CGLib动态生成的代理类实例
//Method为被代理的目标方法的引用
//Object[]为被代理的目标方法的参数列表
//MethodProxy为对目标方法的代理方法的引用
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("我是中介");
}
private void after() {
System.out.println("这是找到的房子,中介费X元");
}
}
3.运行时 测试类CGLibProxyTest.java
public class CGLibProxyTest {
public static void main(String[] args) {
try {
//将CGLib生成的class文件写到磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/usernameXXX/workspace/proxy/cglib_classes");
Customer obj = (Customer) CGLibHouseAgent.getInstance(Customer.class);
obj.findHouse();
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建代理类对象的流程
(备注:第3步设置回调,可以设置一个回调数组Callback[],里面可以包括拦截器CGLibHouseAgent、不处理NoOp.INSTANCE、FixedValue锁定返回固定值、懒加载LazyLoader。在第3步之后,还可设置回调过滤器CallbackFilter,与Callback[]配合,在CGLib回调时,对不同方法执行不同的回调逻辑,或者根本不执行任何操作。在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效。)
###CGLib字节码的生成
总共会生成3个class文件:
在代理类中,有两点需要注意:
代理类 Customer$$EnhancerByCGLIB$$9bb2c927.class
public class Customer$$EnhancerByCGLIB$$9bb2c927 extends Customer implements Factory {
//回调绑定标志,缺省值false。
private boolean CGLIB$BOUND;
//线程本地回调。
//由静态方法CGLIB$STATICHOOK1()初始化,由newInstance方法内部的CGLIB$SET_THREAD_CALLBACKS方法set。
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
//静态回调数组引用变量,由newInstance方法内部的CGLIB$SET_THREAD_CALLBACKS方法set。
private static final Callback[] CGLIB$STATIC_CALLBACKS;
//Enchaner传入的MethodInterceptor对象,由构造方法进行初始化,由CGLIB$BIND_CALLBACKS赋值。
private MethodInterceptor CGLIB$CALLBACK_0;
//findHouse方法的引用变量,由静态方法CGLIB$STATICHOOK1()初始化赋值。
private static final Method CGLIB$findHouse$0$Method;
//findHouse的代理方法的引用,由静态方法CGLIB$STATICHOOK1()初始化赋值。
private static final MethodProxy CGLIB$findHouse$0$Proxy;
//空参数列表,因为findHouse没有形参,由静态方法CGLIB$STATICHOOK1()初始化。
private static final Object[] CGLIB$emptyArgs;
/* ...省略... */
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//反射生成代理类对象
Class var0 = Class.forName("com.tmr.proxy.dynamic.cglibproxy.Customer$$EnhancerByCGLIB$$9bb2c927");
//实际类型是目标类
Class var1;
//反射获取目标类声明的所有方法
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
//反射获取目标类Customer声明的第0个方法,即findHouse方法
CGLIB$findHouse$0$Method = ReflectUtils.findMethods(
new String[]{"findHouse", "()V"},
(var1 = Class.forName("com.tmr.proxy.dynamic.cglibproxy.Customer")).getDeclaredMethods()
)[0];
CGLIB$findHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "findHouse", "CGLIB$findHouse$0");
/* ...省略... */
}
//会由methodProxy.invokeSuper调用的方法。
final void CGLIB$findHouse$0() {
super.findHouse();
}
//代理方法,绑定了回调
public final void findHouse() {
//获取回调,即我们自定义的CGLibHouseAgent类对象
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//若callback不为空,则调用MethodInterceptor类型成员对象的intercept()方法,我们重写增强了的。
//此处相当于intercept(代理类对象,目标方法,目标方法参数,代理方法)
var10000.intercept(this, CGLIB$findHouse$0$Method, CGLIB$emptyArgs, CGLIB$findHouse$0$Proxy);
} else {
//如果没有设置callback,则默认执行父类的方法
super.findHouse();
}
}
/* ...省略... */
//用于绑定回调,为成员变量CGLIB$CALLBACK_0赋值
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
Customer$$EnhancerByCGLIB$$9bb2c927 var1 = (Customer$$EnhancerByCGLIB$$9bb2c927)var0;
//判断绑定标志,首次调用该方法时,CGLIB$BOUND是缺省值false,能通过判断,进行回调绑定
//绑定后,CGLIB$BOUND变为true,之后就不会再进行重复绑定
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
//若没有set线程本地回调,则使用静态回调
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
//若静态回调为null,则没有回调,不为成员CGLIB$CALLBACK_0赋值
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
//构造方法
public Customer$$EnhancerByCGLIB$$9bb2c927() {
CGLIB$BIND_CALLBACKS(this);
}
//为线程本地回调CGLIB$THREAD_CALLBACKS赋值
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
//为静态回调CGLIB$THREAD_CALLBACKS赋值
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
/* ...省略... */
}
可以看到,目标方法findHouse的代理方法CGLIB$findHouse 0 0 0Proxy,是通过MethodProxy.create方法创建的。
分析一下方法代理类 MethodProxy.java
方法代理的作用就是指明class1.sig1方法的代理方法是class2.sig2
public class MethodProxy {
private Signature sig1; //目标方法签名,此处值为findHouse()V
private Signature sig2; //代理方法签名,此处值为CGLIB$findHouse$0()V
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;
//在代理类中调用时,create(目标类对象var1, 代理类对象var0, "()V", "findHouse", "CGLIB$findHouse$0")
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
//初始化fastClassInfo,静态内部类的双检锁单例
private void init() {
if (this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
}
}
}
}
//根据CreateInfo动态生成FastClass对象
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
/* ...省略... */
//调用目标类的方法
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
//调用代理类的方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
private static class CreateInfo {
Class c1; //create时传入的var1,实际类型是目标类Customer
Class c2; //create时传入的var0,实际类型是代理类Customer$$EnhancerByCGLIB$$9bb2c927
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
private static class FastClassInfo {
//目标类的FastClass对象,实际类型是Customer$$FastClassByCGLIB$$1809976e
FastClass f1;
//代理类的FastClass对象,实际类型是Customer$$EnhancerByCGLIB$$9bb2c927$$FastClassByCGLIB$$280b972c
FastClass f2;
int i1; //目标类的目标方法的索引 i1 = f1.getIndex("findHouse()V")
int i2; //代理类的代理方法的索引 i2 = f2.getIndex("CGLIB$findHouse$0()V")
private FastClassInfo() {}
}
}
可以看见,MethodProxy类中,有2个静态内部类CreateInfo和FastClassInfo:
(静态内部类是lazyInit的,CreateInfo在MethodProxy的构造函数中被创建,而FastClassInfo是调用invoke或invokeSuper中的init方法时才创建,并且用了单例模式。)
把intercept方法中的invokeSuper改成invoke会怎么样:死循环,OutOfMemory。
首先明确,invokeSuper方法和invoke方法的两个形参,obj和args,分别是传入intercept方法的代理类实例和目标方法参数。
我们理一下执行流程:
总结 MethodProxy和FastClass结合使用:
不然在后面会报错日期不能为空!
8种机械键盘轴体对比本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?关于Gin的基本使用方法可移步Gin中文文档本文以个人实际工程经验为基础,介绍了一种简易但实用的Web服务端构建方式,demo源码见文末。包含状态的HTTPServer封装要起一个Web服务,首先需要有一个server监听接口并接收http请求。Go在net/http包中有内置的http.server,结构包含了监听端口...
计算机领域中的熔断机制诗书塞外 Python程序员这几天股市好热闹啊, 开年一周N次熔断, 把证监会吓到了, 急忙中止了熔断机制.熔断机制本身其实并不坏, 在计算机的世界里, 也大量地使用了熔断机制.在操作系统中, 某一个程序长时间占用CPU(俗称"卡死"), 就会触发操作系统的熔断机制. 这时操作系统就会弹出一个对话框, 让你选择等待还是直接停止运行.微信中, 一篇文章阅读过于火爆, 阅读人数...
如何让子元素滚动到指定父窗口的指定位置什么是滚动距离读取滚动距离如何设置滚动scrollTop属性scrollTo方法什么是滚动距离比如父元素设置了overflow: hidden;, 当元素里的内容超过元素本身的高度时, 就会出现滚动条, 那么鼠标滑动的距离就是其滚动距离.读取滚动距离<head> <title>test</title> <style> ul { width: 200px;
错误信息:ERROR: Could not find a version that satisfies the requirement scikit-image (from versions: none)ERROR: No matching distribution found for scikit-image原因:一般是网络的问题,需要使用国内的镜像源来加速解决办法pip install scikit-image -i http://pypi.douban.com/simple/ --
在VISTA,Win7,Server 2008操作系统里建立SQL报表时使用reportview访问报表时提示下面错误: 为用户“NT AUTHORITY/NETWORK SERVICE”授予的权限不足,无法执行此操作。 (rsAccessDenied)解决方案:1,在IIS里访问http://localhost/Reports,在属性项里点击“新建角色分配”,在“组或用户名”填入
A. Berzerktime limit per test4 secondsmemory limit per test256 megabytesinputstandard inputoutputstandard outputRick and Morty are playing their own version
"APK文件如何查看源代码":关键词:apk 文件 如何 查看 源代码今天在网上找到一个有效查看apk源代码的方法,经验证确实可行,拿来与大家分享。apk文件其实也是打的压缩包,只是class文件被编译为dex文件,我们很难将其打开来阅读,接下来各位跟着我做便能把这厮变成我们可见的摸样。首先把apk文件后缀改为zip,让后将其解压,在得到的解压文
Python设计模式什么是设计模式设计模式是前辈们对开发经验的总结,是解决特定问题的一系列套路,它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。设计模式分类三类:1)创建型模式:单例、工厂、抽象工厂、建造者、原型2)结构型型模式:适配器、桥接、装饰器、组合、外观、享元、代理3)行为型模式:模板方法、命令、迭代器、观察者、中介、备忘录、解释器、状态、策略、...
异常java.security.InvalidKeyException:illegal Key Size 也就是echostr校验失败,请您检查是否正确解密并输出明文echostr这个错误企业微信登陆地址http://qy.weixin.qq.com/配置成功以后官方回调页面的代码?123456
2019 年 6 月 25 日,在 KubeCon China 2019,全球知名开源组织云原生计算基金会 CNCF 宣布,蚂蚁金服正式成为 CNCF 黄金会员,蚂蚁金服表示将持续加大对开源项目的支持,包括 Kubernetes,Service Mesh,Serverless,安全容器等方向,并发挥自己的力量。在本次大会,蚂蚁金服也与数百名云原生爱好者用五个小时搭建了一个云原生的电商平台,具体...
qt=[1,1, 2,2, 2, 3,3,3,3, 4, 4, 5, 5, 6,6,6,6,6,6]qt=[1,1,0, 2,2, 2, 3]qts=[]count=1maxcount=[]maxcount.append(count)ind=[10,11,12,13,14,15,16]inds=[]for i in range(len(qt)-1): if q...