在MyBatis-Spring的项目中,我们一般会为MyBatis配置两个配置文件 beans-mybatis.xml mybatis-config.xml
其中 beans-mybatis.xml 中配置的是MyBatis 和 Spring结合使用时委托给 spring 管理的 bean。
mybatis-config.xml 中是MyBatis 自身的配置。

例:beans-mybatis.xml

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource" p:configLocation="classpath:mybatis-config.xml"
  2. p:mapperLocations="classpath:mapper/**/*.xml" />
  3. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  4. <property name="dataSource" ref="dataSource" />
  5. </bean>
  6. <tx:annotation-driven transaction-manager="transactionManager" />
  7. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.cn.kvn.usage.dao" />

mybatis-config.xml:

  1. <configuration>
  2. <settings>
  3. <setting name="lazyLoadingEnabled" value="false" />
  4. <!-- logback日志指定打印sql用 -->
  5. <setting name="logPrefix" value="dao." />
  6. </settings>
  7. <typeAliases>
  8. <!-- 别名定义 -->
  9. </typeAliases>
  10. <!-- 插件 -->
  11. <plugins>
  12. <plugin interceptor="com.cn.kvn.framework.jdbc.mybatis.interceptor.PageInterceptor">
  13. <property name="dialectClassName" value="com.cn.kvn.framework.jdbc.mybatis.interceptor.MySQLDialect" />
  14. </plugin>
  15. </plugins>
  16. </configuration>

mybatis-spring的入口就在bean的定义那里(beans-mybatis.xml ): SqlSessionFactoryBean 、MapperScannerConfigurer

#####1. SqlSessionFactoryBean

SqlSessionFactoryBean实现了 InitializingBean ,在 afterPropertiesSet() 中会对 mybatis的配置 p:configLocation="classpath:mybatis-config.xml" 、 p:mapperLocations="classpath:mapper/**/*.xml" 进行解析。

