代理模式
什么是代理模式?
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
举例:
假如小明要买房子,找房子是一件麻烦的事,小明不想这么麻烦,就把这些繁琐的事情委托给一个中介,中介代替小明去找房源,去做房子过户等等一系列事情。
为什么要用代理模式?
- **中介隔离作用:**在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- **开闭原则,增加功能:**代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
- **降低耦合度:**代理对象可以将客户端和目标对象分割开,降低系统的耦合度。
代理模式有哪些缺点?
- 降低系统处理能力: 代理模式在客户端和目标对象之间加啦一个代理类,增加系统处理时间,降低请求处理速度。
- 增肌复杂度: 增加系统负责度
代理模式分为几种?
- 代理模式分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
静态代理
静态代理一般涉及的角色:
抽象对象: 通过接口类或者抽象对象去声明具体对象和代理对象要实现的方法。
代理对象: 代理对象内部含有对真实对象的引用,以达到可以操作真实对象。同时代理对象提供和真实对象的接口给外部,以便任何时候都可以代理真实的对象。同时,代理对象可以在执行真实对象的时候,添加一些额外的操作,相当于对真实对象的封装。
真实对象: 代理对象所代理的真实对象,是我们最终要引用的对象。
具体实现方法
抽象方法 :
package com.common.proxy;
public interface FindHouse {
void findHouse();
}
**真实对象: **
package com.common.proxy;
public class RealFindHouse implements FindHouse{
@Override
public void findHouse() {
System.out.println("开始找房子。。。");
}
}
代理对象:
package com.common.proxy;
public class ProxyFindHouse implements FindHouse{
private static RealFindHouse realFindHouse = null;
@Override
public void findHouse() {
preFindHouse();
if(realFindHouse==null){
realFindHouse = new RealFindHouse();
}
realFindHouse.findHouse();
afterFindHouse();
}
private void preFindHouse(){
System.out.println("找房前准备。。。");
}
private void afterFindHouse(){
System.out.println("找到房子后装修。。。");
}
}
测试类:
package com.common.proxy;
/**
* auth songboliang
* 静态代理测试类
*/
public class ProxyTest {
public static void main(String args[]){
ProxyFindHouse proxyFindHouse=new ProxyFindHouse();
proxyFindHouse.findHouse();
}
}
运行结果:
找房前准备。。。
开始找房子。。。
找到房子后装修。。。
总结:
优点: 可以在不修改目标对象的功能上,进行扩展修改。
缺点: 每一个代理类都必须实现一个委托对象,如果委托对象增加,我们代理类也要随之增加改变,这样我们代理就变的十分臃肿,难以胜任。
动态代理
动态代理是指在运行时动态生成代理类。即代理类的字节码将在运行时生成并载入当前代理的ClassLoader。与静态代理类相比,动态代理具有以下优点:
-
首先,不需要为真实主题谢一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很玛法。如果接口有变动,则真实主题和代理对象都要修改。不利于系统维护。
-
其次,使用一些动态代理的生成方法甚至可以在运行时指定代理类的执行逻辑,从而大大提升系统的灵活性。
动态代理类使用字节码动态生成加载技术,在运行时生成加载类。生成动态代理类的方法很多,如,JDK自带的动态代理、CGLib、Javassist或者ASM库。
- JDK的动态代理使用简单,它内置在JDK中,因此不需要引入第三方包,单相对功能比较弱。
- CGLIB和Javassist都是高级的字节码生成库。总体性能比JDK自带的动态代理好,而且功能强大。
CGLIB引入包依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
- ASM是低级的字节码生成工具,使用ASM已经近乎于在使用Java bytecode编程,对开发人员要求最高,当然,也是性能最好的一种动态代理生成工具。但ASM的使用很繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的维护性较差,如果不是在对性能有苛刻要求的场所,还是推荐CGLIB后者Javassist.
JDK动态代理
动态代理解决静态代理类接口过多的问题,通过反射来实现,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。
设计步骤:
- 编写一个委托类的接口,即静态代理的(FindHouse接口)
- 实现一个真正的委托类,即静态代理的(RealFindHouse类)
- 创建一个动态代理类,实现InvocationHandler接口,并重写invoke方法
- 在测试类中,生成动态代理的对象。
代码开发:
委托接口
/**
*
* 委托类接口
*
*/
public interface FindHouse {
void findHouse();
}
委托类:
/**
*
* 委托类,实现委托接口方法
*
*/
public class RealFindHouse implements FindHouse {
@Override
public void findHouse() {
System.out.println("要找一个海景房");
}
}
动态代理类:
/**
* JDK动态代理类
*/
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
/**
* 可以对真实方法做一些扩展
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result= method.invoke(object, args);
afterRequest();
return result;
}
protected void preRequest(){
System.out.println("找房前的一些准备工作。。。");
}
protected void afterRequest(){
System.out.println("找房子后的一次处理工作。。。");
}
}
测试类:
生成动态代理类的关键方法是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler handler)方法,该方法会根据指定的参数动态创建代理对象。三个参数的意义如下:
/**
*loader 指定代理对象的类加载器
*interfaces 代理对象要实现的接口,这里可以指定多个接口
*h 方法调用的实际处理者,代理对象的方法调用都会转到这里
*/
newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都
会转发给InvocationHandler.invoke()方法,利用拦截器(拦截器必须实现
InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调
用InvokeHandler来处理。
public class Test {
public static void main(String args[]){
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
FindHouse realSubject = new RealFindHouse();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
FindHouse subject = (FindHouse) Proxy.newProxyInstance(loader,new Class[]{FindHouse.class},proxy);
subject.findHouse();
}
}
测试结果:
找房前的一些准备工作。。。
要找一个海景房。。。
找房子后的一次处理工作。。。
生成动态代理类的源码分析:
classLoader 的作用是将字节码文件加载进虚拟机并生成相应的 class, interfaces 就是
被实现的那些接口, h 就是 InvocationHandler
public class Proxy implements java.io.Serializable {
......
/**
*loader 指定代理对象的类加载器
*interfaces 代理对象要实现的接口,这里可以指定多个接口
*h 方法调用的实际处理者,代理对象的方法调用都会转到这里
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
/*
*准备一份被实现的业务接口
*/
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 代理类生成的核心代码
*/
Class<?> cl = getProxyClass0(loader, intfs);
...
}
......
}
这里只是用WeakCache做了proxy classes缓存,通过KeyFactory和ProxyClassFactory作
为WeakCache的键和值,它们都是Proxy的内部静态类,都实现了BiFunction接口。
public class Proxy implements java.io.Serializable {
...
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
/*
* 代理接口数量限制
*/
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
/*
*如果代理类由实现的给定加载器定义给定的接口存在,这将简单地返回缓存的副本;否则,它将通过 ProxyClassFactory创建代理类
*这里的意思是如果有代理类,直接返回缓存的代理对象,如果没有代理类,则通过ProxyClassFactory创建代理类
*/
return proxyClassCache.get(loader, interfaces);
}
...
}
继续查看WeakCache的get方法可看到,proxy class的生成最终调用了ProxyClassFactory
的apply方法。
final class WeakCache<K, P, V> {
...
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
...
//创建子键并从valuesMap检索可能的供应商存储的子键
//代理类生成的主要方法apply();
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
}
...
}
重点看 ProxyClassFactory的定义和appy方法的实现:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
//所有代理类的前缀都加$Proxy
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
...
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
*确认类加载器解析了这个接口的名字到相同的Class对象。
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
*代理类实现的必须是接口,如果不是接口就直接抛异常,JDK的劣势地方
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 接口是否重复,传的接口数组有没有传多个同样的接口类
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
// 定义的proxy class类所在的包
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 记录non-public接口的包(例如private、protect),以便在同一个包中定义代理类。验
* 证所有非公共代理接口都在同一个包中
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// 如果没有non-public proxy接口,默认包路径是com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 选择一个名称去生成代理类
*/
long num = nextUniqueNumber.getAndIncrement();
//代理类的名字是com.sun.proxy.$proxy1
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
*生成指定的代理类
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
...
}
生成代理类的方法:
public class ProxyGenerator {
...
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
//获取proxy class文件
final byte[] var4 = var3.generateClassFile();
//保存
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
//私有构造器
private ProxyGenerator(String var1, Class<?>[] var2, int var3) {
this.className = var1;
this.interfaces = var2;
this.accessFlags = var3;
}
...
}
private byte[] generateClassFile() {
//添加从Object基类里添加的一些方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//添加接口中的方法并实现
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
生成的$proxy.class:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.common.proxy.FindHouse;
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 FindHouse {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
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 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);
}
}
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);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.common.proxy.FindHouse").getMethod("findHouse");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
现在再看生成的$Proxy0.class反编译的java代码中,调用request时会super.h.invoke(this,
m3, (Object[])null); 调用Proxy类中InvocationHandler的invoke方法 .
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"D:\class"); --该设置用于输出cglib动态代理产生的类
总结
newProxyInstance()通过反射生成含有接口方法的proxy class(继承了Proxy类,实现了
需要代理的接口),因此对该对象的所有方法调用都会转发InvocationHandler.invoke()
方法.
现在再看生成的$Proxy0.class反编译的java代码中,调用request时会super.h.invoke(this,
m3, (Object[])null); 调用Proxy类中InvocationHandler的invoke方法 .
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"D:\class"); --该设置用于输出cglib动态代理产生的类
总结
newProxyInstance()通过反射生成含有接口方法的proxy class(继承了Proxy类,实现了
需要代理的接口),因此对该对象的所有方法调用都会转发InvocationHandler.invoke()
方法.