深入解析代理模式的设计模式
# 七大软件设计原则
# 设计模式-工厂模式
# 设计模式-单例模式
# 设计模式-原型模式
# 设计模式-策略模式
# 设计模式-责任链模式
# 设计模式-建造者模式
代理模式(Proxy Pattern) 是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。代理模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式。
代理模式的通用UML类图分析
主要包含以下三种角色:
- 抽象主题角色(Subject):主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象类。
- 真实主题角色(RealSubject):该类也被称为被代理类,该类定义的代理所表示的真实对象,是负责执行系统真正的逻辑业务对象。
- 代理主题角色(Proxy):也被称为代理类,其内部持有RealSubject的引用,因此具备完全的对RealSubject的代理权,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理代码
在代码中一般代理会被理解为代码增强,实际上就是在执行方法的前后添加一段逻辑,而调用这是不知道的,代理还被分为静态代理和动态代理
代理模式的通用写法
Subject:
public interface Subject {
void request();
}
RealSubject:
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("原始类处理请求");
}
}
Proxy:
public class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject) {
this.subject = subject;
}
@Override
public void request() {
before();
this.subject.request();
after();
}
private void before(){
System.out.println("处理请求之前");
}
private void after(){
System.out.println("处理请求之后");
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.request();
}
}
静态代理
这种代理方式需要代理对象和目标对象实现一样的接口,且代理类的代码是在运行之前就需要写好的 优点是:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点也很明显
- 如果接口需要增加一个方法对应的代理类和被代理类都需要添加新的实现。
- 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
代码示例
比如买房,通常卖家只有房源,而卖家一般是没有房源的所以需要找到中介提供房源然后卖房。其中卖房的人就是被代理类,中介相当于代理类。
public interface IPerson {
void sellHouse();
}
public class Seller implements IPerson{
@Override
public void sellHouse() {
System.out.println("我要卖房");
}
}
public class Intermediary implements IPerson {
private final IPerson person;
public Intermediary(IPerson person){
this.person = person;
}
@Override
public void sellHouse() {
before();
person.sellHouse();
after();
}
private void before(){
System.out.println("发布房源");
}
private void after(){
System.out.println("交易完成");
}
}
动态代理
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理(也可以使用cglib动态生成代理对象)
基于JDK实现动态代理
代码示例
还是上述的例子我们用JDK动态代理来实现(接口以及卖家代码都不变,新增代理处理类)
public class JdkIntermediary implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class<?> clz = target.getClass();
return (IPerson)Proxy.newProxyInstance(clz.getClassLoader(),clz.getInterfaces(),this);
}
private void before(){
System.out.println("发布房源");
}
private void after(){
System.out.println("交易完成");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target, args);
after();
return result;
}
}
public class Test {
public static void main(String[] args) {
IPerson person = new Seller();
JdkIntermediary jdkIntermediary = new JdkIntermediary();
IPerson instance = jdkIntermediary.getInstance(person);
instance.sellHouse();
}
}
看上去可能有些人说JdkIntermediary
不就是代理类吗不也是在运行之前生成的吗,但是其实JdkIntermediary
并不是代理类,它可以理解成代理处理程序我们可以断点看一下
真正的代理类是$Proxy0
这个类是在运行时生成的。
优点:接口中新增方法不需要再像静态代理来一样每个方法都实现一遍了,维护比较方便
缺点:被代理类必须实现接口
自己实现JDK动态代理
JDK实现动态代理的原理其实很简单,就是采用字节重组重新生成对象来代替原始对象,以达到动态代理的目的,这里我们可以使用ProxyGenerator
取到代理对象class文件的字节码我们可以输出到磁盘然后通过反编译查看对应的代码如下:
public class Test {
public static void main(String[] args) {
IPerson person = new Seller();
JdkIntermediary jdkIntermediary = new JdkIntermediary();
IPerson instance = jdkIntermediary.getInstance(person);
instance.sellHouse();
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
try {
FileOutputStream fileOutputStream = new FileOutputStream("/Users/xzkj/Desktop/java_study/$Proxy0.class");
fileOutputStream.write($Proxy0s);
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import com.example.demo.proxy.demo.IPerson;
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 IPerson {
private static Method m1;
private static Method m2;
private static Method m3;
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 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 void sellHouse() 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);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.example.demo.proxy.demo.IPerson").getMethod("sellHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
其中h
就是实现 InvocationHandler
的代理处理类,可以发现这里就是调用的处理类的 invoke
方法,其中 m3
就是对应的方法
发现其实底层也是实现了接口的所有方法,只不过这块实现的代码不再是我们自己书写而是JDK帮我们实现
总结一下大致的不步骤如下:
- 获取被代理对象的引用,并且获取它的所有的接口(反射获取)
- JDK动态代理类重新成成一个新的类,同时新的类要实现被代理类实现的所有接口
- 动态生成java代码,新加的业务逻辑方法由一定的逻辑代码调用
- 编译
- 加载到JVM中运行
首先InvocationHandler
肯定是要的我们可以自己实现一个这个类就叫TDInvocationHandler
具体代码如下:
public interface GPInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
就是定义一个接口,当然这里也可以使用原来的InvocationHandler
类
还有一个就是需要自定义ClassLoader
的类需要重写 findClass
方法应为 loadClass
方法中如果没有加载到jvm的类会调用 findClass
方法查找,而JDK中的findClass
是直接抛出异常的需要自己去实现
public class TDClassLoader extends ClassLoader {
private final File classPathFile;
public TDClassLoader(){
String classPath = Objects.requireNonNull(TDClassLoader.class.getResource("")).getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = TDClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile != null){
File classFile = new File(classPathFile,name.replaceAll("\.","/") + ".class");
if(classFile.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
return null;
}
}
实现也很简单其实就是冲磁盘中读取就行
最后实现Proxy
public class TDProxy {
public static final String ln = "\r\n";
public static Object newProxyInstance(ClassLoader classLoader, Class<?> [] interfaces, TDInvocationHandler h){
try {
//1、动态生成源代码.java文件
String src = generateSrc(interfaces);
// System.out.println(src);
//2、Java文件输出磁盘
String filePath = TDProxy.class.getResource("").getPath();
// System.out.println(filePath);
File f = new File(filePath + "$Proxy1.java");
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//3、把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
Iterable<? extends JavaFileObject> iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
task.call();
manage.close();
//4、编译生成的.class文件加载到JVM中来
Class<?> proxyClass = classLoader.loadClass("$Proxy1");
Constructor<?> c = proxyClass.getConstructor(TDInvocationHandler.class);
boolean delete = f.delete();
//5、返回字节码重组以后的新的代理对象
return c.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?>[] interfaces){
StringBuilder sb = new StringBuilder();
sb.append(TDProxy.class.getPackage()).append(";").append(ln);
sb.append("import ").append(interfaces[0].getName()).append(";").append(ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy1 implements ").append(interfaces[0].getName()).append("{").append(ln);
sb.append("TDInvocationHandler h;" + ln);
sb.append("public $Proxy1(TDInvocationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()){
Class<?>[] params = m.getParameterTypes();
StringBuilder paramNames = new StringBuilder();
StringBuilder paramValues = new StringBuilder();
StringBuilder paramClasses = new StringBuilder();
for (int i = 0; i < params.length; i++) {
Class<?> clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type).append(" ").append(paramName);
paramValues.append(paramName);
paramClasses.append(clazz.getName()).append(".class");
if(i > 0 && i < params.length-1){
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
sb.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append(
"(").append(paramNames.toString()).append(") {").append(ln);
sb.append("try{" + ln);
sb.append("Method m = ").append(interfaces[0].getName()).append(".class.getMethod("").append(m.getName()).append("",new Class[]{").append(paramClasses.toString()).append("});").append(ln);
sb.append(hasReturnValue(m.getReturnType()) ? "return " : "").append(getCaseCode("this.h" +
".invoke(this,m,new Object[]{" + paramValues + "})", m.getReturnType())).append(
";").append(ln);
sb.append("}catch(Error _ex) { }");
sb.append("catch(Throwable e){" + ln);
sb.append("throw new UndeclaredThrowableException(e);" + ln);
sb.append("}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
return sb.toString();
}
private static Map<Class,Class> mappings = new HashMap<Class, Class>();
static {
mappings.put(int.class,Integer.class);
}
private static String getReturnEmptyCode(Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "return 0;";
}else if(returnClass == void.class){
return "";
}else {
return "return null;";
}
}
private static String getCaseCode(String code,Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
}
return code;
}
private static boolean hasReturnValue(Class<?> clazz){
return clazz != void.class;
}
private static String toLowerFirstCase(String src){
char [] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
发现其实就是个代码拼接的过程
使用如下:
public class CustomIntermediary implements TDInvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class<?> clz = target.getClass();
return (IPerson) TDProxy.newProxyInstance(clz.getClassLoader(),clz.getInterfaces(),this);
}
private void before(){
System.out.println("发布房源");
}
private void after(){
System.out.println("交易完成");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target, args);
after();
return result;
}
public class Test {
public static void main(String[] args) {
IPerson person = new Seller();
CustomIntermediary jdkIntermediary = new CustomIntermediary();
IPerson instance = jdkIntermediary.getInstance(person);
instance.sellHouse();
}
}
基于Cglib实现动态代理
上文中的JDK实现动态代理,被代理类必须要实现接口,原理是生成的代理类也实现该接口然后把所有的方法实现一遍。这样做还是有局限性。没有实现接口的类就不能使用动态代理了,Cglib帮我们实现了没有实现接口的类如何实现动态代理
代码实现
public class CglibIntermediary implements MethodInterceptor {
public Object getInstance(Class<?> clz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
private void before(){
System.out.println("发布房源");
}
private void after(){
System.out.println("交易完成");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o,objects);
after();
return result;
}
}
public class Seller {
public void sellHouse() {
System.out.println("我要卖房");
}
}
public class Test {
public static void main(String[] args) {
CglibIntermediary cglibIntermediary = new CglibIntermediary();
Seller seller = (Seller) cglibIntermediary.getInstance(Seller.class);
seller.sellHouse();
}
}
原理分析
大概分析逻辑和上文一致我们主要看一下cglib帮我们生成的源码就行
在上文的代码中加上一句代码如下:
public class Test {
public static void main(String[] args) {
//利用CGlib的代理类可以将内存中的.class文件写入到本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/xzkj/Desktop/java_study/$Proxy0_cglib_class");
CglibIntermediary cglibIntermediary = new CglibIntermediary();
Seller seller = (Seller) cglibIntermediary.getInstance(Seller.class);
seller.sellHouse();
}
}
然后会对应生成文件夹,文件夹中有是三个文件 通过代码调试只要代理类应该是第一个
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.demo.proxy.demo.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class Seller$$EnhancerByCGLIB$$2368730a extends Seller implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$sellHouse$0$Method;
private static final MethodProxy CGLIB$sellHouse$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.example.demo.proxy.demo.cglib.Seller$$EnhancerByCGLIB$$2368730a");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$sellHouse$0$Method = ReflectUtils.findMethods(new String[]{"sellHouse", "()V"}, (var1 = Class.forName("com.example.demo.proxy.demo.cglib.Seller")).getDeclaredMethods())[0];
CGLIB$sellHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "sellHouse", "CGLIB$sellHouse$0");
}
final void CGLIB$sellHouse$0() {
super.sellHouse();
}
public final void sellHouse() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$sellHouse$0$Method, CGLIB$emptyArgs, CGLIB$sellHouse$0$Proxy);
} else {
super.sellHouse();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
re