# SpringBootTutorial **Repository Path**: ThroughFog/spring-boot-tutorial ## Basic Information - **Project Name**: SpringBootTutorial - **Description**: SpringBoot学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-27 - **Last Updated**: 2021-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [TOC] # SpringBoot ## 介绍 作用:Springboot是学习微服务框架的基石。 关系:SpringBoot是Spring的工具API框架 ## 社区版idea安装插件 搜索`spring Assistant`,安装。 ## pom.xml `parent`标签 SpringBoot将现有的主流框架都进行了整合,在内部依据完成了jar包的依赖的配置,如果用户需要,则只添加某些核心包 那么所有的依赖都会按照规则自动地下载 `dependency`标签 通过启动项(就是starter)的方式,进行jar包文件加载,同时功能中的配置项,也会自动完成。 `build`标签 完成项目的打包、发布等的一系列的功能 `excludes` 在项目打包时,将这些包排除 `optional`标签 表示父子项目之间依赖不传递 ## maven jar包依赖的传递性 例子: 1. A.jar ---> B.jar 2. B.jar ---> C.jar 仅需导A.jar即可 ## 配置文件 ### SpringBoot属性赋值 #### yml文件 1. 编辑配置文件 `application.yml` ```yaml #定义dept属性值 YML文件默认支持UTF-8 Dept: id: 101 name: test ``` 2. 编辑实体类 `Dept` ```java @Component public class Dept { @Value("${dept.id}") private Integer id; @Value("${dept.name}") private String name; //getter and setter and toString... } ``` 3. 测试 `test` ```java @SpringBootTest //在执行测试方法时自动地启动Spring容器,并注入本类中带有@Autowired的属性 class SpringbootDemo1ApplicationTests { @Autowired private Dept dept; @Test void contextLoads() { System.out.println(dept); } } ``` #### properties文件 `dept.properties` ```properties dept.id2=110 dept.name2=springboot测试 ``` `Dept` ```java @PropertySource(value = "classpath:/dept.properties",encoding = "UTF-8") @Data public class Dept { @Value("${dept.id2}") private Integer id2; @Value("${dept.name2}") private String name2; } ``` ## lombok插件 `@Data`:自动生成`get`、`set`、`toString`方法 `@Accessors(chain=true)`:重写set方法,返回this对象。然后就可以使用链式调用 `@NoArgsConstructor`:无参构造 `@AllArgsConstructor`:全参构造 ## 配置多环境 要求:多环境中每个数据项都应该保持一致。 关键语法:`---`表示环境的分割 规则:每个环境最好有自己的名称。如下代码块定义了一个`test`环境 ```yaml spring: config: activate: on-profile: test ``` 指定默认环境配置名称 ```yaml #默认环境选项,指定默认启动的环境项 spring: config: activate: prod --- ``` ## 热部署 `依赖` ```xml org.springframework.boot spring-boot-devtools ``` #### idea环境配置 快捷键:ctrl + alt + shift + / 点击注册 给`complier.automake.allow.when.app.running`打勾 `settings` 将`Compiler`下的`Build project automatically`、`Compile independent moduled in paraller`勾上 ## 整合Mybatis ### 导包 ```xml mysql mysql-connector-java runtime 8.0.23 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.4 ``` ### url参数 * autoReconnect `boolean` 是否自动重连 * serverTimeZone `Aisa/ShangHai` `GMT%2B8` 时区 * allowMultiQueries `boolean` 是否允许批量操作 * useUnicode `boolean` 是否使用Unicode编码 `application.yml` ```yaml spring: datasource: username: root url: jdbc:mysql://localhost:3306/jtadmin?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver password: root mybatis: type-aliases-package: com.jt.pojo mapper-locations: classpath:/mybatis/mappers/*.xml #开启驼峰映射 configuration: map-underscore-to-camel-case: true ``` ### mapper接口动态代理过程 查看注入的mapper属性的类 ```java @Test void lookProxy() { System.out.println(demoUserMapper.getClass()); } ``` `class com.sun.proxy.$Proxy69` 传入配置文件,根据配置文件信息实例化接口并创建代理。 * SqlSessionFactory包装了JDBC * SqlSession持有jdbc-conn * SqlSession操作数据库,执行SQL语句 ## 整合Mybatis-plus ### 注解 `@TableName`:将对象与表绑定。如果没有添加注解的属性值,默认以类名当作表名进行查询。缺省优化 `@TableId`:绑定主键。缺省优化 * type 主键类型 * NONE 默认 * AUTO 自增 * ASSIGN_UUID 分配一个32位的UUID `@TableField`:其他字段名。缺省优化 * value 数据库字段名 * exist 是否是数据库表字段。可以将表中没有的属性设为`false` ### 接口 继承MP写好的`BaseMapper`,泛型写实体类名。这个Mapper事先写好了基本的单表CRUD操作。 ### YML配置文件 说明:MP包含了M。故不再使用Mybatis依赖。 将原来`mybatis`的配置项改为`mybatis-plus`即可将所有配置项继承过来。 ```yaml mybatis-plus: type-aliases-package: com.jt.pojo mapper-locations: classpath:mybatis/mappers/*.xml #开启驼峰映射 configuration: map-underscore-to-camel-case: true ``` ### 流程 1. 根据userMapper属性找到对应的class类型 2. 根据类型通过反射获取父级接口类型BaseMapper 3. 获取接口类型的泛型`DemoUser.class` 4. 根据`DemoUser.class`获取里面的注解,(获取主键名称),就得到了表名 5. 如上获取属性名。 6. 利用这个对象的`get`方法获取属性值,拼接`sql` 7. MP将拼接好的`sql`交给`mybatis(jdbc)`处理。 ### MP的CURD * 增加 ```java /** * 插入元组 */ @Test public void TestInsert(){ DemoUser user = new DemoUser(); user.setName("MP测试").setSex("男").setAge(19); userMapper.insert(user); } ``` * 修改 **原则**:根据对象中不为null的属性当作set条件(动态sql)。如果是ById的操作,id必须赋值,并且Id当作唯一where条件 ```java /** * 根据ID修改元组 * 修改id=231的数据name="Jack" age=18 */ @Test public void TestUpdate(){ DemoUser user = new DemoUser(); user.setName("MP测试").setSex("男").setAge(19); userMapper.updateById() } ``` * ById查询 ```java /** * 查询id=21的用户 */ @Test public void TestSelectById(){ DemoUser user = userMapper.selectById(21); System.out.println("user = " + user); } ``` > QueryWrapper对象——条件构造器 > > 用于拼接where条件 > > 原则:拼接对象中不为空的属性充当where条件 > > 使用: > > new对象,泛型为要查询的实体类,参数为实例化的对象 > > ``` > QueryWrapper wrapper = new QueryWrapper<>(user1); > ``` * ByWrapper查询等于 ```java System.out.println("-----SelectByWrapper-----"); DemoUser user1 = new DemoUser(); user1.setName("白骨精").setSex("女"); QueryWrapper wrapper = new QueryWrapper<>(user1); List list = userMapper.selectList(wrapper); System.out.println("list = " + list); ``` * ByWrapper其他运算符 > 特殊字符方法: > > 大于——gt 等于——eq > > 小于——lt 大于等于——ge > > 小于等于——le > > 连点方法自动将where条件拼起来,默认and。若为or,则接上一个`or()`方法 ```java @Test public void TestSelectByWrapper(){ System.out.println("-----selectByWrapper-----"); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.ge("age", 18) .eq("sex", "女"); userMapper.selectList(queryWrapper); } ``` * like关键字 > like:前后都有百分号 > > likeLeft:百分号位置靠左 > likeRight:百分号位置靠右 ```java @Test public void TestSelectLike(){ System.out.println("-----selectLike-----"); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.like("name", "精"); List users = userMapper.selectList(queryWrapper); System.out.println("users = " + users); } ``` * orderByDesc 降序 ```java /** * sex=男 * 降序 */ @Test public void TestOderBy(){ System.out.println("-----selectLike-----"); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("sex", "男") .orderByDesc("id"); List users = userMapper.selectList(queryWrapper); System.out.println("users = " + users); } ``` * In **注意:传入数组时需要转化为集合,且数组使用包装类型** `Integer[] ids = {1,3,5,6,7};` `Arrays.asList(ids);` ```java @Test public void TestIn(){ System.out.println("-----selectIn-----"); Integer[] ids = {1,3,5,6,7}; //传入数组时需要转化为具体的集合 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.in("id", Arrays.asList(ids)); List users = userMapper.selectList(queryWrapper); for (DemoUser user : users) { System.out.println("user = " + user); } } ``` > condition:内部编辑一个判断的条件 > 如果返回值为true,则拼接该字段 > 若为false,则不拼接 > > ``` > StringUtils.hasLength(name) > ``` * SelectByDynamic ```java /** * 查询name=xxx and sex > xxx,当属性为空,要求动态生成sql */ @Test public void TestDynamic(){ System.out.println("-----selectDynamic-----"); String name = ""; int age = 18; //传入数组时需要转化为具体的集合 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq(name!=null || "".equals(name), "name", name) .gt(age>0, "age",age); List users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); } ``` * SelectObjs 只返回第一个字段 ```java /** * 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 * 应用:可以根据条件查询Id的值,查询之后为后续的Sql提供数据支持 */ @Test public void TestSelectObjs(){ System.out.println("-----selectObjs-----"); List objs = userMapper.selectObjs(null); objs.forEach(System.out::println); } ``` * Query.select挑选指定字段 ```java /** * 仅查询name/sex字段 */ @Test public void TestSelect(){ System.out.println("-----selectObjs-----"); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select("name","sex"); List list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); } ``` **注意:**传入构造方法参数为`where`条件,使用`select`方法传入的是查询字段 * mapper.selectMap强化上边的方法,仅返回有效字段 ```java /** * 查询指定字段,仅返回有效字段 */ @Test public void TestSelectNotNull(){ System.out.println("-----selectObjs-----"); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select("name","sex"); //根据 Wrapper 条件,查询全部记录(只会返回指定的字段) List> mapList= userMapper.selectMaps(queryWrapper); mapList.forEach(System.out::println); } ``` * update 更新时拼接除了id外的其他where条件 ```java /** * 更新name="Jack"的数据为name="Mack" and sex=其他 */ @Test public void TestUpdateByName(){ System.out.println("-----updateByName-----"); //条件 UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name","Jack"); //修改封装 DemoUser user1 = new DemoUser(); user1.setName("Mack").setSex("其他"); userMapper.update(user1, updateWrapper); } ``` ## SpringMVC ### 目录结构 `resources`——资源文件 ​ `static`——静态资源 ​ `templates`——模板 ### 默认页面跳转机制 主页找`index.html` `2021-04-30 09:52:19.293 INFO 9952 --- [ restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index` ### 注解 `@Controller`:开启SpringMVC机制 `@RequestMapping`:设定请求路径 `@RequestParam`:将参数设为必填项,设置默认值 ### 前后端传值 1. 导入模板标签 ```html ... ``` 2. 后端通过ModelAndView对象设置数据与视图 `mav.addObject(name,value)`——添加数据 `mav.setViewName(viewString)`——添加要跳转的页面 ```java @RequestMapping("/user") public ModelAndView toUser(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("id", 1001); modelAndView.addObject("name", "测试"); modelAndView.setViewName("user"); return modelAndView; } ``` 3. 前端取值 使用`th:text`可以为标签设置文本 使用`${key}`表达式可以从服务器获取数据 ### Servlet 说明:Servlet是浏览器与tomcat服务器进行交互的一种机制 核心对象: 1. Request 包含了用户的所有的请求相关信息(参数..协议..地址..) 2. Response 包含了服务器相关的信息(服务器地址,返回的数据) ### 原理★ * 重要组件 1. 前端控制器 `DispatcherServlet`(内部核心机制) 1. 底层是Servlet 2. 接收用户所有的请求 2. 处理器映射器`HandlerMapping` 1. 查找用户的请求与业务处理的映射 2. tomcat服务器启动时,会加载所有的`@RequestMapping`注解,将其中路径与方法进行绑定。 3. 内部使用`Map`保存,`url`做Key,`Method`全限定名做Value 3. 处理器适配器`HandlerAdapter` 1. 在众多处理器中挑选合适的处理器去执行业务。 4. 视图解析器`ViewRsolver` 1. 实现页面路径拼接(根据`thmeleaf`引擎拼出完整路径) ![image-20210430120258548](README.assets/image-20210430120258548.png) * 步骤 1. 用户发起`请求`,`DispatchServlet`拦截到请求 2. 前端控制器将路径交由`HandlerMapping`解析 3. 处理器映射器找到方法,传回前端控制器 4. 前端控制器将方法交给`HandlerAdapter`(MVC针对不同的配置文件有专门的处理器(运行程序的机制))进行调用(无需人为干预) 5. 当挑选到合适的处理器后,程序开始真正的执行(`Controller`->`Service`->`Mapper`),执行完成返回`ModelAndView`对象 6. 前端控制器获得`ModelAndView`对象,交给`ViewHandler`解析`View`对象,拼接出页面全路径 7. 解析`Model`,将`Model`数据填充到页面中,这一步叫做`视图渲染`,然后交给前端控制器。 8. 前端控制器将完整页面`响应`给用户展现。 ### 简化后端传值跳转 直接将`View`作为字符串传出,数据使用`model`参数进行`addAttribute` ```java @RequestMapping("/user") public String toUser(Model model){ model.addAttribute("id", 101); model.addAttribute("name", "小明"); return "user"; } ``` ### 参数接收 * 输入框 ```java @RequestMapping("/addUser") public String addUser(Integer id,String name){ //内部通过传过来的参数名调用getParameter,类型使用参数类型 System.out.println(id+":"+name); return "success"; } ``` **设定默认值/必填项**:`@RequestParam` ```java @RequestMapping("/addUser") public String addUserParam(@RequestParam(name="id",defaultValue="10010") Integer id, String name){ ... return "success"; } ``` * name/value——接收参数名 * required——是否必填 * defaultValue——默认值 --- * 多选框 **细节:**当前端`name`一样时,SpringMVC会用`,`分割 直接使用数组类型/可变参数,参数为`name`值进行接取,会自动地放入数组。 ```java @RequestMapping("/addUser") public String addUserParam(@RequestParam Integer id, String name,String... hobbies){ //内部通过传过来的参数名调用getParameter,类型使用参数类型 System.out.println(id+":"+name); for (String hobby : hobbies) { System.out.println("hobby = " + hobby); } return "success"; } ``` --- * 对象接收参数 ```java public String addUser(User user){ System.out.println("user = " + user); return "success"; } ``` 原理:在调用方法时,会内部调用了属性的`get`方法 ### 对象参数传递 说明:当后端对象中有属性为引用对象时,前端可以设置`name`为`对象.属性`来为属性赋值。 ```java @Data @Accessors(chain = true) public class User { ... private Dog dog; } ``` ```java @Data @Accessors(chain = true) public class Dog { private Integer id; private String name; } ``` ```html 宠物ID: 宠物姓名: ``` ### 转发与重定向 转发: 1. 一次请求 2. 可携带数据 3. 关键字`forward:` 重定向: 1. 两次请求 2. 不可携带数据 3. 关键字`redirect:` ### restFul 说明:将参数当作请求路径传输 使用:在`requestMapping`的url中将参数写成`{参数名}`的形式,如: * 请求示例:`/getUser/1/tom/18` * 映射示例:`/getUser/{id}/{name}/{age}` **补充:**如果传入的参数都是某一对象的属性,则方法可以使用对象作为参数来接取。 --- 将增删改查通过`get`、`put`、`post`、`delete`四种请求方式来进行分类,简化请求路径 Spring预设的就有`@PutMapping`等注解 ### 返回JSON数据 说明:使用`@ResponseBody`注解可以使返回数据自动转换为JSON类型的数据