SpringBoot
1、springboot的简单入门
什么是SpringBoot?
SpringBoot是Spring社区发布的一个开源项目,在帮助开发者快速并且更简单的构建项目。它使用习惯优于配置的理念让你的项目快速运行起来,使用Spring Boot很容易创建一个独立运行(运行jar,内置Servlet容器,Tomcat、jetty)、准生产级别的基于Spring框架的项目,使用SpringBoot框架,你可以不用或者只需要很少的配置文件。
SpringBoot核心功能
独立运行的Spring项目:可以以jar包形式独立运行,通过java -jar xx.jar即可运行。
内嵌Servlet容器:可以选择内嵌Tomcat、Jetty等。
提供starter简化maven配置:一个maven项目,使用了spring-boot-starter-web时,会自动加载Spring Boot的依赖包。
自动配置Spring:Spring Boot会根据在类路径中的jar包、类,为jar包中的类自动配置Bean。
准生产的应用监控:提供基于http、ssh、telnet对运行时的项目进行监控。
1.1、依赖管理
依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
他的父项目
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
</dependencies>
几乎声明了所有开发中常用的依赖版本号,自动版本仲裁机制
- 开发导入starter场景启动器
1、见到很多spring-boot-starter-*,*就是某种场景
2、只要引入starter,这个场景的所有常规依赖都自动引入
3、见到的*-spring-boot-starter:第三方为我们提供的简化开发的场景
4、所有场景最底层的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
- 无需关注版本号,自动版本仲裁
1、引入依赖默认都可以不写版本号
2、引入非版本仲裁的jar,要写版本号
1.2、自动配置
- 自动配置tomcat
- 引入Tomcat依赖
- 配置Tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
- 自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件
- 自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了全套的web开发场景
- 默认的包结构
- 主程序所在的包以及子包里面所有的组件都会被默认扫描进来
- 无需以前的包扫描配置
- 想要改变扫描路径:
@SpringBootApplication(scanBasePackages = "指定包路径")
或者@ComponentScan("指定包路径")
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("包名")
- 各种配置拥有默认值
- 默认配置最终都是依赖到MultipartProperties
- 配置文件的值最终会绑定到每个类上,这个类会在容器中创建对象
- 按需加载所有配置项
- 非常多的starter
- 引入了哪些场景这个场景自动配置才会开启
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
2、容器功能
2.1、组件添加
1、@Configuration
@Bean:给容器中添加组件,以方法名作为主键的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例,也可以自定义名字
-
基本使用
-
Full模式与Lite模式
Full:proxyBeanMethods = true Lite:proxyBeanMethods = false
- 示例
package com.itguigu.boot.config; import com.itguigu.boot.bean.Pet; import com.itguigu.boot.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 1、配置类里面使用@Bean标注在方法上给容器注册组件 * 2、配置类本身也是一个组件 * 3、proxyBeanMethods:代理bean的方法 * Full:proxyBeanMethods = true * Lite:proxyBeanMethods = false * 组件依赖 * @return */ @Configuration(proxyBeanMethods = false)//告诉springboot这是一个配置类 == 配置文件 public class MyConfig { /** * proxyBeanMethods = true时,外部无论对配置类中的这个组件的注册方法调用多少次获取的都是之间注册容器中的单实例对象 * @return */ @Bean//给容器中添加组件,以方法名作为主键的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例,也可以自定义名字 public User user01(){ User zhangsan = new User("zhangsan", 18); zhangsan.setPet(pet01()); return zhangsan; } @Bean("tom")//自定义名字 public Pet pet01(){ return new Pet("tomcat"); } }
package com.itguigu.boot; import com.itguigu.boot.bean.Pet; import com.itguigu.boot.bean.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class MainApplication { /** * springboot的启动类 * @param args */ public static void main(String[] args) { //1、返回ioc容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class); //2、查看容器里面的组件 String[] names=run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } //3、从容器中获取组件 Pet tom1 = run.getBean("tom", Pet.class); Pet tom2 = run.getBean("tom", Pet.class); System.out.println(tom1==tom2); //如果@Configuration(proxyBeanMethods = true)代理对象调用方法:springboot总会检查这个组件在容器中是否存在 //保持组件单实例 User user01=run.getBean("user01",User.class); Pet tom=run.getBean("tom",Pet.class); System.out.println("用户的宠物:"+(user01.getPet()==tom)); } }
- 最佳实战
- 配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
2、@Import
给容器中自动创建这两个类型的组件,默认组件名就是全类名
3、@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
@ConditionalOnBean(name = "tom")//当容器中存在tom组件时,才执行
@ConditionalOnMissingBean(name = "tom")//当容器中不存在tom组件时,才执行
4、@ImportResource
@ImportResource(“classpath:beans.xml”):导入xml文件
2.2、原生配置文件引入
1、@ImportResource
======================beans.xml=========================
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="haha" class="com.atguigu.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
<bean id="hehe" class="com.atguigu.boot.bean.Pet">
<property name="name" value="tomcat"></property>
</bean>
</beans>
@ImportResource("classpath:beans.xml")
public class MyConfig {}
======================测试=================
boolean haha = run.containsBean("haha");
boolean hehe = run.containsBean("hehe");
System.out.println("haha:"+haha);//true
System.out.println("hehe:"+hehe);//true
2.3、配置绑定
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
public class getProperties {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties pps = new Properties();
pps.load(new FileInputStream("a.properties"));
Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
while(enum1.hasMoreElements()) {
String strKey = (String) enum1.nextElement();
String strValue = pps.getProperty(strKey);
System.out.println(strKey + "=" + strValue);
//封装到JavaBean。
}
}
}
1、@ConfigurationProperties
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
*/
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
2、@Component + @ConfigurationProperties
@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}
3、自动配置原理
3.1、引导加载自动配置类
@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}
1、@SpringBootConfiguration
@Configuration。代表当前是一个配置类
2、@ComponentScan
指定扫描哪些,Spring注解;
4、开发小技巧
4.1、Lombok
简化JavaBean开发
1、 idea中搜索安装lombok插件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
===============================简化JavaBean开发===================================
@NoArgsConstructor//无参构造方法
@AllArgsConstructor //全参构造方法
@Data//get和set方法
@ToString//toString方法
@EqualsAndHashCode//此注解会生成equals(Object other) 和 hashCode()方法。
public class User {
private String name;
private Integer age;
private Pet pet;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
================================简化日志开发===================================
@Slf4j
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
log.info("请求进来了....");
return "Hello, Spring Boot 2!"+"你好:"+name;
}
}
4.2、dev-tools(热部署)
项目或者页面修改以后:Ctrl+F9;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
4.3、Spring Initailizr(项目初始化向导)
1、自动依赖注入
2、自动创建项目结构
3、自动编写好主配置类
5、配置文件(yaml)
5.1、yaml简介
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。 非常适合用来做以数据为中心的配置文件
5.2、基本语法
key: value;kv之间有空格
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
'#'表示注释
字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义
5.3、数据类型
字面量:单个的、不可再分的值。date、boolean、string、number、null
YAML
复制代码
k: v
对象:键值对的集合。map、hash、set、object
YAML
复制代码
行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
数组:一组按次序排列的值。array、list、queue
YAML
复制代码
行内写法: k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
5.4、配置提示
自定义的类和配置文件一般没有提示,如果想要提示则引入下面依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
6、rest使用与原理
@xxxMapping;
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
核心Filter;HiddenHttpMethodFilter
用法: 表单method=post,隐藏域 _method=put
SpringBoot中手动开启
扩展:如何把_method 这个名字换成我们自己喜欢的。
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
Rest原理(表单提交要使用REST的时候)
表单提交会带上_method=PUT
请求过来被HiddenHttpMethodFilter拦截
请求是否正常,并且是POST
获取到_method的值。
兼容以下请求;PUT.DELETE.PATCH
原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
Rest使用客户端工具,
如PostMan直接发送Put、delete等方式请求,无需Filter。
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
7、模板引擎-Thymeleaf
Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发,模板引擎是一个技术名词,是跨领域跨平台的概念,在Java语言体系下有模板引擎,在C#、PHP语言体系下也有模板引擎。除了thymeleaf之外还有Velocity、FreeMarker等模板引擎,功能类似。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。使用thymeleaf创建的html模板可以在浏览器里面直接打开(展示静态数据),这有利于前后端分离。需要注意的是thymeleaf不是spring旗下的。这里我们使用thymeleaf 3版本。
thymeleaf使用
1、引入Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2、自动配置好了thymeleaf
- 所有thymeleaf的配置值都在 ThymeleafProperties
- 配置好了 SpringTemplateEngine
- 配好了 ThymeleafViewResolver
- 我们只需要直接开发页面
3、页面开发
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<h2>
<a href="www.atguigu.com" th:href="${link}">去百度</a> <br/>
<a href="www.atguigu.com" th:href="@{link}">去百度2</a>
</h2>
</body>
</html>
8、拦截器
1、HandlerInterceptor 接口
/**
* 登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
2、配置拦截器
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
}
}
9、文件上传
1、页面表单
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><br>
<input type="submit" value="提交">
</form>
2、文件上传代码
/**
* MultipartFile 自动封装上传过来的文件
* @param email
* @param username
* @param headerImg
* @param photos
* @return
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,//接受页面传过来的单个文件
@RequestPart("photos") MultipartFile[] photos)//接受页面传过来的多个文件
throws IOException {
log.info("上传的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}