荐 [设计模式] 代理模式之 静态代理与动态代理 & Mybatis实例解析

2022-08-07

前言

之前回头看设计模式的时候, 发现对于Java的动态代理还不是特别理解. 又拔了拔书, 随后发现Mybatis里面也用到了这个模式. 在此, 复习和记录一下学习的过程.


准备操作- Java反射机制

Java的反射主要包括3个部分:
* 根据类名获取Class对象
* 根据Class对象获取方法, 并调用.
* 根据Class对象获取所有子接口, 并调用.
* 根据Class对象获取成员, 并调用.
在反射中, 用的比较多的是13这2个部分.

public class ReflectDemo {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 根据类名生成对象
		// Object obj = Class.forName("java.lang.Integer").newInstance();
		
		// 根据类名获取方法 对象实现的所有接口
		
		// java.lang.Comparable 
		Class<?> []classes =  Class.forName("java.lang.Integer").getInterfaces();
		for(Class classobj: classes) {
			System.out.println(classobj.getName());
		}
	}
}

根据其上可以发现java.lang.Integer实现的是java.lang.Comparable接口.

public final class Integer extends Number implements Comparable<Integer> {

Java反射之getInterfaces()方法
Java反射机制详解
Java 基础 (七) 反射机制


准备操作- 类加载器

关于类加载器. 我们在复习JVM详细细节的时候, 会进行详细的讲解.

类加载器主要分为3种:

  • 启动类加载器(Bootstrap Class Loader): 主要加载<JAVA_HOME>/lib目录下文件. 按照文件名进行识别rt.jar tools.jar. 名字无法识别无法加载.
# java.lang.ClassLoader.getClassLoader()
/**
Returns the class loader for the class.  Some implementations may use null to represent the bootstrap class loader. This method will return  null in such implementations if this class was loaded by the bootstrap class loader.
*/
public ClassLoader getClassLoader() {
    ClassLoader cl = getClassLoader0();
    if (cl == null)
        return null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        ClassLoader ccl = ClassLoader.getCallerClassLoader();
        if (ccl != null && ccl != cl && !cl.isAncestor(ccl)) {
            sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
        }
    }
    return cl;
}
  • 扩展类加载器(Extension Class Loader)
    扩展类加载器. 主要加载<JAVA_HOME>/lib/ext目录下的文件. 实现为sun.misc.Launcher$ExtClassLoader.

  • 应用程序加载器(Application Class Loader)
    应用程序加载器,有时也叫系统类加载器. 实现为sun.misc.Launcher$AppClassLoader. 主要加载ClassPath上的类. 我们经常用的就是这种.

public class ReflectDemo {
	public static void testClassLoader() {
		System.out.println(ReflectDemo.class.getClassLoader().getClass().getName().toString());
		// System.out.println(new Object().getClass().getClassLoader().getClass().getName().toString());
	}

输出为sun.misc.Launcher$AppClassLoader. 对于java.lang.Integer类加载器的获取. 暂时还没啥头绪. 输出报了空指针异常, 不知道怎么修复.(汗)

Exception in thread "main" java.lang.NullPointerException
	at com.yanxml.java.arsenal.jdk.core.reflect.ReflectDemo.testClassLoader(ReflectDemo.java:23)
	at com.yanxml.java.arsenal.jdk.core.reflect.ReflectDemo.main(ReflectDemo.java:29)

注: 等过段时间再看吧.


代理模式-静态代理

代理模式思想很简单. 让代理类被代理类的事情. ProxyClass -> RealClass Demo代码如下所示:

// 接口类
public Interface SayInterface{
	public void say();
}

// 实际类
public class RealClass implements SayInterface{
	public void say(){ System.out.println("say-hello");}
}

// 代理类
public class ProxyClass implements SayInterface {
	public RealClass toProxyObject;
	public ProxyClass(RealClass toProxyObject){
		this.toProxyObject = toProxyObject;
	}
	public void say(){
		// 使用代理类完成需要的操作.
		toProxyObject.say();
	}
}

// 测试用例
public class TestDemo{
	public static void main(String []args){
		RealClass realClassObject = new RealClass();
		ProxyClass proxyClassObject = new ProxyClass(realClassObject);
		// 使用代理类完成 说的请求.
		proxyClassObject.say();
	}
}


代理模式-动态代理

动态代理的整个主要流程是通过Proxy.newInstance(classLoader,class<?>[],InnovactionHandler). 这个方法进行展开的. 方法内的细节我们先不谈. 先看下三个参数:

