一、前言

  从本博文开始,正式开启Spring及SpringBoot源码分析之旅。这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识。从本文开始我会持续更新,争取在系列文章更完之后,也能让自己对Spring源码有一个系统的认识。

  在此立下一个flag,希望自己能够坚持下去。如果有幸让您能从系列文章中学到丁点的知识,还请评论,关注,或推荐。如有错误还请在评论区指出,一起讨论共同成长。

二、SpringBoot诞生的历史背景

  随着使用 Spring 进行开发的个人和企业越来越多,Spring 也慢慢从一个单一简洁的小框架变成一个大而全的开源软件,Spring 的边界不断的进行扩充,到了后来 Spring 几乎可以做任何事情了,市面上主流的开源软件、中间件都有 Spring 对应组件支持,人们在享用 Spring 的这种便利之后,也遇到了一些问题。Spring 每集成一个开源软件,就需要增加一些基础配置,慢慢的随着人们开发的项目越来越庞大,往往需要集成很多开源软件,因此后期使用 Spirng 开发大型项目需要引入很多配置文件,太多的配置非常难以理解,并容易配置出错,到了后来人们甚至称 Spring 为配置地狱。

  Spring 似乎也意识到了这些问题,急需有这么一套软件可以解决这些问题,这个时候微服务的概念也慢慢兴起,快速开发微小独立的应用变得更为急迫,Spring 刚好处在这么一个交叉点上,于 2013 年初开始的 Spring Boot 项目的研发,2014年4月,Spring Boot 1.0.0 发布。

  Spring Boot 诞生之初,就受到开源社区的持续关注,陆续有一些个人和企业尝试着使用了 Spring Boot,并迅速喜欢上了这款开源软件。直到2016年,在国内 Spring Boot 才被正真使用了起来,期间很多研究 Spring Boot 的开发者在网上写了大量关于 Spring Boot 的文章,同时有一些公司在企业内部进行了小规模的使用,并将使用经验分享了出来。从2016年到2018年,使用 Spring Boot 的企业和个人开发者越来越多。2018年SpringBoot2.0的发布,更是将SpringBoot的热度推向了一个前所未有的高度。

三、SpringBoot诞生的技术基础

 1、Spring的发展历史

(1)spring1.0时代

  Spring的诞生大大促进了JAVA的发展。也降低了企业java应用开发的技术和时间成本。

(2)spring2.0时代
  对spring1.0在繁杂的xml配置文件上做了一定的优化,让配置看起来越来越简单,但是并没语完全解决xml冗余的问题。

(3)spring3.0时代
  可以使用spring提供的java注解来取代曾经xml配置上的问题,似乎我们曾经忘记了发生什么,spring变得前所未有的简单。Spring3.0奠定了SpringBoot自动装配的基础。3.0提供的java注解使得我们可以通过注解的方式来配置spring容器。省去了使用类似于spring-context.xml的配置文件。

  同年,Servlet3.0规范的诞生为SpringBoot彻底去掉xml(web.xml)奠定了了理论基础(对于servlet3.0来说,web.xml不再是必需品。但是Servlet3.0规范还是建议保留web.xml)。

