目录
入门
第一个SpringBoot程序
我们要创建一个 SpringBoot 项目,步骤如下:
首先新建一个 project,选择 Spring Initializr,其中的一些配置根据实际情况配置即可
点击 next
点击 finish 即可,创建完成后,删除一些没用的文件
删除完成后,项目结构就和之前的 web 项目一样了,注意:FirstExampleApplication 不能删除
点击 FirstExampleApplication ,就可以启动
然后我们来测试一下
编写一个 controller,向页面输入文字
@Controller
@RequestMapping("/test")
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String testHello(){
return "hello,SpringBoot";
}
}
注意:我们要将诸如 controller 包写在与 FirstExampleApplication 同一级
点击启动,由于我的端口号是 8080,因此输入 localhost:8080/test/hello
我们可以发现整个过程中我们没有向之前一样配置一些像 web.xml,前端控制器,tomcat 等等东西,简化了我们开发
修改 tomcat 端口号
在下面文件中添加
重新启动,即可生效
彩蛋
默认启动后,上方会有这样一个图案,我们想对其进行修改
先去找到修改后的样式,可以去关于佛祖的ascii艺术字,Spring Boot 佛祖 Banner-bootschool.net 中选择
在 resources 目录下创建一个 banner.txt,将要修改的复制进去,重启
我们可以发现修改成功了😊😊😊
配置文件
SpringBoot 使用了一个全局的配置文件,它的名字是固定的
application.properties
application.yaml
配置文件的作用:可以用来修改 SpringBoot 自动配置的默认值
YAML的语法
基本语法
语法结构:key:(空格)value,表示一对键值对,必须保证冒号后面有个空格;以空格来控制层级关系,如果一列数据是左对齐的,那么它们就是同一层级的
不同类型值的写法
基本类型
直接 key: value
字符串默认是不同加引号的,不同的引号有不同的作用
"" (双引号):不会转义字符串里的特殊字符,这里的 \n 就表示换行
'' (单引号):会将字符串中的特殊字符转为普通字符串
对象
有两种方式
①
student:
name: king
age: 25
②:行内写法
student1: {name: king, age: 25}
数组
两种方式
①
pets:
- cat
- dog
- pig
②:行内写法
pets1: [cat, dog, pig]
配置文件注入
通过 yaml 配置文件来对对象的属性赋值
配置文件
person:
name: spring
age: 21
happy: false
birth: 2022/2/7
map: {k1: v1, k2: v2}
list:
- code
- music
- girl
dog:
name: 旺财
age: 3
JavaBean
导入该依赖配置文件处理器,编写配置就会有提示
通过 properties 向对象属性赋值(了解)
配置文件
再使用 @PropertySource 来加载指定配置文件
配置文件yml还是properties他们都能获取到值;
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
yaml设置随机数
yaml 中可以设置配置一些随机数,如下
yaml获取前面配置的值
JSR303数据校验
首先开启 validation 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
再在类上加上 @Validated 注解
举例:让 person 的 name 属性满足 email 格式
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Boolean检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) 验证字符串的长度在min和max范围之内.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
@Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
@Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。
数值检查
建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为""时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 被指定的元素必须在合适的范围内
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
配置文件的加载位置
SpringBoot 会将下面四个位置的 application.properties 或者 application.yaml 文件作为 SpringBoot 的默认配置文件
优先级由高到低,高优先级的配置会覆盖低优先级的配置,四个地方的配置文件互补
–file:./config/
–file:./
–classpath:/config/
–classpath:/
多环境配置
使用properties配置多套环境
现在有三个环境,SpringBoot 默认会使用 application.properties
如果我们想修改环境,只需在 application.properties 中添加如下
yaml支持的多文档块方式
同样是三个环境,不同的文档使用 --- 进行分割
server:
port: 8081
spring:
profiles:
active: test #表示激活 test 环境,如果不设置就为默认环境 8081
---
server:
port: 8082
spring:
config:
activate:
on-profile: dev
---
server:
port: 8083
spring:
config:
activate:
on-profile: test
静态资源
在 SpringBoot 中我们可以使用以下方式处理静态资源
①:webjars:以jar包的方式引入静态资源;(少用,不过多说明)
②:在下面几个文件夹中找,并且优先级为 resources > static > public
定制首页
将 index.html 页面放在 public、resources、static 三个任意一个包下,都可以被识别为首页,如果要放在 templates 包下则需要使用 controller 跳转,使用 controller 就需要引入模板引擎
Thymeleaf模板引擎
导入下面依赖
<!--Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
或者在创建新项目时,引入
可以发现我们只需将 html 页面放在 templates 包下,thymeleaf 就会自动去渲染,就可以像之前一样使用 controller 来进行跳转
Thymeleaf语法规则
使用前需先声明名称空间
xmlns:th="http://www.thymeleaf.org"
基本语法
${...} 变量表达式
*{...} 选择变量表达式
#{...} 消息表达式
@{...} 链接url表达式
~{...} 代码块表达式
th属性
th:text :设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。
th:value:设置当前元素的value值,类似修改指定属性的还有th:src,th:href。
th:each:遍历循环元素,和th:text或th:value一起使用。
th:if:条件判断,类似的还有th:unless,th:switch,th:case。
th:insert:代码块引入
th:fragment:定义代码块,方便被th:insert引用。
th:object:声明变量,一般和*{}一起配合使用
th:attr:修改任意属性,类似的还有th:attrappend,th:attrprepend。
其他具体使用到再详细去了解
扩展SpringMVC
SpringBoot在自动配置很多组件的时候,会先看容器中是否有用户自己配置的,如果有就用用户配置的,如果没有就用自动配置的;如果有些组件可以存在多个,例如视图解析器,那么就将用户配置的和自己默认的组合起来。
如果我们想要扩展 SpringMVC,我们就需要去编写一个 @Configuration 注解类,并且实现接口 WebMvcConfigurer,而且不能标注 @EnableWebMvc 注解,如果标注了该注解,那么我们的配置类将会全面接管 SpringBoot,就会使 SpringBoot 的默认配置失效
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
//视图跳转
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
}
员工管理系统
首页实现
所有页面的静态资源都需要用 thymeleaf 接管:@{}
@Controller
public class IndexController implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
登录功能实现
具体的代码写法和 SpringMVC 一模一样,这里没有用数据库,只是简单的测试
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
//具体的业务
if (username.equals("root") && password.equals("123456")){
return "redirect:/main.html";
}else {
//告诉用户登录失败
model.addAttribute("error", "用户名或密码错误");
return "index";
}
}
}
登录拦截器
也是 SpringMVC 中一样配置
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功后应该存在用户session
Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser != null){
return true;
}else {
request.setAttribute("error", "请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
}
}
}
在实现了 WebMvcConfigurer 接口的 controller 中进行扩展
CRUD之员工列表
thymeleaf公共页面抽取
有三种方式进行抽取:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
将 dashboard.html 中的侧边栏抽取出来进行复用
在其他页面引用
我们一般把公用的抽取出来放在一个 html 里,其他页面要使用时直接从该页面引用即可
在引入时我们还可以传递参数给引入的部分
获取传入的参数
员工列表展示
后台查询所有员工
@Controller
public class EmployeeController {
@Autowired
EmployeeMapper employeeMapper;
//查询全部员工
@RequestMapping("/employees")
public String employees(Model model){
Collection<Employee> employees = employeeMapper.getAll();
model.addAttribute("emps", employees);
return "list";
}
}
前台直接获取遍历展示即可
这里 birth 显示出现了问题
可以进行如下修改
CRUD之添加员工
form 表单
<form action="">
<div class="form-group">
<label>LastName</label>
<input type="text" name="lastName" class="form-control" placeholder="海绵宝宝">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" placeholder="1176244270@qq.com">
</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">
<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">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input type="text" name="birth" class="form-control" placeholder="嘤嘤嘤">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
后台代码
@RequestMapping("/toAdd")
public String toAdd(Model model){
//查出所有部门的信息
Collection<Department> departments = departmentMapper.getDepartments();
model.addAttribute("departments", departments);
return "add";
}
@RequestMapping("/addEmployee")
public String addEmployee(Employee employee){
employeeMapper.addEmployee(employee);
return "redirect:/employees";
}
CRUD之修改员工
修改页面表单
<form th:action="@{/updateEmployee}" method="post">
<input type="hidden" name="id" th:value="${employee.getId()}">
<div class="form-group">
<label>LastName</label>
<input th:value="${employee.getLastName()}" type="text" name="lastName" class="form-control" placeholder="海绵宝宝">
</div>
<div class="form-group">
<label>Email</label>
<input th:value="${employee.getEmail()}" type="email" name="email" class="form-control" placeholder="1176244270@qq.com">
</div>
<div class="form-group">
<label>Gender</label><br>
<div class="form-check form-check-inline">
<input th:checked="${employee.getGender()==1}" class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input th:checked="${employee.getGender()==0}" class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}" th:selected="${department.getId()==employee.getDepartment().getId()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input th:value="${#dates.format(employee.getBirth(), 'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="嘤嘤嘤">
</div>
<button type="submit" class="btn btn-primary">修改</button>
</form>
后台代码
@RequestMapping("/toUpdate/{id}")
public String toUpdate(@PathVariable("id")Integer id, Model model){
Employee employee = employeeMapper.getEmployeeById(id);
model.addAttribute("employee", employee);
//查出所有部门的信息
Collection<Department> departments = departmentMapper.getDepartments();
model.addAttribute("departments", departments);
return "update";
}
@RequestMapping("/updateEmployee")
public String updateEmployee(Employee employee){
employeeMapper.update(employee);
return "redirect:/employees";
}
CRUD之删除员工
后台代码
@RequestMapping("/delEmployee/{id}")
public String delEmployee(@PathVariable("id")Integer id){
employeeMapper.delete(id);
return "redirect:/employees";
}
注销
清除 session,返回至登陆页面即可
@RequestMapping("/loginOut")
public String loginOut(HttpSession session){
session.invalidate();
return "redirect:/employees";
}
配置404页面
在 templates 包下创建一个 error 包,将诸如 404、500等错误页面放在里面即可自动识别
SpringBoot操作数据库
新建项目
我们可以使用 SpringBoot 默认的数据源,也可以配置德鲁伊数据源
整合德鲁伊数据源
导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
在 application.yaml 文件中设置
可以自己设置一些 druid 数据源的配置
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
自定义德鲁伊数据源配置
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控:web.xml,ServletRegistrationBean
//因为 SpringBoot 内置了 Servlet 容器,没有 web.xml,替代方法:
@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", "123456");
//允许谁能访问
initParameters.put("allow", "");
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<>();
//这些东西不进行统计
initParameters.put("exclusions", "*.js,*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
druid后台监控页面
整合MyBatis
整合包
用法相差不大,注意细节方面即可,事务会自动提交不用配置
①:创建实体类以及对应 mapper
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
}
//这个注解表示这是一个 mybatis 的 mapper 类
@Mapper
@Repository
public interface UserMapper {
List<User> queryUsers();
User queryUserById(@Param("uid") int id);
int addUser(User user);
int updateUser(User user);
int delUser(int id);
}
②:有 mapper 后,我们就应该配置对应的 mapper.xml 配置文件
放在下面目录中,编写内容也和之前一样
<?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">
<mapper namespace="com.zhouyue.mapper.UserMapper">
<select id="queryUsers" resultType="User">
select * from user
</select>
<select id="queryUserById" resultType="User">
select * from user where id = #{uid}
</select>
</mapper>
在 application.yaml 中配置别名和 mapper 等等
#整合mybatis
mybatis:
type-aliases-package: com.zhouyue.pojo #配置别名
mapper-locations: classpath:mybatis/mapper/*.xml #配置mapper
测试
@SpringBootTest
class DataBaseApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void contextLoads() throws SQLException {
List<User> users = userMapper.queryUsers();
for (User user : users) {
System.out.println(user);
}
}
}
SpringSecurity
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
记住三个类
-
WebSecurityConfigurerAdapter:自定义Security 策略
-
AuthenticationManagerBuilder:自定义认证策略
-
@EnableWebSecurity:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)
“认证”(Authentication)
“授权”(Authorization)
这三个概念是通用的,不只是在 Spring Security 中存在
设置认证和授权
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,但是功能页只有对应权限的人才能访问
//请求授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll() //首页所有人都能访问
.antMatchers("/level1/**").hasRole("vip1") //level1下的页面只有 vip1 才能访问
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//开启没有权限默认到登录页,是自带的登录页
http.formLogin();
}
//认证
//密码编码:There is no PasswordEncoder mapped for the id "null"
//在 Spring Security 5.0+ 新增了很多加密方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//从内存中取
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) //设置密码加密方式
.withUser("zyqqq").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2") //设置认证用户以及权限和密码加密
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vip1","vip2","vip3");
//从数据库中取
// auth.jdbcAuthentication()
}
}
开启注销
//开启注销功能,注销成功跳转到首页
http.logout().logoutSuccessUrl("/");
开启记住我功能
//开启记住我
http.rememberMe();
将登录页定制为自己的页面
绑定记住我
以上只是简单使用
异步任务、邮件发送、定时任务
异步任务
使用 @Async 注解来设置一个方法为异步方法
//告诉Spring这是一个异步的方法
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据正在处理");
}
并且要在主程序中开启异步功能
邮件发送
设置 mail 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
这里使用QQ邮箱,需要开启以下服务,开启后可以获得一个密码,之后要用
在 application 配置文件中配置以下信息
测试发送一个简单的邮件
测试发送一个带附件的复杂邮件
定时任务
TaskScheduler 任务调度者
TaskExecutor 任务执行者
@EnableScheduling //开启定时功能的注解
@Scheduled //什么时候执行
Cron表达式
首先要了解 cron 表达式,可以参考这个大佬的博文
一看就懂:cron 表达式小白一个-CSDN博客cron表达式
可以使用下面工具来进行转换
quartz/Cron/Crontab表达式在线生成工具-BeJSON.com
测试
@Service
public class ScheduledService {
//在一个特定的时间执行这个方法
//cron表达式
//举例:每个两秒执行一次
@Scheduled(cron = "0/2 * * * * ?")
public void hello(){
System.out.println("hello,你被执行了");
}
}