[TOC]
SpringBoot入门
HelloWorld项目
首先创建maven工程
引入依赖:
1 2 3 4 5 6 7 8 9 10 11 12 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.8.RELEASE</version > </parent > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies >
创建主程序:
1 2 3 4 5 6 7 @SpringBootApplication public class MainApplication { public static void main (String[] args) { SpringApplication.run(MainApplication.class, args); } }
编写业务:
1 2 3 4 5 6 7 8 @RestController public class HelloController { @RequestMapping("/") public String hander01 () { return "Hello Spring Boot !" ; } }
直接运行访问http://localhost:8080/
得到结果。
了解自动配置
SpringBoot特点
依赖管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 依赖管理 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.8.RELEASE</version > </parent > 他的父项目 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 2.3.8.RELEASE</version > </parent > 几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
1 2 3 4 5 6 7 8 9 10 11 12 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.8.RELEASE</version > <scope > compile</scope > </dependency >
1 2 1、引入依赖默认都可以不写版本 2、引入非版本仲裁的jar,要写版本号。
1 2 3 4 5 1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。 2、在当前项目里面重写配置 <properties > <mysql.version > 5.1.43</mysql.version > </properties >
自动配置
1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > <version > 2.3.8.RELEASE</version > <scope > compile</scope > </dependency >
自动配好SpringMVC
引入SpringMVC全套组件
自动配好SpringMVC常用组件(功能)
自动配好Web常见功能,如:字符编码问题
SpringBoot帮我们配置好了所有web开发的常见场景
默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
无需以前的包扫描配置
想要改变扫描路径,@SpringBootApplication(scanBasePackages="com.kanxz")
1 2 3 4 5 @SpringBootApplication 等同于 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.kanxz.boot")
各种配置拥有默认值
默认配置最终都是映射到某个类上,如:MultipartProperties
配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
按需加载所有自动配置项
非常多的starter
引入了哪些场景这个场景的自动配置才会开启
SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
…
容器功能
组件添加
@Configuration
Full模式与Lite模式
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #################Configuration使用示例#################### @Configuration(proxyBeanMethods = true) public class MyConfig { @Bean public User user01 () { User zhangsan = new User("zhangsan" , 18 ); zhangsan.setPet(tomcatPet()); return zhangsan; } @Bean("tom") public Pet tomcatPet () { return new Pet("tomcat" ); } } ###################@Configuration测试代码如下########################## @SpringBootApplication public class MainApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tom" , Pet.class); Pet tom02 = run.getBean("tom" , Pet.class); System.out.println("组件:" +(tom01 == tom02)); MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean); User user = bean.user01(); User user1 = bean.user01(); System.out.println(user == user1); User user01 = run.getBean("user01" , User.class); Pet tom = run.getBean("tom" , Pet.class); System.out.println("用户的宠物:" +(user01.getPet() == tom)); } }
@Import
1 2 3 4 5 6 7 8 * @Import({User.class, DBHelper.class}) * 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名 */ @Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false) public class MyConfig {}
@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
详细用法:https://juejin.cn/post/6844903903860015111
原生配置文件引入
@ImportResource
1 2 3 4 5 6 7 8 9 10 11 12 ==============beans.xml============== <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="lisi" class ="com.kanxz.boot.bean.User" > <property name ="name" value ="lisi" /> <property name ="age" value ="20" /> </bean > </beans >
1 2 @ImportResource("classpath:beans.xml") public class MyConfig {}
配置绑定
使用Java读取到properties文件中的内容,并且把它封装到JavaBean中
application.properties:
1 2 mycar.brand =BYD mycar.price =100000
@Component + @ConfigurationProperties
1 2 3 4 5 6 7 @Component @ConfigurationProperties(prefix = "mycar") public class Car { private String brand; private Integer price; ... }
@EnableConfigurationProperties + @ConfigurationProperties
该方法可注册第三方bean
1 2 3 4 5 6 @ConfigurationProperties(prefix = "mycar") public class Car { private String brand; private Integer price; ... }
1 2 3 4 @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(Car.class) public class MyConfig {}
controller类测试:
1 2 3 4 5 6 7 @Autowired private Car car;@RequestMapping("/car") public String car () { return car.toString(); }
配置文件
yaml
基本用法
key: value;kv之间有空格
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
'#'表示注释
字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义
数据类型
1 2 3 4 5 6 行内写法: k: {k1:v1 ,k2:v2 ,k3:v3 }k: k1: v1 k2: v2 k3: v3
1 2 3 4 5 6 行内写法: k: [v1 ,v2 ,v3 ]k: - v1 - v2 - v3
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Data public class Person { private String userName; private Boolean boss; private Date birth; private Integer age; private Pet pet; private String[] interests; private List<String> animal; private Map<String, Object> score; private Set<Double> salary; private Map<String, List<Pet>> allPets; } @Data public class Pet { private String name; private Double weight; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 person: username: 张三 boss: true birth: 2002 /2/22 age: 18 pet: name: 大黄 weight: 10 interests: [篮球 , 游泳 ] animal: - 小黑 - 小白 score: english: 90 math: 90 salary: [111 , 222 ] allPets: sick: - {name: tom } - {name: jerry , weight: 47 } healthy: [{name: mario , weight: 47 }]
配置提示
自定义的类和配置文件绑定一般没有提示,需要添加:
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency >
Web开发
简单功能分析
静态资源目录
只要静态资源放在类路径下: /static
(or /public
or /resources
or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
改变默认的静态资源路径
1 2 3 4 5 6 7 8 spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/ ]
欢迎页支持
静态资源路径下 index.html
可以配置静态资源路径
但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
1 2 3 4 5 6 spring: resources: static-locations: [classpath:/haha/ ]
自定义 Favicon
favicon.ico 放在静态资源目录下即可。
请求参数处理
普通参数与基本注解
注解
@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GetMapping("/car/{id}/owner/{username}") public Map<String, Object> getCar (@PathVariable("id") int id, @PathVariable("username") String name, @PathVariable Map<String, String> kv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map<String, String> header) { Map<String, Object> map = new HashMap<>(); map.put("id" , id); map.put("username" , name); map.put("kv" , kv); map.put("userAgent" , userAgent); map.put("header" , header); return map; }
请求:http://localhost:8080/car/1/owner/zhangsan 可得到相应结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GetMapping("/user") public Map<String, Object> getUser (@RequestParam("age") int age, @RequestParam("name") String name, @RequestParam("interest") List<String> interest, @RequestParam Map<String, String> params, @CookieValue("Idea-90db23ce") Cookie cookie) { HashMap<String, Object> map = new HashMap<>(); map.put("age" , age); map.put("name" , name); map.put("interest" , interest); map.put("params" , params); System.out.println(cookie.getName() + "--->" + cookie.getValue()); return map; }
请求:http://localhost:8080/user?age=18&name=zhangsan&interest=ball&interest=music
可以得到:{“interest”:[“ball”,“music”],“name”:“张三”,“params”:{“age”:“18”,“name”:“张三”,“interest”:“ball”},“age”:18}
控制台输出:Idea-90db23ce—>5482e6f3-30e6-4f4e-873b-0aa04d909edc
1 2 3 4 5 6 7 <form action ="/save" method ="post" > <label for ="username" > username:</label > <input type ="text" name ="username" id ="username" > <br > <label for ="pd" > password:</label > <input type ="password" name ="pd" id ="pd" > <br > <input type ="submit" value ="save" > </form >
1 2 3 4 5 6 7 @PostMapping("/save") public Map<String, String> save (@RequestBody String content) { HashMap<String, String> map = new HashMap<>(); map.put("content" , content); return map; }
输入表单内容,点击按钮,可得到结果:{“content”:“username=123456&pd=654321”}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Controller public class RequestController { @GetMapping("/go") public String go (HttpServletRequest servletRequest) { servletRequest.setAttribute("msg" , "success" ); servletRequest.setAttribute("code" , 200 ); return "forward:/msg" ; } @ResponseBody @GetMapping("/msg") public Map<String, Object> getMsg (@RequestAttribute("msg") String msg, @RequestAttribute("code") int code, HttpServletRequest servletRequest) { HashMap<String, Object> map = new HashMap<>(); map.put("msg" , msg); map.put("code" , code); System.out.println(servletRequest.getAttribute("msg" )); System.out.println(servletRequest.getAttribute("code" )); return map; } }
访问:http://localhost:8080/go,得到:{“msg”:“success”,“code”:200}
控制台输出:
success
200
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Configuration public class WebConfig { @Bean public WebMvcConfigurer webMvcConfigurer () { return new WebMvcConfigurer() { @Override public void configurePathMatch (PathMatchConfigurer configurer) { UrlPathHelper helper = new UrlPathHelper(); helper.setRemoveSemicolonContent(false ); configurer.setUrlPathHelper(helper); } }; } } @GetMapping("/car/{path}") public Map<String, Object> carSell (@PathVariable("path") String path, @MatrixVariable("low") int low, @MatrixVariable("brand") List<String> brand) { HashMap<String, Object> map = new HashMap<>(); map.put("path" , path); map.put("low" , low); map.put("brand" , brand); return map; } 得到结果:{"path" :"sell" ,"low" :34 ,"brand" :["byd" ,"audi" ,"yd" ]} @GetMapping("/boss/{bossId}/{empId}") public Map<String, Object> bossAge (@PathVariable("bossId") String bossId, @PathVariable("empId") String empId, @MatrixVariable(value = "age", pathVar = "bossId") int bossAge, @MatrixVariable(value = "age", pathVar = "empId") int empAge) { HashMap<String, Object> map = new HashMap<>(); map.put("bossId" , bossId); map.put("bossAge" , bossAge); map.put("empId" , empId); map.put("empAge" , empAge); return map; } 得到结果:{"empId" :"2" ,"bossAge" :20 ,"bossId" :"1" ,"empAge" :30 }
视图解析与模板引擎
模板引擎-Thymeleaf
官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
基本语法
1、表达式
表达式名字
语法
用途
变量取值
${…}
获取请求域、session域、对象等值
选择变量
*{…}
获取上下文对象值
消息
#{…}
获取国际化等值
链接
@{…}
生成链接
片段表达式
~{…}
jsp:include 作用,引入公共页面片段
2、字面量
文本值: ‘one text’ , ‘Another one!’ **,…**数字: 0 , 34 , 3.0 , 12.3 **,…**布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格
3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|
4、数学运算
运算符: + , - , * , / , %
5、布尔运算
运算符: and , or
一元运算: ! , not
6、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )
等式: **== , != ( eq , ne ) **
7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
8、特殊操作
无操作: _
设置属性值-th:attr
设置单个值
1 2 3 4 5 6 <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 >
设置多个值
1 <img src ="../../images/gtvglogo.png" th:attr ="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
以上两个的代替写法 th:xxxx
1 2 <input type ="submit" value ="Subscribe!" th:value ="#{subscribe.submit}" /> <form action ="subscribe.html" th:action ="@{/subscribe}" >
所有h5兼容的标签写法
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
迭代
1 2 3 4 5 <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 >
1 2 3 4 5 <tr th:each ="prod,iterStat : ${prods}" th:class ="${iterStat.odd}? 'odd'" > <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 >
条件运算
1 2 3 <a href ="comments.html" th:href ="@{/product/comments(prodId=${prod.id})}" th:if ="${not #lists.isEmpty(prod.comments)}" > view</a >
1 2 3 4 5 <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使用
引入Starter
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
页面开发测试
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Hello</title > </head > <body > <h1 th:text ="${msg}" > Hello</h1 > <h2 > <a href ="www.baidu.com" th:href ="${link}" > google</a > </h2 > </body > </html >
1 2 3 4 5 6 7 8 9 10 @Controller public class ViewTestController { @GetMapping("/view") public String viewTest (HttpServletRequest servletRequest) { servletRequest.setAttribute("msg" , "你好~" ); servletRequest.setAttribute("link" , "https://www.google.com" ); return "hello" ; } }
拦截器
HandlerInterceptor 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 @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" ,"请先登录" ); 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); } }
配置拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**" ) .excludePathPatterns("/" ,"/login" ,"/css/**" ,"/fonts/**" ,"/images/**" ,"/js/**" ); } }
文件上传
页面表单
1 2 3 4 <form method ="post" action ="/upload" enctype ="multipart/form-data" > <input type ="file" name ="file" > <br > <input type ="submit" value ="提交" > </form >
文件上传代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @PostMapping("/upload") public String upload ( @RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("avatar") MultipartFile avatar, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("\n\temail={}\n\tusername={}\n\tavatar={}\n\tphotos={}" , email, username, avatar.getSize(), photos.length); if (!avatar.isEmpty()) { String filename = avatar.getOriginalFilename(); avatar.transferTo(new File("C:\\Users\\MI\\Desktop\\test\\" + filename)); } if (photos.length != 0 ) { for (var photo: photos) { if (!photo.isEmpty()) { String filename = photo.getOriginalFilename(); photo.transferTo(new File("C:\\Users\\MI\\Desktop\\test\\photos\\" + filename)); } } } return "main" ; }
数据访问
SQL
数据源的自动配置-HikariDataSource
导入JDBC场景
1 2 3 4 5 6 7 8 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jdbc</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency >
修改配置项
1 2 3 4 5 6 spring: datasource: url: jdbc:mysql://localhost:3306/user_db?serverTimezone=Asia/Shanghai username: root password: driver-class-name: com.mysql.cj.jdbc.Driver
使用Druid数据源
druid官方github地址
https://github.com/alibaba/druid
引入依赖
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.2.4</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.4</version > </dependency >
配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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.* filters: stat,wall stat-view-servlet: enabled: true login-username: admin login-password: admin resetEnable: false web-stat-filter: enabled: true urlPattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' filter: stat: slow-sql-millis: 1000 logSlowSql: true enabled: true wall: enabled: true config: drop-table-allow: false
SpringBoot配置示例
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
配置项列表https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
MyBatis
https://github.com/mybatis
1 2 3 4 5 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.1.4</version > </dependency >
使用注解
1 2 3 4 5 6 7 @Mapper public interface CityMapper { @Select("select * from city where id=#{id}") public City getById (Long id) ; }
整合 MyBatis-Plus 完成CRUD
mybatis plus 官网
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.1</version > </dependency >
只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
NoSQL
Redis
安装教程:
https://www.cnblogs.com/hunanzp/p/12304622.html
导入依赖:
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
配置:
1 2 3 4 redis: host: 服务器地址 password: 密码 ......
测试:
1 2 3 4 5 6 7 8 9 10 @Autowired StringRedisTemplate redisTemplate; @Test public void testRedisTemplate () { ValueOperations<String, String> operations = redisTemplate.opsForValue(); operations.set("Hello" , "World" ); assertEquals("World" , operations.get("Hello" )); log.debug("【Redis】:set 'Hello' -> '{}' " , operations.get("Hello" )); }
学习自: