1小时搞懂设计模式之代理模式(JDK动态代理)

1 静态代理缺点

我在上一篇关于代理模式博客 代理模式之静态代理中已经对代理模式进行解释这里就不在进行阐述。在说动态代理之前我们来先说说静态代理的缺点来进一步说明使用动态代理的好处。

  1. 由于静态代理中代理类和被代理类实现了相同的接口,但是真实的业务中代理类只是代理部分功能,必须要实现一些不必要实现的方法。另外就是接口中如果新增了方法 所有的实现类都必须实现 代理类也需要实现新增的方法。增加了代码的维护复杂度。
  2. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能。 即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。以上内容摘抄自(https://www.cnblogs.com/baizhanshi/p/6611164.html)
我们这里进行代码举例举例
用户服务接口

package cn.zhuoqianmingyue.examples.proxypattern.log;

public interface UserService {
	public void saveUser();
}

用户服务的实现类

package cn.zhuoqianmingyue.examples.proxypattern.log;

public class UserServiceImpl implements UserService{

	@Override
	public void saveUser() {
		System.out.println("saveUser");
	}
}

代理打印日志类

package cn.zhuoqianmingyue.examples.proxypattern.log;

public class ProxyLog implements UserService{
	private UserService user;
	public ProxyLog(UserService user) {
		this.user = user;
	}
	public void log() {
		System.out.println("调用开始毫秒值:"+System.currentTimeMillis()+" 调用实现类名称:"+user.getClass().getName()+"被调用");
	}
	@Override
	public void saveUser() {
		this.log();
		user.saveUser();
	}
}

测试用例

package cn.zhuoqianmingyue.examples.proxypattern.log;

public class Application {
	public static void main(String[] args) {
		ProxyLog proxyLog = new ProxyLog(new UserServiceImpl());
		proxyLog.saveUser();
	}
}

运行结果:
在这里插入图片描述
从上面的代码我们可以看出如果要在为订单服务添加日志打印的代理 用ProxyLog 就不行了 因为我们ProxyLog 构造中接收的是用户服务的接口。所以如果想为订单服务提供日志代理 就要但是在写一个新的代理类。

2 动态代理代码实现

JDK 动态代理帮我们解决了静态代理的2个缺点。接下来我们还是用日志代理服务来进行演示JDK动态代理。

订单服务接口

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

public interface OrderService {
	public void saveOrder();
}

订单服务的实现类

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

public class OrderServiceImpl implements OrderService{

	@Override
	public void saveOrder() {
		System.out.println("saveOrder.....");
	}
}

日志代理的JDK代理API

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyLog implements InvocationHandler{
	private Object object;
	
	public Object getInstance(Object object) {
		this.object = object;
		Class<?> clazz = object.getClass();
		return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
	}
	public void log() {
		System.out.println("调用开始毫秒值:"+System.currentTimeMillis()+" 调用实现类名称:"+object.getClass().getName()+"被调用");
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		this.log();
		return method.invoke(this.object,args);
	}
}

测试用例:

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

import java.io.IOException;
import cn.zhuoqianmingyue.examples.proxypattern.log.UserService;
import cn.zhuoqianmingyue.examples.proxypattern.log.UserServiceImpl;

public class Application {
	public static void main(String[] args) throws IOException {
		JDKProxyLog proxy = new JDKProxyLog();
		UserService userService = (UserService)proxy.getInstance(new UserServiceImpl());
		userService.saveUser();
		OrderService orderService = (OrderService)proxy.getInstance(new OrderServiceImpl());
		orderService.saveOrder();
	}
}

运行结果:
1小时搞懂设计模式之代理模式(JDK动态代理)_第1张图片
从上面代码我们可以看出: 我们代理订单服务日志代理无需在创建新的代理类 ,也无需在实现不必要实现的接口。

3 JDK动态代理原理

下面我们来一探究竟 JDK动态代理原理,
核心代码就是下图中的代码
Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
通过Proxy 来创建代理对象,那就让我们来看看这个代理对象到底是个什么东东。

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

import cn.zhuoqianmingyue.examples.proxypattern.log.UserService;
import cn.zhuoqianmingyue.examples.proxypattern.log.UserServiceImpl;

public class Application {
	public static void main(String[] args) {
		JDKProxyLog proxy = new JDKProxyLog();
		UserService userService = (UserService)proxy.getInstance(new UserServiceImpl());
		System.out.println(userService.getClass());
	}
}

在这里插入图片描述
这个代理对象是一个名称为$Proxy0的类。

package cn.zhuoqianmingyue.examples.proxypattern.jdkLog;

import java.io.FileOutputStream;
import java.io.IOException;

import cn.zhuoqianmingyue.examples.proxypattern.log.UserService;
import cn.zhuoqianmingyue.examples.proxypattern.log.UserServiceImpl;
import sun.misc.ProxyGenerator;

public class Application {
	public static void main(String[] args) throws IOException {
		JDKProxyLog proxy = new JDKProxyLog();
		UserService userService = (UserService)proxy.getInstance(new UserServiceImpl());
		System.out.println(userService.getClass());
		byte[] generateProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{UserService.class});
		FileOutputStream os = new FileOutputStream("d:/$Proxy0.class");
		os.write(generateProxyClass);
		os.close();
	}
}

