代理模式


为其他对象提供了一种代理以控制对这个对象的访问.

代理对象在客户端和目标对象之间起到中介作用,属于结构性模式。

目的

  1. 保护对象
  2. 增强目标对象

在这里插入图片描述

  • Subject是顶层接口;
  • RealSubject是真实对象(被代理对象)
  • Proxy是的代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理逻辑.

一般分为静态代理和动态代理两种方式.

代理模式的优点

职责清晰: 真实角色就是实现实际的业务逻辑,不用关心其他非本职的事物。
高扩展性: 真实角色可以随时更换或扩展,只需要实现接口就行,而代理不需要有任何变化

静态代理

举个例子,在古代相亲时,父亲都请来媒婆为儿子物色对象,媒婆即做了儿子的代理角色

  1. Subject是顶层接口,Person
public interface Person {

	public void findLove();
}
  1. RealSubject是真实对象Son,实现了顶层接口
public class Son implements Person {
	@Override
	public void findLove() {
		System.out.println("儿子要求:肤白貌美大长腿");
	}
}
  1. Proxy代理对象,实现Subject顶层接口,并持有RealSubject真实对象
public class SteadyProxy {

	public static void main(String[] args) {
		// 媒婆帮儿子找对象
		SteadyMeipo meipo = new SteadyMeipo(new Son());
		meipo.findLove();
	}
}

动态代理

分为jdk和cglib两种方式:jdk方式使用反射技术,cglib底层使用asm技术

在这里插入图片描述

jdk方式

两个重要类:

  • interface InvocationHandler
    Object invoke(Object proxy, Method method, Object[] args)
    动态生成代理类,调用被代理类的方法,完成其他方法的织入

  • class Proxy
    真正生成动态代理的类,提供两个静态方法

    • Class<?> getProxyClass(ClassLoader loader, Class<?>[] interface)
      用来产生代理类,参数要提供interface数组,它会生成这些interface的“虚拟实现”,
      用来冒充真实的对象。
    • Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
      产生代理对象,多了InvocationHandler参数(只是InvocationHandler接口的实现类),
      它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(…)方法
案例
  1. 动态代理类生成类,实现InvocationHandler接口
public class JDKMeipo implements InvocationHandler {

	// 被代理的对象,把引用保存下来
	private Object target;

