SpringBoot的启动简述
一、注解和启动类SpringBootApplication
它是一个复式注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
在以上的注解中,我们要关注的有三个注解:
1.1 @SpringBootConfiguration:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration { }
@Configuration注解
配置注解的功能是将当前类标注为配置类,并将当前类里以 @Bean 注解标记的方法的实例
注入到spring容器中,实例名即为方法名。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
看到这个 @Component 注解, 意味也将会注册为bean, 其内部也可以依赖注入。@Configuration类通常使用
AnnotationConfigApplicationContext或其支持Web的变体AnnotationConfigWebApplicationContext进行引导。
后续可以继续深入学习spring更底层的运行机制......
1.2 @EnableAutoConfiguration
@EnableAutoConfiguration 注解启用自动配置,其可以帮助SpringBoot应用将所有符合
条件的 @Configuration 配置都加载到当前 IoC 容器之中。@EnableAutoConfiguration 借助 AutoConfigurationImportSelector
的帮助,而后者通过实现 electImports()方法来导出Configuration。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
源码分析:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
} protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
AutoConfigurationImportSelector 类的 selectImports() 方法里面通过调用Spring Core 包里 SpringFactoriesLoader
类的 loadFactoryNames()方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
从 ClassPath下扫描所有的 META-INF/spring.factories 配置文件,并将spring.factories 文件中的 EnableAutoConfiguration
对应的配置项通过反射机制实例化为对应标注了 @Configuration 的形式的IoC容器配置类,然后注入IoC容器。
1.3 @ComponentScan
组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加
载到IoC容器中。可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认
Spring框架实现会从声明@ComponentScan所在类的package进行扫描。默认扫描SpringApplication的run方法里的
Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下。
对于该注解,还可以通过 basePackages 属性来更细粒度的控制该注解的自动扫描范围,比如:
@ComponentScan(basePackages = {"cn.shu.controller","cn.shu.entity"}) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
二、SpringApplication类
我们也需要先构造 SpringApplication 类对象,然后调用该对象的 run() 方法。那么接下来就讲讲 SpringApplication 的构造
过程 以及其 run() 方法的流程,搞清楚了这个,那么也就搞清楚了SpringBoot应用是如何运行起来的!
2.1 实例一个SpringApplication对象,根据类名构造参数。
看一下源码:
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 可以为空
this.resourceLoader = resourceLoader;
// 启动资源类不能为空
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用的类型:创建的是 REACTIVE应用、SERVLET应用、NONE 三种中的某一种
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化注册所有实现应用上下文初始化类,Application Context Initializers
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化注册所有实现ApplicationListener的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获得当前执行main方法的类对象,作为启动类。
this.mainApplicationClass = deduceMainApplicationClass();
}
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners,仅仅一个实现类
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor # Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer # FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
2.2 怎么区别应用类别和上下文:REACTIVE、SERVLET、NONE
SERVLET:{ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
REACTIVE: private static final String WEBFLUX_INDICATOR_CLASS = "org."
+ "springframework.web.reactive.DispatcherHandler";
NONE:其他。 static WebApplicationType deduceFromApplicationContext(
Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
2.3 获得所有ApplicationContextInitializer.class的实例,放入初始化容器中。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class)); UampServletInitializer extends SpringBootServletInitializer implements WebApplicationInitializer public class ServletContextApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
2.4 使用 SpringFactoriesLoader查找并加载 classpath下文件中的所有可用的 ApplicationListener。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
三、简要分析SpringApplication类核心方法run()
3.1 源码如下:
public ConfigurableApplicationContext run(String... args) {
//spring-framework提供了一个StopWatch类可以做类似任务执行时间控制,也就是封装了一个对开始时间,结束时间记录操作的Java类
StopWatch stopWatch = new StopWatch();
stopWatch.start();//计时任务的开始
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
//之后逐个调用其started()方法,广播SpringBoot要开始执行了。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//如果为空,就设置ture. System.setProperty("spring.beaninfo.ignore", ignore.toString());
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
//根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
//如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
//否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);
//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//初始化所有自动配置类,调用ApplicationContext的refresh()方法
refreshContext(context); afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
//如果开启日志,则答应执行是时间
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//开始对上下文监听
listeners.started(context);
//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
// 运行所有的监视器
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
3.2 流程图:
3.3 整个流程简要概括如下:
- 通过this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
- 获取并创建 SpringApplicationRunListener 对象.
- 然后由 SpringApplicationRunListener 来发出 starting 消息
- 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
- 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息
- 初始化 ApplicationContext,并设置 Environment,加载相关配置等
- 由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知SpringBoot 应用使用的 ApplicationContext 已准备OK
- 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext 已装填OK
- refresh ApplicationContext,完成IoC容器可用的最后一步
- 由 SpringApplicationRunListener 来发出 started 消息
- 完成最终的程序的启动
- 由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了
说明
- SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。
- ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。
- 通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。
- 初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。
- 初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。
3.4 如果要对SpringApplication进行扩展,我们可以选择如下三种方案:
- 创建ApplicationContextInitializer的实现类
- 创建ApplicationListener的实现类
- 创建ApplicationRunner和CommandLineRunner的实现类
四、SpringBootServletInitializer
4.1继承SpringBootServletInitializer
SpringBootServletInitializer用于替代传统mvc模式中的web.xml。如果你要使用外部的sevvlet容器,例如tomcat。
就需要继承该类并重写configure方法。在创建springboot项目时如果你创建的是war,则系统会默认提供一个继承类。
如果创建时选择的是jar,系统不会提供这个继承!需要自己写这个继承。
如果不继承该类会怎么样呢?答:项目无法使用外部容器启动
如果重复继承会怎么样呢?答:项目可以启动,但是会遇到很多不可预期的问题
SpringBootServletInitializer的核心方法onStartup,里面最重要的方法是createRootApplicationContext(servletContext)。
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
......
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
} }
4.2createRootApplicationContext方法的源码
SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,
并最终调用SpringApplication的run方法的过程。
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//创建SpringApplicationBuilder,并用其生产出SpringApplication对象
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
builder.main(this.getClass());
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
//初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
//指定创建的ApplicationContext类型
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
//传递入口类,并构建SpringApplication对象
//可以通过configure()方法对SpringBootServletInitializer进行扩展
builder = this.configure(builder);
builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(this.getClass()));
} Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
//最后调用SpringApplication的run方法
return this.run(application);
}
4.3扩展SpringBootServletInitializer
与扩展SpringApplication类似,ApplicationContextInitializer和ApplicationListener可以基于SpringApplicationBuilder提供
的public方法进行扩展。
public class ServletInitializer extends SpringBootServletInitializer { @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
application.initializers(xxApplicationContextInitializer1,xxApplicationContextInitializer2);
application.listeners(xxApplicationListener1,xxApplicationListener2)
return application.sources(xxxApplication.class);
} }
SpringBoot的启动简述的更多相关文章
- SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用
目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...
- springboot之启动原理解析
前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...
- Springboot 项目启动后执行某些自定义代码
Springboot 项目启动后执行某些自定义代码 Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRun ...
- SpringBoot的启动流程分析(1)
通过分析我们可以找到 org.springframework.boot.SpringApplication 中如下, public static ConfigurableApplicationCont ...
- 深入理解SpringBoot之启动探究
SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用.可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接 ...
- springBoot项目启动类启动无法访问
springBoot项目启动类启动无法访问. 网上也查了一些资料,我这里总结.下不来虚的,也不废话. 解决办法: 1.若是maven项目,则找到右边Maven Projects --->Plug ...
- java框架之SpringBoot(10)-启动流程及自定义starter
启动流程 直接从 SpringBoot 程序入口的 run 方法看起: public static ConfigurableApplicationContext run(Object source, ...
- SpringBoot项目启动时链接数据库很慢
SpringBoot项目启动时链接数据库很慢 springboot项目在启动时候,如下图所示,链接数据库很慢 解决方法:在mysql 的配置文件中 配置 skip-name-resolve
- SpringBoot一站式启动流程源码分析
一.前言 由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码.那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻Sprin ...
随机推荐
- Java使用JDBC连接Hive
最近一段时间,处理过一个问题,那就是hive jdbc的连接问题,其实也不是大问题,就是url写的不对,导致无法连接.问题在于HiveServer2增加了别的安全验证,导致正常的情况下,传递的参数无法 ...
- 我在DBGridEh增加一栏复选框及对应操作的解决方案
最近客户有个需求,要求对单据列表里指定的单据进行批量审核,很自然的,我想到了在DBGridEh增加一栏复选框的列,审核时遍历所有单据,将打了勾的单据审核就可以了.查阅了网上很多文章,不外有2个方案,1 ...
- xshell 缺少mfc110u.dll
https://www.microsoft.com/zh-CN/download/details.aspx?id=30679 如果 x64 没有反应,就下载 x86 安装试试,
- idea把java web项目打成war包
1.新建artifacts 2.设置你的目录内容路径 3.找到项目web或webapp的路径 4.可以直接添加已经跑通的项目 5.配置完成点OK 6.编译打成war包 7.点击编译结束打完收工 8.找 ...
- Kotlin-Note
数字在需要一个可空的引用时,会进行装箱操作,数字装箱不一定保留同一性. val a = 1000 println(a === a) // 输出 "true" val boxedA ...
- [Vim] 01 凡人操作
历史评价 编辑器之神 一个凡人的自我修养 成神是不可能成神的,只能是先入个门,然后一点一点学 Vim 官网 三种模式 插入模式(注意看光标) 命令模式(注意看光标) 编辑模式(注意看光标) 其实还有一 ...
- HDU 1864 最大报销额 0-1背包
HDU 1864 最大报销额 0-1背包 题意 现有一笔经费可以报销一定额度的发票.允许报销的发票类型包括买图书(A类).文具(B类).差旅(C类),要求每张发票的总额不得超过1000元,每张发票上, ...
- 剑指offer-二叉搜索树与双向链表-python
题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.. # -*- coding:utf-8 -*- # class TreeN ...
- 完整ASP.Net Excel导入
//把EXCEL文件上传到服务器并返回文件路径 private String typename(FileUpload fileloads) { str ...
- 理解PHP面向对象三大特性
一.封装性 目的:保护类里面的数据,让类更安全, protected和private只能在类中或子类访问,通过public提供有限的接口供外部访问,封装是控制访问,而不是拒绝访问 封装关键字:publ ...