01 Spring Boot 核心
来源:互联网 发布:爱奇艺视频转换器mac版 编辑:程序博客网 时间:2024/06/06 19:27
1 基本配置
1.1 @SpringBootApplication
@SpringBootApplication是Spring-Boot核心注解,是一个组合注解,源码如下:
@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})})public @interface SpringBootApplication { @AliasFor( annotation = EnableAutoConfiguration.class, attribute = "exclude" ) Class<?>[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class, attribute = "excludeName" ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {};}
@EnableAutoConfiguration让Spring Boot根据类路径下的jar包依赖,为当前项目进行自动配置。
例如,添加了spring-boot-starter-web依赖,会自动添加Tomcat和SpringMVC的依赖,那么Spring Boot会对Tomcat和SpringMVC进行自动配置。
又如,添加了spring-boot-starter-data-jpa依赖,Spring Boot会自动进行JPA的配置。
Spring Boot会自动扫描@SpringBootApplication所在类的同级包(如,com.wisely.ch5_2_2)以及下级包里的Bean(若为jpa项目还可以扫描标注@Entity的实体类),
1.2 关闭特定的自动配置
通过@SpringBootApplication注解的源码可以发现,关闭特定的配置应该使用@SpringBootApplication注解的exclude参数,例如:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
1.3 定制Banner
(1)修改Banner
在src/main/resource 下新建banner.txt,将在网站http://patorjk.com/software/taag/生成字符粘贴到该文档。
(2)关闭Banner
修改main
public static void main(String[] args) { SpringApplication app = new SpringApplication(Ch522Application.class); app.setBannerMode(Mode.OFF); app.run(args);}
1.4 Spring Boot 配置文件
Spring Boot 使用一个全局配置文件application.properties 或 application.yml,src/main/resource 或类路径的/config下面。
Spring Boot 全局配置文件的作用是对一些默认的配置进行修改,如修改端口号和默认的访问路径,可以在application.properties中添加如下:
server.port=8081server.servlet.context-path=/helloboot
1.5 starter pom
Spring Boot 为我们提供了简化企业开发绝大多数场景的starter pom,只要使用应用场景所需的starter pom,相关的技术配置就会消除。
下面的应用程序starters是Spring Boot在org.springframework.boot
组下提供的:
Spring Boot application starters
spring-rabbit
实现 spring-boot-starter-aop 对面向切面编程的支持,包括spring-aop
和AspectJ spring-boot-starter-batch 对Spring Batch的支持,包括HSQLDB数据库 spring-boot-starter-cloud-connectors 对Spring Cloud Connectors的支持,简化在云平台下(例如,Cloud Foundry 和Heroku)服务的连接 spring-boot-starter-data-elasticsearch 对Elasticsearch搜索和分析引擎的支持,包括spring-data-elasticsearch
spring-boot-starter-data-gemfire 对GemFire分布式数据存储的支持,包括spring-data-gemfire
spring-boot-starter-data-jpa 对”Java持久化API”的支持,包括spring-data-jpa
,spring-orm
和Hibernate spring-boot-starter-data-mongodb 对MongoDB NOSQL数据库的支持,包括spring-data-mongodb
spring-boot-starter-data-rest 对通过REST暴露Spring Data仓库的支持,通过spring-data-rest-webmvc
实现 spring-boot-starter-data-solr 对Apache Solr搜索平台的支持,包括spring-data-solr
spring-boot-starter-freemarker 对FreeMarker模板引擎的支持 spring-boot-starter-groovy-templates 对Groovy模板引擎的支持 spring-boot-starter-hateoas 对基于HATEOAS的RESTful服务的支持,通过spring-hateoas
实现 spring-boot-starter-hornetq 对”Java消息服务API”的支持,通过HornetQ实现 spring-boot-starter-integration 对普通spring-integration
模块的支持 spring-boot-starter-jdbc 对JDBC数据库的支持 spring-boot-starter-jersey 对Jersey RESTful Web服务框架的支持 spring-boot-starter-jta-atomikos 对JTA分布式事务的支持,通过Atomikos实现 spring-boot-starter-jta-bitronix 对JTA分布式事务的支持,通过Bitronix实现 spring-boot-starter-mail 对javax.mail
的支持 spring-boot-starter-mobile 对spring-mobile
的支持 spring-boot-starter-mustache 对Mustache模板引擎的支持 spring-boot-starter-redis 对REDIS键值数据存储的支持,包括spring-redis
spring-boot-starter-security 对spring-security
的支持 spring-boot-starter-social-facebook 对spring-social-facebook
的支持 spring-boot-starter-social-linkedin 对spring-social-linkedin
的支持 spring-boot-starter-social-twitter 对spring-social-twitter
的支持 spring-boot-starter-test 对常用测试依赖的支持,包括JUnit, Hamcrest和Mockito,还有spring-test
模块 spring-boot-starter-thymeleaf 对Thymeleaf模板引擎的支持,包括和Spring的集成 spring-boot-starter-velocity 对Velocity模板引擎的支持 spring-boot-starter-web 对全栈web开发的支持,包括Tomcat和spring-webmvc
spring-boot-starter-websocket 对WebSocket开发的支持 spring-boot-starter-ws 对Spring Web服务的支持除了应用程序的starters,下面的starters可以用于添加[生产准备](../V. Spring Boot Actuator/README.md)的特性。
Spring Boot生产准备的starters
ssh
shell支持最后,Spring Boot包含一些可用于排除或交换具体技术方面的starters。
Spring Boot technical starters
1.6 使用xml进行配置
Spring Boot 提倡零配置,即无xml配置,但在实际项目中可能有些特殊要求你必须使用xml配置,这是我们通过Spring提供的@ImportResource来加载xml配置,例如:
@ImportResource({“classpath:some-context.xml”,”classpath:another-context.xml”})
2 外部配置
Spring Boot 允许使用xml、yaml和命令行参数作为外部配置。
2.1 命令行参数配置
命令行参数是在jar包启动的时候输入
Spring Boot 是基于jar运行的,jar启动命令:
java -jar xx.jar
可以通过下面命令修改tomcat短口号:
java -jar xx.jar –server.port=9090
2.2 常规属性配置
在常规Spring环境下,注入property文件里值的方式,通过@PropertySource指明property的位置,然后通过@Value注入值。在Spring Boot中,只需要在application.properties中定义属性,直接使用@Value注入即可。
实战:
(1)在application.properties增加如下属性:
book.author=iwillbook.name=spring boot
(2)修改入口类
@RestControllerpublic class Controller { @Value("${book.author}") private String boolAuthor; @Value("${book.name}") private String boolName; @RequestMapping("/") String index() { return "The book name is " + boolName + ", the book author is " + boolAuthor; }}
2.3 类型安全的配置(基于properties)
上面使用@Value注入每个属性,显得格外麻烦,Spring Boot还提供了基于类型的安全配置方式,通过@ConfigurationProperties将properties属性和一个bean的属性关联,从而实现类型安全配置。
实战:
(1)新建Spring Boot项目ch6_2_3
(2)在application.properties中添加:
author.name=iwillauhtor.age=27
当然,也可以新建一个properties文件,这就需要使用@ConfigurationProperties的location属性指定properties文件位置,且需要在入口类上配置。
spring boot 1.5以上版本@ConfigurationProperties取消location注解,解决方案如下:
- 在@EnableConfigurationProperties取消激活自定义的配置类(重要)
- 在配置类中采用@Component的方式注册为组件,然后使用@PropertySource来指定自定义的资源目录
(3)类型安全的bean
@Component@ConfigurationProperties(prefix = "author")@PropertySource("classpath:/config/bean.properties")public class AuthorSettings { private String name; private Long age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getAge() { return age; } public void setAge(Long age) { this.age = age; }}
(4)检验代码
@RestControllerpublic class AppController { @Autowired private AuthorSettings authorSettings; @RequestMapping("/") public String index() { return "The author name is " + authorSettings.getName() + ",the author age is " + authorSettings.getAge(); }}
3 日志配置
Spring Boot支持Java Util Logging、log4j、log4j2和Logback作为日志框架,无论使用哪种日志框架,Spring Boot已为当前使用框架的控制台输出和文件输出做好了做好了配置。
4 Profile 配置
Profile是spring针对不同环境对不同的配置提供支持的,全局Profile使用application-{profile}.properties,如(application-prod.properties)。
通过在application.properties中设置spring-profile-active=prod来指定活动的profile。
如生产和开发环境配置文件如下:
application-prod.properties
server.port=8081
application-dev.properties
server.port=80
可以指定要使用的配置文件
application.properties
spring.profiles.active=prod
5 Spring Boot 运行原理
要想直到Spring Boot为我们做了哪些自动配置,看查看下面这些源码
可以通过一下三种方式查看已启动的和未启动的配置报告
(1)运行jar文件增加debug参数
java -jar xx.jar --debug
(2)在application.properties中增加属性
debug=true
(3)配置启动参数
5.1 运行原理
关于Spring Boot 运行原理还是回归到@SpringBootApplication注解上来,它的核心功能是由@EnableAutoConfiguration注解提供的。
@EnableAutoConfiguration注解源码如下:
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {};}
这里关键是@import注解的导入功能,AutoConfigurationImportSelector使用
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
方法来扫描具有META-INF/spring.factories文件的jar包,而spring-boot-autoconfigure-2.x.x.jar就包含一个spring.factories文件,此文件生命了有哪些自动配置,如下(里面有个# AutoConfigure):
5.2 核心注解
- @ConditionalOnBean 当容器里有指定Bean的条件下。
- @ConditionalOnClass 当路径下有指定类的条件下。
- @ConditionalOnExpression 基于SpEL表达式作为判断条件
- @ConditionalOnJava 基于JVM版本作为判断条件。
- @ConditionalOnJndi 在JNDI存在的条件下查找指定位置。
- @ConditionalOnMissingBean 当容器里没有指定Bean的情况下。
- @ConditionalOnMissingClass 当路径没有指定类的情况下。
- @ConditionalOnNotWebApplication 当项目不是web项目的条件下。
- @ConditionalOnProperty 指定的属性是否有指定的值。
- @ConditionalOnResource 类路径是否有指定的值。
- @ConditionalOnSingleCondidate 当指定Bean在容器中只有一个,或虽然由多个但指定首选Bean。
- @ConditionalOnWebApplication 当前项目是web项目的条件下。
下面分析@ConditionalOnWebApplication注解。
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Conditional({OnWebApplicationCondition.class})public @interface ConditionalOnWebApplication { ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY; public static enum Type { ANY, SERVLET, REACTIVE; private Type() { } }}
从源码可以看出,此注解使用的条件OnWebApplicationCondition,下面介绍这个条件是如何构造的:
import java.util.Map;import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotatedTypeMetadata;import org.springframework.util.ClassUtils;import org.springframework.util.ObjectUtils;import org.springframework.web.context.ConfigurableWebEnvironment;import org.springframework.web.context.WebApplicationContext;@Order(-2147483628)class OnWebApplicationCondition extends SpringBootCondition { private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context.support.GenericWebApplicationContext"; OnWebApplicationCondition() { } public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { boolean required = metadata.isAnnotated(ConditionalOnWebApplication.class.getName()); ConditionOutcome outcome = this.isWebApplication(context, metadata, required); if (required && !outcome.isMatch()) { return ConditionOutcome.noMatch(outcome.getConditionMessage()); } else { return !required && outcome.isMatch() ? ConditionOutcome.noMatch(outcome.getConditionMessage()) : ConditionOutcome.match(outcome.getConditionMessage()); } } private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata, boolean required) { Builder message = ConditionMessage.forCondition(ConditionalOnWebApplication.class, new Object[]{required ? "(required)" : ""}); Type type = this.deduceType(metadata); if (Type.SERVLET == type) { return this.isServletWebApplication(context); } else if (Type.REACTIVE == type) { return this.isReactiveWebApplication(context); } else { ConditionOutcome servletOutcome = this.isServletWebApplication(context); if (servletOutcome.isMatch() && required) { return new ConditionOutcome(servletOutcome.isMatch(), message.because(servletOutcome.getMessage())); } else { ConditionOutcome reactiveOutcome = this.isReactiveWebApplication(context); if (reactiveOutcome.isMatch() && required) { return new ConditionOutcome(reactiveOutcome.isMatch(), message.because(reactiveOutcome.getMessage())); } else { boolean finalOutcome = required ? servletOutcome.isMatch() && reactiveOutcome.isMatch() : servletOutcome.isMatch() || reactiveOutcome.isMatch(); return new ConditionOutcome(finalOutcome, message.because(servletOutcome.getMessage()).append("and").append(reactiveOutcome.getMessage())); } } } } private ConditionOutcome isServletWebApplication(ConditionContext context) { Builder message = ConditionMessage.forCondition("", new Object[0]); if (!ClassUtils.isPresent("org.springframework.web.context.support.GenericWebApplicationContext", context.getClassLoader())) { return ConditionOutcome.noMatch(message.didNotFind("web application classes").atAll()); } else { if (context.getBeanFactory() != null) { String[] scopes = context.getBeanFactory().getRegisteredScopeNames(); if (ObjectUtils.containsElement(scopes, "session")) { return ConditionOutcome.match(message.foundExactly("'session' scope")); } } if (context.getEnvironment() instanceof ConfigurableWebEnvironment) { return ConditionOutcome.match(message.foundExactly("ConfigurableWebEnvironment")); } else { return context.getResourceLoader() instanceof WebApplicationContext ? ConditionOutcome.match(message.foundExactly("WebApplicationContext")) : ConditionOutcome.noMatch(message.because("not a servlet web application")); } } } private ConditionOutcome isReactiveWebApplication(ConditionContext context) { Builder message = ConditionMessage.forCondition("", new Object[0]); return context.getResourceLoader() instanceof ReactiveWebApplicationContext ? ConditionOutcome.match(message.foundExactly("ReactiveWebApplicationContext")) : ConditionOutcome.noMatch(message.because("not a reactive web application")); } private Type deduceType(AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnWebApplication.class.getName()); return attributes != null ? (Type)attributes.get("type") : Type.ANY; }}
从isOnWebApplication可以看出,判断条件是:
(1)GenericWebApplicationContext是否在类路径中
(2)容器里是否有名为session的scope
(3)当前容器Environment是否为StandardServletEnvironment
(4)当前的ResourceLoader是否为WebApplicationContext(ResourceLoader是ApplicationContext的顶级接口之一)
(5)我们需要构造ConditionOutCome类的对象来帮助我们,最终通过ConditionOutcome.isMatch返回的布尔值来确定条件。
5.3 实例分析
分析一个简单的Spring Boot 自动配置功能:http编码配置。
常规web项目配置http编码是在web.xml中配置一个filter,如下:
<filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> encoding UTF-8 </init-param></filter><filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
Spring Boot这一点是基于类型安全配置实现的,可以在application.properties中直接设置,具体源码如下:
@ConfigurationProperties( prefix = "spring.http.encoding")public class HttpEncodingProperties { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Charset charset; private Boolean force; private Boolean forceRequest; private Boolean forceResponse; private Map<Locale, Charset> mapping; public HttpEncodingProperties() { this.charset = DEFAULT_CHARSET; } public Charset getCharset() { return this.charset; } public void setCharset(Charset charset) { this.charset = charset; } public boolean isForce() { return Boolean.TRUE.equals(this.force); } public void setForce(boolean force) { this.force = force; } public boolean isForceRequest() { return Boolean.TRUE.equals(this.forceRequest); } public void setForceRequest(boolean forceRequest) { this.forceRequest = forceRequest; } public boolean isForceResponse() { return Boolean.TRUE.equals(this.forceResponse); } public void setForceResponse(boolean forceResponse) { this.forceResponse = forceResponse; } public Map<Locale, Charset> getMapping() { return this.mapping; } public void setMapping(Map<Locale, Charset> mapping) { this.mapping = mapping; } public boolean shouldForce(HttpEncodingProperties.Type type) { Boolean force = type == HttpEncodingProperties.Type.REQUEST ? this.forceRequest : this.forceResponse; if (force == null) { force = this.force; } if (force == null) { force = type == HttpEncodingProperties.Type.REQUEST; } return force.booleanValue(); } public static enum Type { REQUEST, RESPONSE; private Type() { } }}
通过上述配置,并根据条件配置Character.Encording.Filter的Bean,源码如下:
@Configuration@EnableConfigurationProperties({HttpEncodingProperties.class})@ConditionalOnWebApplication( type = Type.SERVLET)@ConditionalOnClass({CharacterEncodingFilter.class})@ConditionalOnProperty( prefix = "spring.http.encoding", value = {"enabled"}, matchIfMissing = true)public class HttpEncodingAutoConfiguration { private final HttpEncodingProperties properties; public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean @ConditionalOnMissingBean({CharacterEncodingFilter.class}) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.RESPONSE)); return filter; } @Bean public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties); } private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final HttpEncodingProperties properties; LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) { this.properties = properties; } public void customize(ConfigurableServletWebServerFactory webServerFactory) { if (this.properties.getMapping() != null) { webServerFactory.setLocaleCharsetMappings(this.properties.getMapping()); } } public int getOrder() { return 0; } }}
5.4 实战
包含当某个类存在时,自动配置这个类的Bean,并可将Bean的属性在application.properties中配置。
(1)新建Maven项目,修改pom文件如下(增加Spring Boot自身自动配置作为依赖)
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wisely</groupId> <artifactId>spring-boot-starter-hello</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-starter-hello</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.0.0.BUILD-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.2</version> <scope>test</scope> </dependency> </dependencies> <!-- 使用Spring Boot 正式版时,无须下列配置 --> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url>; <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url>; <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url>; <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url>; <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories></project>
(2)属性配置
@ConfigurationProperties(prefix = "hello")public class HelloServiceProperties { private static final String MSG = "world"; private String msg = MSG; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}
(3)判断依据类(根据此类是否存在来创建这个类的Bean,这个类可以是第三方类库)
public class HelloService { private String msg; public String sayHello() { return "Hello " + msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}
(4)自动配置类
@Configuration@EnableConfigurationProperties(HelloServiceProperties.class)@ConditionalOnClass(HelloService.class)@ConditionalOnProperty(prefix = "hello",value = {"enabled"}, matchIfMissing = true)public class HelloServiceAutoConfiguration { @Autowired private HelloServiceProperties helloServiceProperties; @Bean @ConditionalOnMissingBean(HelloService.class) public HelloService helloService() { HelloService helloService = new HelloService(); helloService.setMsg(helloServiceProperties.getMsg()); return helloService; }}
代码解释:
根据HelloServiceProperties提供的参数,并通过@ConditionalOnClass判断HelloService这个类在类路径中是否存在,且当容器中没有这个Bean的情况下自动配置这个Bean。
(5)注册配置。在src/main/resources下新建META-INF/spring.factories,填写如下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.wisely.autoconfigure.HelloServiceAutoConfiguration
若有多个配置用逗号隔开。
(6)使用上面创建的starter,新建Spring Boot项目,并将starter作为依赖。
<dependency> <groupId>com.wisely</groupId> <artifactId>spring-boot-starter-hello</artifactId> <version>1.0-SNAPSHOT</version></dependency>
运行类代码如下:
@RestController@SpringBootApplicationpublic class Ch65Application { @Autowired private HelloService helloService; @RequestMapping("/") public String index() { return helloService.sayHello(); } public static void main(String[] args) { SpringApplication.run(Ch65Application.class, args); }}
在application.properties中添加配置msg的内容
hello.msg=iwill
- 01 Spring Boot 核心
- Spring Boot核心配置
- 2.Spring Boot核心
- Spring Boot核心
- Spring Boot之核心
- Spring Boot核心概念
- Spring Boot核心
- Spring Boot 第六章 spring Boot 核心
- Spring Boot核心和限制
- Spring Boot核心-基本配置
- Spring Boot 核心-外部配置
- Spring boot 之其核心
- Spring Boot核心--基本配置
- Spring Boot核心条件注解
- spring boot框架学习2-spring boot核心(1)
- spring boot框架学习3-spring boot核心(2)
- spring boot框架学习4-spring boot核心(3)
- 《02.Spring Boot实战:Spring Boot核心原理剖析》
- java作业gui1
- Socks
- 安卓使用videoview进行音频、视频播放,及播放控制
- 静态资源文件管理
- Hibernate事务处理
- 01 Spring Boot 核心
- 练习3
- 找工作篇
- springMvc视图化,国际化,静态资源处理
- 顺序表的实现
- caffe源码阅读之layer(2)——DataLayer层(2)
- LWIP 实现DHCP及DNS功能
- RecycleView适配器 Persener层
- eclipse查看一个方法被谁引用的快捷键