狂神说springboot
1.说在前面
javase: oop
mysql: 持久化
html+css+js+jquery+框架: 视图
javaweb: 独立开发mvc三层架构的网站(原始版)
ssm: 框架,简化我们的开发流程,但是随着发展,配置开始较为复杂
在这之前,程序都是打包成war包,在tomcat上运行,从springboot开始打包成jar包(内嵌tomcat来运行)
spring再简化: springboot横空出世,微服务架构
服务增多:springcloud来管理微服务
1.1 优点
- 为所有spring开发者更快入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化web项目
- 没有冗余代码生成和xml配置要求
1.2 什么是微服务?
微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合:可以通过http的方式进行互通.
单体应用架构
(all in one) 将一个应用的中的所有内容封装到一个应用中.放到一个war包内.
- 这样做的好处是,易于开发和测试,部署也十分方便,需要拓展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了
- 缺点是,哪怕修改了很小的一个地方,我们都需要停掉整个服务,重新打包,部署这个应用的war包.特别是对于一个大型项目.我们不可能把所有的内容放到一个war里面,我们如何维护和分工都是问题…
微服务架构
所谓微服务架构就是打破all in one的模式,把每个功能元素单独拿出来.把独立的功能元素动态组合,需要的功能才去拿来组合,需要多一些的功能时整合多个功能元素,所以微服务架构就是对功能元素进行复制,而没有对整个应用进行复制.
好处
- 节省资源
- 每个功能元素都是可替换的,可独立升级的软件代码
2.第一个springboot程序
环境
- jdk 1.8
- maven 3.6.3
- springboot
- idea 2021.1.1
官方:提供了一个快速生成的网站
- 可以再官网直接下载后导入idea开发 https://start.spring.io/
- 直接使用idea创建一个springboot项目(一般直接在idea中创建)
在idea中直接生成
选择springweb
这个时候可能会遇到war包下载失败的问题
** 解决方案: **https://blog.csdn.net/weixin_36827459/article/details/108317079?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-1.no_search_link&spm=1001.2101.3001.4242
https://blog.csdn.net/weixin_41450959/article/details/106109392
如果还是无法解决,就调低springboot的版本继续尝试.通常问题可以得到解决
建包时的注意点
新建的包要和application类同一级否则无法识别到
类
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
// http://localhost:8080/hello
@ResponseBody
@RequestMapping("/hello")
public String test01(){
return "hello,world!";
}
}
结果
修改端口号
在resources文件夹下application.properties中==# server.port=8081 修改端口号==
2.1 小彩蛋
修改springboot启动页面的图标
**网站:**https://bootschool.net/ascii
在resource下新建banner.txt文件夹
效果
3.sprinboot自动装配原理(待补充)
自动配置
pom.xml
- spring-boot-dependencies : 核心依赖在父工程中
- 我们在写或者引入sprinboot依赖的时候,不需要指定版本,因为有这些版本仓库
启动器
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
-
启动器就是springboot的启动场景
-
比如spring-boot-starter-web,他就会帮我们自动导入web依赖的环境
-
springboot会将所有的功能场景,都变成一个个启动器
-
我们要使用什么功能,就只需要找到一个个启动器就可以了
主程序
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*
* 本身就是spring的一个组件
* */
@SpringBootApplication
public class Demo2Application {
public static void main(String[] args) {
SpringApplication.run(Demo2Application.class, args);
}
}
注解
@SpringBootConfiguration springboot配置
@Configuration spring配置类
@Component spring组件
@EnableAutoConfiguration 自动配置
@AutoConfigurationPackage 自动配置包
@Import({
Registrar.class}) 导入了选择器
4.springboot配置
springboot的配置文件application.properties到底可以配置一些什么呢?
springboot使用一个全局的配置文件,配置文件名称是固定的
- application.properties
- 语法: key=value
- application.yml
- 语法: key:(空格)value
4.1 yaml
YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。
标记语言
以前的配置文件大多使用xml来配置,但是xml配置起来不如yaml轻巧
# k: v
name: kuquan
# object
student:
name: kuquan
age: 3
# object inline
stuent: {
name: kuquan, age: 3}
# array
petsArr1:
-dog
-cat
# array inline
petsArr2: [cat, pig, dog]
properties只能存键值对
yml的值可以注入到我们的配置类中
4.2 给属性赋值的几种方法
4.2.1 yaml赋值
注入
不影响运行
==@ConfigurationProperties(prefix = “person”)==和配置文件中的属性绑定
结果
如果想避免爆红
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
4.2.2 properties配置
通过properties来配置
对比
- spEL:springbootEL表达式
- 松散绑定:yaml中的last-name可以和类中的lastName绑定,也就是-后面的字母默认大写的,这就是松散绑定.
- JSR303数据校验:我们可以在字段上增加一层过滤器验证,可以保证数据的合法性.
结论
- 配置yml和配置properties都可以获取到值,强烈推荐yml
- 如果我们在某个业务中,只需要获取文件中的某个值,可以使用@value
- 如果说,我们专门编写了一个javaBean来和配置文件进行映射,就直接使用@ConfigurationProperties(prefix = “xxx”)
4.3 JSR303校验
@Validated 数据校验
类上加完数据校验的注解之后,之后的成员变量上可以加注解
4.4 多环境配置及配置文件位置
4.4.1 yaml多文件模块
springboot的多环境配置,选择选择激活哪一个配置文件
spring.profiles.active=(跟application-后面的那个值)
yaml多文件模块
# 选择激活的模块
spring:
profiles:
active: dev
---
server:
port: 8081
# 配置环境名称
spring:
profiles: dev
---
server:
port: 8082
相当于配置了三个文件
4.4.2 springboot配置文件加载优先级
- 优先级1:项目路径下config文件夹下配置文件
- 优先级2:项目路径下配置文件
- 优先级3:资源路径下config文件夹配置文件
- 优先级4:资源路径下配置文件
4.4.3 配置项目访问路径
# 配置项目的访问路径(默认是/)
server:
servlet:
context-path: /edu
4.5 自动装配再理解(待补充)
查看哪些自动配置类生效了
debug: true
5.springboot WEB开发
要解决的问题:
- 导入静态资源
- 首页
- jsp,模板引擎thymleaf
- 装配扩展springmvc
- 视图解析器
- 增删改查
- 拦截器
- 国际化
5.1 静态资源问题
静态资源就放置在这里
静态资源导入的源码
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{
resource});
}
});
}
}
5.1.1 webjars是什么?
可以以maven的方式引入前端资源
*访问资源->http://localhost:8080/webjs/ **
5.1.2 静态资源的放置路径(优先级)
- classpath:/META-INF/resources
- classpath:/resources/ 1
- classpath:/static/ 2
- classpath:/public/ 3
5.2 首页
放置位置
在这三个文件中的中的一个放置一个名为index.html
的文件就会被扫描出来作为首页
5.3 模板引擎Thymeleaf
简介
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
官网 Thymeleaf
maven依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
springboot自带的thymeleaf太老了.不合适了,现在都是基于3.x版本开发
**结论:**只要需要使用thymeleaf,只需要导入对应的依赖就可以了!我们将html放在我们的templates文件夹下即可
5.3.1 thymeleaf使用
- 第一步,导入约束在html中
<html xmlns:th="http://www.thymeleaf.org">
5.3.2 第一个Thymeleaf
- controller
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("msg", "hello springboot");
return "test";
}
- view
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--所有的html元素都可以被thymeleaf接管,直接属性中添加th:xx即可-->
<h1 th:text="${msg}"></h1>
</body>
</html>
- 结果
5.3.3 thymeleaf基本语法
Thymeleaf 基本用法总结 - micrsoft - 博客园 (cnblogs.com)
5.4 mvc配置原理(之后补充)
5.5 springmvc配置(部分)
- 一个配置模板
package com.example.springboot03web.config;
/*
* 全面扩展springmvc
* @Configuration:变成一个配置类
* */
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
/*
* 配置
* */
}
springboot中,有很多的xxx @Configuration,帮助我们进行拓展配置,只要看见这个东西,我们就要注意了,看看它帮我们配置了什么东西.
6.Data
6.1 回顾jdbc
- 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
- 配置数据库链接
spring:
datasource:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
password: 123
- 测试数据源and数据库连接
package com.example.springboot04data;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@SpringBootTest
class Springboot04DataApplicationTests {
@Autowired
private DataSource dataSource;
@Test
void contextLoads() throws SQLException {
/*
* 查看默认数据源
* */
System.out.println(dataSource.getClass());
/*
* 数据库连接
* */
Connection connection = dataSource.getConnection();
System.out.println(connection);
// xxx.template : springboot已经配置好的bean
connection.close();
}
}
- 结果
默认的数据源是springboot内置的hikari
- 测试数据库连接
package com.example.springboot04data.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
public class JDBCController {
@Autowired
private JdbcTemplate jdbcTemplate;
/*
* 没有实体类数据库中的东西可以用map来获取
* */
@GetMapping("/alluser")
public List<Map<String, Object>> test01() {
String sql = "select * from user";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return list;
}
}
结果
**结语:**springboot帮我们整合了jdbc,现在jdbc操作起来也十分的简单
6.2 整合Druid
- 依赖(因为配置druid的时候使用了log4j,所以一并导入)
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
- 配置application
spring:
datasource:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
password: 123
type: com.alibaba.druid.pool.DruidDataSource
# druid 专有配置
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
# 配置监控拦截的filters.stat:监控统计,log4j:日志记录,wall:防止sql注入
filters: stat,wall,log4j
max-pool-prepared-statement-per-connection-size: 20
use-global-data-source-stat: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 在druid控制界面查看sql语句
filter:
stat:
enabled: true
wall:
config:
multi-statement-allow: true
- 配置config
package com.example.springboot04data.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
/*
* yaml中的配置绑定进来
* */
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
/*
* 后台监控 类似于web.xml
* 因为springboot内置了servlet,所以没有web.xml,代替方法ServletRegistrationBean
* */
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
/*
* 配置账号密码
* */
HashMap<String, String> initParameters = new HashMap<>();
/*
* 配置
* 配置的key是固定的
* */
// 登录账号密码
initParameters.put("loginUsername", "admin");
initParameters.put("loginPassword", "123");
// 允许谁能访问 ""表示都可以访问
initParameters.put("allow", "");
// 禁止谁能访问
initParameters.put("deny", "");
bean.setInitParameters(initParameters);
return bean;
}
/*
* filter
* */
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
// 过滤哪些请求
Map<String, String> initParameters = new HashMap<>();
bean.addUrlPatterns("/*");
// bean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
initParameters.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
- 测试
6.3 整合Mybatis
- 依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
- 配置
spring:
datasource:
username: root
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
# 整合mybatis
mybatis:
type-aliases-package: edu.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
- 编写pojo类
package edu.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias("user")
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}
- 编写mapper
package edu.mapper;
import edu.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/*
* @Mapper
* 表示这是一个mybatis的mapper类
* */
@Mapper
@Repository
public interface UserMapper {
List<User> queryAllUser();
User queryUserById(@Param("id") int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(@Param("id") int id);
}
- 编写mapper的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--xxx:接口的全路径-->
<mapper namespace="edu.mapper.UserMapper">
<!--在当前xml中使用二级缓存-->
<cache></cache>
<select id="queryAllUser" resultType="list">
select * from user
</select>
<select id="queryUserById" resultType="user">
select * from user where id=#{id}
</select>
<insert id="addUser" parameterType="user">
insert into user values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="user">
update user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser">
delete from user where id=#{id}
</delete>
</mapper>
- 编写service
package edu.service;
import edu.mapper.UserMapper;
import edu.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public List<User> queryAllUser() {
return userMapper.queryAllUser();
}
public User queryUserById(int id) {
return userMapper.queryUserById(id);
}
public int addUser(User user) {
return userMapper.addUser(user);
}
public int updateUser(User user) {
return userMapper.updateUser(user);
}
public int deleteUser(int id) {
return userMapper.deleteUser(id);
}
}
- 编写controller
package edu.controller;
import edu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService service;
public void setService(UserService service) {
this.service = service;
}
@GetMapping("/user/alluser")
public String queryAllUser() {
return service.queryAllUser().toString();
}
}
- 结果
serMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public List<User> queryAllUser() {
return userMapper.queryAllUser();
}
public User queryUserById(int id) {
return userMapper.queryUserById(id);
}
public int addUser(User user) {
return userMapper.addUser(user);
}
public int updateUser(User user) {
return userMapper.updateUser(user);
}
public int deleteUser(int id) {
return userMapper.deleteUser(id);
}
}
* 编写controller
```java
package edu.controller;
import edu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService service;
public void setService(UserService service) {
this.service = service;
}
@GetMapping("/user/alluser")
public String queryAllUser() {
return service.queryAllUser().toString();
}
}
- 结果