Mybatis基础支持层-资源加载模块

类加载器/单例/资源加载

Posted by Claire on June 23, 2020

mybatis基础支持层-资源加载模块

一、ClassLoader

  • java虚拟机中类加载器,负责加载来自文件、网络或者其他来源的类文件
  • 默认的类加载器有三种
    • Bootstrap ClassLoader : 负责加载 jdk中的rt.jar中的类文件
    • Extension ClassLoader :负责加载jre扩展类库,位于jre/lib/ext下的类文件,或者java.ext.dirs指定的目录下的类文件
    • System ClassLoader(Application ClassLoader): 负责加载classpath环境中的类文件,通常是通过-classpath -cp 或者JAR中Manifest文件的classpath属性指定的
  • 类加载器遵循双亲委派:遵循自底向上检测类是否加载过,自顶向下尝试加载类
  • 双亲委派保证: 子类加载器可以使用父类加载过的类,而父类加载器无法使用子类加载过的类。父类加载过得类子类无法重新加载,保证了JVM的稳定性和安全性
  • 要加载一个类,自定义类加载器1将加载类的工作委托给应用加载器,应用加载器首先看是否自己加载过,如果加载过则加载过程结束,没有加载过则继续向上委托二级父类加载器,如果未加载则继续向上传递,一级父类加载器如果未加载过,则从指定目录尝试加载类,如果加载失败,则向下交由二级父类加载器,如果继续加载失败,向下直至加载请求的子加载器为止
        (顶)  Bootstrap ClassLoader  一级父类加载器
                    |
                    |
            Extension ClassLoader  二级父类加载器
                    |
                    |
      System ClassLoader(Application ClassLoader)  应用加载器
                    |
                    |     |
                    | --- |
 (底)  自定义类加载器1       自定义类加载器2...
  • 继承java.lang.ClassLoader可以实现自己的类加载器,修改内部加载的逻辑。Tomcat\Jboss都涉及自定义类加载器

二、Tomcat中的类加载器

  • 包含自定义的一个Common ClassLoader,它的父加载器是System ClassLoader,负责加载Tomcat通用的一些类和JAR,例如CATALINA_HOME/lib下的jar包

  • Tomcat还会提供CatalinaLoader 和 SharedLoader 两个加载器,通过配置文件指定加载的目录,默认为空,因而这两个类加载器默认情况与Common ClassLoader为同一个类加载器

  • Tomcat会为每一个应用添加一个WebApp ClassLoader ,其父类加载器是Common ClassLoader ,也就是默认可以加载JDK中的类文件,复杂加载指定应用WEB-INF/lib下的jar包和WEB-INF/classes的类文件

  • 由于每个应用都有自己的类加载器,保证了应用之间的资源隔离,采用热部署的时候,会抛弃原来的WebApp ClassLoader创建新的,由于WebApp classLoader 的父加载器都是Common ClassLoader, 因而能够共享Common ClassLoader加载的类库

                     System ClassLoader
                             |
                             |
                             |
                     Common ClassLoader    
                             |
                             |     |
                             | --- |  |
WebApp ClassLoader1   WebApp ClassLoader2    WebApp ClassLoader3                   

三、Mybatis IO包

1. ClassLoaderWrapper

  • IO包中提供了ClassLoaderWrapper 是ClassLoader的包装器,内含多个Class Loader,可以调整类加载器的顺序,可以保证提供给系统使用的是正确的类加载器
class ClassLoaderWrapper{
//默认的类加载器
  ClassLoader defaultClassLoader;
  //系统的类加载器
  ClassLoader systemClassLoader;

  ClassLoaderWrapper() {
    try {
      //初始化系统的classloader  
      systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {
      // AccessControlException on Google App Engine
    }
  }
}
  • class loader列表
  ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader, //传递过来的类加载器
        defaultClassLoader,  //默认的类加载
        Thread.currentThread().getContextClassLoader(),//当前上下文的类加载器
        getClass().getClassLoader(), //当前类类加载器
        systemClassLoader}; //提供的类加载器
  }
  • 主要提供几个核心方法:getResourceAsURL、getResourceAsStream、classForName
 URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

    URL url;

    for (ClassLoader cl : classLoader) {
      if (null != cl) {
        // look for the resource as passed in...
        //通过getResource 获取url
        url = cl.getResource(resource);

        // ...but some class loaders want this leading "/", so we'll add it
        // and try again if we didn't find the resource
        //如果URL为空,如果没有的话继续通过 / 的目录去查找资源
        if (null == url) {
          url = cl.getResource("/" + resource);
        }
        // "It's always in the last place I look for it!"
        // ... because only an idiot would keep looking for it after finding it, so stop looking already.
        if (null != url) {
          //如果url存在则返回
          return url;
        }
      }
    }
    // didn't find it anywhere.
    return null;
  }

   InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    //与getResourceAsURL 类似,调用ClassLoader的 getResourceAsStream 方法
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) {
          returnValue = cl.getResourceAsStream("/" + resource);
        }

        if (null != returnValue) {
          return returnValue;
        }
      }
    }
    return null;
  }

   Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
    //通过Class.forName查找对应名称的类
    for (ClassLoader cl : classLoader) {
      if (null != cl) {
        try {
          Class<?> c = Class.forName(name, true, cl);
          if (null != c) {
            return c;
          }
        } catch (ClassNotFoundException e) {
          // we'll ignore this until all classloaders fail to locate the class
        }
      }
    }
    throw new ClassNotFoundException("Cannot find class: " + name);
  }
  • Resources内部有一个静态变量ClassLoaderWrapper,Resources 内部的方法都是调用ClassLoaderWrapper的相关方法实现的