  • ClassLoader: 根据前文的技术准备. 我们知道这边基本上就是应用程序加载器. 也就是sun.misc.Launcher$AppClassLoader. 可以通过Demo.class.getClassLoader()进行获取.
  • class<?> []: 这个是需要代理的类实现的接口. 具体为什么, 目前不太明白.还要看具体的技术细节. RealClass.class.getInterfaces()方法获取. 详情可以看下技术准备的反射.
  • InnovactionHandler: 是一个接口. 主要传入实现该接口的一个对象. 该接口主要维护的是一个public Object invoke(Object proxy, Method method, Object[] args)方法.

OK. 我们下面给出一个Demo程序. 来看下具体如何实现.

# 接口和实际类

// 统一接口.

public interface ObjInterface {
	public void say();
}

/**
 * 实现接口的类.
 * 
 * */
public class RealObject implements ObjInterface{

	public void say() {
		System.out.println("I m Real Object.");
	}

}
# 实现的InnovactionHandler接口的对象 & 代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class RealObjectHandler implements InvocationHandler{
	
	public Object obj;
	
	public RealObjectHandler(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return method.invoke(obj, args);
	}

}

# 代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class NewDynamicProxyDemoRetry {
	
	public static void main(String []args) {
		// 首先创建代理对象
		RealObject toBeProxyObj = new RealObject();
		// 生成代理器
		InvocationHandler invocationHandler = new RealObjectHandler(toBeProxyObj);
		// 
		ObjInterface obj = (ObjInterface)Proxy.newProxyInstance(ObjInterface.class.getClassLoader(), new Class[] {ObjInterface.class}, invocationHandler);
		ObjInterface obj2 = (ObjInterface)Proxy.newProxyInstance(ObjInterface.class.getClassLoader(), ObjInterface.class.getInterfaces(), invocationHandler);

		obj.say();
	}
		
}

值得注意的是, 强制转换(xx)Porxy.newInstance.和 newProxyInstance(xx,xx,xx). 这2处都是面向接口的. 否则会报如下错误信息.


// Exception 1
// Exception in thread "main" java.lang.IllegalArgumentException: com.yanxml.design.book.design.proxy.dynamic.retry.RealObject is not an interface


// Exception 2
// Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.yanxml.design.book.design.proxy.dynamic.retry.RealObject
// at com.yanxml.design.book.design.proxy.dynamic.retry.NewDynamicProxyDemoRetry.main(NewDynamicProxyDemoRetry.java:12)
  • 其他标准化写法
/**
 * 动态代理工厂.
 * 
 * */
public class NewDynamicClassProxyDemo<T> {
	public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler) {
		return (T)Proxy.newProxyInstance(classLoader, interfaces, handler);
	} 
	
	public static <T> T newProxyInstance(ObjInterface objInterface) {
		ObjInterface toPorxy = objInterface;
		ClassLoader classLoader = toPorxy.getClass().getClassLoader();
		Class[] classes = toPorxy.getClass().getInterfaces();
		InvocationHandler handler = new RealObjectHandler(toPorxy);
		return (T)Proxy.newProxyInstance(objInterface.getClass().getClassLoader(), classes , handler);
	}
	
	
}

Mybatis 内的动态代理

略. 待补充. (晚点补上吧.)

Spring内的动态代理

略. 待补充.


Reference

本文地址:https://blog.csdn.net/u010416101/article/details/107282245