简化Spring应用开发的一个框架
整个Spring技术栈的一个大整合
J2EE开发的一站式解决方案
2014,martin fowler
单体应用:ALL IN ONE
微服务
架构风格
一个应用应该是一组小型服务;可以通过HTTP的方式进行互通;
每一个功能元素最终都是一个可独立替换和独立升级的软件单元;
详细参照微服务文档
3. Spring Boot HelloWorld
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >1.5.9.RELEASE</version >
</parent >
<dependencies >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
</dependency >
</dependencies >
java编写一个主程序;启动Spring Boot应用
@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
@ SpringBootApplication
public class HelloWorldMainApplication {
public static void main (String [] args ) {
SpringApplication .run (HelloWorldMainApplication .class ,args );
}
}
java编写相关的Controller、Service
@ Controller
public class HelloController {
@ ResponseBody
@ RequestMapping ("/hello" )
public String hello (){
return "Hello World!" ;
}
}
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<build >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
</plugins >
</build >
spring-boot-dependencies 管理版本
以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >1.5.9.RELEASE</version >
</parent >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-dependencies</artifactId >
<version >1.5.9.RELEASE</version >
<relativePath >../../spring-boot-dependencies</relativePath >
</parent >
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters,只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
</dependency >
Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@ SpringBootApplication
public class HelloWorldMainApplication {
public static void main (String [] args ) {
SpringApplication .run (HelloWorldMainApplication .class ,args );
}
}
@SpringBootConfiguration:Spring Boot的配置类;
@Configuration:配置类上来标注这个注解;
配置类也是容器中的一个组件;@Component
@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{}
@EnableAutoConfiguration:开启自动配置功能;
@AutoConfigurationPackage:自动配置包, 用户代码组件配置
@Import(AutoConfigurationPackages.Registrar.class):
+ Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class;
将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器
@Import(EnableAutoConfigurationImportSelector.class): Spring代码组件配置
EnableAutoConfigurationImportSelector:将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类 AutoConfiguration;就是给容器中导入这个场景需要的所有组件,并配置好这些组件;
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作
spring-boot-autoconfig-2.0 RELEASE.jar
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
5. 使用Spring Initializer快速创建Spring Boot项目
IDEA:使用 Spring Initializer快速创建项目
IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目;
选择我们需要的模块;向导会联网创建Spring Boot项目;
默认生成的Spring Boot项目;
主程序已经生成好了,我们只需要我们自己的逻辑
resources文件夹中目录结构
static:保存所有的静态资源; js css images;
templates:保存所有的模板页面;默认不支持JSP页面;可以使用模板引擎(freemarker、thymeleaf);
application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
STS使用 Spring Starter Project快速创建项目
SpringBoot使用一个全局的配置文件,配置文件名是固定的;
application.properties
application.yml
修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;
YAML(YAML Ain't Markup Language)
+ YAML A Markup Language:是一个标记语言
+ YAML isn't Markup Language:不是一个标记语言;
+ 以数据为中心,比json、xml等更适合做配置文件;
k:(空格)v:表示一对键值对(空格必须有);
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
属性和值也是大小写敏感
server:
port: 8081
path: /hello
数字,字符串,布尔
字符串默认不用加上单引号或者双引号
双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: "v1 \n v2":输出;v1 换行 v2
单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
+ name: ‘v1 \n v2’:输出;v1 \n v2
friends:
lastName: zhangsan
age: 20
friends: {lastName: zhangsan,age: 18}
pets:
- cat
- dog
- pig
pets: [cat,dog,pig]
配置文件
properties required native-to-ascii conversion
# ymal
person:
lastName: hello
age: 18
boss: false
birth: 2017/12/12
maps: {k1: v1,k2: 12}
lists:
- lisi
- zhaoliu
dog:
name: 小狗
age: 12
person.last-name=yufeng
person.age=18
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15
javaBean
将配置文件中配置的每一个属性的值,映射到这个组件中
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
prefix = "person":配置文件中哪个下面的所有属性进行一一映射
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
@Value获取值和@ConfigurationProperties获取值比较
@Value 注解
配置文件: @Value("${person.last-name}")
动态运算: @Value("#{11*2}")
值: @Value("true")
只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
@ConfigurationProperties
@Value
功能
批量注入配置文件中的属性
一个个指定
松散绑定
支持
不支持
SpEL
不支持
支持
JSR303数据校验
支持
不支持
复杂类型封装(集合)
支持
不支持
@ Component
@ ConfigurationProperties (prefix = "person" )
@ Validated
public class Person {
//@Value("${person.last-name}")
private String lastName ;
//@Value("#{11*2}")
private Integer age ;
//@Value("true")
private Boolean boss ;
private Date birth ;
private Map <String ,Object > maps ;
private List <Object > lists ;
private Dog dog ;
@PropertySource&@ImportResource&@Bean
@PropertySource:加载指定的配置文件
@ConfigurationProperties(prefix = "person")默认从全局配置文件中获取值;
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效;
Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;
想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上
<?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 id="helloService" class="com.bp.springboot.service.HelloService"></bean>
</beans>
@ImportResource(locations = {"classpath:beans.xml"})
SpringBoot推荐给容器中添加组件的方式
配置类@Configuration 等于 Spring配置文件
使用@Bean给容器中添加组件
@Configuration
public class MyAppConfig {
@Bean
public HelloService helloService02(){
return new HelloService();
}
}
```java
${random.value}, ${random.int}, ${random.long}
$ {random.int(10)}, ${random.int[1024,65536]}
#### 占位符获取之前配置的值,如果没有可以是用:指定默认值
```properties
person.last-name=张三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
# default value for person.hello
person.dog.name=${person.hello:hello}_dog
person.dog.age=15
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
默认使用application.properties的配置;
server:
port: 8081
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dec
---
server:
port: 8084
spring:
profiles: prod
在配置文件中指定 spring.profiles.active=dev
命令行参数 java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
虚拟机参数 -Dspring.profiles.active=dev
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
– file:./config/
– file:./
– classpath:/config/
– classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件互补配置
我们还可以通过spring.config.location来改变默认的配置文件位置
配置文件: spring.config.location
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
命令行参数
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
多个配置用空格分开; --配置项=值
来自java:comp/env的JNDI属性
Java系统属性(System.getProperties())
操作系统环境变量
RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找
优先加载带profile
jar包外部的application-{profile}.properties或application.yml配置文件
jar包内部的application-{profile}.properties或application.yml配置文件
再来加载不带profile
jar包外部的application.properties或application.yml配置文件
jar包内部的application.properties或application.yml配置文件
@Configuration注解类上的@PropertySource
通过SpringApplication.setDefaultProperties指定的默认属性
参考官方文档
配置文件能配置的属性参照
SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
@EnableAutoConfiguration
利用AutoConfigurationImportSelector给容器中导入一些组件, 查看getAutoConfigurationEntry()方法的内容;
List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
SpringFactoriesLoader.loadFactoryNames()
扫描所有jar包类路径下 META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
每一个AutoConfiguration类自动配置类进行自动配置功能;
以HttpEncodingAutoConfiguration为例解释自动配置原理;
根据当前不同的条件判断,决定这个配置类是否生效
//表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@ Configuration
//启动指定类的ConfigurationProperties功能;
//将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
@ EnableConfigurationProperties (HttpEncodingProperties .class )
//Spring底层@Conditional注解(Spring注解版),根据不同的条件
// 如果满足指定的条件, 整个配置类里面的配置就会生效;判断当前应用是否是web应用,如果是,当前配置类生效
@ ConditionalOnWebApplication
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器
@ ConditionalOnClass (CharacterEncodingFilter .class )
//判断配置文件中是否存在某个配置spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
@ ConditionalOnProperty (prefix = "spring.http.encoding" , value = "enabled" , matchIfMissing = true )
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties ;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration (HttpEncodingProperties properties ) {
this .properties = properties ;
}
@ Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ ConditionalOnMissingBean (CharacterEncodingFilter .class ) //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter () {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter ();
filter .setEncoding (this .properties .getCharset ().name ());
filter .setForceRequestEncoding (this .properties .shouldForce (Type .REQUEST ));
filter .setForceResponseEncoding (this .properties .shouldForce (Type .RESPONSE ));
return filter ;
}
但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在Properties类中封装者;配置文件能配置什么就可以参照某个功能对应的这个属性类
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值和bean的属性进行绑定
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
SpringBoot启动会加载大量的自动配置类
我们看我们需要的功能有没有SpringBoot默认写好的自动配置类
我们再来看这个自动配置类中到底配置了哪些组件, 只要我们要用的组件有,我们就不需要再来配置了
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
@Conditional扩展注解
作用(判断是否满足当前指定条件)
@ConditionalOnJava
系统的java版本是否符合要求
@ConditionalOnBean
容器中存在指定Bean;
@ConditionalOnMissingBean
容器中不存在指定Bean;
@ConditionalOnExpression
满足SpEL表达式指定
@ConditionalOnClass
系统中有指定的类
@ConditionalOnMissingClass
系统中没有指定的类
@ConditionalOnSingleCandidate
容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty
系统中指定的属性是否有指定的值
@ConditionalOnResource
类路径下是否存在指定资源文件
@ConditionalOnWebApplication
当前是web环境
@ConditionalOnNotWebApplication
当前不是web环境
@ConditionalOnJndi
JNDI存在指定项
Positive matches:(自动配置类启用的)
-----------------
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
- @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
Negative matches:(没有启动,没有匹配成功的自动配置类)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
AopAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)
System.out.println("");将关键数据打印在控制台;写在一个文件
框架来记录系统的一些运行时信息;日志框架 logging.jar
高大上的几个功能?异步模式?自动归档 logging-good.jar
将以前框架卸下来?换上新的框架,重新修改之前相关的API logging-prefect.jar
JDBC---数据库驱动;
写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;
+ 给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;
JUL, JCL, Jboss-logging, logback, log4j, log4j2、slf4j
底层是Spring框架,Spring框架默认是用JCL
SpringBoot选用 SLF4j和logback
日志门面 (日志的抽象层)
日志实现
SLF4j(Simple Logging Facade for Java)
Log4j JUL(java.util.logging) Log4j2 Logback
不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;
导入slf4j的jar和logback的实现jar
每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
application(slf4j+logback): Spring(commons-logging), Hibernate(jboss-logging), MyBatis
统一日志记录,即使是别的框架和我一起统一使用slf4j进行输出
如何让系统中所有的日志都统一到slf4j
将系统中其他日志框架先排除出去
用中间包来替换原有的日志框架
我们导入slf4j其他的实现
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
SpringBoot底层也是使用slf4j+logback的方式进行日志记录
SpringBoot也把其他的日志都替换成了slf4j;
中间替换包
springboot 2.0: jul-to-slf4j, log4j-to-slf4j
springboot 1.0: jul-over-slf4j, log4j-over-slfj
SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;==**
@ SuppressWarnings ("rawtypes" )
public abstract class LogFactory {
static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = "http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j" ;
static LogFactory logFactory = new SLF4JLogFactory ();
logging.level.com.bp =trace
# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径;
# logging.file=G:/springboot.log
# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
logging.path =/spring/log
# 在控制台输出的日志的格式
logging.pattern.console =%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file =%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
logging.file
logging.path
Example
Description
(none)
(none)
只在控制台输出
指定文件名
(none)
my.log
输出日志到my.log文件
(none)
指定目录
/var/log
输出到指定目录的 spring.log 文件中
给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了
logback.xml:直接就被日志框架识别了;
logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误 no applicable action for [springProfile]
Logging System
Customization
Logback
logback-spring.xml
, logback-spring.groovy
, logback.xml
, logback.groovy
Log4j2
log4j2-spring.xml
or log4j2.xml
JDK (Java Util Logging)
logging.properties
<layout class =" ch.qos.logback.classic.PatternLayout" >
<springProfile name =" dev" >
<pattern >%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern >
</springProfile >
<springProfile name =" !dev" >
<pattern >%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern >
</springProfile >
</layout >
可以按照slf4j的日志适配图,进行相关的切换;
slf4j+log4j的方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
创建SpringBoot应用,选中我们需要的模块
SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
自动配置
AutoConfiguration:帮我们给容器中自动配置组件
Properties:配置类来封装配置文件的内容
/webjars/**, classpath:/META-INF/resources/webjars/ 找资源
webjars:以jar包的方式引入静态资源
在访问的时候只需要写webjars下面资源的名称即可
localhost:8080/webjars/jquery/3.3.1/jquery.js
//可以设置和静态资源有关的参数,缓存时间等
@ ConfigurationProperties (prefix = "spring.resources" , ignoreUnknownFields = false )
public class ResourceProperties implements ResourceLoaderAware { }
@ Configuration (proxyBeanMethods = false )
public class WebMvcAutoConfiguration {
@ Override
public void addResourceHandlers (ResourceHandlerRegistry registry ) {
if (!this .resourceProperties .isAddMappings ()) {
logger .debug ("Default resource handling disabled" );
return ;
}
Integer cachePeriod = this .resourceProperties .getCachePeriod ();
if (!registry .hasMappingForPattern ("/webjars/**" )) {
customizeResourceHandlerRegistration (
registry .addResourceHandler ("/webjars/**" )
.addResourceLocations (
"classpath:/META-INF/resources/webjars/" )
.setCachePeriod (cachePeriod ));
}
String staticPathPattern = this .mvcProperties .getStaticPathPattern ();
//静态资源文件夹映射
if (!registry .hasMappingForPattern (staticPathPattern )) {
customizeResourceHandlerRegistration (
registry .addResourceHandler (staticPathPattern )
.addResourceLocations (
this .resourceProperties .getStaticLocations ())
.setCachePeriod (cachePeriod ));
}
}
//配置欢迎页映射
@ Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping (
ResourceProperties resourceProperties ) {
return new WelcomePageHandlerMapping (resourceProperties .getWelcomePage (),
this .mvcProperties .getStaticPathPattern ());
}
//配置喜欢的图标
@ Configuration
@ ConditionalOnProperty (value = "spring.mvc.favicon.enabled" , matchIfMissing = true )
public static class FaviconConfiguration {
private final ResourceProperties resourceProperties ;
public FaviconConfiguration (ResourceProperties resourceProperties ) {
this .resourceProperties = resourceProperties ;
}
@ Bean
public SimpleUrlHandlerMapping faviconHandlerMapping () {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping ();
mapping .setOrder (Ordered .HIGHEST_PRECEDENCE + 1 );
//所有 **/favicon.ico
mapping .setUrlMap (Collections .singletonMap ("**/favicon.ico" ,
faviconRequestHandler ()));
return mapping ;
}
@ Bean
public ResourceHttpRequestHandler faviconRequestHandler () {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler ();
requestHandler
.setLocations (this .resourceProperties .getFaviconLocations ());
return requestHandler ;
}
}
}
<!-- 引入jquery-webjar--> 在访问的时候只需要写webjars下面资源的名称即可
<dependency >
<groupId >org.webjars</groupId >
<artifactId >jquery</artifactId >
<version >3.3.1</version >
</dependency >
"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/":当前项目的根路径
欢迎页, 静态资源文件夹下的所有index.html页面;被"/**"映射
+ localhost:8080/ 找index页面
所有的 **/favicon.ico 都是在静态资源文件下找;
指定静态资源文件夹 spring.resources.static-locations
JSP, Velocity, Freemarker, Thymeleaf
SpringBoot推荐的Thymeleaf;
引入properties切换thymeleaf 和 thymeleaf-layout-dialect版本
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-thymeleaf</artifactId >
2.1.6
</dependency >
<properties >
<thymeleaf .version>3.0.9.RELEASE</thymeleaf .version>
<!-- 布局功能的支持程序 thymeleaf3主程序 layout2以上版本 -->
<!-- thymeleaf2 layout1-->
<thymeleaf-layout-dialect .version>2.2.2</thymeleaf-layout-dialect .version>
</properties >
HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;
@ ConfigurationProperties (prefix = "spring.thymeleaf" )
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset .forName ("UTF-8" );
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType .valueOf ("text/html" );
public static final String DEFAULT_PREFIX = "classpath:/templates/" ;
public static final String DEFAULT_SUFFIX = ".html" ;
<html lang =" en" xmlns : th =" http://www.thymeleaf.org" >
<!DOCTYPE html>
< html lang ="en " xmlns:th ="http://www.thymeleaf.org ">
< head >
< meta charset ="UTF-8 ">
< title > Title</ title >
</ head >
< body >
< h1 > 成功!</ h1 >
<!--th:text 将div里面的文本内容设置为 -->
< div th:text ="${hello} "> 这是显示欢迎信息</ div >
</ body >
</ html >
th:text;改变当前元素里面的文本内容;
th:任意html属性;来替换原生属性的值
表达式
Simple expressions:(表达式语法)
Variable Expressions: ${...}:获取变量值;OGNL;
获取对象的属性、调用方法
使用内置的基本对象:
# ctx : the context object.
# vars: the context variables.
# locale : the context locale.
# request : (only in Web Contexts) the HttpServletRequest object.
# response : (only in Web Contexts) the HttpServletResponse object.
# session : (only in Web Contexts) the HttpSession object.
# servletContext : (only in Web Contexts) the ServletContext object.
内置的一些工具对象
# execInfo : information about the template being processed.
# messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
# uris : methods for escaping parts of URLs/URIs
# conversions : methods for executing the configured conversion service (if any).
# dates : methods for java.util.Date objects: formatting, component extraction, etc.
# calendars : analogous to #dates , but for java.util.Calendar objects.
# numbers : methods for formatting numeric objects.
# strings : methods for String objects: contains, startsWith, prepending/appending, etc.
# objects : methods for objects in general.
# bools : methods for boolean evaluation.
# arrays : methods for arrays.
# lists : methods for lists.
# sets : methods for sets.
# maps : methods for maps.
# aggregates : methods for creating aggregates on arrays or collections.
# ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样, 配合 th:object 使用
<div th:object =" ${session.user}" >
<p>Name: <span th:text =" *{firstName}" >Sebastian</span>.</p>
<p>Surname: <span th:text =" *{lastName}" >Pepper</span>.</p>
<p>Nationality: <span th:text =" *{nationality}" >Saturn</span>.</p>
</div>
Message Expressions: # {...}:获取国际化内容
Link URL Expressions: @{...}:定义URL, @{/order/process(execId =${execId},execType =' FAST' )}
Fragment Expressions: ~{...}:片段引用表达式, <div th:insert =" ~{commons :: main}" >...</div>
Literals(字面量)
Text literals: ' one text' , ' Another one!'
Number literals: 0 , 34 , 3.0 , 12.3
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: + , - , * , / , %
Minus sign (unary operator): -
Boolean operations:(布尔运算)
Binary operators: and , or
Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
Comparators: > , < , >= , <= ( gt , lt , ge , le )
Equality operators: == , != ( eq , ne )
Conditional operators:条件运算(三元运算符)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
Special tokens:
No-Operation: _
1. Spring MVC auto-configuration
https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration
class of type WebMvcConfigurerAdapter
, but without @EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
or ExceptionHandlerExceptionResolver
you can declare a WebMvcRegistrationsAdapter
instance providing such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
Spring Boot 通过WebMvcAutoConfiguration自动配置好了SpringMVC
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
自动配置了ViewResolver视图解析器:根据方法的返回值得到视图对象View,视图对象决定如何渲染,转发,重定向
ContentNegotiatingViewResolver:组合所有的视图解析器的;
自定义:自己给容器中添加一个视图解析器;自动的将其组合进来
Support for serving static resources, including support for WebJars 和 静态资源文件夹路径
Static index.html
support
Custom Favicon
support
自动注册了Converter, GenericConverter, Formatter beans.
Converter:类型转换使用Converter
Formatter:格式化器; 2017.12.17 === Date;
自己添加的格式化器转换器,我们只需要放在容器中即可
Support for HttpMessageConverters
HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---Json;
HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;
自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中@Bean,@Component
Automatic registration of MessageCodesResolver 定义错误代码生成规则
Automatic use of a ConfigurableWebBindingInitializer bean
我们可以配置一个ConfigurableWebBindingInitializer来替换默认的
找不到则super初始化WebDataBinder, 把请求数据绑定到java bean
springboot 在自动配置组件时候,先看容器中有没有自己的配置, 如果没有,则用默认配置,如果允许多个组件,则组合
<mvc : view-controller path =" /hello" view-name =" success" />
<mvc : interceptors >
<mvc : interceptor >
<mvc : mapping path =" /hello" />
<bean ></bean >
</mvc : interceptor >
</mvc : interceptors >
编写一个配置类(@Configuration),实现WebMvcConfigurer接口;不能标注@EnableWebMvc
WebMvcAutoConfiguration是SpringMVC的自动配置类
在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
容器中所有的WebMvcConfigurer都会一起起作用,我们的配置类也会被调用;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@ Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@ Override
public void addViewControllers (ViewControllerRegistry registry ) {
//浏览器发送 /atguigu 请求来到 success
registry .addViewController ("/atguigu" ).setViewName ("success" );
}
}
@ Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite ();
//从容器中获取所有的WebMvcConfigurer
@ Autowired (required = false )
public void setConfigurers (List <WebMvcConfigurer > configurers ) {
if (!CollectionUtils .isEmpty (configurers )) {
this .configurers .addWebMvcConfigurers (configurers );
}
}
}
全面接管SpringMVC并失效springmvc的自动配置
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了
我们需要在配置类中添加@EnableWebMvc
@EnableWebMvc的核心
@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;
导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;
WebMvcAutoConfiguration 只有在没有WebMvcConfigurationSupport才生效
@ Import (DelegatingWebMvcConfiguration .class )
public @interface EnableWebMvc {}
@ Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}
@ Configuration
@ ConditionalOnWebApplication
@ ConditionalOnClass ({ Servlet .class , DispatcherServlet .class ,
WebMvcConfigurerAdapter .class })
//容器中没有这个组件的时候,这个自动配置类才生效
@ ConditionalOnMissingBean (WebMvcConfigurationSupport .class )
@ AutoConfigureOrder (Ordered .HIGHEST_PRECEDENCE + 10 )
@ AutoConfigureAfter ({ DispatcherServletAutoConfiguration .class ,
ValidationAutoConfiguration .class })
public class WebMvcAutoConfiguration {}
SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
在SpringBoot中会有非常多的Configurer帮助我们进行扩展配置
在SpringBoot中会有很多的Customizer帮助我们进行定制配置
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@ Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
//所有的WebMvcConfigurerAdapter组件都会一起起作用
@ Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter (){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter () {
@ Override
public void addViewControllers (ViewControllerRegistry registry ) {
registry .addViewController ("/" ).setViewName ("login" );
registry .addViewController ("/index.html" ).setViewName ("login" );
}
};
return adapter ;
}
}
编写国际化配置文件,抽取页面需要显示的国际化消息
SpringBoot自动配置好了管理国际化资源文件的组件
指定基础名字 spring.messages.basename=i18n.login
@ ConfigurationProperties (prefix = "spring.messages" )
public class MessageSourceAutoConfiguration {
/**
* Comma-separated list of basenames (essentially a fully-qualified classpath
* location), each following the ResourceBundle convention with relaxed support for
* slash based locations. If it doesn't contain a package qualifier (such as
* "org.mypackage"), it will be resolved from the classpath root.
*/
private String basename = "messages" ;
//我们的配置文件可以直接放在类路径下叫messages.properties;
@ Bean
public MessageSource messageSource () {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource ();
if (StringUtils .hasText (this .basename )) {
//设置国际化资源文件的基础名(去掉语言国家代码的)
messageSource .setBasenames (StringUtils .commaDelimitedListToStringArray (
StringUtils .trimAllWhitespace (this .basename )));
}
if (this .encoding != null ) {
messageSource .setDefaultEncoding (this .encoding .name ());
}
messageSource .setFallbackToSystemLocale (this .fallbackToSystemLocale );
messageSource .setCacheSeconds (this .cacheSeconds );
messageSource .setAlwaysUseMessageFormat (this .alwaysUseMessageFormat );
return messageSource ;
}
}
去页面获取国际化的值 #{}
<!DOCTYPE html>
< html lang ="en " xmlns:th ="http://www.thymeleaf.org ">
< head >
< title > Signin Template for Bootstrap</ title >
< link href ="asserts/css/bootstrap.min.css " th:href ="@{/webjars/bootstrap/4.0.0/css/bootstrap.css} " rel ="stylesheet ">
< link href ="asserts/css/signin.css " th:href ="@{/asserts/css/signin.css} " rel ="stylesheet ">
</ head >
< body class ="text-center ">
< form class ="form-signin " action ="dashboard.html ">
< img class ="mb-4 " th:src ="@{/asserts/img/bootstrap-solid.svg} " src ="asserts/img/bootstrap-solid.svg " alt ="" width ="72 " height ="72 ">
< h1 class ="h3 mb-3 font-weight-normal " th:text ="#{login.tip} "> Please sign in</ h1 >
< label class ="sr-only " th:text ="#{login.username} "> Username</ label >
< input type ="text " class ="form-control " placeholder ="Username " th:placeholder ="#{login.username} " required ="" autofocus ="">
< label class ="sr-only " th:text ="#{login.password} "> Password</ label >
< input type ="password " class ="form-control " placeholder ="Password " th:placeholder ="#{login.password} " required ="">
< div class ="checkbox mb-3 ">
< label >
< input type ="checkbox " value ="remember-me "/> [[#{login.remember}]]
</ label >
</ div >
< button class ="btn btn-lg btn-primary btn-block " type ="submit " th:text ="#{login.btn} "> Sign in</ button >
< p class ="mt-5 mb-3 text-muted "> © 2017-2018</ p >
< a class ="btn btn-sm "> 中文</ a >
< a class ="btn btn-sm "> English</ a >
</ form >
</ body >
</ html >
原理:国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象);根据浏览器语言设置信息切换国际化
@ Bean
@ ConditionalOnMissingBean
@ ConditionalOnProperty (prefix = "spring.mvc" , name = "locale" )
public LocaleResolver localeResolver () {
if (this .mvcProperties .getLocaleResolver () == WebMvcProperties .LocaleResolver .FIXED ) {
return new FixedLocaleResolver (this .mvcProperties .getLocale ());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver ();
localeResolver .setDefaultLocale (this .mvcProperties .getLocale ());
return localeResolver ;
}
点击链接切换国际化, 请求头的的区域信息
编写自己的localeResolver
讲localeResolver 加入容器中
/**
* 可以在连接上携带区域信息
*/
public class MyLocaleResolver implements LocaleResolver {
@ Override
public Locale resolveLocale (HttpServletRequest request ) {
String l = request .getParameter ("l" );
Locale locale = Locale .getDefault ();
if (!StringUtils .isEmpty (l )){
String [] split = l .split ("_" );
locale = new Locale (split [0 ],split [1 ]);
}
return locale ;
}
}
@ Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@ Bean
public LocaleResolver localeResolver (){
return new MyLocaleResolver ();
}
}
# 禁用缓存
spring.thymeleaf.cache=false
页面修改完成以后ctrl+f9:重新编译, 错误信息是否为空进行判断
< p style ="color: red " th:text ="${msg} " th:if ="${not #strings.isEmpty(msg)} "> </ p >
public String login (@ RequestParam ("username" ) String username , @ RequestParam ("password" ) String password , Map <String , Object > map ) {
return "redirect:/main" ;
}
@ Bean
public WebMvcConfigurer webMvcConfigurer () {
return new WebMvcConfigurer () {
@ Override
public void addViewControllers (ViewControllerRegistry registry ) {
registry .addViewController ("/main.html" ).setViewName ("dashboard" );
}
};
}
拦截器进行登陆检查
Springboot 1.0 不会拦截静态资源
Springboot 2.0 拦截静态资源
public class LoginHandlerInterceptor implements HandlerInterceptor {
@ Override
public boolean preHandle (HttpServletRequest request , HttpServletResponse response , Object handler ) throws Exception {
Object user = request .getSession ().getAttribute ("loginUser" );
if (user == null ){
//未登陆,返回登陆页面
request .setAttribute ("msg" ,"没有权限请先登陆" );
request .getRequestDispatcher ("/index.html" ).forward (request ,response );
return false ;
}
return true ;
}
}
RestfulCRUD:CRUD满足Rest风格;
URI:/资源名称/资源标识, HTTP请求方式区分对资源CRUD操作
普通CRUD(uri来区分操作)
RestfulCRUD
查询
getEmp
emp---GET
添加
addEmp?xxx
emp---POST
修改
updateEmp?id=xxx&xxx=xx
emp/{id}---PUT
删除
deleteEmp?id=1
emp/{id}---DELETE
实验功能
请求URI
请求方式
查询所有员工
emps
GET
查询某个员工(来到修改页面)
emp/1
GET
来到添加页面
emp
GET
添加员工
emp
POST
来到修改页面(查出员工进行信息回显)
emp/1
GET
修改员工
emp
PUT
删除员工
emp/1
DELETE
< div th:fragment ="copy ">
© 2011 The Good Thymes Virtual Grocery
</ div >
< div th:insert ="~{footer :: copy} "> </ div >
~{templatename::selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名
insert的公共片段在div标签中
如果使用th:insert等属性进行引入,可以不用写~{}:
行内写法可以加上:[[~{}]];[(~{})];
三种引入公共片段的th属性:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
< footer th:fragment ="copy ">
© 2011 The Good Thymes Virtual Grocery
</ footer >
引入方式
< div th:insert ="footer :: copy "> </ div >
< div th:replace ="footer :: copy "> </ div >
< div th:include ="footer :: copy "> </ div >
效果
< div >
< footer >
© 2011 The Good Thymes Virtual Grocery
</ footer >
</ div >
< footer >
© 2011 The Good Thymes Virtual Grocery
</ footer >
< div >
© 2011 The Good Thymes Virtual Grocery
</ div >
< nav class ="col-md-2 d-none d-md-block bg-light sidebar " id ="sidebar ">
< div class ="sidebar-sticky ">
< ul class ="nav flex-column ">
< li class ="nav-item ">
< a class ="nav-link active "
th:class ="${activeUri=='main.html'?'nav-link active':'nav-link'} "
href ="# " th:href ="@{/main.html} ">
< svg xmlns ="http://www.w3.org/2000/svg " width ="24 " height ="24 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round " class ="feather feather-home ">
< path d ="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z "> </ path >
< polyline points ="9 22 9 12 15 12 15 22 "> </ polyline >
</ svg >
Dashboard < span class ="sr-only "> (current)</ span >
</ a >
</ li >
</ ul >
</ div >
</ nav >
<!--引入侧边栏;传入参数-->
< div th:replace ="commons/bar::#sidebar(activeUri='emps') "> </ div >
< form >
< div class ="form-group ">
< label > LastName</ label >
< input type ="text " class ="form-control " placeholder ="zhangsan ">
</ div >
< div class ="form-group ">
< label > Email</ label >
< input type ="email " class ="form-control " placeholder ="zhangsan@atguigu.com ">
</ div >
< div class ="form-group ">
< label > Gender</ label > < br />
< div class ="form-check form-check-inline ">
< input class ="form-check-input " type ="radio " name ="gender " value ="1 ">
< label class ="form-check-label "> 男</ label >
</ div >
< div class ="form-check form-check-inline ">
< input class ="form-check-input " type ="radio " name ="gender " value ="0 ">
< label class ="form-check-label "> 女</ label >
</ div >
</ div >
< div class ="form-group ">
< label > department</ label >
< select class ="form-control ">
< option > 1</ option >
< option > 2</ option >
< option > 3</ option >
< option > 4</ option >
< option > 5</ option >
</ select >
</ div >
< div class ="form-group ">
< label > Birth</ label >
< input type ="text " class ="form-control " placeholder ="zhangsan ">
</ div >
< button type ="submit " class ="btn btn-primary "> 添加</ button >
</ form >
日期的格式化;SpringMVC将页面提交的值需要转换为指定的类型;
默认日期是按照/的方式, 修改spring.mvc.date-format定制日期格式
修改添加二合一表单
导入配置HiddenHttpMethodFilter
页面创建一个post表单
创建一个input项,name="_method";值就是我们指定的请求方式
@ Bean
public FilterRegistrationBean hiddenHttpMethodFilter () {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean (new HiddenHttpMethodFilter ());
filterRegistrationBean .setUrlPatterns (Arrays .asList ("/*" ));
return filterRegistrationBean ;
}
<!--需要区分是员工修改还是添加;-->
< form th:action ="@{/emp} " method ="post ">
< input type ="hidden " name ="_method " value ="put " th:if ="${emp!=null} "/>
< input type ="hidden " name ="id " th:if ="${emp!=null} " th:value ="${emp.id} ">
< div class ="form-group ">
< label > LastName</ label >
< input name ="lastName " type ="text " class ="form-control " placeholder ="zhangsan " th:value ="${emp!=null}?${emp.lastName} ">
</ div >
< div class ="form-group ">
< label > Email</ label >
< input name ="email " type ="email " class ="form-control " placeholder ="zhangsan@atguigu.com " th:value ="${emp!=null}?${emp.email} ">
</ div >
< div class ="form-group ">
< label > Gender</ label > < br />
< div class ="form-check form-check-inline ">
< input class ="form-check-input " type ="radio " name ="gender " value ="1 " th:checked ="${emp!=null}?${emp.gender==1} ">
< label class ="form-check-label "> 男</ label >
</ div >
< div class ="form-check form-check-inline ">
< input class ="form-check-input " type ="radio " name ="gender " value ="0 " th:checked ="${emp!=null}?${emp.gender==0} ">
< label class ="form-check-label "> 女</ label >
</ div >
</ div >
< div class ="form-group ">
< label > department</ label >
<!--提交的是部门的id-->
< select class ="form-control " name ="department.id ">
< option th:selected ="${emp!=null}?${dept.id == emp.department.id} " th:value ="${dept.id} " th:each ="dept:${depts} " th:text ="${dept.departmentName} "> 1</ option >
</ select >
</ div >
< div class ="form-group ">
< label > Birth</ label >
< input name ="birth " type ="text " class ="form-control " placeholder ="zhangsan " th:value ="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')} ">
</ div >
< button type ="submit " class ="btn btn-primary " th:text ="${emp!=null}?'修改':'添加' "> 添加</ button >
</ form >
< tr th:each ="emp:${emps} ">
< td th:text ="${emp.id} "> </ td >
< td > [[${emp.lastName}]]</ td >
< td th:text ="${emp.email} "> </ td >
< td th:text ="${emp.gender}==0?'女':'男' "> </ td >
< td th:text ="${emp.department.departmentName} "> </ td >
< td th:text ="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')} "> </ td >
< td >
< a class ="btn btn-sm btn-primary " th:href ="@{/emp/}+${emp.id} "> 编辑</ a >
< button th:attr ="del_uri=@{/emp/}+${emp.id} " class ="btn btn-sm btn-danger deleteBtn "> 删除</ button >
</ td >
</ tr >
< script >
$ ( ".deleteBtn" ) . click ( function ( ) {
//删除当前员工的
$ ( "#deleteEmpForm" ) . attr ( "action" , $ ( this ) . attr ( "del_uri" ) ) . submit ( ) ;
return false ;
} ) ;
</ script >
默认效果:浏览器,返回一个默认的错误页面
如果是其他客户端,默认响应一个json数据
ErrorMvcAutoConfiguration;错误处理的自动配置;
DefaultErrorAttributes
ErrorPageCustomizer
BasicErrorController
处理默认/error请求
ModelAndView 返回 html 数据
ResponseEntity 返回 json 数据
转入页面并传入错误状态码
DefaultErrorViewResolver
从模板文件夹找 /error/${code}.html
从静态资源文件夹 /error/${code}.html
客户端错误 4xx.html, 服务端错误 5xx.html 作为默认错误
public class DefaultErrorAttributes {
@ Override
public Map <String , Object > getErrorAttributes (RequestAttributes requestAttributes , boolean includeStackTrace ) {
Map <String , Object > errorAttributes = new LinkedHashMap <String , Object >();
errorAttributes .put ("timestamp" , new Date ());
addStatus (errorAttributes , requestAttributes );
addErrorDetails (errorAttributes , requestAttributes , includeStackTrace );
addPath (errorAttributes , requestAttributes );
return errorAttributes ;
}
}
public class ErrorPageCustomizer {
@ Value ("${error.path:/error}" )
private String path = "/error" ;
}
@ Controller
@ RequestMapping ("${server.error.path:${error.path:/error}}" )
public class BasicErrorController extends AbstractErrorController {
@ RequestMapping (produces = "text/html" )
public ModelAndView errorHtml (HttpServletRequest request ,
HttpServletResponse response ) {
HttpStatus status = getStatus (request );
Map <String , Object > model = Collections .unmodifiableMap (getErrorAttributes (
request , isIncludeStackTrace (request , MediaType .TEXT_HTML )));
response .setStatus (status .value ());
//去哪个页面作为错误页面
ModelAndView modelAndView = resolveErrorView (request , response , status , model );
return (modelAndView == null ? new ModelAndView ("error" , model ) : modelAndView );
}
@ RequestMapping
@ ResponseBody
public ResponseEntity <Map <String , Object >> error (HttpServletRequest request ) {
Map <String , Object > body = getErrorAttributes (request ,
isIncludeStackTrace (request , MediaType .ALL ));
HttpStatus status = getStatus (request );
return new ResponseEntity <Map <String , Object >>(body , status );
}
}
@ Override
public ModelAndView resolveErrorView (HttpServletRequest request , HttpStatus status ,
Map <String , Object > model ) {
ModelAndView modelAndView = resolve (String .valueOf (status ), model );
if (modelAndView == null && SERIES_VIEWS .containsKey (status .series ())) {
modelAndView = resolve (SERIES_VIEWS .get (status .series ()), model );
}
return modelAndView ;
}
private ModelAndView resolve (String viewName , Map <String , Object > model ) {
//默认SpringBoot可以去找到一个页面 error/404
String errorViewName = "error/" + viewName ;
//模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this .templateAvailabilityProviders
.getProvider (errorViewName , this .applicationContext );
if (provider != null ) {
//模板引擎可用的情况下返回到errorViewName指定的视图地址
return new ModelAndView (errorViewName , model );
}
//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
return resolveResource (errorViewName , model );
}
有模板引擎的情况下;error/状态码;**
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);
+ timestamp:时间戳
+ status:状态码
+ error:错误提示
+ exception:异常对象
+ message:异常消息
没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
将我们的定制数据携带出去;
出现错误以后,会来到/error请求,会被BasicErrorController处理
响应出去可以获取的数据是由getErrorAttributes得到的
完全来编写一个ErrorController的实现类放在容器中;
页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;
容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;
自定义ErrorAttributes
//给容器中加入我们自己定义的ErrorAttributes
@ Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@ Override
public Map <String , Object > getErrorAttributes (RequestAttributes requestAttributes , boolean includeStackTrace ) {
Map <String , Object > map = super .getErrorAttributes (requestAttributes , includeStackTrace );
map .put ("company" ,"atguigu" );
return map ;
}
}
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器;
注册Servlet三大组件Servlet, Filter, Listener
由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件
ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean
@ Bean
public ServletRegistrationBean myServlet (){
ServletRegistrationBean registrationBean = new ServletRegistrationBean (new MyServlet (),"/myServlet" );
return registrationBean ;
}
@ Bean
public FilterRegistrationBean myFilter (){
FilterRegistrationBean registrationBean = new FilterRegistrationBean ();
registrationBean .setFilter (new MyFilter ());
registrationBean .setUrlPatterns (Arrays .asList ("/hello" ,"/myServlet" ));
return registrationBean ;
}
```java
@ Bean
public ServletListenerRegistrationBean myListener (){
ServletListenerRegistrationBean <MyListener > registrationBean = new ServletListenerRegistrationBean <>(new MyListener ());
return registrationBean ;
}
SpringBoot帮我们自动SpringMVC的时候,自动的注册SpringMVC的前端控制器;DIspatcherServlet;
@ Bean (name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME )
@ ConditionalOnBean (value = DispatcherServlet .class , name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME )
public ServletRegistrationBean dispatcherServletRegistration (DispatcherServlet dispatcherServlet ) {
ServletRegistrationBean registration = new ServletRegistrationBean (dispatcherServlet , this .serverProperties .getServletMapping ());
registration .setName (DEFAULT_DISPATCHER_SERVLET_BEAN_NAME );
registration .setLoadOnStartup (
this .webMvcProperties .getServlet ().getLoadOnStartup ());
if (this .multipartConfig != null ) {
registration .setMultipartConfig (this .multipartConfig );
}
return registration ;
}
Tomcat(默认使用)
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
</dependency >
Jetty
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
<exclusions >
<exclusion >
<artifactId >spring-boot-starter-tomcat</artifactId >
<groupId >org.springframework.boot</groupId >
</exclusion >
</exclusions >
</dependency >
<dependency >
<artifactId >spring-boot-starter-jetty</artifactId >
<groupId >org.springframework.boot</groupId >
</dependency >
Undertow
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
<exclusions >
<exclusion >
<artifactId >spring-boot-starter-tomcat</artifactId >
<groupId >org.springframework.boot</groupId >
</exclusion >
</exclusions >
</dependency >
<dependency >
<artifactId >spring-boot-starter-undertow</artifactId >
<groupId >org.springframework.boot</groupId >
</dependency >
ServletWebServerFactoryAutoConfiguration:嵌入式的Servlet容器自动配置
@ Configuration (proxyBeanMethods = false )
@ AutoConfigureOrder (Ordered .HIGHEST_PRECEDENCE )
@ ConditionalOnClass (ServletRequest .class )
@ ConditionalOnWebApplication (type = Type .SERVLET )
@ EnableConfigurationProperties (ServerProperties .class )
@ Import ({ ServletWebServerFactoryAutoConfiguration .BeanPostProcessorsRegistrar .class ,
ServletWebServerFactoryConfiguration .EmbeddedTomcat .class ,
ServletWebServerFactoryConfiguration .EmbeddedJetty .class ,
ServletWebServerFactoryConfiguration .EmbeddedUndertow .class })
public class ServletWebServerFactoryAutoConfiguration {
@ Bean
@ ConditionalOnClass (name = "org.apache.catalina.startup.Tomcat" )
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer (
ServerProperties serverProperties ) {
return new TomcatServletWebServerFactoryCustomizer (serverProperties );
}
}
ServletWebServerFactoryConfiguration 创建web container
@ Configuration (proxyBeanMethods = false )
class ServletWebServerFactoryConfiguration {
@ Configuration (proxyBeanMethods = false )
@ ConditionalOnClass ({ Servlet .class , Tomcat .class , UpgradeProtocol .class })
@ ConditionalOnMissingBean (value = ServletWebServerFactory .class , search = SearchStrategy .CURRENT )
static class EmbeddedTomcat {
@ Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory () {
return factory ;
}
}
@ Configuration (proxyBeanMethods = false )
@ ConditionalOnClass ({ Servlet .class , Server .class , Loader .class , WebAppContext .class })
@ ConditionalOnMissingBean (value = ServletWebServerFactory .class , search = SearchStrategy .CURRENT )
static class EmbeddedJetty {
@ Bean
JettyServletWebServerFactory JettyServletWebServerFactory () {
return factory ;
}
}
}
TomcatServletWebServerFactory 创建 tomcat 容器
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory , ResourceLoaderAware {
@ Override
public WebServer getWebServer (ServletContextInitializer ... initializers ) {
if (this .disableMBeanRegistry ) {
Registry .disableRegistry ();
}
Tomcat tomcat = new Tomcat ();
File baseDir = (this .baseDirectory != null ) ? this .baseDirectory : createTempDir ("tomcat" );
tomcat .setBaseDir (baseDir .getAbsolutePath ());
Connector connector = new Connector (this .protocol );
connector .setThrowOnFailure (true );
tomcat .getService ().addConnector (connector );
customizeConnector (connector );
tomcat .setConnector (connector );
tomcat .getHost ().setAutoDeploy (false );
configureEngine (tomcat .getEngine ());
for (Connector additionalConnector : this .additionalTomcatConnectors ) {
tomcat .getService ().addConnector (additionalConnector );
}
prepareContext (tomcat .getHost (), initializers );
return getTomcatWebServer (tomcat );
}
}
修改和server有关的配置(ServerProperties, WebServerFactoryCustomizer);
server.port =8081
server.context-path =/crud
server.tomcat.uri-encoding =UTF-8
编写 WebServerFactoryCustomizer 嵌入式的Servlet容器的定制器;来修改Servlet容器的配置
@ Bean //一定要将这个定制器加入到容器中
public WebServerFactoryCustomizer webServerFactoryCustomizer () {
return new WebServerFactoryCustomizer <ConfigurableServletWebServerFactory >() {
@ Override
public void customize (ConfigurableServletWebServerFactory factory ) {
factory .setPort (8090 );
}
};
}
容器中导入了BeanPostProcessorsRegistrar注册WebServerFactoryCustomizerBeanPostProcessor
循环WebServerFactoryCustomizer进行赋值
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor , BeanFactoryAware {
@ Override
public Object postProcessBeforeInitialization (Object bean , String beanName ) throws BeansException {
if (bean instanceof WebServerFactory ) {
postProcessBeforeInitialization ((WebServerFactory ) bean );
}
return bean ;
}
private void postProcessBeforeInitialization (WebServerFactory webServerFactory ) {
LambdaSafe .callbacks (WebServerFactoryCustomizer .class , getCustomizers (), webServerFactory )
.withLogger (WebServerFactoryCustomizerBeanPostProcessor .class )
.invoke ((customizer ) -> customizer .customize (webServerFactory ));
}
}
SpringBoot根据导入的依赖情况,给容器中添加相应的 ServletWebServerFactoryConfiguration, e.g. TomcatServletWebServerFactory
ServletWebServerFactoryAutoConfiguration 利用 BeanPostProcessorsRegistrar 注册后置处理器 WebServerFactoryCustomizerBeanPostProcessor
WebServerFactoryCustomizerBeanPostProcessor 调用 postProcessBeforeInitialization
SpringBoot应用启动运行run方法
创建AnnotationConfigEmbeddedWebApplicationContext 或者 AnnotationConfigApplicationContext
refreshContext(context)
refresh(context)
onRefresh(); ServletWebServerApplicationContext重写了onRefresh方法
ServletWebServerApplicationContext 获取 ServletWebServerFactory
ServletWebServerFactory 创建对象
TomcatWebServer 启动 tomcat server
public void refresh () throws BeansException , IllegalStateException {
synchronized (this .startupShutdownMonitor ) {
try {
// Initialize other special beans in specific context subclasses.
onRefresh ();
} catch (BeansException ex ) {
throw ex ;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches ();
}
}
}
public class ServletWebServerApplicationContext {
@ Override
protected void onRefresh () {
super .onRefresh ();
createWebServer ();
}
private void createWebServer () {
WebServer webServer = this .webServer ;
ServletContext servletContext = getServletContext ();
if (webServer == null && servletContext == null ) {
ServletWebServerFactory factory = getWebServerFactory ();
this .webServer = factory .getWebServer (getSelfInitializer ());
}
else if (servletContext != null ) {
try {
getSelfInitializer ().onStartup (servletContext );
}
catch (ServletException ex ) {
throw new ApplicationContextException ("Cannot initialize servlet context" , ex );
}
}
initPropertySources ();
}
}
}
public class TomcatWebServer {
public TomcatWebServer (Tomcat tomcat , boolean autoStart ) {
Assert .notNull (tomcat , "Tomcat Server must not be null" );
this .tomcat = tomcat ;
this .autoStart = autoStart ;
initialize ();
}
private void initialize () throws WebServerException {
this .tomcat .start ();
}
}
优点:简单、便携;
缺点:默认不支持JSP、优化定制比较复杂, 定制器 ServerProperties, WebServletContainerCustomizer
外置的Servlet容器:外面安装Tomcat---应用war包的方式打包;
必须创建一个war项目;(利用idea创建好目录结构)
将嵌入式的Tomcat指定为provided;
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-tomcat</artifactId >
<scope >provided</scope >
</dependency >
必须编写一个SpringBootServletInitializer的子类,并调用configure方法
启动服务器就可以使用
public class ServletInitializer extends SpringBootServletInitializer {
@ Override
protected SpringApplicationBuilder configure (SpringApplicationBuilder application ) {
return application .sources (SpringBoot04WebJspApplication .class );
}
}
war包:启动服务器,服务器启动SpringBoot应用,启动ioc容器;
Servlet 3.0 规范
服务器启动会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例
ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
ServletContainerInitializer实现类还可以使用@HandlesTypes加载所需要的类
流程
启动Tomcat
org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer
SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>
为这些WebApplicationInitializer类型的类创建实例;
每一个WebApplicationInitializer都调用自己的onStartup;
SpringBootServletInitializer implements WebApplicationInitializer 的类会被创建对象,并执行onStartup方法
SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器
子类ServletInitializer 重写了这个方法,将SpringBoot的主程序类传入了进来
Spring的应用就启动并且创建IOC容器
没有tomcat的容器的class, 所以不会启动内置的tomcat
public class SpringBootServletInitializer implements WebApplicationInitializer {
protected WebApplicationContext createRootApplicationContext (
ServletContext servletContext ) {
//1、创建SpringApplicationBuilder
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 );
//调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = configure (builder );
//使用builder创建一个Spring应用
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 );
}
//启动Spring应用
return run (application );
}
}
public ConfigurableApplicationContext run (String ... args ) {
StopWatch stopWatch = new StopWatch ();
stopWatch .start ();
ConfigurableApplicationContext context = null ;
FailureAnalyzers analyzers = null ;
configureHeadlessProperty ();
SpringApplicationRunListeners listeners = getRunListeners (args );
listeners .starting ();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments (
args );
ConfigurableEnvironment environment = prepareEnvironment (listeners ,
applicationArguments );
Banner printedBanner = printBanner (environment );
context = createApplicationContext ();
analyzers = new FailureAnalyzers (context );
prepareContext (context , environment , listeners , applicationArguments ,
printedBanner );
//刷新IOC容器
refreshContext (context );
afterRefresh (context , applicationArguments );
listeners .finished (context , null );
stopWatch .stop ();
if (this .logStartupInfo ) {
new StartupInfoLogger (this .mainApplicationClass )
.logStarted (getApplicationLog (), stopWatch );
}
return context ;
}
catch (Throwable ex ) {
handleRunFailure (context , listeners , analyzers , ex );
throw new IllegalStateException (ex );
}
}
Docker是一个开源的应用容器引擎;是一个轻量级容器技术;
Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像;
运行中的这个镜像称为容器,容器启动是非常快速的。
docker主机(Host):安装了Docker程序的机器
docker客户端(Client):连接docker主机进行操作;
docker仓库(Registry):用来保存各种打包好的软件镜像;
docker镜像(Images):软件打包好的镜像;放在docker仓库中;
docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用
安装linux虚拟机
VMWare、VirtualBox(安装);
+ 导入虚拟机文件centos7-atguigu.ova;
+ 双击启动linux虚拟机;使用 root/ 123456登陆
+ 使用客户端连接linux服务器进行命令操作;
+ 设置虚拟机网络: 桥接网络===选好网卡====接入网线;
+ 设置好网络以后使用命令重启虚拟机的网络, service network restart
+ 查看linux的ip地址
+ 使用客户端连接linux;
1. 检查内核版本,必须是3.10及以上
uname -r
2. 安装docker
yum install docker
3. 输入y确认安装
4. 启动docker
[root@localhost ~ ]# systemctl start docker
[root@localhost ~ ]# docker -v
Docker version 1.12.6, build 3e8e77d/1.12.6
5. 开机启动docker
[root@localhost ~ ]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
6. 停止docker
systemctl stop docker
操作
命令
说明
检索
docker search 关键字 eg:docker search redis
我们经常去docker hub上检索镜像的详细信息,如镜像的TAG。
拉取
docker pull 镜像名:tag
:tag是可选的,tag表示标签,多为软件的版本,默认是latest
列表
docker images
查看所有本地镜像
删除
docker rmi image-id
删除指定的本地镜像
1. 搜索镜像
[root@localhost ~ ]# docker search tomcat
2. 拉取镜像
[root@localhost ~ ]# docker pull tomcat
3. 根据镜像启动容器
docker run --name mytomcat -d tomcat:latest
4. docker ps
查看运行中的容器
5. 停止运行中的容器
docker stop 容器的id
6. 查看所有的容器
docker ps -a
7. 启动容器
docker start 容器id
8. 删除一个容器
docker rm 容器id
9. 启动一个做了端口映射的tomcat
[root@localhost ~ ]# docker run -d -p 8888:8080 tomcat
-d:后台运行
-p: 将主机的端口映射到容器的一个端口 主机端口:容器内部的端口
10. 为了演示简单关闭了linux的防火墙
service firewalld status ;查看防火墙状态
service firewalld stop:关闭防火墙
11. 查看容器的日志
docker logs container-name/container-id
docker pull mysql
[root@localhost ~ ]# docker run --name mysql01 -d mysql
42f09819908bb72dd99ae19e792e0a5d03c48638421fa64cce5f8ba0f40f5846
mysql退出了
[root@localhost ~ ]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42f09819908b mysql " docker-entrypoint.sh" 34 seconds ago Exited (1) 33 seconds ago mysql01
538bde63e500 tomcat " catalina.sh run" About an hour ago Exited (143) About an hour ago compassionate_
goldstine
c4f1ac60b3fc tomcat " catalina.sh run" About an hour ago Exited (143) About an hour ago lonely_fermi
81ec743a5271 tomcat " catalina.sh run" About an hour ago Exited (143) About an hour ago sick_ramanujan
//错误日志
[root@localhost ~ ]# docker logs 42f09819908b
error: database is uninitialized and password option is not specified
You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD;这个三个参数必须指定一个
[root@localhost ~ ]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
b874c56bec49fb43024b3805ab51e9097da779f2f572c22c695305dedd684c5f
[root@localhost ~ ]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b874c56bec49 mysql " docker-entrypoint.sh" 4 seconds ago Up 3 seconds 3306/tcp mysql01
[root@localhost ~ ]# docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
ad10e4bc5c6a0f61cbad43898de71d366117d120e39db651844c0e73863b9434
[root@localhost ~ ]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad10e4bc5c6a mysql " docker-entrypoint.sh" 4 seconds ago Up 2 seconds 0.0.0.0:3306-> 3306/tcp mysql02
docker run --name mysql03 -v /conf/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
把主机的/conf/mysql文件夹挂载到 mysqldocker容器的/etc/mysql/conf.d文件夹里面
改mysql的配置文件就只需要把mysql配置文件放在自定义的文件夹下(/conf/mysql)
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
指定mysql的一些配置参数
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-jdbc</artifactId >
</dependency >
<dependency >
<groupId >mysql</groupId >
<artifactId >mysql-connector-java</artifactId >
<scope >runtime</scope >
</dependency >
spring :
datasource :
username : root
password : ${password}
url : jdbc:mysql://127.0.0.1/jdbc?serverTimeZone=UTC
driver-class-name : com.mysql.cj.jdbc.Driver
默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;
数据源的相关配置都在DataSourceProperties里面;
自动配置原理:
DataSourceConfiguration,根据配置创建数据源, 可以使用spring.datasource.type指定自定义的数据源类型;
SpringBoot默认可以支持: org.apache.tomcat.jdbc.pool.DataSource, HikariDataSource, BasicDataSource
自定义数据源类型
@ ConditionalOnMissingBean (DataSource .class )
@ ConditionalOnProperty (name = "spring.datasource.type" )
static class Generic {
@ Bean
public DataSource dataSource (DataSourceProperties properties ) {
return properties .initializeDataSourceBuilder ().build ();
}
}
DataSourceInitializer is ApplicationListener
runSchemaScripts();运行建表语句, schema-*.sql 或则用 spring.datasource.schema 在application.yml指定
runDataScripts();运行插入数据的sql语句, data-*.sql
initialization-mode : always
schema :
- classpath:payment.sql
操作数据库:自动配置了JdbcTemplate操作数据库
配置一个管理后台的Servlet
配置一个web监控的filter
添加 log4j:log4j:1.2.17
@ Configuration
public class DruidConfig {
//绑定属性
@ ConfigurationProperties (prefix = "spring.datasource" )
@ Bean
public DataSource druid (){
return new DruidDataSource ();
}
//配置Druid的监控
@ Bean
public ServletRegistrationBean statViewServlet (){
ServletRegistrationBean bean = new ServletRegistrationBean (new StatViewServlet (), "/druid/*" );
Map <String ,String > initParams = new HashMap <>();
initParams .put ("loginUsername" ,"admin" );
initParams .put ("loginPassword" ,"admin" );
initParams .put ("allow" ,"" );//默认就是允许所有访问
initParams .put ("deny" ,"192.168.15.21" );
bean .setInitParameters (initParams );
return bean ;
}
//配置一个web监控的filter
@ Bean
public FilterRegistrationBean webStatFilter (){
FilterRegistrationBean bean = new FilterRegistrationBean ();
bean .setFilter (new WebStatFilter ());
Map <String ,String > initParams = new HashMap <>();
initParams .put ("exclusions" ,"*.js,*.css,/druid/*" );
bean .setInitParameters (initParams );
bean .setUrlPatterns (Arrays .asList ("/*" ));
return bean ;
}
}
<dependency >
<groupId >org.mybatis.spring.boot</groupId >
<artifactId >mybatis-spring-boot-starter</artifactId >
<version >1.3.1</version >
</dependency >
@ Mapper
public interface DepartmentMapper {
@ Select ("select * from department where id=#{id}" )
public Department getDeptById (Integer id );
@ Delete ("delete from department where id=#{id}" )
public int deleteDeptById (Integer id );
@ Options (useGeneratedKeys = true ,keyProperty = "id" )
@ Insert ("insert into department(departmentName) values(#{departmentName})" )
public int insertDept (Department department );
@ Update ("update department set departmentName=#{departmentName} where id=#{id}" )
public int updateDept (Department department );
}
自定义MyBatis的配置规则;给容器中添加一个ConfigurationCustomizer;
@ org .springframework .context .annotation .Configuration
public class MyBatisConfig {
@ Bean
public ConfigurationCustomizer configurationCustomizer (){
return new ConfigurationCustomizer (){
@ Override
public void customize (Configuration configuration ) {
configuration .setMapUnderscoreToCamelCase (true );
}
};
}
}
使用MapperScan批量扫描所有的Mapper接口
@ MapperScan (value = "com.bp.springboot.mapper" )
@ SpringBootApplication
public class SpringBoot06DataMybatisApplication {
public static void main (String [] args ) {
SpringApplication .run (SpringBoot06DataMybatisApplication .class , args );
}
}
JPA:ORM(Object Relational Mapping);
编写一个实体类(bean)和数据表进行映射,并且配置好映射关系;
@ Entity
@ Table (name = "tbl_user" )
public class User {
@ Id
@ GeneratedValue (strategy = GenerationType .IDENTITY )
private Integer id ;
@ Column (name = "last_name" ,length = 50 )
private String lastName ;
}
编写一个Dao接口来操作实体类对应的数据表(Repository)
public interface UserRepository extends JpaRepository <User ,Integer > {}
spring :
jpa :
hibernate :
ddl-auto : update
show-sql : true
23.1 重要的事件回调机制配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
ApplicationRunner
CommandLineRunner
判断应用类型,REACTIVE,SERVLET, NONE
SpringFactoriesLoader 从classpath找到 "META-INF/spring.factories"
ApplicationContextInitializer
ApplicationListener
寻找有main方法的启动类
public class SpringApplication {
public SpringApplication (ResourceLoader resourceLoader , Class <?>... primarySources ) {
this .resourceLoader = resourceLoader ;
Assert .notNull (primarySources , "PrimarySources must not be null" );
this .primarySources = new LinkedHashSet <>(Arrays .asList (primarySources ));
this .webApplicationType = WebApplicationType .deduceFromClasspath ();
setInitializers ((Collection ) getSpringFactoriesInstances (ApplicationContextInitializer .class ));
setListeners ((Collection ) getSpringFactoriesInstances (ApplicationListener .class ));
this .mainApplicationClass = deduceMainApplicationClass ();
}
}
SpringApplicationRunListener : **从classpath找到 "META-INF/spring.factories" 获取 SpringApplicationRunListener, EventPublishingRunListener 作为 SpringApplicationRunListener
EventPublishingRunListener 持有SpringApplication中所有的ApplicationListener
EventPublishingRunListener以后会广播事件到所有的ApplicationListener, ApplicationListener根据事件的类型进行更新
public class EventPublishingRunListener implements SpringApplicationRunListener , Ordered {
private final SpringApplication application ;
private final String [] args ;
private final SimpleApplicationEventMulticaster initialMulticaster ;
public EventPublishingRunListener (SpringApplication application , String [] args ) {
this .application = application ;
this .args = args ;
this .initialMulticaster = new SimpleApplicationEventMulticaster ();
for (ApplicationListener <?> listener : application .getListeners ()) {
this .initialMulticaster .addApplicationListener (listener );
}
}
}
回调 SpringApplicationRunListener.starting() -> ApplicationStartingEvent
prepareEnvironment()
创建环境 ConfiguratableEnvironment
回调 SpringApplicationRunListener.environmentPrepared() -> ApplicationEnvironmentPreparedEvent
createApplicationContext()
prepareContext()
复制enviroment 到容器
回调ApplicationContextInitializer.initialze()
回调SpringApplicationRunListener.contextPrepared(context) -> ApplicationContextInitializedEvent
回调SpringApplicationRunListener.contextLoaded(context) -> ApplicationPreparedEvent
refreshContext()
IOC 容器初始化,创建singleton类
创建嵌入式WebContainer 并启动
afterRefresh()
started()
回调SpringApplicationRunListener.started(context) -> ApplicationStartedEvent
回调IOC容器中获取所有 ApplicationRunner 和 CommandLineRunner 的run()
返回IOC容器
阶段
事件
starting
ApplicationStartingEvent
prepareEnvironment
ApplicationEnvironmentPreparedEvent
prepareContext
ApplicationContextInitializedEvent ApplicationPreparedEvent
refreshContext
afterRefresh
started
ApplicationStartedEvent
public class SpringApplication {
public ConfigurableApplicationContext run (String ... args ) {
StopWatch stopWatch = new StopWatch ();
stopWatch .start ();
ConfigurableApplicationContext context = null ;
Collection <SpringBootExceptionReporter > exceptionReporters = new ArrayList <>();
configureHeadlessProperty ();
SpringApplicationRunListeners listeners = getRunListeners (args );
listeners .starting ();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments (args );
ConfigurableEnvironment environment = prepareEnvironment (listeners , applicationArguments );
configureIgnoreBeanInfo (environment );
Banner printedBanner = printBanner (environment );
context = createApplicationContext ();
exceptionReporters = getSpringFactoriesInstances (SpringBootExceptionReporter .class ,
new Class [] { ConfigurableApplicationContext .class }, context );
prepareContext (context , environment , listeners , applicationArguments , printedBanner );
refreshContext (context );
afterRefresh (context , applicationArguments );
stopWatch .stop ();
if (this .logStartupInfo ) {
new StartupInfoLogger (this .mainApplicationClass ).logStarted (getApplicationLog (), stopWatch );
}
listeners .started (context );
callRunners (context , applicationArguments );
}
catch (Throwable ex ) {
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 ;
}
}
META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer =\
com.bp.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener =\
com.bp.springboot.listener.HelloSpringApplicationRunListener
ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer <ConfigurableApplicationContext > {
@ Override
public void initialize (ConfigurableApplicationContext applicationContext ) {
System .out .println ("ApplicationContextInitializer...initialize..." +applicationContext );
}
}
SpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
public HelloSpringApplicationRunListener (SpringApplication application , String [] args ){
}
@ Override
public void starting () {
System .out .println ("SpringApplicationRunListener...starting..." );
}
@ Override
public void environmentPrepared (ConfigurableEnvironment environment ) {
Object o = environment .getSystemProperties ().get ("os.name" );
System .out .println ("SpringApplicationRunListener...environmentPrepared.." +o );
}
@ Override
public void contextPrepared (ConfigurableApplicationContext context ) {
System .out .println ("SpringApplicationRunListener...contextPrepared..." );
}
@ Override
public void contextLoaded (ConfigurableApplicationContext context ) {
System .out .println ("SpringApplicationRunListener...contextLoaded..." );
}
@ Override
public void finished (ConfigurableApplicationContext context , Throwable exception ) {
System .out .println ("SpringApplicationRunListener...finished..." );
}
}
@ Component
public class HelloApplicationRunner implements ApplicationRunner {
@ Override
public void run (ApplicationArguments args ) throws Exception {
System .out .println ("ApplicationRunner...run...." );
}
}
@ Component
public class HelloCommandLineRunner implements CommandLineRunner {
@ Override
public void run (String ... args ) throws Exception {
System .out .println ("CommandLineRunner...run..." + Arrays .asList (args ));
}
}
//指定这个类是一个配置类
@ Configuration
//在指定条件成立的情况下自动配置类生效
@ ConditionalOn (...)
//指定自动配置类的顺序
@ AutoConfigureAfter
//结合相关Properties类来绑定相关的配置
@ ConfigurationPropertie
//让Properties生效加入到容器中
@ EnableConfigurationProperties
将需要启动就加载的自动配置类,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
<?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.bp.starter</groupId >
<artifactId >atguigu-spring-boot-starter-autoconfigurer</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >atguigu-spring-boot-starter-autoconfigurer</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >1.5.10.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
</properties >
<dependencies >
<!-- 引入spring-boot-starter;所有starter的基本配置-->
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter</artifactId >
</dependency >
</dependencies >
</project >
启动器只用来做依赖导入;
专门来写一个自动配置模块;
启动器依赖自动配置;别人只需要引入启动器
@ ConfigurationProperties (prefix = "atguigu.hello" )
public class HelloProperties {
private String prefix ;
private String suffix ;
public String getPrefix () {
return prefix ;
}
public void setPrefix (String prefix ) {
this .prefix = prefix ;
}
public String getSuffix () {
return suffix ;
}
public void setSuffix (String suffix ) {
this .suffix = suffix ;
}
}
public class HelloService {
HelloProperties helloProperties ;
public HelloProperties getHelloProperties () {
return helloProperties ;
}
public void setHelloProperties (HelloProperties helloProperties ) {
this .helloProperties = helloProperties ;
}
public String sayHellAtguigu (String name ){
return helloProperties .getPrefix ()+"-" +name + helloProperties .getSuffix ();
}
}
@ Configuration
@ ConditionalOnWebApplication
@ EnableConfigurationProperties (HelloProperties .class )
public class HelloServiceAutoConfiguration {
@ Autowired
HelloProperties helloProperties ;
@ Bean
public HelloService helloService (){
HelloService service = new HelloService ();
service .setHelloProperties (helloProperties );
return service ;
}
}
<?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.bp.starter</groupId >
<artifactId >atguigu-spring-boot-starter</artifactId >
<version >1.0-SNAPSHOT</version >
<dependencies >
<dependency >
<groupId >com.bp.starter</groupId >
<artifactId >atguigu-spring-boot-starter-autoconfigurer</artifactId >
<version >0.0.1-SNAPSHOT</version >
</dependency >
</dependencies >
</project >