	public Object getInstance(Object object){
		this.target = object;
		Class<?> clazz = object.getClass();
		return Proxy.newProxyInstance(clazz.getClassLoader() , clazz.getInterfaces(),this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		Object object = method.invoke(this.target , args);
		after();
		return object;
	}

	private void before(){
		System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
		System.out.println("开始物色");
	}
	private void after(){
		System.out.println("如果合适的话,就准备办事");
	}
}
  1. client代理测试
public class Test {
	public static void main(String[] args) {
		Person obj = (Person) new JDKMeipo().getInstance(new Customer());
		obj.findLove();
	}
}

debug发现此时生成的Person对象为$Proxy0@1234,其实就是为我们动态生成了代理类,执行时使用该代理类进行程序执行。

可以使用如下代码将生成的代理类反编译出来,查看真正的代理类代码:

public class Test {
	public static void main(String[] args) {
		Person obj = (Person) new JDKMeipo().getInstance(new Customer());
		obj.findLove();

		// 动态代理生成的类使用字节码重组,这里将其保存在硬盘上,后反编译分析
		byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
		FileOutputStream os = null;
		try {
			os = new FileOutputStream("D://$Proxy0.class");
			os.write(bytes);
			os.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

执行上面的代码,会在D盘生成一个$Proxy0.class文件,使用相应的反编译工具,即可看到其源文件,如下所示为:

public final class Proxy0 extends Proxy implements Person{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  public Proxy0(){
    super(paramInvocationHandler);
  }

  public final boolean equals(){
    try{
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException){
      throw localRuntimeException;
    }
    catch (Throwable localThrowable){
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void findLove(){
    try{
      this.h.invoke(this, m3, null);
      return;
    }catch (RuntimeException localRuntimeException){
      throw localRuntimeException;
    }catch (Throwable localThrowable){
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString() {
    try{
      return ((String)this.h.invoke(this, m2, null));
    }catch (RuntimeException localRuntimeException){
      throw localRuntimeException;
    }catch (Throwable localThrowable){
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode(){
    try{
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }catch (RuntimeException localRuntimeException){
      throw localRuntimeException;
    }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("com.melody.proxy.manul.dynamic.Person").getMethod("findLove", 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());
    }
  }
}

观察发现:$Proxy0继承了Proxy类,同时实现了Person接口,而且重写了Person中的findLove方法,在静态快中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。

手写动态代理类的产生逻辑
  1. 创建GPInvocationHandler接口,对应jdk中的InvocationHandler类
public interface GPInvovationHandler {
	public Object invoke(Object proxy, Method method, Object... args) throws Throwable;
}
  1. 创建GPProxy类,对应jdk中的Proxy类,主要负责生产代理类
public class GPProxy {

	public static final String ln = "\r\n";

	public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces,
										  GPInvovationHandler handler) {

		// 动态生成源代码.java文件
		String src = generateSrc(interfaces);

		System.out.println(src);
		try {
			// java文件输出磁盘
			String filePath = GPProxy.class.getResource("").getPath();
			File f = new File(filePath + "$Proxy0.java");
			FileWriter fw;

			fw = new FileWriter(f);
			fw.write(src);
			fw.flush();
			fw.close();

			// 把生成的java文件编译成class文件
			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
			StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
			Iterable iterable = manager.getJavaFileObjects(f);
			JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);

			task.call();
			manager.close();

			//把生成的class文件加载到JVM中
			Class proxyClass = classLoader.findClass("$Proxy0");
			Constructor c = proxyClass.getConstructor(GPInvovationHandler.class);
			f.delete();

			return c.newInstance(handler);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static String generateSrc(Class<?>[] interfaces) {
		StringBuilder sb = new StringBuilder();
		sb.append("package com.melody.proxy.dynamic.manual;" + ln);
		sb.append("import com.melody.proxy.dynamic.manual.Person;" + ln);
		sb.append("import java.lang.reflect.*;" + ln);
		sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
		sb.append("GPInvovationHandler h;" + ln);
		sb.append("public $Proxy0(GPInvovationHandler 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 + " " + paramName);
				paramValues.append(paramName);
				paramClasses.append(clazz.getName() + ".class");

				if (i > 0 && i < params.length - 1) {
					paramNames.append(",");
					paramClasses.append(",");
					paramValues.append(",");
				}
			}
			sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ")" +
					" " +
					"{" + ln);
			sb.append("try{" + ln);

			sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new " +
					"Class[]{" + paramClasses.toString() + "});" + ln);

			sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new " +
					"Object[]{" + paramValues + "})", m.getReturnType()) + ";" + ln);

			sb.append("} catch (NoSuchMethodException e) {\n" +
					"\t\t\te.printStackTrace();\n" +
					"\t\t} catch (Throwable throwable) {\n" +
					"\t\t\tthrowable.printStackTrace();\n" +
					"\t\t}");
			sb.append(getReturnEmptyCode(m.getReturnType()));
			sb.append("}");
		}

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

	private static Map<Class, Class> mappings = new HashMap<>();

	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 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);
	}
}
  1. 创建GPClassLoader。类加载器,负责将上述生产的$Proxy0代理类加载到jvm中
public class GPClassLoader extends ClassLoader {

	private File classPathFile;

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

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

		String className = GPClassLoader.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 (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException 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;
	}
}
  1. 创建GPMeipo媒婆来,调用GPProxy进行对代理对象的功能进行增强
public class GPMeipo implements GPInvovationHandler {

	/** 被代理的对象,把引用保存下来 */
	private Object target;

	public Object getInstance(Object target) {
		this.target = target;
		Class<?> clazz = target.getClass();

		return GPProxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		method.invoke(this.target, args);
		after();
		return null;
	}

	private void before() {
		System.out.println("我是媒婆,我要给你找对偶像,现在已经确认你的需求");
		System.out.println("开始物色");
	}

	private void after() {
		System.out.println("如果合适的话,就主备办事");
	}
}
  1. 测试类如下:
public class Main {

	public static void main(String[] args) {

		Person p = (Person) new GPMeipo().getInstance(new Customer());
		System.out.println(p.getClass());
		p.findLove();
	}
}

到此,手写jdk动态代理就完成了

cglib方式
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.1_3</version>
		</dependency>
  1. cglib实现的媒婆实现类CglibMeipo如下:对应上述的GPMeipo 类
public class CglibMeipo implements MethodInterceptor {

	public Object getInstance(Class<?> clazz) throws Exception {
		Enhancer enhancer = new Enhancer();

		//要把哪个设置为即将生成的新类的父类
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	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("我是媒婆:我要给你介绍对象,现在已经确认你的需求");
		System.out.println("开始物色");
	}

	private void after() {
		System.out.println("如果合适的话,就主备办事");
	}
}
  1. 测试类如下:
public class CglibTest {

	public static void main(String[] args) {
		try {
			// 将生成的代理类写入如下文件夹
			System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY , "D://cglib");
			Person obj = (Person) new CglibMeipo().getInstance(Son.class);
			obj.findLove();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

cglib代理执行效率之所以比jdk高,是因为cglib采用了fastclass机制,它的简单原理是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个index,这个index当做一个入参,FastClass就可以直接定位要调用的方法并直接进行调用,省去了反射调用。
具体cglib的原理这里不做详细说明,这里简单看下jdk与cglib实现动态代理的区别

  1. jdk动态代理实现了被代理对象的接口,cglib代理继承了被代理对象
  2. jdk动态代理和cglib代理都在运行期生成字节码,jdk动态码直接写Class字节码,cglib代理使用ASM框架写Class字节码,cglib代理实现更复杂,生成代理类比jdk动态代理效率低;
    3.jdk动态代理调用代理方法是通过反射机制,cglib代理是通过FastClass机制直接调用方法的,cglib代理的执行效率更高。

AOP中的动态代理模型

面向切面编程,不做过多赘述。
在这里插入图片描述

  1. 抽象主题类
public interface ISubject {
		void doSomething(String str);
}
  1. 真实主题
public class RealSubject implements ISubject {
	@Override
	public void doSomething(String str) {
		System.out.println("做某些逻辑处理-----------");
	}
}
  1. 通知接口和实现
public interface Advice {
	public void exec();
}

public class BeforeAdvice implements Advice {
	@Override
	public void exec() {
		System.out.println("前置通知被执行...");
	}
}

public class AfterAdvice implements Advice {
	@Override
	public void exec() {
		System.out.println("后置通知被执行...");
	}
}
  1. 动态代理的处理类
public class MyInvocationHandler implements InvocationHandler {

	/** 被代理的对象 */
	private Object target;

	public MyInvocationHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		//判断是否符合织入条件
		if (true) {
			new BeforeAdvice().exec();
		}
		Object o = method.invoke(this.target, args);

		//判断是否符合织入条件
		if (true) {
			new AfterAdvice().exec();
		}
		return o;
	}
}
  1. 动态代理类生成类
public class DynamicProxy {

	protected static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler invocationHandler) {

		return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
	}
}

//具体业务的动态代理生成类,接收ISubject类型,并为其生成相应的代理类
public class SubjectDynamicProxy extends DynamicProxy{


	public static ISubject newProxyInstance(ISubject subject) {
		// 获得classloader
		ClassLoader classLoader = subject.getClass().getClassLoader();
		//获得接口数字
		Class<?>[] interfaces = subject.getClass().getInterfaces();

		// 获得handler
		InvocationHandler handler = new MyInvocationHandler(subject);
		return (ISubject) newProxyInstance(classLoader, interfaces, handler);
	}
}
  1. 测试类
public class TestClient {

	public static void main(String[] args) {
		//定义一个主题
		ISubject subject = new RealSubject();

		//定义subject的代理
		ISubject proxy = SubjectDynamicProxy.newProxyInstance(subject);
		proxy.doSomething("hahahaha");
	}
}

在执行真正的业务方法之前,插入了一个前置通知方法,永远会在业务方法之前发送一个通知,这就是横切面编程:在不改变我们已有代码结构的情况下增强或控制对象的行为。 这里需要注意一点:实现JDK的动态代理,被代理类必须实现一个接口(其他的动态代理技术如CGLIB可以没有接口)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值