(4)spring4.0时代
  4.0 时代我们甚至连xml配置文件都不需要了完全使用java源码级别的配置与spring提供的注解就能快速的开发spring应用程序,但仍然无法改变Java Web应用程序的运行模式,我们仍然需要将war部署到Web Server 上,才能对外提供服务。

  4.0开始全面支持java8.0

  同年,Servlet3.1规范诞生(tomcat8开始采用Servlet3.1规范)。

 2、Servlet3.0奠定了SpringBoot 零xml配置的基础

  分析SpringBoot如何省去web.xml还得从Servlet3.0的规范说起。Servlet3.0规范规定如下(摘自穆茂强 张开涛翻译的Servlet3.1规范,3.0和3.1在这一点上只有一些细节上的变换,在此不做过多介绍):

  ServletContainerInitializer类通过jar services API查找。对于每一个应用,应用启动时,由容器创建一个ServletContainerInitializer 实例。 框架提供的ServletContainerInitializer实现必须绑定在 jar 包 的META-INF/services 目录中的一个叫做 javax.servlet.ServletContainerInitializer 的文件,根据 jar services API,指定 ServletContainerInitializer 的实现。除 ServletContainerInitializer 外,我们还有一个注解@HandlesTypes。在 ServletContainerInitializer 实现上的@HandlesTypes注解用于表示感兴趣的一些类,它们可能指定了 HandlesTypes 的 value 中的注解(类型、方法或自动级别的注解),或者是其类型的超类继承/实现了这些类之一。无论是否设置了 metadata-complete,@HandlesTypes 注解将应用。当检测一个应用的类看是否它们匹配 ServletContainerInitializer 的 HandlesTypes 指定的条件时,如果应用的一个或多个可选的 JAR 包缺失,容器可能遇到类装载问题。由于容器不能决定是否这些类型的类装载失败将阻止应用正常工作,它必须忽略它们,同时也提供一个将记录它们的配置选项。如果ServletContainerInitializer 实现没有@HandlesTypes 注解,或如果没有匹配任何指定的@HandlesType,那么它会为每个应用使用 null 值的集合调用一次。这将允许 initializer 基于应用中可用的资源决定是否需要初始化 Servlet/Filter。在任何 Servlet Listener 的事件被触发之前,当应用正在启动时,ServletContainerInitializer 的 onStartup 方法将被调用。ServletContainerInitializer’s 的onStartup 得到一个类的 Set,其或者继承/实现 initializer 表示感兴趣的类,或者它是使用指定在@HandlesTypes 注解中的任意类注解的。

  这个规范如何理解呢?

  简单来说,当实现了Servlet3.0规范的容器(比如tomcat7及以上版本)启动时,通过SPI扩展机制自动扫描所有已添加的jar包下的META-INF/services/javax.servlet.ServletContainerInitializer中指定的全路径的类,并实例化该类,然后回调META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的ServletContainerInitializer的实现类的onStartup方法。 如果该类存在@HandlesTypes注解,并且在@HandlesTypes注解中指定了我们感兴趣的类,所有实现了这个类的onStartup方法将会被调用。

  再直白一点来说,存在web.xml的时候,Servlet容器会根据web.xml中的配置初始化我们的jar包(也可以说web.xml是我们的jar包和Servlet联系的中介)。而在Servlet3.0容器初始化时会调用jar包META-INF/services/javax.servlet.ServletContainerInitializer中指定的类的实现(javax.servlet.ServletContainerInitializer中的实现替代了web.xml的作用,而所谓的在@HandlesTypes注解中指定的感兴趣的类,可以理解为具体实现了web.xml的功能,当然也可以有其他的用途)。

四、从Spring源码中分析SpringBoot如何省去web.xml

1、META-INF/services/javax.servlet.ServletContainerInitializer

上一节中我们介绍了SpringBoot诞生的技术基础和Servlet3.0规范。这一章节,我们通过Spring源码来分析,Spring是如何实现省去web.xml的。

如下图所示,在org.springframework:spring-web工程下,META-INF/services/javax.servlet.ServletContainerInitializer文件中,指定了将会被Servlet容器启动时回调的类。

2、SpringServletContainerInitializer

查看 SpringServletContainerInitializer  类的源码,发现确实如如上文所说,实现了 ServletContainerInitializer  ,并且也在 @HandlesTypes 注解中指定了,感兴趣的类 WebApplicationInitializer

可以看到onStartup方法上有一大段注释,翻译一下大致意思:

servlet 3.0+容器启动时将自动扫描类路径以查找实现Spring的webapplicationinitializer接口的所有实现,将其放进一个Set集合中,提供给 SpringServletContainerInitializer  onStartup的第一个参数(翻译结束)。

在Servlet容器初始化的时候会调用 SpringServletContainerInitializer  的onStartup方法,继续看onStartup方法的代码逻辑,在该onStartup方法中利用逐个调用webapplicationinitializer所有实现类中的onStartup方法。

 @HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer { /**
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
* implementations present on the application classpath.
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
* Servlet 3.0+ containers will automatically scan the classpath for implementations
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
* such types to the {@code webAppInitializerClasses} parameter of this method.
* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
* this method is effectively a no-op. An INFO-level log message will be issued notifying
* the user that the {@code ServletContainerInitializer} has indeed been invoked but that
* no {@code WebApplicationInitializer} implementations were found.
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
* they will be instantiated (and <em>sorted</em> if the @{@link
* org.springframework.core.annotation.Order @Order} annotation is present or
* the {@link org.springframework.core.Ordered Ordered} interface has been
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
* method will be invoked on each instance, delegating the {@code ServletContext} such
* that each instance may register and configure servlets such as Spring's
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
* or any other Servlet API componentry such as filters.
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
* @see WebApplicationInitializer#onStartup(ServletContext)
* @see AnnotationAwareOrderComparator
*/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
} if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
} servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
} }

3、WebApplicationInitializer

查看 WebApplicationInitializer  接口,这个接口也就是上文中所说的Servlet3.0规范中 @HandlesTypes(WebApplicationInitializer.class) 注解中所指定的感兴趣的类。