反编译内容如下:

import cn.zhuoqianmingyue.examples.proxypattern.log.UserService;
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 UserService
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  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 void saveUser()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    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);
    }
  }

  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);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("cn.zhuoqianmingyue.examples.proxypattern.log.UserService").getMethod("saveUser", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

具体原理是通过字节码重组方式来实现的。
实现步骤大致如下:
通过反射获取被代理对象的引用获取到它的所有的接口如下面的代码 中clazz.getInterfaces() 通过Proxy 来生成新的代理类。

Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);

动态生成代理类Java代码 并且该类要实现被代理类的接口同时继承Proxy类。通过反射将被代理类的Method获取到 然后再调用实现InvocationHandler的代理类的invoke方法。
1小时搞懂设计模式之代理模式(JDK动态代理)_第2张图片
1小时搞懂设计模式之代理模式(JDK动态代理)_第3张图片

1小时搞懂设计模式之代理模式(JDK动态代理)_第4张图片
根据新生成java代码编译成.class文件 然后重新加载到jvm中进行运行。

4 自己实现JDK动态代理原理

在了解了原理后我们可以自己动手来写JDK动态代理,

4.1 编写自定义的InvocationHandler 接口

package cn.zhuoqianmingyue.examples.proxypattern.jdk.custom.executor;

import java.lang.reflect.Method;

public interface CustomInvocationHandler {
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

4.2 编写自定义的ClassLoader

通过双亲委派模型 编写自定义的ClassLoader 通过重写findclass 然后再方法内在通过defineclass:解析定义.class字节流,返回class对象。

package cn.zhuoqianmingyue.examples.proxypattern.jdk.custom;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class CustomClassLoader extends ClassLoader{
	 private File classPathFile;

	    public CustomClassLoader(){
	        String classPath = CustomClassLoader.class.getResource("").getPath();
	        this.classPathFile = new File(classPath);
	    }

	    @Override
	    protected Class<?> findClass(String name) throws ClassNotFoundException {

	        String className = CustomClassLoader.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();
	                }finally {
	                    if(null != in){
	                        try {
	                            in.close();
	                        } catch (IOException e) {
	                            e.printStackTrace();
	                        }
	                    }

	                    if(out != null){
	                        try {
	                            out.close();
	                        } catch (IOException e) {
	                            e.printStackTrace();
	                        }
	                    }
	                }
	            }

	        }

	        return null;

	    }
}

4.3 编写自定义的Proxy类

1 先通过反射获取接口中的所有方法 动态生成源代码.java文件
2 把生成的Java文件编译成 .class 文件
3 将生成的.class 文件加载到JVM中
4 将.java 文件进行删除
5 将字节码重组的代理对象返回

package cn.zhuoqianmingyue.examples.proxypattern.jdk.custom;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import cn.zhuoqianmingyue.examples.proxypattern.jdk.custom.executor.CustomInvocationHandler;

public class CustomProxy {
	 public static final String ln = "\r\n";

