MyBatis初始化过程解析----广西11选5平台出租源码解析

  • 准备工作
  • 为了看清楚广西11选5平台出租的 Q1446595067 整个初始化过程,先创建一个简单的Java项目,目录结构如下图所示:

    1.1 Product 产品实体类

    public class Product {
        private long id;
        private String productName;
        private String productContent;
        private String price;
        private int sort;
        private int falseSales;
        private long category_id;
        private byte type;
        private byte state;
        // PS:省略setter、getter函数
    }

    1.2 ProductMapper 产品持久化接口

    public interface ProductMapper {
        /**
         * 查询所有的产品
         * @return
         */
        List<Product> selectProductList();
    }

    1.3 ProductMapper.xml 产品映射文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    
    <mapper namespace="team.njupt.mapper.ProductMapper">
        <select id="selectProductList" resultType="team.njupt.entity.Product">
            select * from product
        </select>
    </mapper>

    1.4 db.properties 数据库配置文件

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/waimai?useUnicode=true&characterEncoding=utf8
    username=root
    password=xxxxxx

    1.5 mybatis.xml MyBatis的配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties resource="db.properties">
            <!--<property name="username" value="dev_user"/>-->
            <!--<property name="password" value="F2Fa3!33TYyg"/>-->
        </properties>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="team/njupt/mapper/ProductMapper.xml"/>
        </mappers>
    </configuration>

    1.6 Main 主函数

    public class Main {
        public static void main(String[] args) throws IOException {
    
            String resource = "mybatis.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new
                                                 SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
                List<Product> productList = productMapper.selectProductList();
                for (Product product : productList) {
                    System.out.printf(product.toString());
                }
            } finally {
                sqlSession.close();
            }
        }
    }
    1. MyBatis初始化过程
      2.1 获取配置文件

    当系统初始化时,首先会读取配置文件,并将其解析成InputStream
    java
    String resource = "mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    2.2 创建SqlSessionFactoryBuilder对象

    从SqlSessionFactoryBuilder的名字中可以看出,SqlSessionFactoryBuilder是用来创建SqlSessionFactory对象的。
    来看一下SqlSessionFactoryBuilder源码:

    SqlSessionFactoryBuilder中只有一些重载的build函数,这些build函数的入参都是MyBatis配置文件的输入流,返回值都是SqlSessionFactory;由此可见,SqlSessionFactoryBuilder的作用很纯粹,就是用来通过配置文件创建SqlSessionFactory对象的。
    2.3 SqlSessionFactory创建过程

    下面具体来看一下,build函数是如何创建SqlSessionFactory对象的。(暂时可以先跳过)

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
              XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
              return build(parser.parse());
        } catch (Exception e) {
              throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
              ErrorContext.instance().reset();
              try {
                    inputStream.close();
              } catch (IOException e) {
                    // Intentionally ignore. Prefer previous error.
              }
        }
    }

    2.3.1 构造XMLConfigBuilder对象

    build函数首先会构造一个XMLConfigBuilder对象,从名字上大致可以猜到,该对象是用来解析XML配置文件的。下面来看一下XMLConfigBuilder的体系结构。

    XMLxxxBuilder是用来解析XML配置文件的,不同类型XMLxxxBuilder用来解析MyBatis配置文件的不同部位。比如:XMLConfigBuilder用来解析MyBatis的配置文件,XMLMapperBuilder用来解析MyBatis中的映射文件(如上文提到的ProductMapper.xml),XMLStatementBuilder用来解析映射文件中的SQL语句。
    
    这些XMLxxxBuilder都有一个共同的父类——BaseBuilder。这个父类维护了一个全局的Configuration对象,MyBatis的配置文件解析后就以Configuration对象的形式存储。
    
    当创建XMLConfigBuilder对象时,就会初始化Configuration对象,并且在初始化Configuration对象的时候,一些别名会被注册到Configuration的typeAliasRegistry容器中。
    
    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }
    
    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    
        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        typeAliasRegistry.registerAlias("LRU", LruCache.class);
        typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
        ……
    }

    2.3.2 解析配置文件

    当有了XMLConfigBuilder对象之后,接下来就可以用它来解析配置文件了。

    private void parseConfiguration(XNode root) {
        try {
          // 解析<properties>节点
          propertiesElement(root.evalNode("properties"));
          // 解析<settings>节点
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          // 解析<typeAliases>节点
          typeAliasesElement(root.evalNode("typeAliases"));
          // 解析<plugins>节点
          pluginElement(root.evalNode("plugins"));
          // 解析<objectFactory>节点
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          // 解析<reflectorFactory>节点
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // 解析<environments>节点
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 解析<mappers>节点
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

    从上述代码中可以看到,XMLConfigBuilder会依次解析配置文件中的<properties>、< settings >、< environments>、< typeAliases >、< plugins >、< mappers >等属性。下面介绍下几个重要属性的解析过程。

    2.3.2.1 节点的解析过程

    节点定义
    
    <properties resource="org/mybatis/example/config.properties">
    <property name="username" value="dev_user"/>
    <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    
    节点解析
    
    /**
    * @Param context <properties>节点
    */
    private void propertiesElement(XNode context) throws Exception {
      if (context != null) {
        // 获取<properties>节点的所有子节点
        Properties defaults = context.getChildrenAsProperties();
        // 获取<properties>节点上的resource属性
        String resource = context.getStringAttribute("resource");
        // 获取<properties>节点上的url属性
        String url = context.getStringAttribute("url");
        // resource和url不能同时存在
        if (resource != null && url != null) {
          throw new BuilderException("The properties element cannot specify both a URL and"+
            " a resource based property file reference.  Please specify one or the other.");
        }
        if (resource != null) {
          // 获取resource属性值对应的properties文件中的键值对,并添加至defaults容器中
          defaults.putAll(Resources.getResourceAsProperties(resource));
        } else if (url != null) {
          // 获取url属性值对应的properties文件中的键值对,并添加至defaults容器中
          defaults.putAll(Resources.getUrlAsProperties(url));
        }
        // 获取configuration中原本的属性,并添加至defaults容器中
        Properties vars = configuration.getVariables();
        if (vars != null) {
          defaults.putAll(vars);
        }
        parser.setVariables(defaults);
        // 将defaults容器添加至configuration中
        configuration.setVariables(defaults);
      }
    }
    
    首先读取<resources>节点下的所有<resource>节点,并将每个节点的name和value属性存入Properties中。
    然后读取<resources>节点上的resource、url属性,并获取指定配置文件中的name和value,也存入Properties中。(PS:由此可知,如果resource节点上定义的属性和properties文件中的属性重名,那么properties文件中的属性值会覆盖resource节点上定义的属性值。)
    最终,携带所有属性的Properties对象会被存储在Configuration对象中。

    2.3.2.2 节点的解析过程

    节点的定义如下:
    
    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="multipleResultSetsEnabled" value="true"/>
    </settings>
    
    节点的解析过程:
    <settings>属性的解析过程和 <properties>属性的解析过程极为类似,这里不再赘述。最终,所有的setting属性都被存储在Configuration对象中。

    2.3.2.3 属性的解析过程

    <typeAliases>属性的定义方式有如下两种:

    方式1:
    
    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
      <typeAlias alias="Blog" type="domain.blog.Blog"/>
    </typeAliases>
    
    方式2:
    
    <typeAliases>
      <package name="domain.blog"/>
    </typeAliases>

    采用这种方式时,MyBatis会为指定包下的所有类起一个别名,该别名为首字母小写的类名。

    <typeAliases>节点的解析过程如下:

    private void typeAliasesElement(XNode parent) {
      if (parent != null) {
        // 遍历<typeAliases>下的所有子节点
        for (XNode child : parent.getChildren()) {
          // 若当前结点为<package>
          if ("package".equals(child.getName())) {
            // 获取<package>上的name属性(包名)
            String typeAliasPackage = child.getStringAttribute("name");
            // 为该包下的所有类起个别名,并注册进configuration的typeAliasRegistry中
            configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
          } else {// 如果当前结点为< typeAlias >
            // 获取alias和type属性
            String alias = child.getStringAttribute("alias");
            String type = child.getStringAttribute("type");
            // 注册进configuration的typeAliasRegistry中
            try {
              Class<?> clazz = Resources.classForName(type);
              if (alias == null) {
                typeAliasRegistry.registerAlias(clazz);
              } else {
                typeAliasRegistry.registerAlias(alias, clazz);
              }
            }
            catch (ClassNotFoundException e) {
              throw new BuilderException("Error registering typeAlias for ‘" + alias + "‘. Cause: " + e, e);
            }
          }
        }
      }
    }
    
    如果<typeAliases>节点下定义了<package>节点,那么MyBatis会给该包下的所有类起一个别名(以类名首字母小写作为别名)
    如果<typeAliases>节点下定义了<typeAlias>节点,那么MyBatis就会给指定的类起指定的别名。
    这些别名都会被存入configuration的typeAliasRegistry容器中。

    2.3.2.4 节点的解析过程

    <mappers>节点的定义方式有如下四种:

    方式1:

    <mappers>
        <package name="org.mybatis.builder"/>
    </mappers>

    方式2:

    <mappers>
        <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
    </mappers>

    方式3:

    <mappers>
        <mapper url="file:///var/mappers/AuthorMapper.xml"/>
    </mappers>

    方式4:

    <mappers>
        <mapper class="org.mybatis.builder.AuthorMapper"/>
    </mappers>

    <mappers>节点的解析过程如下:

    private void mapperElement(XNode parent) throws Exception {
      if (parent != null) {
        // 遍历<mappers>下所有子节点
        for (XNode child : parent.getChildren()) {
          // 如果当前节点为<package>
          if ("package".equals(child.getName())) {
            // 获取<package>的name属性(该属性值为mapper class所在的包名)
            String mapperPackage = child.getStringAttribute("name");
            // 将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中
            configuration.addMappers(mapperPackage);
          }else {// 如果当前节点为<mapper>
            // 依次获取resource、url、class属性
            String resource = child.getStringAttribute("resource");
            String url = child.getStringAttribute("url");
            String mapperClass = child.getStringAttribute("class");
            // 解析resource属性(Mapper.xml文件的路径)
            if (resource != null && url == null && mapperClass == null) {
              ErrorContext.instance().resource(resource);
              // 将Mapper.xml文件解析成输入流
              InputStream inputStream = Resources.getResourceAsStream(resource);
              // 使用XMLMapperBuilder解析Mapper.xml,并将Mapper Class注册进configuration对象的mapperRegistry容器中
              XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
              mapperParser.parse();
            } else if (resource == null && url != null && mapperClass == null) {
    
              // 解析url属性(Mapper.xml文件的路径)
              ErrorContext.instance().resource(url);
              InputStream inputStream = Resources.getUrlAsStream(url);
              XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
              mapperParser.parse();
            }else if (resource == null && url == null && mapperClass != null) {
    
               // 解析class属性(Mapper Class的全限定名)
              // 将Mapper Class的权限定名转化成Class对象
              Class<?> mapperInterface = Resources.classForName(mapperClass);
              // 注册进configuration对象的mapperRegistry容器中
              configuration.addMapper(mapperInterface);
            } else {
              throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
            }
          }
        }
      }
    }
    
    MyBatis会遍历<mappers>下所有的子节点,如果当前遍历到的节点是<package>,则MyBatis会将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中。
    如果当前节点为<mapper>,则会依次获取resource、url、class属性,解析映射文件,并将映射文件对应的Mapper Class注册到configuration的mapperRegistry容器中。

    其中,<mapper>节点的解析过程如下

    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
    configuration.getSqlFragments());
    mapperParser.parse();

    在解析前,首先需要创建XMLMapperBuilder,创建过程如下:

    private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource,
                                                            Map<String, XNode> sqlFragments) {
      // 将configuration赋给BaseBuilder
      super(configuration);
      // 创建MapperBuilderAssistant对象(该对象为MapperBuilder的协助者)
      this.builderAssistant = new  MapperBuilderAssistant(configuration, resource);
      this.parser = parser;
      this.sqlFragments = sqlFragments;
      this.resource = resource;
    }
    
    首先会初始化父类BaseBuilder,并将configuration赋给BaseBuilder;
    然后创建MapperBuilderAssistant对象,该对象为XMLMapperBuilder的协助者,用来协助XMLMapperBuilder完成一些解析映射文件的动作。

    当有了XMLMapperBuilder后,便可进入解析<mapper>的过程:

    public void parse() {
      // 若当前的Mapper.xml尚未被解析,则开始解析
      // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了
      if (!configuration.isResourceLoaded(resource)) {
        // 解析<mapper>节点
        configurationElement(parser.evalNode("/mapper"));
        // 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析
        configuration.addLoadedResource(resource);
        // 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中
        bindMapperForNamespace();
      }
    
      parsePendingResultMaps();
      parsePendingCacheRefs();
      parsePendingStatements();
    }

    configurationElement函数

    private void configurationElement(XNode context) {
      try {
        // 获取<mapper>节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.equals("")) {
          throw new BuilderException("Mapper‘s namespace cannot be empty");
        }
        // 将namespace属性值赋给builderAssistant
        builderAssistant.setCurrentNamespace(namespace);
        // 解析<cache-ref>节点
        cacheRefElement(context.evalNode("cache-ref"));
        // 解析<cache>节点
        cacheElement(context.evalNode("cache"));
        // 解析<parameterMap>节点
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 解析<resultMap>节点
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        // 解析<sql>节点
        sqlElement(context.evalNodes("/mapper/sql"));
        // 解析sql语句
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
      }
      catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
      }
    }

    resultMapElements函数
    该函数用于解析映射文件中所有的<resultMap>节点,这些节点会被解析成ResultMap对象,存储在Configuration对象的resultMaps容器中。

    <resultMap>节点定义如下:

    <resultMap id="userResultMap" type="User">
      <constructor>
         <idArg column="id" javaType="int"/>
         <arg column="username" javaType="String"/>
      </constructor>
      <result property="username" column="user_name"/>
      <result property="password" column="hashed_password"/>
    </resultMap>

    <resultMap>节点的解析过程:

    private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
      ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
      // 获取<ResultMap>上的id属性
      String id = resultMapNode.getStringAttribute("id",
          resultMapNode.getValueBasedIdentifier());
      // 获取<ResultMap>上的type属性(即resultMap的返回值类型)
      String type = resultMapNode.getStringAttribute("type",
          resultMapNode.getStringAttribute("ofType",
              resultMapNode.getStringAttribute("resultType",
                  resultMapNode.getStringAttribute("javaType"))));
      // 获取extends属性
      String extend = resultMapNode.getStringAttribute("extends");
      // 获取autoMapping属性
      Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
      // 将resultMap的返回值类型转换成Class对象
      Class<?> typeClass = resolveClass(type);
      Discriminator discriminator = null;
      // resultMappings用于存储<resultMap>下所有的子节点
      List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
      resultMappings.addAll(additionalResultMappings);
      // 获取并遍历<resultMap>下所有的子节点
      List<XNode> resultChildren = resultMapNode.getChildren();
      for (XNode resultChild : resultChildren) {
        // 若当前节点为<constructor>,则将它的子节点们添加到resultMappings中去
        if ("constructor".equals(resultChild.getName())) {
          processConstructorElement(resultChild, typeClass, resultMappings);
        }else if("discriminator".equals(resultChild.getName())) {
          // 若当前节点为<discriminator>,则进行条件判断,并将命中的子节点添加到resultMappings中去
          discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
        }else {
          // 若当前节点为<result>、<association>、<collection>,则将其添加到resultMappings中去
          // PS:flags仅用于区分当前节点是否是<id>或<idArg>,因为这两个节点的属性名为name,而其他节点的属性名为property
          List<ResultFlag> flags = new ArrayList<ResultFlag>();
          if ("id".equals(resultChild.getName())) {
            flags.add(ResultFlag.ID);
          }
          resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }
      }
      // ResultMapResolver的作用是生成ResultMap对象,并将其加入到Configuration对象的resultMaps容器中(具体过程见下)
      ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
      try {
        return resultMapResolver.resolve();
      }catch (IncompleteElementException  e) {
        configuration.addIncompleteResultMap(resultMapResolver);
        throw e;
      }
    }

    ResultMapResolver这个类很纯粹,有且仅有一个函数resolve,用于构造ResultMap对象,并将其存入Configuration对象的resultMaps容器中;而这个过程是借助于MapperBuilderAssistant.addResultMap完成的。

    public ResultMap resolve() {
      return assistant.addResultMap(this.id, this.type, this.extend,  this.discriminator, this.resultMappings, this.autoMapping);
    }

    sqlElement函数
    该函数用于解析映射文件中所有的<sql>节点,并将这些节点存储在当前映射文件所对应的XMLMapperBuilder对象的sqlFragments容器中,供解析sql语句时使用。

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

    2.3.3 创建SqlSessionFactory对象

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse());
      } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
      } finally {
        ErrorContext.instance().reset();
        try {
          inputStream.close();
        } catch (IOException e) {
          // Intentionally ignore. Prefer previous error.
        }
      }
    }

    回过头来再看一下SqlSessionFactory的build函数,刚才说了半天,介绍了XMLConfigBuilder解析映射文件的过程,解析完成之后parser.parse()函数会返回一个包含了映射文件解析结果的configuration对象,紧接着,这个对象将作为参数传递给另一个build函数,如下:

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

    这个函数将configuration作为参数,创建了DefaultSqlSessionFactory对象。
    DefaultSqlSessionFactory是接口SqlSessionFactory的一个实现类,SqlSessionFactory的体系结构如下图所示:

    原文地址:http://blog.51cto.com/13903861/2153170

    时间: 2024-09-30 15:05:14

    MyBatis初始化过程解析----广西11选5平台出租源码解析的相关文章

    ArrayList.add() 方法吉林快-三平台出租源码解析

    吉林快-三平台出租Q1446595067解析源码的方法>>> list.add("hello"); 该方法涉及到的方法被我复制到了一个类中,至于解释,全在注释上.初次解析,别喷我!!! 如有不足望评论,随时补充. package com.nc.sourceCode; import java.lang.reflect.Array;import java.util.ArrayList;import java.util.Arrays;import java.util.Lis

    【转】Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Hashtable介绍第2部分 Hashtable数据结构第3部分 Hashtable源码解析(基于JDK1.6.0_45)第4部分 Hashtable遍历方式第5部分 Hashtable示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3310887.h

    linux下终端11选5平台出租常用命令和vi命令修改文件及保存的使用方法

    首先11选5平台出租haozbbs.comQ1446595067介绍一下Ubuntu下各个目录的一般作用: /这就是根目录,一台电脑有且只有一个根目录,所有的文件都是从这里开始的.举个例子:当你在终端里输入"/home",你其实是在告诉电脑,先从/(根目录)开始,再进入到home目录./root系统管理员(root user)的目录.至于系统管理员的权限有多大我这里就不在废话了.因此,请小心使用root帐号./boot系统启动文件,所有与系统启动有关的文件都保存在这里 . /bin 这

    68:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析及其在Spark中的应用源码解析

    今天给大家带来的是王家林老师的scala编程讲座的第68讲:Scala并发编程原生线程Actor.Cass Class下的消息传递和偏函数实战解析 昨天讲了Actor的匿名Actor及消息传递,那么我们今天来看一下原生线程Actor及CassClass下的消息传递,让我们从代码出发: case class Person(name:String,age:Int)//定义cass Class class HelloActor extends Actor{//预定义一个Actor  def act()

    Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Hashtable介绍第2部分 Hashtable数据结构第3部分 Hashtable源码解析(基于JDK1.6.0_45)第4部分 Hashtable遍历方式第5部分 Hashtable示例 转载:http://www.cnblogs.com/skywang12345/p/3310887.html 第

    Flink 源码解析 —— JobManager 处理 SubmitJob 的过程

    JobManager 处理 SubmitJob https://t.zsxq.com/3JQJMzZ 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门 3.Flink 从0到1学习 -- Flink 配置文件详解 4.Flink 从0到1学习 -- Data Source 介绍 5.Flink 从0到1学习 -- 如何自定义 Data Source ? 6.Flink

    Andfix热修复框架原理及源码解析-上篇

    热补丁介绍及Andfix的使用 Andfix热修复框架原理及源码解析-上篇 Andfix热修复框架原理及源码解析-下篇 1.不知道如何使用的同学,建议看看我上一篇写的介绍热补丁和Andfix的使用,这样你才有一个大概的框架.通过使用Andfix,其实我们心中会有一个大概的轮廓,它的工作原理,大概就是,所谓的补丁文件,就是通过打包工具apkpatch比对新的apk和旧的apk之间的差异.然后让我们的旧包运行的时候,就加载它,把以前的一些信息替换掉.我们现在就抱着这个大方向去深入源码探个究竟!!首先

    第二章 Google guava cache源码解析1--构建缓存器

    1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) 2.使用实例 具体在实际中使用的例子,去查看<第七章 企业项目开发--本地缓存guava cache>,下面只列出测试实例: import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit;

    Android xUtils3源码解析之图片模块

    初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()