截取一段很重要的注释。这段注释告诉我们实现该接口的类主要需要实现的功能就是web.xml中配置文件中配置的内容。

 /*
* <servlet>
* <servlet-name>dispatcher</servlet-name>
* <servlet-class>
* org.springframework.web.servlet.DispatcherServlet
* </servlet-class>
* <init-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
* </init-param>
* <load-on-startup>1</load-on-startup>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>dispatcher</servlet-name>
* <url-pattern>/</url-pattern>
* </servlet-mapping>}</pre>
*
*/
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}

4、SpringBoot的 WebApplicationInitializer 的实现

查看SpringBoot  SpringBootServletInitializer 源码,该类在spring-boot依赖包中。

仔细看下面的标蓝的代码。不难发现这正是Servlet容器(tomcat)如何找到SpringBoot并启动它的。

 package org.springframework.boot.web.support;

 import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment; /**
* An opinionated {@link WebApplicationInitializer} to run a {@link SpringApplication}
* from a traditional WAR deployment. Binds {@link Servlet}, {@link Filter} and
* {@link ServletContextInitializer} beans from the application context to the servlet
* container.
* <p>
* To configure the application either override the
* {@link #configure(SpringApplicationBuilder)} method (calling
* {@link SpringApplicationBuilder#sources(Object...)}) or make the initializer itself a
* {@code @Configuration}. If you are using {@link SpringBootServletInitializer} in
* combination with other {@link WebApplicationInitializer WebApplicationInitializers} you
* might also want to add an {@code @Ordered} annotation to configure a specific startup
* order.
* <p>
* Note that a WebApplicationInitializer is only needed if you are building a war file and
* deploying it. If you prefer to run an embedded container then you won't need this at
* all.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
* @see #configure(SpringApplicationBuilder)
*/
public abstract class SpringBootServletInitializer implements WebApplicationInitializer { protected Log logger; // Don't initialize early private boolean registerErrorPageFilter = true; /**
* Set if the {@link ErrorPageFilter} should be registered. Set to {@code false} if
* error page mappings should be handled via the Servlet container and not Spring
* Boot.
* @param registerErrorPageFilter if the {@link ErrorPageFilter} should be registered.
*/
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
this.registerErrorPageFilter = registerErrorPageFilter;
} @Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case a ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
} protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
builder = configure(builder);
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.getSources().add(ErrorPageFilterConfiguration.class);
}
return run(application);
} /**
* Returns the {@code SpringApplicationBuilder} that is used to configure and create
* the {@link SpringApplication}. The default implementation returns a new
* {@code SpringApplicationBuilder} in its default state.
* @return the {@code SpringApplicationBuilder}.
* @since 1.3.0
*/
protected SpringApplicationBuilder createSpringApplicationBuilder() {
return new SpringApplicationBuilder();
} /**
* Called to run a fully configured {@link SpringApplication}.
* @param application the application to run
* @return the {@link WebApplicationContext}
*/
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
} private ApplicationContext getExistingRootWebApplicationContext(
ServletContext servletContext) {
Object context = servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (context instanceof ApplicationContext) {
return (ApplicationContext) context;
}
return null;
} /**
* Configure the application. Normally all you would need to do is to add sources
* (e.g. config classes) because other settings have sensible defaults. You might
* choose (for instance) to add default command line arguments, or set an active
* Spring profile.
* @param builder a builder for the application context
* @return the application builder
* @see SpringApplicationBuilder
*/
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder;
} }

5、查看Spring官方文档

查看Spring 5.0.14官方文档:https://docs.spring.io/spring/docs/5.0.14.RELEASE/spring-framework-reference/web.html#spring-web

文档中给出在传统的springMVC中在web.xml中的配置内容

 <web-app>
<!-- 初始化Spring上下文 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<!-- 初始化DispatcherServlet -->
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>