org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory():

  1. protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
  2. Configuration configuration; // MyBatis 的配置,存放 mybatis-config.xml 中的配置
  3. XMLConfigBuilder xmlConfigBuilder = null; // mybatis-config.xml 的解析器,解析出来的内容放在 Configuration 中
  4. if (this.configLocation != null) {
  5. xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  6. configuration = xmlConfigBuilder.getConfiguration();
  7. } else {
  8. configuration = new Configuration();
  9. configuration.setVariables(this.configurationProperties);
  10. }
  11.  
  12. if (this.objectFactory != null) {
  13. configuration.setObjectFactory(this.objectFactory);
  14. }
  15.  
  16. if (this.objectWrapperFactory != null) {
  17. configuration.setObjectWrapperFactory(this.objectWrapperFactory);
  18. }
  19.  
  20. if (hasLength(this.typeAliasesPackage)) { // typeAlias : 类型别名,用于sqlmap中类型(parameterType、resultType)的配置
  21. String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
  22. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  23. for (String packageToScan : typeAliasPackageArray) {
  24. configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); // 注册 typeAliases
  25. }
  26. }
  27.  
  28. if (!isEmpty(this.typeAliases)) { // typeAlias : 类型别名,用于sqlmap中类型(parameterType、resultType)的配置
  29. for (Class<?> typeAlias : this.typeAliases) {
  30. configuration.getTypeAliasRegistry().registerAlias(typeAlias); // 注册 typeAliases
  31. }
  32. }
  33.  
  34. if (!isEmpty(this.plugins)) { // plugins : mybatis 的插件,即 MyBatis 的拦截器和扩展点。可以针对Executor 、 StatementHandler 、 ResultSetHandler 和 PameterHandler 进行拦截扩展
  35. for (Interceptor plugin : this.plugins) {
  36. configuration.addInterceptor(plugin); // 添加 MyBatis Interceptor
  37. }
  38. }
  39.  
  40. if (hasLength(this.typeHandlersPackage)) { // typeHandler : 类型转换器,对java类型和sqlmap中的类型进行转换
  41. String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
  42. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  43. for (String packageToScan : typeHandlersPackageArray) {
  44. configuration.getTypeHandlerRegistry().register(packageToScan); // 注册 typeHandler
  45. }
  46. }
  47.  
  48. if (!isEmpty(this.typeHandlers)) { // typeHandler : 类型转换器,对java类型和sqlmap中的类型进行转换
  49. for (TypeHandler<?> typeHandler : this.typeHandlers) {
  50. configuration.getTypeHandlerRegistry().register(typeHandler); // 注册 typeHandler
  51. }
  52. }
  53.  
  54. if (xmlConfigBuilder != null) {
  55. try {
  56. xmlConfigBuilder.parse(); // xmlConfigBuilder解析 mybatis-spring.xml 配置
  57. } catch (Exception ex) {
  58. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  59. } finally {
  60. ErrorContext.instance().reset(); // 重置 ErrorContext
  61. }
  62. }
  63.  
  64. if (this.transactionFactory == null) {
  65. this.transactionFactory = new SpringManagedTransactionFactory(); // 默认的 transactionFactory
  66. }
  67.  
  68. Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
  69. configuration.setEnvironment(environment);
  70.  
  71. if (this.databaseIdProvider != null) {
  72. try {
  73. configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); // DatabaseId 用于对多数据库的支持
  74. } catch (SQLException e) {
  75. throw new NestedIOException("Failed getting a databaseId", e);
  76. }
  77. }
  78.  
  79. if (!isEmpty(this.mapperLocations)) {
  80. for (Resource mapperLocation : this.mapperLocations) {
  81. if (mapperLocation == null) {
  82. continue;
  83. }
  84.  
  85. try {
  86. XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  87. configuration, mapperLocation.toString(), configuration.getSqlFragments());
  88. xmlMapperBuilder.parse(); // xmlMapperBuilder:解析 mapperLocation 中指定的 sqlmap 文件,即解析 sql 语句
  89. } catch (Exception e) {
  90. throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
  91. } finally {
  92. ErrorContext.instance().reset(); // 重置 ErrorContext
  93. }
  94.  
  95. }
  96. }
  97. return this.sqlSessionFactoryBuilder.build(configuration); // 将 configuration 配置设置到 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory 中
  98. }

附:ErrorContext: 解析配置文件时,用于存储异常信息(ThreadLocal实现的)

跟配置解析相关的类:

XMLConfigBuilder : 解析 mybatis-config.xml 中 MyBatis 自身的配置。
XmlMapperBuilder : 解析 sqlmap 中的配置,包括 <resultMap> 和 sql 语句
  ResultMapResolver : 解析 sqlmap 中的 <resultMap> 节点
  XMLStatementBuilder : 解析 sql 语句(<insert>、<delete>、<update>、<select>)

举例:mybatis-config.xml 的解析是通过 XMLConfigBuilder 来完成的

mybatis-config.xml中能够配置的属性:(XMLConfigBuilder#parseConfiguration(XNode))

  1. private void parseConfiguration(XNode root) {
  2. try {
  3. propertiesElement(root.evalNode("properties")); //issue #117 read properties first
  4. typeAliasesElement(root.evalNode("typeAliases"));
  5. pluginElement(root.evalNode("plugins"));
  6. objectFactoryElement(root.evalNode("objectFactory"));
  7. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  8. settingsElement(root.evalNode("settings"));
  9. environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
  10. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  11. typeHandlerElement(root.evalNode("typeHandlers"));
  12. mapperElement(root.evalNode("mappers"));
  13. } catch (Exception e) {
  14. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  15. }
  16. }

其中<settings>标签可以配置的属性如下:(XMLConfigBuilder#settingsElement(XNode))

  1. configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
  2. configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  3. configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
  4. configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  5. configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
  6. configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
  7. configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
  8. configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  9. configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  10. configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
  11. configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  12. configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  13. configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
  14. configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
  15. configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  16. configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
  17. configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
  18. configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  19. configuration.setLogPrefix(props.getProperty("logPrefix"));
  20. configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
  21. configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));

