springboot入门,springboot自动配置原理,模板引擎Thymeleaf

    技术2022-07-11  92

    一、springboot入门

    1.springboot的基本概述

    1.1 诞生背景 在普通的java项目中,大量的XML文件配置起来是很繁琐就会导致开发效率低,整合第三方框架的配置可能存在冲突问题导致部署效率低,还有其它的问题,传统java项目的打包方式:打包成一个war放入到tomcatwebapps目录下进行执行,也就是说需要依赖外部的tomcat服务器才能执行。 1.2 springboot的优点 快速创建独立运行的Spring项目以及与主流框架集成 使用嵌入式的Servlet容器,应用无需打成WAR包 starters自动依赖与版本控制 大量的自动配置,简化开发,也可修改默认值 无需配置XML,无代码生成,开箱即用 准生产环境的运行时应用监控 与云计算的天然集成 1.3 微服务 微服务其实是一种架构风格,它提倡我们在开发的时候,一个应用应该是一组小型服务,每一个小服务都运行在自己的进程内,每一个小服务都通过HTTP的方式进行互通 使用微服务架构,每一个功能元素都是可以独立替换和独立升级的软件单元

    2.第一个springboot项目

    2.1 创建maven工程 使用idea工具创建一个maven工程,该工程为普通的java工程即可 2.2 添加依赖

    在pom.xml里面导入起步依赖

    <!--spring-boot-starter-parent整合第三方常用框架的依赖信息--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent>

    SpringBoot要集成SpringMVC进行Controller的开发,所以项目要导入web的启动依赖

    <!--spring-boot-starter-web 是springboot整合springMVC是maven的依赖继承关系 --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>

    2.3 编写controller 2.4 编写启动类

    @EnableAutoConfiguration注解:作用在于让 Spring Boot根据应用所声明的依赖来对Spring框架进行自动配置这个注解告诉Spring Boot根据添加的jar依赖猜测你想如何配置Spring。由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。@EnableAutoConfiguration扫描的时候,只能扫描到当前类@ComponentScan注解:由于@EnableAutoConfiguration注解只能扫描当前的类,这样对Controller里面的类进行管理很不方便,这个时候,我们可以用@ComponentScan注解来配置扫描包的范围。我们可以将启动器抽取成一个单独的类 @EnableAutoConfiguration @ComponentScan("com.oracle.controller") public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }

    但是使用@ComponentScan进行扫包的时候,包比较多的情况下,写起来比较麻烦。比如我要扫码多个包: @ComponentScan(basePackages = {"com.oracle.user.controller","com.oracle.order.controller"})

    我们可以使用@SpringBootApplication,一劳永逸的解决以上问题 @SpringBootApplication被@Configuration、@EnableAutoConfiguration、@ComponentScan 注解所修饰,换言之 Springboot 提供了统一的注解来替代以上三个注解

    扫包范围:在启动类上加上@SpringBootApplication注解,当前包下或者子包下所有的类都可以扫到。 2.5 使用@SpringBootApplication注解编写启动类 但是此时需要注意App类所在的包中的位置

    3.springboot项目中的细节

    3.1 父项目

    <!--spring-boot-starter-parent整合第三方常用框架的依赖信息--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> 他的父项目是 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.1.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent> 它真正管理springboot项目里面的所有依赖版本,以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)

    3.2 启动器

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

    spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件; Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动

    4.使用Spring Initializr快速创建springboot项目

    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脚手架的步骤

    5.springboot的配置

    SpringBoot使用一个全局的配置文件,配置文件名是固定的 application.properties server.port=8081 application.yml server: port: 8081

    5.1 YAML的基本语法 k:(空格)v:表示一对键值对(空格必须有); 以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的 属性和值也是大小写敏感;

    server: port: 8081 path: /hello

    此时port和path就是在同一个层级 5.2 值的写法

    字面量:普通的值(字符串,数字,布尔值) k: v:字面直接来写; 字符串默认不用加上单引号或者双引号; “”:双引号;会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi ‘’:单引号;不会转义特殊字符,特殊字符终只是一个普通的字符串数据 name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi

    对象 k: v:在下一行来写对象的属性和值的关系;注意缩进 对象还是k: v的方式

    animal: name: Sunny age: 12

    行内写法:

    friend: {fname: Oscar,age: 20} 数组(List Set) 用- 值表示数组中的一个元素 pets: - cat - dog - fish

    行内写法:

    names: [cat,dog,fish]

    5.3 实例:将JavaBean注入到配置文件中 分别提供实体类 Person Dog

    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; //提供 Get Set方法 toString方法 } public class Dog { private String name; private Integer age; //提供 Get Set方法 toString方法 }

    在application.ymal中注入属性

    person: lastName: Oscar age: 19 boss: false birth: 2019/10/10 maps: {k1: v1,k2: v2} lists: - eric - kobe dog: name: 大黄 age: 2

    在注入的Person的类上面添加注解 注意

    @ConfigurationProperties:告诉springboot 将当前类的所有属性和配置文件中的相关信息进行绑定 prefix:配置文件中的哪个属性进行一一映射 @Component:只有将这个类配置在Spring容器中 才能使用@ConfigurationProperties的功能 使用@Autowired即可访问该对象

    以导入配置文件处理器,以后编写配置就有提示了

    <!--配置文件处理器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>

    测试

    @AutoWired Person person; @RequestMapping("/hello") public String hello(){ System.out.println(person); return "这是我的第一个springboot程序"; }

    5.4 Properties文件的配置

    在application.properties文件中配置

    server.port=8081 person.last-name=张三 person.age=12 person.birth=2019/12/30 person.boss=false person.lists=a,b,c person.maps.k1=value1 person.maps.k2=value2 person.dog.name=大黄 person.dog.age=1

    解决在application.properties文件中的中文乱码问题: 5.5 @Value获取值和@ConfigurationProperties获取值比校

    在spring中@Value相当于在配置文件中的 <bean id=” ” class=” ”><property name=” ” value=” ”></ property></bean>

    @Value注解支持字面量的注解方式

    从配置文件中获取 application.yml配置文件中定义:

    emp: empName: kobe age: 19 isMarried: true

    Emp中使用@Value注解中获取属性 使用SPEL表达式获取 通过比较@ConfigurationProperties和@Value的区别 最大的区别就是@ConfigurationProperties支持数据校验,而@Value则不支持。 在application.yml文件中 email的值只能是邮箱类型,否则启动报错 总结: 配置文件yml还是properties他们都能获取到值; 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value; 如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;

    5.6 @PropertySource 在前面的配置中,使用@ConfigurationProperties注解可以获取配置文件中的属性值。但是@ConfigurationProperties读取的配置文件是全局的,也就是说这个注解只能加载全局的配置文件(application.yml/application.properties).但是全局配置文件一般定义的都是有关spring配置的信息。如果我们需要定义一些与spring配置无关的信息,那么该如何呢?

    我们可以定义局部的配置文件,然后使用@PropertySource注解进行获取。 1.定义emp.properties配置文件

    emp.empName=kobe emp.age=19 emp.email=risswen@sina.com emp.isMarried=true

    2.定义实体类Emp 5.7 @ImportResource

    导入Spring的配置文件,让配置文件里面的内容生效; Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别; 想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上

    实例: 定义一个UserSercice方法,使用xml配置文件的方式管理UserService类。如何在springboot项目中获取?

    定义UserService

    public class UserService { public void test(){ System.out.println("这是UserService里面的方法"); } }

    定义bean.xml配置文件,管理bean

    <bean id="userService" class="com.oracle.service.UserService"></bean>

    在启动类上添加@importResource注解

    @SpringBootApplication @ImportResource(locations = {"classpath:bean.xml"})//加载类路径下面的配置文件 public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }

    测试

    @RestController public class controller { @Autowired ApplicationContext applicationContext; @RequestMapping("/hello") public String hello(){ UserService userService = (UserService) applicationContext.getBean("userService"); userService.test(); return "这是我的第一个springboot程序"; } }

    注意!!!:在springboot项目中,我们在spring容器中添加组件,我们一般不用以上使用xml的方式。Springboot推荐我们使用全注解的方式在spring容器添加组件。

    1、配置类@Configuration------>Spring配置文件 2、使用@Bean给容器中添加组件

    定义OrderService

    public class OrderService { public void test(){ System.out.println("这是OrderService里面的方法"); } }

    定义配置类

    package com.oracle.config; import com.oracle.service.OrderService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration /** * 指明当前类就是一个配置类,用来替代之前的配置文件<bean class=""></bean> */ public class MyAppConfig { //将方法的返回值添加到spring容器中,组件的默认id就是当前方法名 @Bean public OrderService orderService(){ return new OrderService(); } }

    测试调用

    @RestController public class controller { @Autowired ApplicationContext applicationContext; @RequestMapping("/hello") public String hello(){ OrderService userService = (OrderService) applicationContext.getBean("orderService"); userService.test(); return "这是我的第一个springboot程序"; } }

    5.8 配置文件的占位符 随机数

    ${random.uuid} ${random.int(18,60)} ${random.value} …… randomdemo: #名字不能以驼峰命名 否则启动报错 id: ${random.uuid} 取随机的uuid age: ${random.int(18,60)} 取指定范围的随机数 address: ${random.value} 获取随机字符串

    默认值

    randomdemo: id: ${random.uuid} age: ${random.int(18,60)} address: ${random.value} name: ${:铁蛋} 如果不写 默认值就是铁蛋

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

    –file:./config/ 项目根目录下面的config文件夹 优先级最高 –file:./ 项目根目录下面 –classpath:/config/ resources文件夹下面的config文件夹 –classpath:/ resources文件夹下面 优先级最低

    优先级由高到底,高优先级的配置会覆盖低优先级的配置; SpringBoot会从这四个位置全部加载主配置文件;互补配置;

    测试: 在以上各级文件夹下面定义application.properties文件,里面定义server.port端口号。看看到底以哪一个端口号为准。

    小细节: 如果需要给访问路径加上虚拟目录,那么在配置文件中可以配置:

    server: port: 8081 servlet: context-path: /springboot #指定虚拟路径

    6 springboot的自动配置原理

    (1) SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration

    @SpringBootApplication @ImportResource(locations = {"classpath:bean.xml"}) public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }

    (2)点击进入@SpringBootApplication注解的源码 (3)点击进入@EnableAutoConfiguration注解的源码 利用EnableAutoConfigurationImportSelector给容器中导入一些组件

    (4)点击进入AutoConfigurationImportSelector这个类里面 这个类里面有一个selectImports方法 (5)点击进入getCandidateConfigurations方法 (7)点击进入SpringFactoriesLoader,进入SpringFactoriesLoader类 (8)查看SpringFactoriesLoader里面的loadFactoryNames方法 (9)加载的组件

    7.springboot与日志

    7.1 springboot整合log4j日志记录 在resources目录下面创建log4j.properties日志文件,并引入

    #log4j.rootLogger=CONSOLE,info,error,DEBUG log4j.rootLogger=info,error,CONSOLE,DEBUG log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n log4j.logger.info=info log4j.appender.info=org.apache.log4j.DailyRollingFileAppender log4j.appender.info.layout=org.apache.log4j.PatternLayout log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n log4j.appender.info.datePattern='.'yyyy-MM-dd log4j.appender.info.Threshold = info log4j.appender.info.append=true #log4j.appender.info.File=/home/admin/pms-api-services/logs/info/api_services_info log4j.appender.info.File=/Users/dddd/Documents/testspace/pms-api-services/logs/info/api_services_info log4j.logger.error=error log4j.appender.error=org.apache.log4j.DailyRollingFileAppender log4j.appender.error.layout=org.apache.log4j.PatternLayout log4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n log4j.appender.error.datePattern='.'yyyy-MM-dd log4j.appender.error.Threshold = error log4j.appender.error.append=true #log4j.appender.error.File=/home/admin/pms-api-services/logs/error/api_services_error log4j.appender.error.File=/Users/dddd/Documents/testspace/pms-api-services/logs/error/api_services_error log4j.logger.DEBUG=DEBUG log4j.appender.DEBUG=org.apache.log4j.DailyRollingFileAppender log4j.appender.DEBUG.layout=org.apache.log4j.PatternLayout log4j.appender.DEBUG.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n log4j.appender.DEBUG.datePattern='.'yyyy-MM-dd log4j.appender.DEBUG.Threshold = DEBUG log4j.appender.DEBUG.append=true #log4j.appender.DEBUG.File=/home/admin/pms-api-services/logs/debug/api_services_debug log4j.appender.DEBUG.File=/Users/dddd/Documents/testspace/pms-api-services/logs/debug/api_services_debug log4j\u4EE3\u7801 private static final Logger logger = LoggerFactory.getLogger(IndexController.class);

    引入log4j依赖

    <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency>

    Controller

    @RestController public class LoggController { private static final Logger logger = LoggerFactory.getLogger(LoggController.class); @RequestMapping("/printLog") public String printLog(){ logger.info("日志打印输出了......"); return "Hello World....."; } }

    7.2 使用Aop统一处理Web请求日志

    导入依赖

    <!--Aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--lombok依赖:下面的slf4j会用到--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>

    Aop处理日志的类

    package com.bianyiit.aspect; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; @Aspect @Component @Slf4j public class WebLogAspect { @Pointcut("execution(public * com.bianyiit.controller.*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 log.info("URL : " + request.getRequestURL().toString()); log.info("HTTP_METHOD : " + request.getMethod()); log.info("IP : " + request.getRemoteAddr()); Enumeration<String> enu = request.getParameterNames(); while (enu.hasMoreElements()) { String name = (String) enu.nextElement(); log.info("name:{},value:{}", name, request.getParameter(name)); } } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 处理完请求,返回内容 log.info("RESPONSE : " + ret); } }

    控制类

    @RequestMapping("/getMember") public String getMember(String name,Integer age){ return "success"; }

    请求路径: http://localhost:8080/getMember?name=sunny&age=12

    二、springboot web开发

    使用SpringBoot; 1)、创建SpringBoot应用,选中我们需要的模块; 2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 3)、自己编写业务代码;

    1.springboot对静态资源的映射规则

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

    "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" "/":当前项目的根路径

    测试效果: 如果要实现http://localhost:8081/springboot_day03/static/test04.html 欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射 localhost:8080/ 找index.html页面

    2.模板引擎

    引入Thymeleaf依赖

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

    只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染; 编写控制器类:(注意:这里应该使用@Controller注解,而不是restController注解)

    @Controller public class HelloController { @RequestMapping("/hello") public String success(){ return "success";//跳转到templates下面的success.html页面 } }

    使用http://localhost:8080/hello即可访问

    2.1 Thymeleaf语法初体验

    需求:通过控制器实现页面跳转,并将数据传递给前台html页面 在HTML页面导入thymeleaf的名称空间

    <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org">

    编写控制器类

    @Controller public class HelloController { @RequestMapping("/hello") public String success(Map<String,Object> map){ map.put("name","Sunny"); return "success"; } }

    页面取值

    <body> <h3>这是一个成功页面</h3> 欢迎你,<span th:text="${name}"></span> </body>

    2.4 thyemleaf的语法规则

    th语法 th:text;改变当前元素里面的文本内容; th:任意html属性;来替换原生属性的值

    <body> <h3>这是一个成功页面</h3> 欢迎你,<span th:text="${name}" id="s1" class="span1" th:id="${name}" th:class="${name}"></span> </body>

    以上的th:id、th:class的值就替换了原来的id和class属性的值 小实验 控制器:

    @RequestMapping("/hello") public String success(Map<String,Object> map){ map.put("name","Oscar"); map.put("content","<h2>大家好</h2>"); List<String> list = new ArrayList<>(); list.add("Sunny"); list.add("Kobe"); list.add("Ketty"); map.put("list",list); return "success"; }

    HTML页面展示

    <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>这是一个成功页面</h3> 欢迎你,<span th:text="${name}" id="s1" class="span1" th:id="${name}" th:class="${name}"></span> <hr> <!--循环展示--> <h2> <div th:each="list:${list}" th:text="${list}"></div> </h2> <!--th:utext--> <div th:utext="${content}"></div> <!--转义--> <div th:text="${content}"></div> <!--不转义--> <!--循环战士的另一种写法--> <div id="box" th:each="names:${list}">[[${names}]]</div> </body> </html>

    2.5 springboot中springMVC

    Springmvc的扩展配置 在springboot中,提供了很多springMVC的自动配置,但是这些自动配置还不满足我们的开发需求,所以我们可以在默认的配置上进行扩展。

    如何扩展? 编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc

    @Configuration public class MyMVCConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/oracle").setViewName("index"); } }

    以上代码:指定了我们访问路径跳转到的执行页面。

    全面接管springMVC的配置 SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了我们需要在配置类中添加@EnableWebMvc即可。 测试:这样我们直接访问项目的静态页面就会访问不到

    Processed: 0.016, SQL: 9