构建Spring环境

采用ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");方式构建Spring容器并查看其内部运行过程.

Spring 版本 5.1.3.RELEASE

        <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>

测试类

public class User {
private String username;
private String password; public User(String username, String password) {
this.username = username;
this.password = password;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
}
}

Spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="user" class="com.jimisun.learnspringboot.web.User">
<constructor-arg index="0" value="jimisun"/>
<constructor-arg index="1" value="jimisun"/>
</bean> </beans>

测试方法Main

public class Main {
public static void main(String[] args) {
// 用我们的配置文件来启动一个 ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
System.out.println("context 启动成功");
User user = context.getBean(User.class);
System.out.println(user.toString());
}
}

快速进入Debug查看IOC容器构建源码

ApplicationContext 启动过程中,会创建SPring Bean容器,然后初始化相关Bean,再向Bean中注入其相关依赖。

所以我们仅仅需要Debug跟踪Main方法中ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");这一句代码,查看Spring是如何创建ApplicationContext容器并将xml中的配置信息装配进容器的.

Spring IOC源码步骤分析

第一步: 检查并设置Spring XML配置文件

功能:设置此应用程序上下文的配置文件位置,如果未设置;Spring可以根据需要使用默认值

setConfigLocations(configLocations);

在Main方法Debug启动进入断点,按F7跟进入其方法查看,会进入ClassPathXmlApplicationContext类的构造方法中

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
private Resource[] configResources;
// 如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
...
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
setConfigLocations(configLocations);
if (refresh) {
refresh(); // 核心方法 剩余的所有步骤都在此方法中!!!
}
}
...
}

首先执行"设置配置位置setConfigLocations"的方法,解析SpringXML配置文件地址存储到configLocations属性中。

	public void setConfigLocations(@Nullable String... locations) {
//判断配置路径是否为null
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
//循环将配置文件路径存储到属性configLocations中
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}

第二步:执行创建Bean容器之前的准备工作

注意:除了第一步设置XML配置文件路径,剩余的步骤都在该类的refresh();这个方法中执行,所以我们需要Debug跟进入这个方法

refresh();方法如下所示;因为整个SpringApplication的构建都在这个方法 里面所以就现在这里展现一下和大家混个脸熟.

public void refresh() throws BeansException, IllegalStateException {
 //对下面的代码块添加同步锁
synchronized (this.startupShutdownMonitor) {
 //第二步: 执行创建容器前的准备工作 :记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
prepareRefresh();
  //第三步:创建Bean容器,加载XML配置信息 : 如果存在容器进行销毁旧容器,创建新容器,解析XML配置文件为一个个BeanDefinition定义注册到新容器(BeanFactory)中,注意Bean未初始化
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//第四步: 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
//第五步:加载并执行后置处理器
postProcessBeanFactory(beanFactory);
//执行postProcessBeanFactory()方法
invokeBeanFactoryPostProcessors(beanFactory);
// 实例化拦截Bean创建的后置处理器beanPostProcessors
registerBeanPostProcessors(beanFactory);
//第六步: 初始化Spring容器的消息源
initMessageSource();
//第七步:初始化Spring容器事件广播器
initApplicationEventMulticaster();
// 空方法
onRefresh();
//第八步:注册事件监听器 
registerListeners();
//第九步核心方法:初始化(构造)所有在XML文件中配置的单例非延迟加载的bean
finishBeanFactoryInitialization(beanFactory);
//第十步:清理缓存,如果容器中存Bean名为lifecycleProcessor的Bean 对其进行注册,如果不存在创建一个DefaultLifecycleProcessor进行注册
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 摧毁已经创建的单身人士以避免悬空资源。
destroyBeans();
// 重置'有效'标志。
cancelRefresh(ex);
// 向调用者传播异常。
throw ex;
} finally {
//重置Spring核心的工具类的缓存
resetCommonCaches();
}
}
}

第二步的主要工作:准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符,初始化事件属性。

prepareRefresh();

protected void prepareRefresh() {
// 记录启动时间,
// 将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean类型
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
//打印Logger
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
} // 在上下文环境中初始化任何占位符属性源 空方法 默认情况下不执行任何操作。
initPropertySources();
// 校验 xml 配置文件
getEnvironment().validateRequiredProperties();
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

第三步:创建 Bean 容器,加载并注册 Bean