#####2. MapperScannerConfigurer

MapperScannerConfigurer 的作用是扫描 java 的 Dao 接口,来与 mapper 做映射关系

org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)

  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  2. if (this.processPropertyPlaceHolders) {
  3. processPropertyPlaceHolders();
  4. }
  5.  
  6. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  7. scanner.setAddToConfig(this.addToConfig);
  8. scanner.setAnnotationClass(this.annotationClass);
  9. scanner.setMarkerInterface(this.markerInterface);
  10. scanner.setSqlSessionFactory(this.sqlSessionFactory);
  11. scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  12. scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  13. scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  14. scanner.setResourceLoader(this.applicationContext);
  15. scanner.setBeanNameGenerator(this.nameGenerator);
  16. scanner.registerFilters(); // java类型过滤器,排除一些不符合要求的 Dao
  17. scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); // 进行 Dao 扫描,扫描 basePackage 包下面的类
  18. }

org.mybatis.spring.mapper.ClassPathMapperScanner#scan(String... basePackages):

  1. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  2. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); // 调用 ClassPathBeanDefinitionScanner#doScan()解析出bean
  3. for (BeanDefinitionHolder holder : beanDefinitions) {
  4. GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
  5.  
  6. // the mapper interface is the original class of the bean
  7. // but, the actual class of the bean is MapperFactoryBean
  8. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); // mapperInterface 是 Dao 对应的 bean 的原始类型
  9. definition.setBeanClass(MapperFactoryBean.class); // MapperFactoryBean 是 Dao 对应的 bean 的实际类型。也就是所有的 Dao 对应的 bean ,最后都是 MapperFactoryBean,通过 MapperFactoryBean 来做 Dao 的代理,将CRUD操作分发到具体的 sqlmap 中的 sql 去执行。
  10.  
  11. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  12.  
  13. boolean explicitFactoryUsed = false;
  14. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  15. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  16. explicitFactoryUsed = true;
  17. } else if (this.sqlSessionFactory != null) {
  18. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  19. explicitFactoryUsed = true;
  20. }
  21.  
  22. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  23. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  24. explicitFactoryUsed = true;
  25. } else if (this.sqlSessionTemplate != null) {
  26. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  27. explicitFactoryUsed = true;
  28. }
  29.  
  30. if (!explicitFactoryUsed) {
  31. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); // 让 MapperFactoryBean 可以通过 @Autowired 注解来进行注入。即注入 xxxDao
  32. }
  33.  
  34. return beanDefinitions;
  35. }

#####3. MapperFactoryBean

MapperFactoryBean 继承了SqlSessionDaoSupport,默认使用了 org.mybatis.spring.SqlSessionTemplate 来操作CRUD方法。

org.mybatis.spring.SqlSessionTemplate 的构造方法:

  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
  2. PersistenceExceptionTranslator exceptionTranslator) {
  3. notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  4. notNull(executorType, "Property 'executorType' is required");
  5. this.sqlSessionFactory = sqlSessionFactory;
  6. this.executorType = executorType;
  7. this.exceptionTranslator = exceptionTranslator; // 默认使用 MyBatisExceptionTranslator ,对 SQL 异常进行友好转换
  8. this.sqlSessionProxy = (SqlSession) newProxyInstance(
  9. SqlSessionFactory.class.getClassLoader(),
  10. new Class[] { SqlSession.class },
  11. new SqlSessionInterceptor());
  12. }

