自动配置原理
思维导图
SpringBoot所有的自动配置都是在启动的时候扫描并加载spring.factories
中的所有xxxxxAutoConfiguration
自动配置类,而自动配置类是否生效则看是否满足条件。我们可以通过引入对应的start依赖使自动配置类生效。
application.yaml(application.properties)文件中进行手动配置,不进行配置则会使用默认的配置属性。在SpringBoot启动时自动加载了大量的xxxxxAutoConfiguration
自动配置类,而这些类中存在对应的xxxProperties
配置类,里面的属性与我们将要配置的application.yaml文件中的属性对应。
SpringApplication.run()
- 判断是web项目还是普通的项目
- 推断并找到当前的主类
- 找到所有的监听器设置到listener属性中
application.yaml
SpringBoot推荐使用的配置文件。
- 可为类的属性注入值,类上使用
@ConfigurationProperties(prefix="xxx")
注解与配置文件绑定,导入依赖开启提示功能
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
- 可配置多个环境
server:
port: 8080
spring:
profiles:
active: dev # 使用的环境
---
server:
port: 8081
spring:
profiles: dev
---
server:
port: 8082
spring:
profiles: test
静态资源
- 存放位置
"classpath:/META-INF/resources/"
,"classpath:/resources/"
,"classpath:/static/"
,"classpath:/public/"
- 默认首页使用
index.html
命名,创建在静态资源包下
扩展配置
以WebMvc配置为例,转自2.3.1版本官方文档
(如果使用自定义的类进行配置加上@Bean
交给SpringBoot)
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own
@Configuration
class of typeWebMvcConfigurer
but without@EnableWebMvc
.
If you want to provide custom instances of
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of typeWebMvcRegistrations
and use it to provide custom instances of those components.
If you want to take complete control of Spring MVC, you can add your own
@Configuration
annotated with@EnableWebMvc
, or alternatively add your own@Configuration
-annotatedDelegatingWebMvcConfiguration
as described in the Javadoc of@EnableWebMvc
.
CRUD实验
Thymeleaf
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- template包下的静态资源文件,只能由controller层的请求进行访问,同时要记得导入模板引擎。
- thymeleaf使用文档
- 抽取html中的公共部分使用
th:fragment
和th:replace
,()
中可以传递参数
定制首页
- 首页位置
- 首页转发
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 配置首页视图
registry.addViewController("/").setViewName("login");
registry.addViewController("/login.html").setViewName("login");
}
}
国际化
- 创建i18n文件
- 配置文件
spring:
# 国际化配置
messages:
basename: i18n.login
- 页面通过thymeleaf获取值
- 在请求中加入国际化所需的参数
<a th:href="@{/index.html(l='zh_CN')}">中文</a>
<a th:href="@{/index.html(l='en_US')}">English</a>
- 自定义
LocaleResolver
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
// 获取请求参数
String language = httpServletRequest.getParameter("l");
// 若果没有请求参数采用默认值
Locale locale = Locale.getDefault();
// 请求参数不为空
if (!StringUtils.isEmpty(language)){
// 分割语言和地区
String[] s = language.split("_");
locale = new Locale(s[0], s[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
- 记得在自定义的
WebMvcConfigurer
配置类中注册组件
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
拦截器
- 自定义拦截器
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("user");
if (user == null) {
request.setAttribute("msg", "还没有登录,请先登录");
request.getRequestDispatcher("/login.html").forward(request, response);
return false;
} else {
return true;
}
}
}
- 同样记得加入到定义的
WebMvcConfigurer
配置类中
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).
addPathPatterns("/**").
excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
}
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //0:女 1:男
private Department department;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
查询所有
- 操作数据库查询,由于
Employee
实体类中的department
属性是对象,所以采用一对一的查询方式,通过d_id
查询department表,将查询的结果赋值给department
属性
public interface EmployeeMapper {
@Select("select * from employee")
@Results({
// property:为employee类中对应的字段
// column:为数据库中对应的字段并作为参数传给后面的方法
// one:一对一查询
@Result(property = "department", column = "d_id", one = @One(select = "com.diana.mapper.DepartmentMapper.getDeptById"))
})
List<Employee> getEmps();
}
public interface DepartmentMapper {
@Select("select * from department where id=#{id}")
Department getDeptById(Integer id);
}
- 编写controller代码
@Controller
public class EmployeeController {
@Autowired
EmployeeMapper employeeMapper;
@GetMapping("/emps")
public String getEmps(Model model){
List<Employee> emps = employeeMapper.getEmps();
model.addAttribute("emps", emps);
return "emp/list";
}
}
- 前端页面获取数据
<tr th:each="emp:${emps}">
<td th:text="${emp.id}"></td>
<td th:text="${emp.lastName}"></td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender==0?'女':'男'}"></td>
<td th:text="${emp.department.departmentName}"></td>
</tr>
增加
- 通过GET请求访问添加页面,部门使用下拉框,所以将数据库中的所有部门查询后,传递过去
// 前往添加员工页面
@GetMapping("/emp")
public String toAddEmpPage(Model model){
List<Department> depts = departmentMapper.getDepts();
model.addAttribute("depts", depts);
return "emp/add";
}
// 获取所有部门
@Select("select * from department")
List<Department> getDepts();
- 通过POST请求从页面获取信息,注意页面中的
name
属性值要与实体类属性值保持一致
// 添加员工
@PostMapping("/emp")
public String addEmp(Employee employee){
employeeMapper.addEmp(employee);
return "redirect:/emps";
}
// 添加员工
@Insert("insert into employee(lastName,email,gender,d_id) values(#{lastName},#{email},#{gender},#{department.id})")
int addEmp(Employee employee);
更新
- 通过路径参数获取更新的员工id
<a class="btn btn-sm btn-primary" th:href="@{/update/}+${emp.id}">编辑</a>
- 编写前往更新页面的方法,考虑数据回显
// 前往更新员工页面
@GetMapping("/update/{id}")
public String toUpdateEmpPage(@PathVariable("id") Integer id, Model model){
// 数据回显
Employee emp = employeeMapper.getEmpById(id);
model.addAttribute("emp", emp);
List<Department> depts = departmentMapper.getDepts();
model.addAttribute("depts", depts);
return "emp/update";
}
// 通过id查找员工
@Select("select * from employee where id=#{id}")
@Results({
@Result(property = "department", column = "d_id", one = @One(select = "com.diana.mapper.DepartmentMapper.getDeptById"))
})
Employee getEmpById(Integer id);
- 更新员工信息
// 更新员工信息
@PostMapping("/update")
public String updateEmp(Employee employee){
employeeMapper.updateEmp(employee);
return "redirect:/emps";
}
// 更新员工信息
@Update("update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{department.id} where id=#{id}")
int updateEmp(Employee employee);
删除
// 删除员工
@GetMapping("/delete/{id}")
public String delEmp(@PathVariable("id") Integer id){
employeeMapper.delEmp(id);
return "redirect:/emps";
}
// 删除员工
@Delete("delete from employee where id=#{id}")
int delEmp(Integer id);
一对多查询 一个部门所有的员工
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {
private Integer id;
private String departmentName;
private List<Employee> employees;
}
// 一对多查询Dept
// 通过dept的id查找emps
@Select("select * from employee where d_id=#{deptId}")
List<Employee> getEmpByDeptId(Integer deptId);
//一对多查询以dept的id为参数调用"com.diana.mapper.EmployeeMapper.getEmpByDeptId"方法查询emp
@Select("select * from department where id=#{id}")
@Results({
// 查询id
@Result(property = "id", column = "id"),
// 以部门id为参数查询员工
@Result(property = "employees", column = "id", many = @Many(select = "com.diana.mapper.EmployeeMapper.getEmpByDeptId"))
})
Department getDeptAndEmp(Integer id);