SpringBoot
一、SpringBoot简介
整个Spring技术栈的一个大整合;
它还是一个微服务框架
–jdk1.8:Spring Boot 推荐jdk1.7及以上;java version "1.8.0_112"
–maven3.x:maven 3.3以上版本;Apache Maven 3.3.9
–IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64、STS
二、项目搭建
1、创建普通maven项目
<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> <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应用里面的所有依赖版本; Spring Boot的版本仲裁中心; 以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号) <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> spring-boot-starter-web: spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件; Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
/** * @SpringBootApplication 来标注这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用; */ @SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { // Spring应用启动起来 SpringApplication.run(HelloWorldMainApplication.class,args); } }
@Controller public class HelloController { @ResponseBody @RequestMapping("/hello") public String hello(){ return "Hello World!"; } }
5、简化部署
SpringBoot是将一个应用打成jar包,所以需要引入下面的插件
<!-- 这个插件,可以将应用打包成一个可执行的jar包;--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
部分注解解释
标注在某个类上,表示这是一个Spring Boot的配置类;
@Configuration:配置类上来标注这个注解;配置类 相当于 配置文件;配置类也是容器中的一个组件。
@EnableAutoConfiguration:开启自动配置功能; 以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration是告诉SpringBoot开启自动配置功能;这样自动配置才能生效;
选择我们需要的模块;向导会联网创建Spring Boot项目;
默认生成的Spring Boot项目;
-
主程序已经生成好了,我们只需要我们自己的逻辑
-
resources文件夹中目录结构
-
static:保存所有的静态资源; js css images;
-
templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf);
-
application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
-
三、
SpringBoot使用一个全局的配置文件,配置文件名是固定的;
- application.properties
- application.yml
配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;
标记语言(yml):
以前的配置文件;大多都使用的是 xxxx.xml文件;
YAML:以数据为中心,比json、xml等更适合做配置文件;
server: port: 8081
yml语法
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
属性和值大小写敏感;
1、值为字符串
字符串默认不用加上单引号或者双引号;
- "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
- name: "zhangsan \n lisi":输出;zhangsan 换行 lisi
- '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
- name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
2、值为
friends: lastName: zhangsan age: 20 # 行内写法 friends: {lastName: zhangsan,age: 18}
3、值为数组
pets: - cat - dog - pig # 行内写法 pets: [cat,dog,pig]
1)书写配置文件
person: lastName: hello age: 18 boss: false birth: 2017/12/12 maps: {k1: v1,k2: 12} lists: - lisi - zhaoliu dog: name: 小狗 age: 12
2)书写javaBean:
/** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定; * prefix = "person":配置文件中哪个下面的所有属性进行一一映射 * * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能; * */ @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;
3)导入配置文件处理器,以后编写yml文件就有提示了
<!--导入配置文件处理器,配置文件进行绑定就会有提示--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
@Component public class Person { @Value("$(person.lastName)") private String lastName; @Value("#{11*2}") private Integer age; @Value("true") private Boolean boss;
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
@Component @ConfigurationProperties(prefix = "person") @Validated // 开启校验 public class Person { //设定lastName字段的格式,lastName必须是邮箱格式 @Email private String lastName; private Integer age; private Boolean boss;
@PropertySource&@ImportResource&@Bean注解
// 指定使用person.properties来自动注入字段 @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标注在一个配置类上
@ImportResource(locations = {"classpath:beans.xml"}) // 导入Spring的配置文件让其生效
但是springboot不建议采用这种方式加载配置文件,而是推荐书写配置类的方式
1、配置类@Configuration<===>Spring配置文件
2、使用@Bean给容器中添加组件
/** * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件 * * 在配置文件中用<bean><bean/>标签添加组件 * */ @Configuration public class MyAppConfig { //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名 @Bean public HelloService helloService02(){ System.out.println("配置类@Bean给容器中添加组件了..."); return new HelloService(); } }
配置文件占位符
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
demo
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 # 如果${}包括的值不存在==>会生成 ${person.hello}_dog # 当然也可以使用下面这种方式设置默认值 ==> 会生成hello_dog person.dog.name=${person.hello:hello}_dog person.dog.age=15
多profile文件
由于在一个项目的开发环境和生产环境的配置文件会有不同,所以spring boot引入profile来方便我们切换
但是默认使用application.properties的配置,要使用profile配置需要我们手动配置;
以下面的yml配置文件为例
server: port: 8081 spring: profiles: active: prod # 激活方式1:active表示激活profile是pord的内个配置文件,如果不激活则port是8081 --- server: port: 8083 spring: profiles: dev --- server: port: 8084 spring: profiles: prod #指定属于哪个环境
1、在配置文件中指定
spring.profiles.active=dev
2、命令行
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
也可以直接在idea测试的时候,配置传入命令行参数
3、虚拟机参数:
-Dspring.profiles.active=dev
- –file:./config/
- –file:./
- –classpath:/config/
- –classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
我们还可以通过spring.config.location来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
四、日志
1、SLFJ的使用
导入slf4j的jar和 logback的实现jar
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"); } }
2、遗留问题
统一使用slf4j进行输出??
- 1、将系统中其他日志框架先排除出去;
- 2、用中间包来替换原有的日志框架;
- 3、我们导入slf4j其他的实现
3、SpringBoot中日志关系
springboot整合了spring等多个采用不同日志系统的框架,所以肯定有我们上面所说的问题,我们来看看他是怎么解决的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
总结:
1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录
2)、SpringBoot也把其他的日志都替换成了slf4j;
3)、中间替换包
4、springboot中日志框架的使用
//记录器 Logger logger = LoggerFactory.getLogger(getClass()); @Test public void contextLoads() { //System.out.println(); //日志的级别; //由低到高 trace<debug<info<warn<error //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效 logger.trace("这是trace日志..."); logger.debug("这是debug日志..."); //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别 logger.info("这是info日志..."); logger.warn("这是warn日志..."); logger.error("这是error日志..."); }
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
默认格式:%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
SpringBoot修改日志的默认配置(默认只输出INFO级别以上的)
logging.level.com.atguigu=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
当然除了上面直接修改application.yml/properties文件,我们还可以直接书写相应日志框架的配置文件
springBoot中日志框架的默认文件是:logback.xml,这个文件直接就被日志框架识别了;
如果我们想让springboot帮我们管理,可以起名
例如:
<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>
在项目启动时我们要指定dev/pord环境,这样他就会根据环境设置不同的输出格式了
五、Web开发
2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
3)、自己编写业务代码;
SpringBoot静态资源映射
1 @Override 2 public void addResourceHandlers(ResourceHandlerRegistry registry) { 3 if (!this.resourceProperties.isAddMappings()) { 4 logger.debug("Default resource handling disabled"); 5 return; 6 } 7 Integer cachePeriod = this.resourceProperties.getCachePeriod(); 8 if (!registry.hasMappingForPattern("/webjars/**")) { 9 customizeResourceHandlerRegistration( 10 registry.addResourceHandler("/webjars/**") 11 .addResourceLocations( 12 "classpath:/META-INF/resources/webjars/") 13 .setCachePeriod(cachePeriod)); 14 } 15 String staticPathPattern = this.mvcProperties.getStaticPathPattern(); 16 //静态资源文件夹映射 17 if (!registry.hasMappingForPattern(staticPathPattern)) { 18 customizeResourceHandlerRegistration( 19 registry.addResourceHandler(staticPathPattern) 20 .addResourceLocations( 21 this.resourceProperties.getStaticLocations()) 22 .setCachePeriod(cachePeriod)); 23 } 24 } 25 26 //配置欢迎页映射 27 @Bean 28 public WelcomePageHandlerMapping welcomePageHandlerMapping( 29 ResourceProperties resourceProperties) { 30 return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(), 31 this.mvcProperties.getStaticPathPattern()); 32 } 33 34 //配置喜欢的图标 35 @Configuration 36 @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true) 37 public static class FaviconConfiguration { 38 39 private final ResourceProperties resourceProperties; 40 41 public FaviconConfiguration(ResourceProperties resourceProperties) { 42 this.resourceProperties = resourceProperties; 43 } 44 45 @Bean 46 public SimpleUrlHandlerMapping faviconHandlerMapping() { 47 SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); 48 mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); 49 //所有 **/favicon.ico 50 mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", 51 faviconRequestHandler())); 52 return mapping; 53 } 54 55 @Bean 56 public ResourceHttpRequestHandler faviconRequestHandler() { 57 ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); 58 requestHandler.setLocations(this.resourceProperties.getFaviconLocations()); 59 return requestHandler; 60 } 61 62 }
通过上面源码可知:
webjars:以jar包的方式引入静态资源;
Demo:
1/我们先引入jquery的jar包
<!--引入jquery-webjar-->在访问的时候只需要写webjars下面资源的名称即可
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
2/查看引入的jar包
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/":当前项目的根路径
3)、欢迎页;被"/**"映射;所以会查找静态资源文件夹下的所有index.html页面;
localhost:8080/ 找index页面
4)、所有的 **/favicon.ico 都是在静态资源
改变静态文件夹位置:
在配置文件中配置:spring.resources.static-locations=classpath:/hello/,classpath:x5456/
模板引擎Thymeleaf
1、引入thymeleaf
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 我们也可以切换thymeleaf版本 <properties> <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> </properties>
2、Thymeleaf使用
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页面放在classpath:/templates/,thymeleaf就能自动渲染;
使用:
1、导入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
2、使用thymeleaf语法;
<!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> <!--如果hello有值,将会替代之前的文本信息--> </body> </html>
3、语法规则
1)、th:text;改变当前元素里面的文本内容;
th:任意html属性;来替换原生属性的值
2)、表达式
Simple expressions:(表达式语法) Variable Expressions: ${...}:获取变量值;OGNL; 1)、获取对象的属性、调用方法 2)、使用内置的基本对象: #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. ${session.foo} 3)、内置的一些工具对象: #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="${session.user}: <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: _
SpringBoot Demo
1、扩展SpringBoot功能
2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
Demo:扩展SpringMVC的功能,路由映射
方式1:继承这个抽象类,重写其中的方法
// 1.使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能 // 2.不要使用@EnableWebMvc注解接管SpringMVC @Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { // 3.重写抽象类的方法 @Override public void addViewControllers(ViewControllerRegistry registry) { //浏览器发送 / 请求来到 success registry.addViewController("/").setViewName("success"); }
方式2:直接new这个抽象类,并注册到容器中
@Configuration public class MyMvcConfig { @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; } }
2、配置国际化
步骤:
在application.properties文件中配置国际化:
# 设置项目路径
server.servlet.context-path=/curd
# 设置国际化文件路径
spring.messages.basename=i18n.login
# 禁用thymeleaf模板引擎的缓存
spring.thymeleaf.cache=false
2)、使用ResourceBundleMessageSource管理国际化资源文件
SpringBoot已经帮我们配置好了
3)、在页面使用fmt:message取出国际化内容
@{} # 取url,会根据项目路径改变
#{} # 取国际化内容
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Signin Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link 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}" alt="" width="72" height="72"> <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tips}">Please sign in</h1> <label class="sr-only" th:text="#{login.username}">Username</label> <input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <label class="sr-only" th:text="#{login.password}">Password</label> <input type="password" class="form-control" 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>
注:可能会出现中文乱码问题,原因是idea对properties文件编码加载问题
4)、点击链接切换国际化
SpringBoot底层默认配置是根据请求头的参数来实现国际化切换的
1 @Bean 2 @ConditionalOnMissingBean 3 @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") 4 public LocaleResolver localeResolver() { 5 if (this.mvcProperties 6 .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { 7 return new FixedLocaleResolver(this.mvcProperties.getLocale()); 8 } 9 AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); 10 localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); 11 return localeResolver; 12 }
根据链接修改语言
/** * 可以在连接上携带区域信息 */ 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; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } } @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
3、实现用户登陆
@Controller public class LoginController { @PostMapping("/user/login") public String login(String username, String password, Map<String,Object> map){ // 进行验证 if(!StringUtils.isEmpty(username) && "123456".equals(password)){ return "dashboard"; } map.put("msg","登录失败!"); return "login"; } } // 在login.html添加下面代码 // th:if="${not #strings.isEmpty(msg)}":如果msg不为空则显示 <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
4、自定义拦截器
/** * 登陆检查 */ 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; }else{ //已登陆,放行请求 return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
别忘了还要注册啊
@Configuration public class MyMvcConfig { @Bean //将组件注册在容器 public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/login.html").setViewName("login"); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/main.html"); // 只拦截main.html } }; return adapter; } @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
5、发送put、delete请求
<!--需要区分是员工修改还是添加;--> <form th:action="@{/emp}" method="post"> <!--发送put请求修改员工数据--> <!-- 1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的) 2、页面创建一个post表单 3、创建一个input项,name="_method";值就是我们指定的请求方式 --> <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>
delete请求就是把input标签的value属性的值改为delete
错误处理机制
1、定制错误页面
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);
页面能获取的信息;
- timestamp:时间戳
- status:状态码
- error:错误提示
- exception:异常对象
- message:异常消息
- errors:JSR303数据校验的错误都在这里
2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
(new SpringApplication(sources)).run(args);
private void initialize(Object[] sources) { //保存主配置类 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } //判断当前是否一个web应用 this.webEnvironment = deduceWebEnvironment(); //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有main方法的主配置类 this.mainApplicationClass = deduceMainApplicationClass(); }
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories SpringApplicationRunListeners listeners = getRunListeners(args); //回调所有的获取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //创建环境完成后回调SpringApplicationRunListener.environmentPrepared()方法;表示环境准备完成 Banner printedBanner = printBanner(environment); //创建ApplicationContext,决定创建web的ioc还是普通的ioc context = createApplicationContext(); analyzers = new FailureAnalyzers(context); //准备上下文环境;将environment保存到ioc中;而且applyInitializers(); //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法 //回调所有的SpringApplicationRunListener的contextPrepared(); prepareContext(context, environment, listeners, applicationArguments, printedBanner); //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded方法; //刷新容器:ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat) //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置) refreshContext(context); //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调 //ApplicationRunner先回调,CommandLineRunner再回调 afterRefresh(context, applicationArguments); //所有的SpringApplicationRunListener回调finished方法 listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //整个SpringBoot应用启动完成以后返回启动的ioc容器; return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
SpringBoot整合Spring Security
1、pom.xml
<?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>cn.x5456.springboot</groupId> <artifactId>securitytest</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>securitytest</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.15.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> <!--以下两项如果不配置,解析themleaft 会有问题--> <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.2.RELEASE</version> <!--模板与security集成--> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、书写安全配置类
/** * 安全配置类 */ @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{ /** * 重写父类方法,自定义配置 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/css/**","/js/**","/fonts/**","/index").permitAll() // 可以访问 .antMatchers("/users/**").hasRole("ADMIN") // 需要相应角色才能访问 .and() .formLogin() // 基于form表单登陆验证 .loginPage("/login").failureUrl("/login-error"); // 定义登录页面 } /** * 认证信息管理 * @param auth * @throws Exception */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.inMemoryAuthentication() // 认证信息存在内存中(可以放数据库等位置) .withUser("x5456").password("5456").roles("ADMIN"); } }
3、书写Controller
/** * 主页控制器 */ @Controller public class MainController { @GetMapping("/") public String root(){ return "redirect:/index"; } @GetMapping("/index") public String index(){ return "index"; } @GetMapping("/login") public String login(){ return "login"; } @GetMapping("/login-error") public String loginError(Model model){ model.addAttribute("loginError",true); model.addAttribute("errorMsg","登陆失败,用户名或密码输入如错误!"); return "login"; } }
4、html页面
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <!--导入springsecurity4的依赖--> <head th:replace="~{fragments/header :: header}"> <!--引入头部--> </head> <body> <div class="container blog-content-container"> <!--用户登陆后显示--> <div sec:authorize="isAuthenticated()"> <p>已有用户登录</p> <p>登陆的用户为:<span sec:authentication="name"></span></p> <p>用户角色为:<span sec:authentication="principal.authorities"></span></p> </div> <!--用户未登陆时显示--> <div sec:authorize="isAnonymous()"> <p>未有用户登陆</p> </div> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html>