SqlSessionTemplate 又是通过 sqlSessionProxy 来操作CRUD方法的。
sqlSessionProxy 是JDK 动态代理,通过 SqlSessionInterceptor 来拦截 org.apache.ibatis.session.SqlSession 接口里面的 CRUD 操作。

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor:

  1. /**
  2. * Proxy needed to route MyBatis method calls to the proper SqlSession got
  3. * from Spring's Transaction Manager
  4. * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
  5. * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
  6. */
  7. private class SqlSessionInterceptor implements InvocationHandler {
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  9. SqlSession sqlSession = getSqlSession(
  10. SqlSessionTemplate.this.sqlSessionFactory,
  11. SqlSessionTemplate.this.executorType,
  12. SqlSessionTemplate.this.exceptionTranslator); // 从当前线程中获取一个 SqlSession ,如果没有,就创建一个新的 SqlSession。(org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(ExecutorType execType))。这样 spring 和 mybatis 就结合在一起了
  13. try {
  14. Object result = method.invoke(sqlSession, args); // 执行 SqlSession 的方法(CRUD操作)。
  15. if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
  16. // force commit even on non-dirty sessions because some databases require
  17. // a commit/rollback before calling close()
  18. sqlSession.commit(true);
  19. }
  20. return result;
  21. } catch (Throwable t) {
  22. Throwable unwrapped = unwrapThrowable(t); // 对 Method#invoke(Object, Object...) 的异常进行解封装
  23. if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
  24. // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
  25. closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  26. sqlSession = null;
  27. Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); // 对 SQL 异常进行友好转换
  28. if (translated != null) {
  29. unwrapped = translated;
  30. }
  31. }
  32. throw unwrapped;
  33. } finally {
  34. if (sqlSession != null) {
  35. closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  36. }
  37. }
  38. }
  39. }

至此,我们还有一个问题没有解决:我们在调用 xxDao 中的方法 xxMethod 时,mybatis-spring.jar 是如何找到 xxMethod 对应的配置文件 xxMapper.xml 中的 sql 语句的?

它是通过多个 JDK 动态代理,去拦截 mapperInterface(xxDao)中的方法来实现的。具体可以看下图:

通过上图,前面的疑问就迎刃而解了。^_^

附:MapperFactoryBean 中的 sqlSessionFactory 是如何注入的?

在 Spring 容器里面 IJobBeforehandRetryDao.java 对应的 beanName = "IJobBeforehandRetryDao"

相应的 RootBeanDefinition为:

  1. Root bean: class [org.mybatis.spring.mapper.MapperFactoryBean]; scope=singleton; abstract=false;
  2. lazyInit=false; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false;
  3. factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null;
  4. defined in file [E:\gitWorkspace\dal-job\dal-job-core\target\classes\com\kvn\dal\core\dao\IJobBeforehandRetryDao.class]

IJobBeforehandRetryDao 对应的 bean 是一个 FactoryBean。Spring 对 FactoryBean 里面的属性有特殊处理。

AbstractAutowireCapableBeanFactory#autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs)

MapperFactoryBean#setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)会被调用。

这样,MapperFactoryBean 中的属性 sqlSession就会被赋值。 this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);