2.ResolverUtil

根据指定条件到指定包下的类,使用的条件使用Test接口表示 内含一个classloader,默认使用上下文的类加载器,也可以setClassloader设置 Test接口有两个实现,isA用于检测类是否继承指定类和接口,AnnotatedWith用于检测类是否添加了指定注解 也可自定义实现Test接口实现自定义的检测项,Test中的matches方法用于判断是否符合条件

public interface Test {

    /**
     * Will be called repeatedly with candidate classes. Must return True if a class
     * is to be included in the results, false otherwise.
     *
     * @param type
     *          the type
     * @return true, if successful
     */
    boolean matches(Class<?> type);
  }

  /**
   * A Test that checks to see if each class is assignable to the provided class. Note
   * that this test will match the parent type itself if it is presented for matching.
   * 判断是否类继承了指定的类或接口
   */
  public static class IsA implements Test {

    /** The parent. */
    //绑定父类
    private Class<?> parent;

    /**
     * Constructs an IsA test using the supplied Class as the parent class/interface.
     * 注入父类
     * @param parentType
     *          the parent type
     */
    public IsA(Class<?> parentType) {
      this.parent = parentType;
    }

    /** Returns true if type is assignable to the parent type supplied in the constructor. */
    @Override
    public boolean matches(Class<?> type) {
      return type != null && parent.isAssignableFrom(type);
    }

    @Override
    public String toString() {
      return "is assignable to " + parent.getSimpleName();
    }
  }

  /**
   * A Test that checks to see if each class is annotated with a specific annotation. If it
   * is, then the test returns true, otherwise false.
   * 判断类是否使用了指定注解
   */
  public static class AnnotatedWith implements Test {

    /** The annotation. */
    //绑定注解
    private Class<? extends Annotation> annotation;

    /**
     * Constructs an AnnotatedWith test for the specified annotation type.
     * 注入注解
     * @param annotation
     *          the annotation
     */
    public AnnotatedWith(Class<? extends Annotation> annotation) {
      this.annotation = annotation;
    }

    /** Returns true if the type is annotated with the class provided to the constructor. */
    @Override
    public boolean matches(Class<?> type) {
      return type != null && type.isAnnotationPresent(annotation);
    }

    @Override
    public String toString() {
      return "annotated with @" + annotation.getSimpleName();
    }
  }
  ...
  }
  • 使用Test接口相关方法
public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) {
    if (packageNames == null) {
      return this;
    }

    //查找类的实现,创建Test实现isA
    Test test = new IsA(parent);
    for (String pkg : packageNames) {
      //传入test 查找包下的所有类,加载类,调用test.matches检测是否符合要求
      find(test, pkg);
    }

    return this;
  }

public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
    if (packageNames == null) {
      return this;
    }

    //查找类是否使用注解,创建Test实现AnnotatedWith 
    Test test = new AnnotatedWith(annotation);
    for (String pkg : packageNames) {
      //传入检测方法test,查找包下所有类,加载类,调用test.matches检测是否符合要求
      find(test, pkg);
    }

    return this;
  }

3.单例模式

  • 单例模式:环境中有且仅有一个实例对象
  • 优点:
    • 1.避免频繁地创建对象
    • 2.减少了GC次数
    • 3.节省内存资源
    • 4.加快对象访问速度
  • 单例例子
//懒汉,延迟加载,get的时候才去初始化,对于创建对象会消耗比较多资源的情况比较适用
public class Singleton {
 
	private static Singleton instance=null;
	
	private Singleton() {};
	
	public static Singleton getInstance(){
		
		if(instance==null){
			instance=new Singleton();
		}
		return instance;
	}
}
//饿汉,
public class Singleton {
 
	private static Singleton instance=null;
	
	private Singleton() {};
	
	public static Singleton getInstance(){
		
		if(instance==null){
			instance=new Singleton();
		}
		return instance;
	}
}
//双重校验锁
//volatile syschronized 进行双重校验
public class Singleton {

    public volatile  Singleton _INSTANCE = null;

    public Singleton getInstance(){
        if(_INSTANCE == null){
            synchronized (Singleton.class){
                if(_INSTANCE == null){
                    _INSTANCE =  new Singleton();
                }
            }
        }
        return  _INSTANCE;
    }
}
//内部类
//类加载机制保证只创建一个对象。
//第一次访问类中静态字段时就触发类的加载,并且同一个类只加载一次。
//由类加载器负责加锁,保证线程安全
//因而内部类的方法会比双重校验锁更加简洁,也同样安全
public class Singleton{
 
	
	private Singleton() {};
	
	private static class SingletonHolder{
		private static Singleton instance=new Singleton();
	} 
	
	public static Singleton getInstance(){
		return SingletonHolder.instance;
	}
}

//枚举类

5.VFS

  • VFS Virtual File System 用来查找指定路径下的资源
  • Mybatis中提供了JBoss6VFS DefaultVFS的两个实现