主要工作:进行销毁旧容器,创建新容器,加载BeanDefinition到BeanFactory

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果ApplicationContext中已经的BeanFactory属性已经有值,销毁此BeanFactory所有 Bean,关闭 BeanFactory,重新创建一个新的Bean容器设置给ApplicationContext的beanFactory属性
if (hasBeanFactory()) {
//销毁容器
destroyBeans();
//创建类型为DefaultListableBeanFactory新容器放入BeanFactory变量中
closeBeanFactory();
}
try {
//创建类型为DefaultListableBeanFactory新容器放入BeanFactory变量中
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置BeanFactory的序列化ID也就是其类名
beanFactory.setSerializationId(getId());
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
//这个方法将根据配置,加载各个Bean,然后放到 BeanFactory 中 注意:这里的加载并不是初始化这个Bean 而是以Key-value的形式存储在beanFactory; beanName-> beanDefinition 的 map
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

第四步:配置 Bean容器: prepareBeanFactory

主要工作:在Bean容器创建完毕会"手动"注册一些特殊的 bean。官网这样解释: " 配置工厂的标准上下文特征,例如上下文的ClassLoader和后处理器 "。

具体方法 : prepareBeanFactory(factory) ;

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 这里设置为加载当前 ApplicationContext 类的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置 Bean的表达式解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//默认添加一个ApplicationContextAwareProcessor的BeanPostProcessor,实现了ApplicationContextAware接口的Bean,Spring会将上下文ApplicationContext注入Bean属性中
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 下面几行的意思就是,如果某个 bean 依赖于以下几个接口的实现类,在自动装配的时候忽略它们,
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
/**
* 下面几行就是为特殊的几个 bean 赋值,如果有 bean 依赖了以下几个,会注入这边相应的值,
* 之前我们说过,"当前 ApplicationContext 持有一个 BeanFactory",这里解释了第一行
* ApplicationContext 还继承了 ResourceLoader、ApplicationEventPublisher、MessageSource
* 所以对于这几个依赖,可以赋值为 this,注意 this 是一个 ApplicationContext
* 那这里怎么没看到为 MessageSource 赋值呢?那是因为 MessageSource 被注册成为了一个普通的 bean
*/ beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
//注册早期后处理器以检测内部bean作为ApplicationListeners
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// 如果检测到LoadTimeWeaver 准备编织 不是我们本章的重点无需关注
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
//默认注册 environment systemEnvironment systemProperties的Bean 我们可以选择覆盖
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
```

第五步: 处理自定义Bean的后置处理器

主要功能:实例化在XML配置中实现了BeanFactoryPostProcessor和BeanPostProcessors接口的Bean并执行其回调方法.注意:此时普通的Bean仍然并没有初始化

//实例化并调用XML配置中实现了BeanFactoryPostProcessors接口的的回调
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
//实例化并调用XML配置中实现了BeanPostProcessors接口的的回调
registerBeanPostProcessors(beanFactory);
//注解:这里在创建完成Bean容器后执行BeanFactoryPostProcessors接口的回调,我们可以在Bean容器初始化完成的时候完成我们自己的业务逻辑(很少用),然后是registerBeanPostProcessors(beanFactory)方法,此方法的官方解释是:"Register bean processors that intercept bean creation(如果存在则注册拦截bean创建的bean后置处理器)"

第六步: 初始化Spring容器的消息源

主要功能: 初始化MessageSource。如果在此上下文中未定义,则使用parent。

// 初始化ApplicationContext的消息源。
initMessageSource();
	protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//判断beanFactory中是否有messageSource的Bean
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// 使MessageSource知道父MessageSource
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
//如果没有父MessageSource,则此消息源设置为父MessageSource
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// 如果没有则创建一个默认的DelegatingMessageSource消息源
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}

第七步: 初始化Spring容器事件广播器

PS : 在实际项目中我们很少会用到Spring的事件广播器,因为现在都是分布式应用了局部通讯很少使用了 一篇很棒的关于Spring容器的事件讲解 https://juejin.im/post/5a543ceb518825734a748c44

主要功能 : 注册Spring的事件广播器用于广播Spring的内置事件和自定义事件

initApplicationEventMulticaster();


protected void initApplicationEventMulticaster() {
//初始化ApplicationEventMulticaster
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
//如果在上下文中没有定义,则创建一个默认的SimpleApplicationEventMulticaster。
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}

第八步:注册事件监听器

主要功能 : 实例化实现ApplicationListener接口的bean。

// 注册监听器
finishBeanFactoryInitialization(beanFactory);
	protected void registerListeners() {
//首先注册静态指定的侦听器
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
//下面是我们自定义的监听器,Spring文档中给出的建议是 "不要在这里初始化FactoryBeans:我们需要保留所有常规bean"
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
//使用已经注册的事件广播器,发布早期的应用程序事件......
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}

第九步: 实例化所有的单例Bean

功能:执行到这一步,Spring.xml配置文件中的特殊的Bean该注册的也注册了,该调用的也调用了,就剩下了普通的Bean了,在这一步就都实例化了.(仅仅是非延迟实例化的单例Bean),也就是说这一步就已经完成了Bean工厂(ApplicationContext)的初始化了.

// 实例化所有SPring.xml配置文件中配置的非延迟实例化的单例Bean
finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化此上下文的转换服务
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
} //如果没有bean后处理器,则注册默认的嵌入值解析器(例如PropertyPlaceholderConfigurer bean)之前注册过;此时,主要用于注释属性值的分辨率。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
} // 尽早初始化LoadTimeWeaverAware bean以允许尽早注册其变换器。
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 停止使用临时ClassLoader进行类型匹配。
beanFactory.setTempClassLoader(null);
// 允许缓存所有bean定义元数据,而不期望进一步的更改。
beanFactory.freezeConfiguration();
// 实例化所有剩余(非延迟初始化)单例。
beanFactory.preInstantiateSingletons();
}

第十步:完成ApplicationContext容器的初始化收

功能:进行相关的容器创建完成时的操作,回收相关资源

 finishRefresh();
resetCommonCaches();
	protected void finishRefresh() {
//清除上下文级资源缓存(例如来自扫描的ASM元数据)。
clearResourceCaches();
//为此上下文初始化生命周期处理器。
initLifecycleProcessor();
// 首先将刷新传播到生命周期处理器。
getLifecycleProcessor().onRefresh();
// 广播最终事件
publishEvent(new ContextRefreshedEvent(this));
// 如果处于活动状态,请参与LiveBeansView
LiveBeansView.registerApplicationContext(this);
}
//清除一些单例的工具类的缓存
protected void resetCommonCaches() {
ReflectionUtils.clearCache();
AnnotationUtils.clearCache();
ResolvableType.clearCache();
CachedIntrospectionResults.clearClassLoader(getClassLoader());
}

应该学习到的知识

  • Spring容器中管理的Bean是Class吗?

可以看到Bean容器中的Bean定义映射关系的Map中存放的是key(String)->GenericBeanDefinition的映射,那么GenericBeanDefinition又是什么呢?

BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。

  • SpringBean是被ApplicationContext管理的吗?

通过Debug的过程中我们可以看到我们使用ClassPathXmlApplicationContext构造的ApplicationContext对象其实在内部维护了一个属性名为beanFactory,我们的SpringBean都被定义在这个属性里面,也就是说beanFactory这个属性才是容器,ApplicationContext仅仅是做了一层包装.那么beanFactory又是什么呢?

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
...
}

可以看到DefaultListableBeanFactory类也是Bean容器,而且是继承了所有其他的容器的功能,可以说是最为强大的容器;例如具有(分层,获取多个容器,注入功能....)

本章小结

第一次参阅源码写的比较慎重,其中由于身体抱恙又有所当误,所以在发布本章的时候也是几天后了,总的来说本章并没有什么重点,仅仅是把Spring的IOC容器的启动过程进行了标注,并未做过多底层的深度剖析,例如loadBeanDefinitions(beanFactory)Spring如何将XMl文件的配置装载入Bean工厂,以及后面的每个注释都可以新开一篇长篇大论的文章,后面尽可能的在Spring Framework深度剖析专栏中更为详细的学习Spring整体架构源码

该教程所属Java工程师之Spring Framework深度剖析专栏,本系列相关博文目录 Java工程师之Spring Framework深度剖析专栏

Spring Framework框架容器核心源码逐步剖析的更多相关文章

  1. 【JavaScript框架封装】使用原生js封装的类似于JQuery的框架及核心源码分享(多文件版本)

    这个版本的JQuery是对上一个版本的JQuery,使用了require.js进行了二次封装,基本上把前面的每一个框架封装成为一个单独的模块,最终的目录结构如下: 由于代码量和目录比较多,这个封装好的 ...

  2. 【JavaScript框架封装】自己动手封装一个涵盖JQuery基本功能的框架及核心源码分享(单文件版本)

    整个封装过程及阅读JQuery源码的过程基本上持续了一个月吧,最终实现了一个大概30%的JQuery功能的框架版本,但是里面涉及的知识点也是非常多的,总共的代码加上相关的注释大概在3000行左右吧,但 ...

  3. Spring Cloud-Ribbon ILoadBalancer负载均衡器核心源码(四)

    Ribbon负载均衡相关类 AbstractloadBalancer ILoadBalancer的抽象实现类 public abstract class AbstractLoadBalancer im ...

  4. 47、Spark SQL核心源码深度剖析(DataFrame lazy特性、Optimizer优化策略等)

    一.源码分析 1. ###入口org.apache.spark.sql/SQLContext.scala sql()方法: /** * 使用Spark执行一条SQL查询语句,将结果作为DataFram ...

  5. 手撕spring核心源码,彻底搞懂spring流程

    引子 十几年前,刚工作不久的程序员还能过着很轻松的日子.记得那时候公司里有些开发和测试的女孩子,经常有问题解决不了的,不管什么领域的问题找到我,我都能帮她们解决.但是那时候我没有主动学习技术的意识,只 ...

  6. Mybatis的初始化和结合Spring Framework后初始化的源码探究

    带着下面的问题进行学习: (1)Mybatis 框架或 Spring Framework 框架对数据层 Mapper 接口做了代理,那是做了 JDK 动态代理还是 CGLIB 代理? (2)Mappe ...

  7. iOS 开源库系列 Aspects核心源码分析---面向切面编程之疯狂的 Aspects

    Aspects的源码学习,我学到的有几下几点 Objective-C Runtime 理解OC的消息分发机制 KVO中的指针交换技术 Block 在内存中的数据结构 const 的修饰区别 block ...

  8. Backbone事件机制核心源码(仅包含Events、Model模块)

    一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性.   二.遗留问题 1.b ...

  9. Spring Framework框架解析(1)- 从图书馆示例来看xml文件的加载过程

    引言 这个系列是我阅读Spring源码后的一个总结,会从Spring Framework框架的整体结构进行分析,不会先入为主的讲解IOC或者AOP的原理,如果读者有使用Spring的经验再好不过.鉴于 ...

随机推荐

  1. MySQL索引优化入门

    索引简介 官方定义:索引(Index) 是帮助MySQL高效获取数据的数据结构.大家一定很好奇,索引为什么是一种数据结构,它又是怎么提高查询的速度?我们拿最常用的二叉树来分析索引的工作原理.看下面的图 ...

  2. Grunt--Less

    摘要: 之前介绍了自动构建工具Grunt,其中有一个模块是"grunt-contrib-less",下面是配置Grunt自动编译less文件. 安装: Grunt是基于node,功 ...

  3. python连接mysql数据库封装

    源码: import pymysql class MysqlConnect(object): # 魔术方法, 初始化, 构造函数 def __init__(self, host, user, pass ...

  4. 苹果官方xcodeprojectbuild设置指南

    https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/ ...

  5. backbone学习笔记:模型(Model)(2)属性验证

    Backbone的属性验证有2种方法: 1.Backbone自带简单的验证方法,但是验证规则需要自己实现 通过validate()方法进行验证,验证规则写在此方法里. var RoomModel = ...

  6. 彻底删除weblogic域方法

    1.删除 D:\Oracle\Middleware\wlserver_10.3\common\nodemanager\nodemanager.domains 里的base_domain域内容 #Dom ...

  7. 【Android】Android中如何取消调转界面后EditText默认获取聚焦问题

    参考资料: https://www.cnblogs.com/dream-cichan/p/aaaa.html http://blog.csdn.net/u013703461/article/detai ...

  8. 【代码审计】JTBC(CMS)_PHP_v3.0 任意文件上传漏洞分析

      0x00 环境准备 JTBC(CMS)官网:http://www.jtbc.cn 网站源码版本:JTBC_CMS_PHP(3.0) 企业版 程序源码下载:http://download.jtbc. ...

  9. Jsoup(一)-- HelloWorld

    1.简介 jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据. ...

  10. map 集合的遍历

    List<Map<String,Object>> autoReplyList= wechatService.queryAutoReplyByOrg(orgId); for(Ma ...