MyBatis 与 Spring 是如何结合在一起工作的——mybatis-spring(version:1.2.2)的更多相关文章

  1. spring和mybatis集成,自动生成model、mapper,增加mybatis分页功能

    软件简介 Spring是一个流行的控制反转(IoC)和面向切面(AOP)的容器框架,在java webapp开发中使用广泛.http://projects.spring.io/spring-frame ...

  2. spring 5.x 系列第6篇 —— 整合 mybatis + druid 连接池 (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...

  3. spring 5.x 系列第5篇 —— 整合 mybatis + druid 连接池 (xml配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...

  4. Spring Boot2 系列教程(二十二)整合 MyBatis 多数据源

    关于多数据源的配置,前面和大伙介绍过 JdbcTemplate 多数据源配置,那个比较简单,本文来和大伙说说 MyBatis 多数据源的配置. 其实关于多数据源,我的态度还是和之前一样,复杂的就直接上 ...

  5. Spring Boot教程(三十七)整合MyBatis

    Spring中整合MyBatis就不多说了,最近大量使用Spring Boot,因此整理一下Spring Boot中整合MyBatis的步骤.搜了一下Spring Boot整合MyBatis的文章,方 ...

  6. Spring Boot 2.x基础教程:使用MyBatis的XML配置方式

    上一篇我们介绍了如何在Spring Boot中整合我们国人最常用的MyBatis来实现对关系型数据库的访问.但是上一篇中使用了注解方式来实现,而对于很多MyBatis老用户还是习惯于XML的开发方式, ...

  7. 从零搭建Spring Boot脚手架(3):集成mybatis

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文集成了一些基础的功能,比如统一返回体.统一异常处理.快速类型转换.参数校验等常用必备功能,并编写了一些单元测试进行验证,今天 ...

  8. spring boot中连接数据库报错500(mybatis)

    spring boot中连接数据库报错500(mybatis) pom.xml中的依赖 <!-- 集成mybatis--> <dependency> <groupId&g ...

  9. 从零搭建Spring Boot脚手架(5):整合 Mybatis Plus

    1. 前言 在上一文中我根据Mybatis中Mapper的生命周期手动实现了一个简单的通用Mapper功能,但是遗憾的是它缺乏实际生产的检验.因此我选择更加成熟的一个Mybatis开发增强包.它就是已 ...

随机推荐

  1. CAS (8) —— Mac下配置CAS到JBoss EAP 6.4(6.x)的Standalone模式(服务端)

    CAS (8) -- Mac下配置CAS到JBoss EAP 6.4(6.x)的Standalone模式(服务端) jboss版本: jboss-eap-6.4-CVE-2015-7501 jdk版本 ...

  2. Material Design 相关资源

    Materialpalette -- Material配色工具 Materialup -- Material设计灵感: Material Design 相关好文: <超全面总结!深聊MATERI ...

  3. 命令行模式启动VMWare虚拟机

    工作中使用到在centos中安装vmware Workstation部署虚拟机,以前都是使用图形界面启动虚拟机,由此要调整VNC的分辨率大小,重启VNC Server后所有虚拟机都关闭了.事后分析可能 ...

  4. java 正则表达式 验证字符串 只包含汉字英文数字

    String content = “testContent”; String regex="^[a-zA-Z0-9\u4E00-\u9FA5]+$"; Pattern patter ...

  5. yizhihx ubuntu config

    install shadowsocks:http://www.cnblogs.com/huangshiyu13/p/8973967.html download: https://pan.baidu.c ...

  6. C语言中内存分配问题:

    推荐: C语言中内存分配 Linux size命令和C程序的存储空间布局 本大神感觉,上面的链接的内容,已经很好的说明了: 总结一下: 对于一个可执行文件,在linux下可以使用 size命令列出目标 ...

  7. Java并发编程笔记—摘抄—基础知识

    什么是线程安全 当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 竞态 ...

  8. Java如何在正则表达式中匹配重复单词?

    在Java编程中,如何在正则表达式中匹配重复单词? 以下示例显示了如何使用regex.Matcher类的p.matcher()方法和m.group()方法在正则表达式中搜索重复的单词. package ...

  9. svn -- svn简介

    一.为什么需要SVN 你们在做中级项目中,都是采用小组合作开发的?那么说说你们在后期整合中遇到问题? 主要应用于: 1.协作开发 2.远程协作 3.版本回退 二.什么是SVN l svn全称SubVe ...

  10. 手机web——自适应网页设计(html/css控制)http://mobile.51cto.com/ahot-409516.htm

    http://mobile.51cto.com/ahot-409516.htm 一. 允许网页宽度自动调整: "自适应网页设计"到底是怎么做到的? 其实并不难. 首先,在网页代码的 ...