SpringBoot

一、SpringBoot简介

简化Spring应用开发的一个框架;

整个Spring技术栈的一个大整合;

J2EE开发的一站式解决方案;

它还是一个微服务框架

环境约束

–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

–SpringBoot 1.5.9.RELEASE:1.5.9;

二、项目搭建

1、创建普通maven项目

2、导入spring boot相关的依赖

<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相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器

3、编写一个主程序;启动Spring Boot应用

/**
 *  @SpringBootApplication 来标注这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
 */
@SpringBootApplication
public class HelloWorldMainApplication {

    public static void main(String[] args) {

        // Spring应用启动起来
        SpringApplication.run(HelloWorldMainApplication.class,args);
    }
}

4、编写相关的Controller、Service

@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>

部分注解解释

@SpringBootConfiguration标注在某个类上,表示这是一个Spring Boot的配置类;

  @Configuration:配置类上来标注这个注解;配置类 相当于 配置文件;配置类也是容器中的一个组件。

@EnableAutoConfiguration:开启自动配置功能; 以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration是告诉SpringBoot开启自动配置功能;这样自动配置才能生效;

使用Spring Initializer快速创建Spring Boot项目

IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目;

选择我们需要的模块;向导会联网创建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等更适合做配置文件;

YAML:配置例子

server:
  port: 8081

yml语法

k:(空格)v:表示一对键值对(空格必须有);

空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的

属性和值大小写敏感;

1、值为字符串

字符串默认不用加上单引号或者双引号;

  • "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
    • name: "zhangsan \n lisi":输出;zhangsan 换行 lisi
  • '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
    • name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi

2、值为对象/Map

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>

使用@Value注解注入值

@Component
public class Person {

	@Value("$(person.lastName)")
    private String lastName;
	@Value("#{11*2}")
    private Integer age;
	@Value("true")
    private Boolean boss;

@Value注入值和@ConfigurationProperties注入值比较

配置文件yml还是properties他们都能获取到值;

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;

如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;

配置文件注入值数据校验

@Component
@ConfigurationProperties(prefix = "person")
@Validated	// 开启校验
public class Person {

   //设定lastName字段的格式,lastName必须是邮箱格式
    @Email
    private String lastName;
    
    private Integer age;

    private Boolean boss;

@PropertySource&@ImportResource&@Bean注解

@PropertySource:加载指定的配置文件;

// 指定使用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-{profile}.properties/yml

但是默认使用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  #指定属于哪个环境

激活配置的3种方式

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

 

配置文件加载位置

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

  • –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");
  }
}

每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;

2、遗留问题

Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis...这么多日志框架,我们都能统一使用slf4j进行输出??

如何让系统中所有的日志都统一到slf4j;

  • 1、将系统中其他日志框架先排除出去;
  • 2、用中间包来替换原有的日志框架;
  • 3、我们导入slf4j其他的实现

3、SpringBoot中日志关系

springboot整合了spring等多个采用不同日志系统的框架,所以肯定有我们上面所说的问题,我们来看看他是怎么解决的。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Spring boot将其他的日志模块排除掉,然后导入相应的slf4j来替代,最终还是使用的logback来记录日志的

总结:

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帮我们管理,可以起名logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能

 例如:

       <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开发

开发步骤:

1)、创建SpringBoot应用,选中我们需要的模块;

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 }
源码

通过上面源码可知:

1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;

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包

2)、"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射

默认存放静态资源的主要有以下几个目录

"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功能

1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(例如ViewResolver),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、配置国际化

步骤:

1)、编写国际化配置文件

在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、定制错误页面

1)、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到 对应的页面;

我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);

页面能获取的信息;

  • timestamp:时间戳
  • status:状态码
  • error:错误提示
  • exception:异常对象
  • message:异常消息
  • errors:JSR303数据校验的错误都在这里

2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;

3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;

2、如何定制错误的json数据

SpringBoot启动配置原理

应用启动流程:

(new SpringApplication(sources)).run(args);

1、创建SpringApplication对象

源码:

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();
}

2、运行run方法

源码:

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>

 

posted @ 2018-08-09 13:34  想54256  阅读(413)  评论(0编辑  收藏  举报