	    public static Object newProxyInstance(CustomClassLoader classLoader,Class<?> [] interfaces,CustomInvocationHandler h){

	       try {
	           //1、动态生成源代码.java文件
	           String src = generateSrc(interfaces);
	           //2、Java文件输出磁盘
	           String filePath = CustomProxy.class.getResource("").getPath();
	           System.out.println(filePath);
	           File f = new File(filePath + "$Proxy0.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 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.findClass("$Proxy0");
	          Constructor c = proxyClass.getConstructor(CustomInvocationHandler.class);
	         // f.delete();

	           //5、返回字节码重组以后的新的代理对象
	           return c.newInstance(h);
	       }catch (Exception e){
	           e.printStackTrace();
	       }
	        return null;
	    }
	    //生成Java文件的内容
	    private static String generateSrc(Class<?>[] interfaces){

	            StringBuffer sb = new StringBuffer();
	            sb.append("package cn.zhuoqianmingyue.examples.proxypattern.jdk.custom;" + ln);
	            sb.append("import cn.zhuoqianmingyue.examples.proxypattern.jdk.custom.executor.CustomInvocationHandler;" + ln);
	            sb.append("import cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.Person;" + ln);
	            sb.append("import java.lang.reflect.Method;" + ln);
	            sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);

	                sb.append("CustomInvocationHandler h;" + ln);

	                sb.append("public $Proxy0(CustomInvocationHandler h) { " + ln);

	                    sb.append("this.h = h;");

	                sb.append("}" + ln);


	                for (Method m : interfaces[0].getMethods()){
	                	if("void".equals(m.getReturnType().getName())) {
	                		sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
	                        sb.append("try{" + ln);
	                            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
	                            sb.append("this.h.invoke(this,m,null);" + ln);
	                        sb.append("}catch(Throwable e){" + ln);
	                        sb.append("e.printStackTrace();" + ln);
	                        sb.append("}");
	                        sb.append("}");
	                	}else {
	                		sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
	                        sb.append("try{" + ln);
	                            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
	                            sb.append("return ("+ m.getReturnType().getName()+") this.h.invoke(this,m,null);" + ln);
	                        sb.append("}catch(Throwable e){" + ln);
	                        sb.append("e.printStackTrace();" + ln);
	                        sb.append("}");
	                        sb.append("return null;");
	                        sb.append("}");
	                	}
	                    
	                }

	            sb.append("}" + ln);
	            System.out.println(sb);
	            return sb.toString();
	    }
}

我们在通过上面的自定义JDK动态代理实现 黄牛买票的例子:
定义购票者的接口

package cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd;

public interface Person {
	public String getName();
	public void buyTicket();
}

北漂购票者

package cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.impl;

import cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.Person;
/**
 * 北漂
 * @author zhuoqianmingyue
 *
 */
public class NorthDrift implements Person{
	private String name = "";//姓名
	private String form = "";//买票起始地
	private String to = "";//买票的目的地
	public String getTo() {
		return to;
	}
	public void setTo(String to) {
		this.to = to;
	}
	public String getForm() {
		return form;
	}
	public void setForm(String form) {
		this.form = form;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public NorthDrift(String name,String form,String to) {
		this.name = name;
		this.form = form;
		this.to = to;
	}
	/**
	 * 买票的方法
	 */
	public void buyTicket() {
		System.out.println("名字叫:"+name+"从"+form+"到 "+to+"的火车票!");
	}

}

买票黄牛代理类

package cn.zhuoqianmingyue.examples.proxypattern.jdk.custom;

import java.lang.reflect.Method;

import cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.Person;
import cn.zhuoqianmingyue.examples.proxypattern.jdk.custom.executor.CustomInvocationHandler;
/**
 * 买票黄牛代理类
 * @author zhuoqianmingyue
 *
 */
public class CustomTicketScalper implements CustomInvocationHandler{
	private Person target;
	
	public Object getInstance(Person target) {
		this.target = target;
		Class<? extends Person> clazz = target.getClass();
		//clazz.getInterfaces() 获取到对象实现的接口
		return CustomProxy.newProxyInstance(new CustomClassLoader(), clazz.getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("我是黄牛 !我帮"+target.getName()+"买一张");
		//this.target.buyTicket();
		 method.invoke(this.target,args);
		return null;
	}
}

测试类

package cn.zhuoqianmingyue.examples.proxypattern.jdk.test;

import java.io.IOException;
import cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.Person;
import cn.zhuoqianmingyue.examples.proxypattern.jdk.agentd.impl.NorthDrift;
import cn.zhuoqianmingyue.examples.proxypattern.jdk.custom.CustomTicketScalper;

public class TestApplication {
	
	public void custom() {
		CustomTicketScalper ts = new CustomTicketScalper();
		Person p = (Person)ts.getInstance(new NorthDrift("zqmy","beijing","shanghai"));
		System.out.println(p.getClass());
		
		p.buyTicket();
	}
	public static void main(String[] args) throws IOException {
		TestApplication application = new TestApplication();
		application.custom();
	
	}
}

测试结果:
1小时搞懂设计模式之代理模式(JDK动态代理)_第5张图片
参考文献:
https://www.cnblogs.com/baizhanshi/p/6611164.html
https://blog.csdn.net/qq_31777123/article/details/80036682

你可能感兴趣的:(【设计模式】)