自动装配原理
SpringBoot特点
优点:
(1)创建独立Spring应用
(2)内嵌web服务器
(3)自动start依赖,简化构建配置
(4)自动配置Spring以及第三方功能
(5)提供生产级别的监控、健康检测以及外部化配置
(6)无代码生成、无需编写XML
缺点:
(1)迭代快,需要时刻关注变化
(2)封装太深,内部原理负责,不容易精通
微服务:微服务是一种架构风格,一个应用拆分为一组小型服务;每个服务运行在自己的进程中,也就是可以独立部署和升级;服务之间使用轻量级HTTP交互;服务围绕业务功能拆分;可以由全自动部署机制独立部署;去中心化、服务自治。服务可以使用不同的语言、不同的存储技术。
1.依赖管理
父项目做依赖管理
依赖管理 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> </parent> 他的父项目 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.4.RELEASE</version> </parent> 几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
开发导入starter场景启动器
1、见到很多 spring-boot-starter-* : *就某种场景 2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入 3、SpringBoot所有支持的场景 https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter 4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。 5、所有场景启动器最底层的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
无需关注版本号,自动版本仲裁:1.引入依赖默认都可以不写版本;2.引入非版本仲裁的jar,要写版本号
可以修改默认版本号
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。 2、在当前项目里面重写配置 <properties> <mysql.version>5.1.43</mysql.version> </properties>
2.自动配置
自动配置好Tomcat
引入Tomcat依赖
配置Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
自动配置好SpringMVC
引入SpringMVC全套组件
自动配置好SpringMVC常用组件
自动配置好Web常见功能,如:字符编码问题
SpringBoot帮我们配置好了所有web开发的常用场景
默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
无需以前的包扫描配置
想要改变扫描路径,@SpringBootApplication(scanBasePackages=”com.atgui“)或者@ComponentScan指定扫描路径
@SpringBootApplication 等同于 @SpringBootConfiguration//此类为配置类 @EnableAutoConfiguration//开启扫描 @ComponentScan("com.atguigu.boot")//扫描路径
自动配置原理入门
引导加载自动配置类
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication{}
1.@SpringBootConfiguration 代表当前是个配置类
2.@ComponentScan 指定扫描哪些
3.@EnableAutoConfiguration
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}
1.@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件 public @interface AutoConfigurationPackage {} //利用Registrar给容器中导入一系列组件 //将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
2.@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件 2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类 3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件 4、从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
配置文件
1.properties
同以前的properties用法
2.yaml
基本语法:
key:value;kv之间有空格
大小写敏感
使用缩进表示表示层级关系
缩进不允许使用tab,只允许空格
‘#’表示注释
字符串无需加引号
# yaml表示以上对象 person: userName: zhangsan boss: false birth: 2019/12/12 20:12:33 age: 18 pet: name: tomcat weight: 23.4 interests: [篮球,游泳] animal: - jerry - mario score: english: first: 30 second: 40 third: 50 math: [131,140,148] chinese: {first: 128,second: 136} salarys: [3999,4999.98,5999.99] allPets: sick: - {name: tom} - {name: jerry,weight: 47} health: [{name: mario,weight: 47}]
Web开发
简单功能分析
1.静态资源访问
只要静态资源放在类路径下:called/static(or / public / or / resources or / META-INF / resources)
访问:当前项目根路径/ + 静态资源名
原理:静态映射/**
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404.
改变默认的静态资源路径:
spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/]
2.静态资源访问前缀
默认无前缀
spring: mvc: static-path-pattern: /res/**
当前项目 + static-path-pattern + 静态资源名 = 静态资源文件下找
3.欢迎页支持
静态资源下 index.html
可以配置静态资源路径
但是不可以配置静态资源的访问前缀。否则导致index.html
spring: # mvc: # static-path-pattern: /res/** 这个会导致welcome page功能失效 resources: static-locations: [classpath:/haha/]
请求参数处理
1.请求映射
(1)rest使用与原理
@xxxMaping
Rest风格支持(使用http请求方式动词来表示对资源的操作)
@RequestMapping(value = "/user",method = RequestMethod.GET) public String getUser(){ return "GET-张三"; } @RequestMapping(value = "/user",method = RequestMethod.POST) public String saveUser(){ return "POST-张三"; } @RequestMapping(value = "/user",method = RequestMethod.PUT) public String putUser(){ return "PUT-张三"; } @RequestMapping(value = "/user",method = RequestMethod.DELETE) public String deleteUser(){ return "DELETE-张三"; } @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); } //自定义filter @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter(){ HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter(); methodFilter.setMethodParam("_m"); return methodFilter; }
Rest原理(表单提交要使用REST的时候)
表单提交会带上_method = PUT
请求过来被HiddenHttpMethodFilter拦截
请求是否正常,并且是POST
获取到_method的值
兼容以下请求:PUT.DELETE.PATCH
原生request(post),包装模式requestWrapper重写了getMethod方法,返回的是传入的值。
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requestWrapper的。
普通参数与基本注解
(1)注解
@PathVariable @RequestHeader @ModelAttribute @RequestParam @CookieValue @RequestBody
@MatrixVariable
@RestController public class ParameterTestController {//1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd //2、SpringBoot默认是禁用了矩阵变量的功能 // 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。 // removeSemicolonContent(移除分号内容)支持矩阵变量的 //3、矩阵变量必须有url路径变量才能被解析 @GetMapping("/cars/{path}") public Map carsSell(@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand, @PathVariable("path") String path){ Map<String,Object> map = new HashMap<>(); map.put("low",low); map.put("brand",brand); map.put("path",path); return map; } // /boss/1;age=20/2;age=10 @GetMapping("/boss/{bossId}/{empId}") public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){ Map<String,Object> map = new HashMap<>(); map.put("bossAge",bossAge); map.put("empAge",empAge); return map; } }
(2) Servlet API:
WebRequset ServletRequset MultipartRequest HttpSession 等
(3)复杂参数
Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes(重定向数据)、ServletResponse(response)、SessionStatus
Map<String,Object> map, Model model, HttpServletRequest request 都是可以给request域中放数据, request.getAttribute();
(4)自定义对象参数
可以自动类型转换与格式化,可以级联封装
/** * 姓名: <input name="userName"/> <br/> * 年龄: <input name="age"/> <br/> * 生日: <input name="birth"/> <br/> * 宠物姓名:<input name="pet.name"/><br/> * 宠物年龄:<input name="pet.age"/> */ @Data public class Person { private String userName; private Integer age; private Date birth; private Pet pet; } @Data public class Pet { private String name; private String age; } result
数据响应与内容协商
响应JSON
(1)jackson.jar + @ResponseBody
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> web场景自动引入了json场景 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
给前端自动返回json数据
视图解析器与模板引擎
视图解析:SpringBoot默认不支持JSP,需要引入第三方模板引擎技术实现技术渲染
视图解析
1、视图解析原理流程
1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
4、processDispatchResult 处理派发结果(页面改如何响应)
- 1、render(mv, request, response); 进行页面渲染逻辑
- 1、根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】
- 1、所有的视图解析器尝试是否能根据当前返回值得到View对象
- 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
- 3、ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
- 4、view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
- RedirectView 如何渲染【重定向到一个页面】
- 1、获取目标url地址
- 2、response.sendRedirect(encodedURL);
视图解析:
- 返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);
- 返回值以 redirect: 开始: new RedirectView() --》 render就是重定向
- 返回值是普通字符串: new ThymeleafView()--->
模板引擎-Thymeleaf
(1)基本语法
(2)设置属性值-th:attr
设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}"> <fieldset> <input type="text" name="email" /> <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/> </fieldset> </form>
设置多个值
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
其他写法
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/> <form action="subscribe.html" th:action="@{/subscribe}">
(3)迭代
<tr th:each="prod : ${prods}"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> </tr>
(4)条件运算
<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}"> <p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p> <p th:case="*">User is some other thing</p> </div>
thymeleaf使用
(1)引入starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
构建后台管理系统
(1)项目创建
thymeleaf、web-starter、devtools、lombok
(2)静态资源处理
自动配置好,我们只需要把所有静态资源放到static文件下
(3)路径构建
th:action="@{/login}"
(4)模板抽取
th:insert/replace/include
(5)页面跳转
@PostMapping("/login") public String main(User user, HttpSession session, Model model){ if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){ //把登陆成功的用户保存起来 session.setAttribute("loginUser",user); //登录成功重定向到main.html; 重定向防止表单重复提交 return "redirect:/main.html"; }else { model.addAttribute("msg","账号密码错误"); //回到登录页面 return "login"; } }
(6)数据渲染
@GetMapping("/dynamic_table") public String dynamic_table(Model model){ //表格内容的遍历 List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); model.addAttribute("users",users); return "table/dynamic_table"; }
<table class="display table table-bordered" id="hidden-table-info"> <thead> <tr> <th>#</th> <th>用户名</th> <th>密码</th> </tr> </thead> <tbody> <tr class="gradeX" th:each="user,stats:${users}"> <td th:text="${stats.count}">Trident</td> <td th:text="${user.userName}">Internet</td> <td >[[${user.password}]]</td> </tr> </tbody> </table>
拦截器
(1)HandlerInterceptor接口
/** * 登录检查 * 1、配置好拦截器要拦截哪些请求 * 2、把这些配置放在容器中 */ @Slf4j public class LoginInterceptor implements HandlerInterceptor { /** * 目标方法执行之前*/ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("preHandle拦截的请求路径是{}",requestURI); //登录检查逻辑 HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser"); if(loginUser != null){ //放行 return true; } //拦截住。未登录。跳转到登录页 request.setAttribute("msg","请先登录"); // re.sendRedirect("/"); request.getRequestDispatcher("/").forward(request,response); return false; } /** * 目标方法执行完成以后*/ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle执行{}",modelAndView); } /** * 页面渲染以后*/ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion执行异常{}",ex); } }
(2)配置拦截器
/** * 1、编写一个拦截器实现HandlerInterceptor接口 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors) * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】 */ @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") //所有请求都被拦截包括静态资源 .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求 } }
(3)拦截器原理
1.根据当前请求,找到HandlerExecutionChain(可以处理请求的handler以及handler的所有拦截器)
2.先来顺序所有拦截器的preHandle方法
如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandler
如果当前拦截器返回为false。直接,倒序执行所有已经执行了的拦截器afterCompletion。
3.如果任何一个拦截器返回false。直接跳出不执行目标方法。
4.所有拦截器都返回True。执行目标方法
5.倒序执行所有拦截器的postHandle
6.前面的任何步骤出现异常都会直接倒序触发afterCompletion
7.页面成功渲染完成以后,也会倒序触发afterCompletion
文件上传
(1)页面表单
<form method="post" action="/upload" enctype="multipart/form-data"> <input type="file" name="file"><br> <input type="submit" value="提交"> </form>
(2)文件上传代码
/** * MultipartFile 自动封装上传过来的文件 * @param email * @param username * @param headerImg * @param photos * @return */ @PostMapping("/upload") public String upload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImg") MultipartFile headerImg, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("上传的信息:email={},username={},headerImg={},photos={}", email,username,headerImg.getSize(),photos.length); if(!headerImg.isEmpty()){ //保存到文件服务器,OSS服务器 String originalFilename = headerImg.getOriginalFilename(); headerImg.transferTo(new File("H:\\cache\\"+originalFilename)); } if(photos.length > 0){ for (MultipartFile photo : photos) { if(!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File("H:\\cache\\"+originalFilename)); } } } return "main"; }
异常处理
(1)错误处理
默认规则:
默认情况下,Spring Boot 提供/error 处理所有错误的映射
对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个”whitelable“错误视图,以HTML格式呈现相同数据。
数据访问
SQL
(1)数据源的自动配置-HikariDataSource
导入JDBC
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
修改配置项
spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver
使用Druid数据源
引入druid-starter
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
配置实例
spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver druid: aop-patterns: com.atguigu.admin.* #监控SpringBean filters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙) stat-view-servlet: # 配置监控页功能 enabled: true login-username: admin login-password: admin resetEnable: false web-stat-filter: # 监控web enabled: true urlPattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' filter: stat: # 对上面filters里面的stat的详细配置 slow-sql-millis: 1000 logSlowSql: true enabled: true wall: enabled: true config: drop-table-allow: false
整合MyBatis操作
引入依赖
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency>
配置模式
全局配置文件
SqlSessionFactory:自动配置好了
SqlSession:自动配置了SqlSessionTemplate组合了SqlSession
@Import(AutoConfigurationMapperScannerRegistrar.class)
Mapper:只要我们写的操作MyBatis的接口标准了@Mapper就会被自动扫描进来
# 配置mybatis规则 mybatis: config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置 mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置 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.atguigu.admin.mapper.AccountMapper"> <!-- public Account getAcct(Long id); --> <select id="getAcct" resultType="com.atguigu.admin.bean.Account"> select * from account_tbl where id=#{id} </select> </mapper>
注解模式
@Mapper public interface CityMapper { @Select("select * from city where id=#{id}") public City getById(Long id); public void insert(City city); }
CRUD功能
@GetMapping("/user/delete/{id}") public String deleteUser(@PathVariable("id") Long id, @RequestParam(value = "pn",defaultValue = "1")Integer pn, RedirectAttributes ra){ userService.removeById(id); ra.addAttribute("pn",pn); return "redirect:/dynamic_table"; } @GetMapping("/dynamic_table") public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){ //表格内容的遍历 // response.sendError // List<User> users = Arrays.asList(new User("zhangsan", "123456"), // new User("lisi", "123444"), // new User("haha", "aaaaa"), // new User("hehe ", "aaddd")); // model.addAttribute("users",users); // // if(users.size()>3){ // throw new UserTooManyException(); // } //从数据库中查出user表中的用户进行展示 //构造分页参数 Page<User> page = new Page<>(pn, 2); //调用page进行分页 Page<User> userPage = userService.page(page, null); // userPage.getRecords() // userPage.getCurrent() // userPage.getPages() model.addAttribute("users",userPage); return "table/dynamic_table"; }
NoSQL
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以作为数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串,散列,列表,集合,有序集合与范围查询。Redis内置了复制,LUA脚本,LRU驱动事件,事务和不同级别的磁盘持久化,并通过Redis哨兵和自动分区提供高可用性。
Redis自动配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
单元测试
引入测试依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
测试实例:
@SpringBootTest class Boot05WebAdminApplicationTests { @Test void contextLoads() { } }
断言
(1)简单断言
assertEquals 判断两个对象或两个原始类型是否相等
assertNotEquals 判断两个对象或两个原始类型是否不相等
assertSame 判断两个对象引用是否指向相同的对象
assertNotSame 判断两个对象引用是否指向不同的对象
assertTure 判断给定的布尔值是否为true
assertFalse 判断给定的布尔值是否为false
assertNull 判断给定的对象引用是否为null
assertNotNull 判断给定的对象引用是否不为null
(2)数组断言
通过assertArrayEquals方法来判断两个对象或者原始类型的数组是否相等
@Test @DisplayName("array assertion") public void array() { assertArrayEquals(new int[]{1, 2}, new int[] {1, 2}); }
标签:知识点,SpringBoot,spring,boot,new,public,starter,String From: https://www.cnblogs.com/baifeili/p/16536745.html