文章目录
本文为狂神说SpringBoot教程学习笔记 狂神说-JavaSpringBoot教程IDEA版
第一个springboot程序
-
在IDEA中创建springboot工程
-
目录:在同级目录下建包
-
controller包中的hellocontroller:
通过完成配置可以实现在网页中使用地址访问内容
在pom.xml中添加spring-boot-starter-web依赖即可实现web功能,其中内嵌了tomcat
package com.jinfz.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //改变代码之后需要重启项目 //下方代码,因为有两层的mapping,所以在网址中需要输入/hello/hello @Controller @RequestMapping("/hello") public class hellocontroller { @GetMapping("/hello") @ResponseBody public String hello(){ //调用业务,接收前端参数! return "hello"; } }
-
在resources–properties中更改配置
更改项目端口号:
#更改项目端口号 server.port=8081
如果使用yml配置文件,则会有对应格式,在下文中有讲解
-
在resources中创建banner.txt,可以更改编译中展示的横幅:
原理初探(?)
自动配置:
pom.xml
- spring-boot-dependencies:核心依赖在父工程中(…)
- 我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库
启动器start
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- springboot的启动场景
- 比如spring-boot-stater-web:自动导入web环境的所有依赖
- spring将一个个的功能场景变成启动器
- 要实现什么功能,只需要找到对应的启动器
start
(即在pom.xml中导入依赖,然后重新加载maven依赖,有时候可能还需要重启项目才能生效)
//@SpringBootApplication:标注这个类是一个springboot的应用
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
//将springboot这个应用启动
//反射加载上面的类的对象,以实现功能
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
@SpringBootApplication是个组合注解,其内包含以下内容:
@ComponentScan //扫描下列的包,可以剔除哪些东西
@SpringBootConfiguration(配置)
@Configuration
@Component
//点进去两层,发现其只是spring的一个组件
@EnableAutoConfiguration(自动配置)
@AutoConfigurationPackage//自动配置包
@Import({Registrar.class})//自动配置 `包注册`
@Import({AutoConfigurationImportSelector.class}) //自动配置 导入选择器(import selector)
//获取所有的配置(获取候选的配置:核心方法)
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
**结论:**spring中所有的自动配置都在启动类中扫描并加载–spring.factories
,但不一定生效。要判断条件是否成立,只要导入了对应的starter,就有对应的启动器,然后自动装配就会生效,然后就配置成功了。
- springboot启动时,从类路径下/META-INF/spring.factories中获取指定的值
- 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置
- 以前需要手动配置的东西,spring帮我们做了
- 整个javaEE,解决方案和自动配置的东西都在
spring-boot-autoconfigure-2.4.5.RELEASE.jar
这个包下 - 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
- 容器中也会存在很多的xxxxAutoconfigure 文件,这些类会给容器中导入这个场景所需要的所有组件
关于springboot
- 自动装配
- run()
- 判断普通项目还是web项目
- 推断当前的主类
- 。。。
全面接管springmvc配置
SpingBoot配置
对比yaml、properties
yaml语法讲解
application.properties
- 语法结构:
key=value
application.yml
- 语法结构:
key : 空格 value
- 注意要有空格(对于空格的要求极其严格)
修改springboot的默认配置
yaml中可以配置很多东西(随机值,默认值,判断是否存在)
yaml可以直接给实体类赋值(统一赋值)(严格要求空格)application.yml
person:
name: qinjiang
age: 12
happy: true
maps: {k1: v1,k2: v2}
list:
-code
-music
-girls
dogs:
name: wangcai
age: 3
在person.java
中绑定指定的加载:
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Data birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
public Person() {
}
public Person(String name, Integer age, Boolean happy, Data birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.name = name;
this.age = age;
this.happy = happy;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public Boolean getHappy() {
return happy;
}
public Data getBirth() {
return birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public List<Object> getLists() {
return lists;
}
public Dog getDog() {
return dog;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
public void setBirth(Data birth) {
this.birth = birth;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", happy=" + happy +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量诸如配置文件中的属性 | |
松散绑定 | 支持 | |
SpEL | 不支持 | |
JSR303数据校验 | 支持 | |
复杂类型封装 | 支持 |
jsr303数据格式校验
(其中的几种用法)
javaconfig:在其中可以设置全局(?)
对于不同的配置版本,可以设置为dev、test环境【多环境下切换:实际开发中会用到多个环境】
yaml:
server:
port: 8082
spring:
config:
activate:
on-profile: dev
properties: 设置不同的文件名
applicatin-dev.properties
表示开发环境
applicatin-test.properties
表示测试环境
SpringBoot Web开发
静态资源处理
spring boot项目中没有传统的webapp,因此有了替代项。
要解决的问题:
- 导入 静态资源(html、css)【在resources/static/、resources/public/、resources/templates/】
- 首页 :
localhost:8080/hello
- jsp,模板引擎 Thymeleaf
- 装配拓展SpringMVC(文件上传,json)
- 增删改查
- 拦截器
- 国际化(中英文切换)
导入web资源的方法:
-
webjars网站:可以通过maven的方式引入
-
静态资源目录:
-
classpath:/**(该目录下的所有内容均可访问)【使用时直接在后方地址填写需要访问的文件名】
-
classpath:/resources/【优先级较高】【存放upload上传的文件】
-
classpath:/static/【优先级其次】【存放静态资源:图片。。】
-
classpath:/public/【优先级最后】【存放公共资源】
**总结:**在springboot,可以使用以下方式处理静态资源
-
webjars
localhost:8080/webjars/
-
public, static, /**, resources,
-
静态资源目录优先级
首页定制
创建index.html文件,将其放在resources下的三个文件夹中的任意一个,如然后直接访问localhost:8080
-
可以在properties文件中配置
server.servlet.=/kuang //实现对于网址的拓展,使访问localhost:8080不再能直接访问主页,需要添加/kuang
-
如果使用thymeleaf引擎托管,需要做如下设置:
-
手动加入命名空间
xmlns:th="http://www.thymeleaf.org"
-
在本地url地址前,需要进行如下修改:
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> //在href前添加th:,并将本地url地址用@{}包裹
-
Thymeleaf模板引擎
可以在controller中进行页面跳转,访问template中的文件
结论:需要使用Thymeleaf,只需要导入对应的依赖就可以了。(注意:版本应为3.0.x及以上)
MVC配置原理及扩展SpringMVC
自定义一个视图解析器:
//如果想要自定义一些定制化的功能,只需要写这个组件,只需要将它交给spring boot,会帮我们自动装配
//拓展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//ViewResolver实现了视图解析器接口的类,我们就可以把它看做视图解析器
//自定义了一个自己的视图解析器
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
视图跳转:
//如果要扩展springmvc,官方推荐使用下列方法:@Configuration
@Configuration
//@EnableWebMvc 会导入一个类:DelegatingWebMvcConfiguration:从容器中获取所有的webmvcconfig
//该注解不能写,会使自动配置失效
public class MyMvcConfig implements WebMvcConfigurer {
//视图跳转
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/kuang").setViewName("test");
}
}
在springboot中,有非常多的xxx Configuration帮助我们进行扩展配置,在源码中需要注意它帮我们配置了哪些功能。
后端部分
数据库构建(伪造)
后续通过mysql创建数据库
-
在pojo中创建对象
员工
、部门
//员工表 @Data @NoArgsConstructor @AllArgsConstructor public class Employee { private Integer id; private String lastName; private String email; private Integer gender;//0表示女,1代表男 private Department department; //注意导入utils下的Date,如果导入sql下的date会报错。 }
注意事项:
1.使用@Data需要先导入lombok依赖 lombok中的@Data注解
2.使用@NoArgsConstructor、@AllArgsConstructor,引入无参构造、有参构造
//部门表 @Data @AllArgsConstructor @NoArgsConstructor //有参、无参构造 public class Department { private Integer id; private String departmentName; }
-
在dao中创建
EmployeeDao
、DepartmentDao
//员工DAO @Repository public class EmployeeDao { //模拟数据 public static Map<Integer, Employee> employees=null; //员工有所属的部门 @Autowired private DepartmentDao departmentDao; static{ employees=new HashMap<Integer,Employee>();//创建一个部门表 employees.put(1005,new Employee(1005, "123","123@qq.com", 1, new Department(101, "教学部"))); employees.put(1004,new Employee(1004, "123","123@qq.com", 1, new Department(101, "体育部"))); employees.put(1003,new Employee(1003, "123","123@qq.com", 1, new Department(101, "调研部"))); employees.put(1002,new Employee(1002, "123","123@qq.com", 1, new Department(101, "后勤部"))); employees.put(1001,new Employee(1001, "123","123@qq.com", 1, new Department(101, "运营部"))); } //操作如下(实现增删改查的功能): //主键自增 private static Integer initId=1006; //增加一个员工 public void save(Employee employee){ if(employee.getId()==null){ employee.setId(initId++); //新增加员工,主键以初始值自增 } employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); employees.put(employee.getId(), employee); //通过上述方法获取id,并将id给到emploee[部门关联外键] } //查询全部员工信息 public Collection<Employee> getAll(){ return employees.values(); } //通过id查询员工 public Employee getEmployeeById(Integer id){ return employees.get(id); } //删除员工 public void delete(Integer id){ employees.remove(id); } }
注:这两部分不懂写法
//部门dao @Repository //让该类被spring 托管 public class DepartmentDao { //初始化数据,模拟数据库 public static Map<Integer, Department> departments=null; static{ departments=new HashMap<Integer,Department>();//创建一个部门表 departments.put(105,new Department(105, "教学部")); departments.put(104,new Department(104, "体育部")); departments.put(103,new Department(103, "调研部")); departments.put(102,new Department(102, "后勤部")); departments.put(101,new Department(101, "运营部")); } //获得所有部门信息 public Collection<Department> getDepartments(){ return departments.values(); } //通过id得到部门 public Department getDepartmentById(Integer id){ return departments.get(id); } }
首页实现:
首页配置:注意,所有页面的静态资源都需要用thymeleaf接管;@{}
页面国际化
(切换页面语言)
(此处为可实现功能。在Thymeleaf引擎中,多处修改index.html等文件,很复杂。使用vue应该比较简单,故跳过。)
登录功能实现
在index.html中对输入内容进行提取:
<input type="text" name="username" class="form-control" placeholder="Username" required="" autofocus="">
<input type="password" name="password" class="form-control" placeholder="Password" required="">
编写loginController.java,实现具体的业务(判断用户名密码是否正确):
@Controller
public class loginController {
@RequestMapping("/user/login")
public String login(
@RequestParam("username") String username,
@RequestParam("password") String password,
Model model){
//具体的业务:判断用户名密码是否正确
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
return "dashboard";
}else{
//告诉用户登录失败
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
}
以上代码运行时,当用户名或密码发生错误时不会显示提示信息,需要在index.html中添加:
<!--消息回显,并且在其中根据条件给出提示信息-->
<!--此处为thymeleaf中一段高级代码,只有当msg不为空才会显示-->
<p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
注:位置放于页面中哪一段?对应代码位置。
自定义URL
解决办法:
需要在MyMvcConfig中增加一层映射关系
registry.addViewController("/main.html").setViewName("dashboard");
同时修改loginCotroller.java
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
return "redirect:/main.html";
登录拦截器
截止到目前,在未登录的情况下也能访问localhost:8080/main.html
,因此设置拦截器
-
在config文件夹下创建LoginHandlerInterceptor.java
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){ request.setAttribute("msg", "没有权限,请先登录!"); request.getRequestDispatcher("/index.html").forward(request, response); return false; }else{ return true; } } }
-
注意修改loginController.java
@Controller public class loginController { @RequestMapping("/user/login") public String login( @RequestParam("username") String username, @RequestParam("password") String password, Model model, //此处增加了一个输入值session HttpSession session){ if(!StringUtils.isEmpty(username) && "123456".equals(password)){ //此处设置session的值 session.setAttribute("loginUser", username); return "redirect:/main.html"; }else{ model.addAttribute("msg", "用户名或密码错误!"); return "index"; } } }
-
在MyMvcConfig中配置拦截器
//利用重写方法配置拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**")//此处拦截所有请求 .excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**"); //此处将登录所必须的页面及页面元素进行的排除,保证能够正常访问 }
小功能:修改dashboard.html中的company name,即可完成下图中的展示:
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <!--将原先的company name改为[[${session.loginUser}]]-->
员工管理系统
展示员工列表
狂神笔记:
员工列表展示
- 提取公共页面(fragment,insert/replace)
- th:fragment=“sidebar”
- th:replace="~{commons/commons::topbar}"
- 如果需要传递参数,可以直接使用()传参,接收判断即可!
- 列表循环展示(查询。此处为前端内容)
- 添加员工信息
- 修改员工信息
- 删除以及404处理
需要实现的功能:在dashboard
中点击员工管理的项目,可以跳转到list.html
中的对应部分
-
创建EmployeeController.java
先行步骤,将employee的所有查询结果都放入/emps路径下【此时,需要在resources目录下创建emps目录,并将list.html放入其中,方便管理?】
@Controller public class EmployeeController { //本来应该调用service层,但没写service,所以此处直接调用DAO层 @Autowired EmployeeDao employeeDao; //DAO层中应该是将方法写死了,所以此处直接调用 //Model model的作用是将其返回给前端!!! @RequestMapping("/emps") public String list(Model model){ Collection<Employee> employees = employeeDao.getAll(); model.addAttribute("emps", employees); return "/emp/list"; } }
-
修改dashboard中的custom部分,将其标题栏变为中文显示,并将跳转地址变为本地查询地址
<li class="nav-item"> <a class="nav-link" th:href="@{/emps}"> 员工管理 </a> </li>
-
同时修改list.html,保证点击跳转后的页面保持一致。代码与上相同。
本课程中,该部分还对以下前端内容进行了实现:
对于页面中功能条栏点击跳转后高亮的功能进行了实现;
将各页面中公共的部分的代码抽取至commons.html,并在各页面中使用这些fragment;
可以在resources中配置一个commons.html,将页面中公共的部分抽取,实现代码复用。(类似于组件化?)
本课程中,这一部分在Thymeleaf引擎下进行讲解,复杂,故跳过。
记住功能和思想即可。
查询员工信息(即在点击员工管理时显示原定数据库中所有员工的信息):
-
修改list.html中的部分代码:
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>Section title</h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <!--表头--> <th>id</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>birth</th> </tr> </thead> <tbody> <!--数据获取--> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}"></td> <td th:text="${emp.getLastName()}"></td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()}"></td> <td th:text="${emp.department.getDepartmentName()}"></td> <td> <!--此处为操作按钮的编写--> <button class="btn btn-sm btn-primary">编辑</button> <button class="btn btn-sm btn-danger">删除</button> </td> </tr> </tbody> </table> </div> </main>
添加员工信息
如何编写一个网站
- 前端设计构思:页面样式;数据
- 设计数据库(数据库设计难点!)
- 前端让它能够自动运行,独立化工程
- 数据接口如何对接:json,对象all in one
- 前后端联调测试
-
有一套自己熟悉的后台模板:工作必要 x-admin
-
前端界面:至少能通过前端框架组合出一个网站页面
- index
- about
- blog
- post
- user
-
让这个网站独立运行!(一个月)
数据库连接
整合JDBC使用
-
创建新项目,在起始页添加 SpringWeb、JDBC API、MySQL Driver依赖
-
配置application.yml文件,同时连接数据库
spring: datasource: username: "root" password: "mysql883721@" url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver
-
编写JDBCController.java启动类,编写增删改查业务:
@RestController public class JDBCController { @Autowired JdbcTemplate jdbcTemplate; //查询数据库的所有信息 @GetMapping("/userList") public List<Map<String, java.lang.Object>> userList(){ String sql="select * from user"; List<Map<String, java.lang.Object>> list_maps = jdbcTemplate.queryForList(sql); return list_maps; } //增删改(查): //增: @GetMapping("/addUser") public String addUser(){ String sql="insert into mybatis.user(id,name,pwd) values(3,'liuxi','bbaaaaaaa')"; jdbcTemplate.update(sql); return "add-ok"; } //改: @RequestMapping("/updateUser/{id}") public String updateUser(@PathVariable("id") int id){ String sql="update mybatis.user set name=?,pwd=? where id="+id; Object[] objects=new Object[2]; objects[0]="121212"; objects[1]="zzzzzz"; jdbcTemplate.update(sql,objects); return "update-ok"; } //删: @RequestMapping("/deleteUser/{id}") public String deleteUser(@PathVariable("id") int id){ String sql="delete from mybatis.user where id=?"; jdbcTemplate.update(sql,id); return "delete-ok"; } }
整合Druid数据源
阿里巴巴开源平台,自带监控平台、日志。
对于不同的数据源,有什么区别?
数据库的底层永远是JDBC。
Druid实现后台监控功能
-
编写DruidConfig.java配置类
@Configuration public class DruidConfig(){ @ConfigurationProperties(prefix="spring.datasourse") @Bean public DataSource(){ return new DruidDataSource(); } //后台监控,相当于web.xml //因为SpringBoot内置了servlet容器,所以没有 @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*"); //后台需要有人登录,账号密码配置 HashMap<String,String> initParameters = new HashMap<>(); //增加配置 initParmeters.put("loginUsername","admin");//登录key是固定的 initParmeters.put("loginPassword","123456"); //允许谁能访问 initParameters.put("allow","");//如果为空,则所有人都能访问;可以写某个人 //禁止谁能访问 initParameters.put("kuangshen","192.168.11.123");//用户名+ip bean.setInitParameters();//设置初始化参数 return bean; } //filter 过滤器 public FilterRegistrationBean webStatFilter(){ new FilterRegistrationBean().var; bean.setFilter(new WebStatFilter()); //可以过滤哪些请求 Map<String,String> initParameters =new HashMAp<>(); //这些东西不进行统计 initParameters.put("exclusions","*.js,*.css,/druid/*"); bean.setInitParameters(); } }
整合Mybatis框架
整合包 mybatis-spring-boot-starter
该部分为重点
-
新建项目,导入web、JDBC、mysql依赖;手动导入
mybatis-spring-boot-starter
依赖 -
配置
application.yml
,连接数据库spring: datasource: username: root password: mysql883721@ url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver
-
在pojo中编写
User.java
,定义数据格式@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
-
编写接口类
UserMapper.interface
【不清楚该部分的作用】@Mapper @Repository public interface UserMapper { List<User> queryUserList(); User queryUserById(int id); int addUser(User user); int updateUser(User user); int deleteUser(int id); }
对于此处的@Mapper,存在另外一种解决方案:【在启动类中添加注解@MapperScan()】
@SpringBootApplication @MapperScan("com.jinfz,mapper") public class Springboot05MybatisApplication { public static void main(String[] args) { SpringApplication .run(Springboot05MybatisApplication.class, args); } }
-
编写控制类
UserController.java
@RestController public class UseController { @Autowired private UserMapper userMapper; @GetMapping("/queryUserList") public List<User> queryUserList(){ List<User> userList = userMapper.queryUserList(); for(User user:userList){ System.out.println(user); } return userList; } }
-
在
application.yml
中添加配置,整合mybatis#整合mybatis mybatis: type-aliases-package: com.jinfz.pojo mapper-locations: classpath:mybatis/mapper/*.xml
-
创建目录resouces–mybatis–mapper,并添加
UserMapper.xml
文件【代码样式从官网导入】mybatis官方文档
需要手动写SQL语句:
<?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.jinfz.mapper.UserMapper"> <select id="queryUserList" resultType="User"> select * from user </select> <insert id="addUser" parameterType="User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <update id="updateUser" parameterType="User"> update user set name=#{name},pwd=#{pwd} where id=#{id} </update> <delete id="deleteUSer" parameterType="int"> delete from user where id=#{id} </delete> </mapper>
-
业务层调用DAO层,controller层调用service层【对于该部分仍不清晰。待回顾总结】
SpringSecurity权限控制
狂神笔记:
安全
过滤器、拦截器也可以实现,但框架实现更简单。
做网站时,应该在最开始时考虑安全
架构一旦确定,不方便再修改添加安全。
shiro、springsecurity:除了类不一样,名字不一样;认证,授权(vip1,vip2,…)
在框架中只需要一两个注解就可以实现,而拦截器需要复杂代码。
- 功能权限
- 访问权限
- 菜单权限
- …拦截器,过滤器:大量的原生代码,冗余
记住以下几个类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
EnableWebSecurity:开启WebSecurity模式
SpringSecurity中的两个主要目标是“认证”和“授权”(访问控制)
“认证”(Authentication)
“授权”(Authorization)
-
创建新项目,导入必要的依赖,手动导入spring security依赖
-
导入静态资源 [教程案例素材链接](Spring security教程案例素材(狂神说Java之SpringBoot教程集合版): 狂神SpringBoot教程IDEA版中p34中用到的页面素材,学习Spring security (gitee.com))
-
编写RouterController.java控制类,设置路由信息
@Controller public class RouterController { @RequestMapping({"/","index","index.html"}) public String index(){ return "index"; } @RequestMapping("/toLogin") public String toLogin(){ return "views/login"; } @RequestMapping("/level1/{id}") public String toLevel1(@PathVariable("id") int id){ return "views/level1/"+id; } @RequestMapping("/level2/{id}") public String toLevel2(@PathVariable("id") int id){ return "views/level2/"+id; } @RequestMapping("/level3/{id}") public String toLevel3(@PathVariable("id") int id){ return "views/level3/"+id; } }
各部分路由分别对应各静态资源的文件夹位置
-
编写SecurityConfig.java配置类
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //链式编程 @Override protected void configure(HttpSecurity http) throws Exception { //首页所有人可以访问,功能页只有对应有权限的人才能访问 //请求授权的规则 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); //没有权限需要跳转到登录页,下列代码开启登录页面 http.formLogin(); //开启了注销功能 //http.logout(); //http.logout().logoutUrl("/"); //网站防止攻击功能,csrf功能,默认开启 http.csrf().disable(); //关闭csrf功能 http.logout().logoutSuccessUrl("/index"); } //认证,springboot之前的版本中可以直接使用,但目前不可以用 //密码编码:passwordEncode //在新版中,增加了很多的加密方式 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //这些数据正常应该从数据库中读 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip1") .and() .withUser("jinfz").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3"); } }
注:两个方法皆为方法重写,重写后对于方法进行自己的改写(重写中方法名相同,但使用不同)
对于数据库的授权,方式如下:
@Autowired private DataSource dataSource; @Autowired private void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ //ensure the passwords are encoded properly UserBuilder users = User.withDefaultPasswordEncoder(); auth .jdbcAuthentication() .dataSource(dataSource) .withDefaultScheme() .withUser(users.username("user").password("password").roles("USER)) .withUser(user.username("admin").password("password").roles("USER","ADMIN")); }
对于thymeleaf引擎下的前端部分,仅对可实现功能进行记录,不要求记住操作
P36 注销及权限控制
-
导入thymeleaf-spring security依赖
-
导入命名空间,修改index.html,判断用户登录状态,并据此判断是否显示用户名和权限,显示登陆按钮还是注销按钮
-
根据用户的权限(hasRole)动态显示菜单
index.html
P37 记住我及首页定制
-
记住我
http.rememberMe(); //记住我功能(本质是cookie,默认保持两周)
-
首页定制
【前端后端联调】
打开源码
HttpSecurity.java
,根据其中注解中有的功能对需要的功能进行实现
P38-P45 Shiro
- 与SpringBoot/SpringSecurity平级的安全框架
多看开源项目,多学习
Swagger接口文档
狂神笔记:
swagger的作用和概念
了解前后端分离
在SpringBoot中集成Swagger
前后端分离:
后端:后端控制层(controller),服务层,数据访问层
前端:前端控制层,视图层
- 伪造后端数据,json交互。不需要后端,前端工程依旧能跑起来
前后端如何交互==> API接口
前后端相对独立,松耦合
前后端甚至可以部署在不同的服务器上
产生一个问题:
- 前后端联调,前后端人员无法做到及时协商,尽早解决
解决方案:
首先制定scheme,实时更新最新的API,降低集成风险;
Swagger
Swagger
API文档与API定义同步更新
直接运行,可以在线测试API接口
swagger2
swagger-ui
SpringBoot集成Swagger
-
新建项目,手动导入Springfox-Swagger2、SwaggerUI(对应Swagger 2.x)(Swagger 3.0需要导入springfox-boot-starter)
-
编写helloworld(即编写HelloController控制类)
@RestController public class HelloController { //每个项目默认都会有一个error请求 @RequestMapping("/hello") public String hello(){ return "hello"; } }
-
编写SwaggerConfig.java配置Swagger
@Configuration @EnableSwagger2 //开启swagger public class SwaggerConfig { }
此处仅是启动,并未进行详细配置(采用默认配置)
-
测试访问
区别于视频中的Swagger 2.x,Swagger 3.0之后的版本需要导入
springfox-boot-starter
依赖,同时访问的网址修改为http://localhost:8080/swagger-ui/(或者http://localhost:8080/swagger-ui/index.html)
配置Swagger
- Swagger的bean实例 Docket:
@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {
//配置了Swagger的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
//看源码,完成对输入参数的设置
//配置Swagger信息=info类
}
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("jinfz","http://s.weibo.com" ,"yijia7590jfz@163.com" );
return new ApiInfo("狂神的Api文档", "Api Documentation", "v1.0",
"urn:tos", contact, "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
}
}
-
Swagger配置扫描接口
@Bean public Docket docket(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.jinfz.swagger.controller")) /* 此处指定扫描的包(或者使用any()扫描全部;none()不扫描; withClassAnnotation(HelloController.class)扫描注解class)*/ .build(); //看源码,完成对输入参数的设置 //配置Swagger信息=info类
-
配置是否启动
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false)
//是否启动Swagger
.select()
.apis(RequestHandlerSelectors.basePackage("com.jinfz.swagger.controller"))
.build();
小问题:希望swagger只在生产环境中使用,在发布时不适使用
- 判断是不是生产环境,flag=false
- 注入enable(flag)
@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {
@Bean
public Docket docket(Environment environment){ //此处需要注入接口类
Profiles profiles=Profiles.of("dev","pro"); //此处为注解类,非实体类
//获取项目环境
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.jinfz.swagger.controller"))
.build();
}
}
配置API文档的分组
不同人负责不同的部分,则可以在swagger页面选择并展示不同人的内容
.groupName("狂神")
如何配置多个分组:new多个Docket实例即可(多个Bean)
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("A");
}
@Bean
public Docket docket3(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("B");
}
如果没有编写其他内容,则默认可以扫描全局
实体类配置
-
在pojo文件夹中编写
User.java
public class User { public String userame; public String password; }
-
在
HelloController.java
控制类中进行配置//只要我们的接口中,返回值中存在实体类,就会被扫描到swagger中并返回 @PostMapping("/user") public User user(){ return new User(); }
-
为了方便他人使用swagger文档,可以为文档中的内容添加注释
SwaggerConfig.java
@ApiModel("用户实体类") public class User { @ApiModelProperty("用户名") public String userame; @ApiModelProperty("密码") public String password; }
此处可以给swagger页面中的所有东西加上注释
总结:swagger
- 可以通过swagger给一些比较难理解的属性或者接口,添加注释信息
- 接口文档实时更新
- 可以在线测试
【注意点】出于安全考虑、节省内存,在正式发布时需要关闭swagger(判断环境,决定是否显示swagger)
任务
异步任务
定时任务
邮件发送-
异步任务手动方法:手动添加间隔定时(3s)【过于麻烦,实际中不可能使用】
@RestController
public class AsyncController {
@RequestMapping("/hello")
public String hello(){
asyncService.hello();//停止3s,前台
return "OK";
}
}
@Service
public class AsyncService {
public void hello(){
//以下为通过设置时间间隔手动实现多线程处理。但过于麻烦
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据正在处理...");
}
}
利用spring中的功能实现异步任务:
-
在main方法的启动类中开启异步任务
@SpringBootApplication @EnableAsync //开启异步注解功能 public class Springboot09TaskApplication { public static void main(String[] args) { SpringApplication.run(Springboot09TaskApplication.class, args); } }
-
在service中编写AsyncService.java服务类
@RestController public class AsyncController { @Autowired AsyncService asyncService; @RequestMapping("/hello") public String hello(){ asyncService.hello();//停止3s,前台 return "OK"; } }
-
在service层中进行配置
@Service public class AsyncService { @Async //告诉spring这是一个异步方法 public void hello(){ //以下为通过设置时间间隔手动实现多线程处理。但过于麻烦 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("数据正在处理..."); } }
分两步:在方法上添加注解,在spring启动类中开启这个功能
邮件任务
使用springboot完成邮件发送
-
在
pom.xml
中添加spring-boot-starter-mail
配置<!--javax:mail配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
在application.properties中配置邮箱信息
spring.mail.username=yijia7590jfz@163.com spring.mail.password=xxxxxxxxxxx//此处为pop3/smtp中的授权码,需手动获取 spring.mail.host=smtp.163.com #qq会有加密验证,163没有
-
对邮件发送功能进行测试,在测试类中编写代码
@Autowired JavaMailSenderImpl mailSender; @Test void contextLoads1() { //一个简单的邮件 SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject("你好啊"); mailMessage.setText("每天都要开心啊"); mailMessage.setTo("yijia7590jfz@163.com"); mailMessage.setFrom("yijia7590jfz@163.com"); mailSender.send(mailMessage); } @Test void contextLoads2() throws MessagingException { //一个复杂的邮件 MimeMessage mimeMessage = mailSender.createMimeMessage(); //组装 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"UTF-8"); //此处有异常,在更多操作中对异常进行抛出(throws) helper.setSubject("每天都好"); helper.setText("<p style='color:red'>希望每天都能快乐度过~</p>",true); //此处可以对发送的文本进行html格式编辑,需在后方设置html:true,以支持html //添加附件,此处使用相对路径 helper.addAttachment("1.jpg", new File("C:\\Users\\18241\\Desktop\\1.jpg")); helper.setTo("yijia7590jfz@163.com"); helper.setFrom("yijia7590jfz@163.com"); mailSender.send(mimeMessage); }
此处仅为测试,实际应该放在controller或者service中。可以封装为方法:
/** * @Param html * @Param subject * @Param text * * 在方法中,需要对导入的参数进行解释,以便来人 */ public void sendMail(Boolean html,String subject,String text) throws MessagingException { //一个复杂的邮件 MimeMessage mimeMessage = mailSender.createMimeMessage(); //组装 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,html); //此处有异常,在更多操作中对异常进行抛出(throws) helper.setSubject(subject); helper.setText(text,true); helper.addAttachment("1.jpg", new File("C:\\Users\\18241\\Desktop\\1.jpg")); helper.setTo("yijia7590jfz@163.com"); helper.setFrom("yijia7590jfz@163.com"); mailSender.send(mimeMessage); }
定时任务
用于发布定时任务。
由于学习期需要完成的项目中暂时未想到此功能,故暂时跳过。
【P53】
附:出现的问题及解决办法
maven无法打jar包
打jar包方式:
双击package
即可开始打包
出现的问题:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources) on project springboot-01-helloworld: Input length = 1 -> [Help 1]
解决办法:
修改pom文件中的依赖(第一次修改完成后需要重启项目)原文链接
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--在这里修改版本-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.3</version>
</plugin>
<!---->
</plugins>
</build>
其他可能的解决办法:在设置中将java版本号改为实际的版本 原文链接
解决问题后的显示:
未找到maven插件
未找到插件 ‘org.springframework.boot:spring-boot-maven-plugin:’
**解决办法:**添加版本号
报错:Error resolving template [index]
Springboot+thymeleaf+mybatis 报Error resolving template [index], template might not exist的异常
小技巧
-
代码块统一向前缩进:
shift
+Tab
小功能
可以更改标签页的图标,替换成自定义图片(新版不可用,需要自寻找方法)方法链接
阅读源码过程中的问题
阅读源码过程中的一些单词:
implement实现
metadata元数据(源数据?)
attributes属性
properties属性(配置类?)
注解介绍
@ResponseBody
@ConfigurationProperties(prefix = “person”):绑定指定的加载 person.java
PO、VO、POJO、DTO、DAO、Service包等常见包的理解
@Validated :信息合法性验证
前后端连接部分
展示员工列表
部分中的代码:Model model的作用是将其返回给前端!!!
Mybatis
部分:对于MVC进行解释:
C,即controller层,负责前端和后端的对接;
M,数据和业务;
V,html。