文档中提供了一个如何使用基于java代码的方式配置Servlet容器example

 public class MyWebApplicationInitializer implements WebApplicationInitializer {

     @Override
public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration
//通过注解的方式初始化Spring的上下文
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
//注册spring的配置类(替代传统项目中xml的configuration)
ac.register(AppConfig.class);
ac.refresh(); // Create and register the DispatcherServlet
//基于java代码的方式初始化DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}

对比官方文档给出的example,不难发现上面这段java代码就是SpringBoot省去web.xml的具体实现方法。上面  MyWebApplicationInitializer   正是 WebApplicationInitializer ( @HandlesTypes(WebApplicationInitializer.class) )  接口的实现。

官方文档提供的 MyWebApplicationInitializer  类正是SpringBoot不依赖与web.xml的关键代码。

SpringBoot中具体实现web.xml中配置的代码没有官方文档中的example这么简单,SpringBoot中具体初始化 DispatcherServlet 的类是 DispatcherServletAutoConfiguration 。感兴趣的话可以断点调试一下。

五、总结

以上章节介绍了SpringBoot诞生的历史背景,每一个新技术的诞生,都是场景驱动的。然后介绍了SpringBoot能做到不依赖web.xml的技术条件。最后通过源码分析了SpringBoot中具体的实现。

下一篇博文将利用本文讲到的知识基于Spring springframework内置tomcat简单模拟SpringBoot的基本功能。简单说就是实现一个简易版的SpringBoot。

本文是笔者查阅大量资料,阅读大量Spring源码总结出来的,原创不易,转载请注明出处。

如有错误请在评论区留言指正。

参考文献:

https://blog.csdn.net/adingyb/article/details/80707471

https://www.jianshu.com/p/c6f4df3d720c

SpringBoot源码篇:深度分析SpringBoot如何省去web.xml的更多相关文章

  1. SpringBoot源码篇:Spring5内置tomcat实现code-based的web.xml实现

    一.简介 上篇文章讲了SpingBoot诞生的历史背景和技术演进背景,并通过源码说明了SpringBoot是如何实现零配置的包括如何省去web.xml配置的原理.本文接上一篇文章,通过demo演示Sp ...

  2. 如何搭建自己的SpringBoot源码调试环境?--SpringBoot源码(一)

    1 前言 这是SpringBoot2.1源码分析专题的第一篇文章,主要讲如何来搭建我们的源码阅读调试环境.如果有经验的小伙伴们可以略过此篇文章. 2 环境安装要求 IntelliJ IDEA JDK1 ...

  3. Spring Boot源码(二):SPI去除web.xml

    SPI广泛用于dubbo,spring boot,spring cloud alibaba等 关于SPI,可见SPI-Service Provider Interface 继续上篇文章 上面三句代码的 ...

  4. 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何搭建自己的SpringBoot源码调试环境?--SpringBoot源码(一). 前面搭建好了自己本地的S ...

  5. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  6. 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二) 上一篇分析了SpringBoo ...

  7. SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)

    SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...

  8. 从SpringBoot源码分析 主程序配置类加载过程

    1.@Import(AutoConfigurationPackages.Registrar.class) 初始SpringBoot 我们知道在SpringBoot 启动类上有一个@SpringBoot ...

  9. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

随机推荐

  1. 彻底删除kafka下面的topic

    如果只是用kafka-topics.sh的delete命令删除topic,会有两种情况: 如果当前topic没有使用过即没有传输过信息:可以彻底删除 如果当前topic有使用过即有过传输过信息:并没有 ...

  2. 使用ceph命令提示handle_connect_reply connect got BADAUTHORIZER

    输入命令提示如下错误: [root@node1 ~]# rados -p testpool ls 2017-10-21 06:13:25.743045 7f8f89b6d700 0 -- 192.16 ...

  3. Java父类构造器的讲解

    众所周知,对于Java中的所有类而言,它们有一个根父类,即java.lang.Object类. 对于Java中构造器执行的顺序而言,程序执行的顺序为,先执行父类的非静态代码块,然后执行父类的相应的构造 ...

  4. LaTeX技巧203:如何实现等号对齐_LaTeX_Fun_新浪博客

    LaTeX技巧203:如何实现等号对齐_LaTeX_Fun_新浪博客 我们在进行公式的输入排版的时候,通常希望公式比较齐整,所以需要一些等号对齐,或者左对齐,关于公式的左对齐前文已经介绍了方法.htt ...

  5. struts2 json返回试验

    <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-/ ...

  6. CodeForces 1107F. Vasya and Endless Credits

    题目简述:给定 $n \leq 500$ 个贷款方式,其中第$i$个贷款额为$a_i$元,需要$k_i$个月偿还,每月还贷$b_i$元.在每个月月初可申请其中一个贷款,而在每个月月底时需要还贷.求:( ...

  7. sklearn交叉验证法(Cross Validation)

    import numpy as np from sklearn import datasets from sklearn.cross_validation import train_test_spli ...

  8. BerkeleyDB原理及其对应API

    BerkeleyDB(简称为BDB)是一种以key-value为结构的嵌入式数据库引擎: 嵌入式:bdb提供了一系列应用程序接口(API),调用这些接口很简单,应用程序和bdb所提供的库一起编译/链接 ...

  9. hdu1061

    #include <iostream>#include <cstdio>using namespace std;int mod_exp(int a, int b, int c) ...

  10. Sharepoint2013商务智能学习笔记之Excel Service服务配置(四)

    第一步,新建Excel Service应用程序 第二步,管理中心,在应用程序管理区域点管理服务应用程序,进入应用程序管理列表, 再点击刚才新建好的ExcelServiceApplication进入ex ...