目录
springboot中mapper的加载接口代理对象有两种方法
springboot 是 spring 快速开发脚手架,通过约定大于配置的方式,快速构建和启动 spring 项目.
springboot根据我们项目中所引入的依赖,比如引入了springmvc构件,就会判断出是要进行springmvc的web开发,就会把springmvc的相关基本配置自动配置好了,不需要我们在xml中配置。 比如配置前端控制器DispatcherServlet、配置视图解析器、配置静态资源访问、处理器映射器、处理器适配器等一系列组件,
spring 的缺点:
1.复杂的配置
项目各种配置是开发时的损耗, 写配置挤占了写应用程序逻辑的时间。
2.混乱的依赖管理
项目的依赖管理非常的繁琐。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这是一个棘手的问题。并且,一旦选错了依赖的版本,随之而来的就是各种的不兼容的bug
spring boot 可以解决上面 2 个问题
快速开发 spring 应用的框架
内嵌 tomcat 和 jetty 容器,不需要单独安装容器,使用main方法就可以 直接启动发布一个 web应用
简化 maven 配置,通过继承 parent构件 ,一站式引入需要的各种依赖(启动器),简化依赖管理
通过 约定大约配置的方式可以实现基于注解的零配置思想
和各种流行框架, spring web mvc , mybatis , spring cloud 无缝整合
官网
总结
spring boot 是 spring 快速开发脚手架,通过约定大于配置,优化了混乱的依赖管理,和复杂的配置,让我们用java -jar方式,运行启动 java web 项目
需求:创建 HelloController, 在页面中打印 hello spring boot…
先新建一个空的工程:
设置 jdk 版本 :
新建一个 module :
使用 maven 来构建
然后填写项目坐标
项目结构:
SpringBoot 提供了一个名为 spring-boot-starter-parent 的构件,里面已经对各种常用依赖(并非全部)的版本进行了管理,我们的项目需要以这个项目为父工程,这样我们就不用操心依赖的版本问题了,需要什么依赖,直接引入坐标即可!
为了让 SpringBoot 帮我们完成各种自动配置,我们必须引入 SpringBoot 提供的自动配置依赖,我们称为启动器 。因为我们是web 项目,这里我们引入 web 启动器
默认情况下, maven 工程的 jdk 版本是 1.5 ,而开发使用的是 11 ,因此这里我们需要修改 jdk 版本
4.0.0
com.bowei.springboot
springbootdemo
1.0-SNAPSHOT
11
org.springframework.boot
spring-boot-starter-parent
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-web
注意的是,我们并没有在这里指定版本信息。因为 SpringBoot 的父工程已经对版本进行了管理了。
Spring Boot 项目通过 main函数来启动的,我们需要创建一个启动类,编写 main 函数,这个启动类是固定的,后面会通过工具自动生成。
下面,就可以像以前那样开发SpringMVC的项目
运行 main函数,查看控制台,可以看到监听的端口信息
1 )监听的端口是 8080
2 ) SpringMVC 的映射路径是: /
3 ) /hello 路径已经映射到了 HelloController 中的 hello() 方法
打开页面访问: http://localhost:8080/hello
上面demo没有任何的配置,就可以实现一个SpringMVC的项目了,快速、高效。
但是没有任何的 xml ,那么如果要配置一个 Bean 该怎么办?比如我们要配置一个数据库连接池,
以前xml中需要这样配置
在Spring3.0开始,Spring官方就已经开始推荐使用java配置来代替传统的xml配置了
spring 全注解配置主要靠 java 类和一些注解,比较常用的注解有
@Configuration :声明一个类作为配置类,代替xml文件
@Bean :声明在方法上,将方法的返回值作为对象反转到 Spring 容器中管理,代替 标签
@value :属性注入
@PropertySource :指定外部属性文件,
下面测试spring全注解配置是如何读取到属性文件
首先引入 Druid 连接池依赖
创建一个 jdbc.properties 文件,编写 jdbc 属性
java 配置来实现连接池配置
在 HelloController 中测试
Debug运行并查看属性注入成功了
这是spring的全注解配置实现的一个bean对象的创建和属性的注入,下面看下spring boot提供的属性注入方式。
上述通过 java 配置方式实现了连接池的配置。不过属性注入使用的是 @Value注解。这种方式它只能注入基本类型值。
在 SpringBoot 中,提供了一种新的属性注入方式,支持各种 java 基本数据类型及复杂类型的注入
1 )新建一个属性读取类,用来进行属性注入:
新建属性读取类JdbcProperties,通过添加注解 @ConfigurationProperties ( prefix = “jdbc” )读取application.properties中属性值,将来在其他地方就可以注入JdbcProperties这个对象,从中获取属性值。
将jdbc.properties改成application.properties
2)在JdbcConfig2中注入JdbcProperties对象,获取属性值给数据源,最后Bean注解将数据源反转给spring容器,将来在其他地方可以通过注入方式使用dataSource数据源。
通过 @EnableConfigurationProperties(JdbcProperties.class) 来声明要使用 JdbcProperties 这个类的对象
三种方式注入JdbcProperties
@Autowired 注入
构造函数注入
声明有 @Bean 的方法参数注入
上述采用的是第三种属性注入方式
debug
觉得这种方式似乎更麻烦了,事实上这种方式有更强大的功能,也是 SpringBoot 推荐的注入方式。两者对比关系
上述分别通过两种@Value和@ConfigurationProperties注解来读取properties文件,给Properties类中对应的属性赋值
@ConfigurationProperties优势:
Relaxed binding:松散绑定
不严格要求属性文件中的属性名与成员变量名一致。支持驼峰,中划线,下划线等等转换,甚至支持对象引导。比如:user.friend.name :代表的是 user 对象中的 friend 属性中的 name 属性,显然 friend 也是对象。@value 注解就难以完成这样的注入方式。
meta-data support :元数据支持,帮助 IDE 生成属性提示(写开源框架会用到)。
如果一段属性只有一个 Bean 需要使用,我们无需将其注入到一个类( JdbcProperties )中。而是直接在需要的地方声明即可。
不再需要属性读取类,@ConfigurationProperties 这个注解是放在需要的方法上,SpringBoot会自动把相关属性通过set方法注入到bean对象中。
我们直接把 @ConfigurationProperties(prefix = “jdbc”) 声明在需要使用的 @Bean 的方法上,然后SpringBoot 就会自动调用这个Bean(此处是DataSource)的set方法,然后完成注入。使用的前提是:该类必须有对应属性的set方法!
application.properties
将properties中属性值注入到dataSource
上述一个整合了 SpringMVC 的 WEB 工程开发,变的无比简单,那些繁杂的配置都消失不见了,这
是如何做到的?
这些都是从 springboot 启动器开始的
重点关注@SpringBootApplication注解
点击进入,查看源码,这里重点的注解有 3 个:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
继续点击查看源码:
通过这段我们可以看出,在这个注解上面,又有一个 @Configuration 注解。这个注解的作用就是声明当前类是一 个配置类,然后Spring 会自动扫描到添加了@Configuration 的类,并且读取其中的配置信息。
继续点击查看源码:并没有看到什么特殊的地方。我们查看注释:
配置组件扫描的指令。提供了类似与
通过 basePackageClasses 或者 basePackages 属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包
而我们的 @SpringBootApplication 注解声明的类就是 main 函数所在的启动类,因此扫描的包是该类所在包及其子包。因此,一般启动类会放在一个比较前的包目录中。
关于这个注解,官网上有一段说明
The second class-level annotation is @EnableAutoConfiguration . This annotation tells Spring Boot to “guess” how you want to confifigure Spring, based on the jar dependencies that you have added. Since spring- boot - starter - web added Tomcat and Spring MVC, the auto-confifiguration assumes that you are developing a web application and sets up Spring accordingly
第二级的注解 @EnableAutoConfiguration ,告诉 SpringBoot 基于你所添加的依赖,去 “ 猜测 ” 你想要如何配置 Spring。比如我们引入了 spring - boot - starter - web ,而这个启动器中帮我们添加了 tomcat 、 SpringMVC 的 依赖。此时自动配置就知道你是要开发一个web 应用,所以就帮你完成了 web 及 SpringMVC 的默认配置了!
总结, SpringBoot 内部对大量的第三方库进行了默认配置,我们引入对应库所需的依赖,那么默认配置就会生效。默认配置是如何生效的?
@EnableAutoConfiguration 会开启 SpringBoot 的自动配置,并且根据你引入的依赖来生效对应的默认配置, springboot如何做到的?
引入了一个依赖: spring-boot-autoconfigure ,其中定义了大量自动配置类:
非常多,几乎涵盖了现在主流的开源框架,例如:
redis
jms
amqp
jdbc
jackson
mongodb
jpa
solr
elasticsearch
例如 SpringMVC ,查看 mvc 的自动配置类:
打开 WebMvcAutoConfifiguration :
这个类上的 4 个注解
@Configuration :声明这个类是一个配置类
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
这里的条件是 OnClass ,也就是满足以下类存在: Servlet 、 DispatcherServlet 、 WebMvcConfigurer ,其中 Servlet只要引入了 tomcat 依赖自然会有,后两个需要引入 SpringMVC 才会有。这里就是判断你是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效!
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
这个条件与上面不同, OnMissingBean ,是说环境中没有指定的 Bean 这个才生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个WebMVCConfigurationSupport 的类,那么这个默认配置就会失效!
接着,我们查看该类中定义了什么:
视图解析器:
处理器适配器( HandlerAdapter ):
等等
SpringBoot为我们提供了默认配置,而默认配置生效的条件一般有两个:
引入了相关依赖
没有自定义配置类
上述已经实现 mvc 自动配置(在pom中添加了启动器),另外还需解决以下 3 个问题
在SpringBoot的全局属性文件中,端口通过以下方式配置: server.port = 80
我们使用springmvc需要对静态资源如下处理
springboot对静态资源做了如下处理,只要在以下路径下的静态资源都能被访问到,一般都是放在resources下的static中
ResourceProperties 的类,里面就定义了静态资源的默认查找路径:
默认的静态资源路径为:
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public
只要静态资源放在这些目录中任何一个, SpringMVC 都会帮我们处理。
5.1.3.添加拦截器
springmvc中的拦截器配置
springmvc中的拦截器在springboot中是如何配置的
首先我们定义一个拦截器:
package com.cbw.interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.debug("处理器执行前执行!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.debug("处理器执行后执行!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
logger.debug("跳转后执行!");
}
}
通过实现 WebMvcConfigurer 并添加 @Configuration 注解来实现自定义部分 SpringMvc 配置:
ant path 路径匹配通配符
‘’ 匹配任何单字符
‘*’ 匹配 0 或者任意数量的字符
‘/**’ 匹配 0 或者更多的目录
目录结构
运行并查看日志
发现日志什么都没有,因为我们记录的 log 级别是 debug ,默认显示 info 以上,我们需要进行配置。
SpringBoot 通过 logging.level.*=debug 来配置日志级别, * 填写包名
测试
springboot根据依赖自动的帮我们配置jdbc,不要忘了数据库驱动,SpringBoot并不知道我们用的什么数据库,这里我们选择MySQL。
springboot整合jdbc就不需要引入连接池依赖,因为 引入 jdbc 启动器的时候, SpringBoot 已经自动帮我们引入了一个连接池
HikariCP 应该是目前速度最快的连接池了,它与 c3p0 的对比
只需要指定连接池参数
# 连接四大参数
spring.datasource.url = jdbc:mysql://localhost:3306/tb_userserverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123
# 可省略,SpringBoot自动推断
spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.minimum-idle=10
实体类
持久层 这里使用的是原始的dao
测试
数据库
SpringBoot 官方并没有提供 Mybatis 的启动器,不过 Mybatis 官网 自己实现了
pom添加Mybatis启动器
配置,基本没有需要配置的
数据表和实体类还是上面的
mapper接口
mapper接口映射文件
1.sqlSession.getMapper(UserMapper.class)
2.mybatis反向生成
1.使用@Mapper注解(不推荐)
需要注意,这里没有配置 mapper 接口扫描包,因此我们需要给每一个 Mapper 接口添加 @Mapper 注解,才能被识别。
@Mapper
public interface UserMapper {
}
2.设置MapperScan注解扫描包(推荐)
启动类上设置@MapperScan("mapper 所在的包 ") ,自动搜索包中的接口,产生 mapper 的代理对象
测试
概念
使用 Mybatis 时,最大的问题是,要写大量的重复 SQL 语句在 xml 文件中,除了特殊的业务逻辑 SQL 语句之外,还有大量结构类似的增删改查SQL 。而且,当数据库表结构改动时,对应的所有 SQL以及实体类都需要更改。这大量增加了程序员的负担。
使用tk mybatis ,优化了表与实体类之间的映射,表的改动不影响实体类属性(通过注解纠正), 不需要写mapper映射文件,只需要写mapper接口继承tk mybatis给我们提供的Mapper接口,我们就可以使用tk mybatis提供的内置方法完成增删改查操作。
避免重复书写 CRUD 映射的框架有两个
1.通用 mybatis ( tk mybatis )
2.mybatis plus ,功能更加强大,后面实战项目中讲解
通用Mapper的作者也为自己的插件编写了启动器,我们直接引入即可:
实体类
tk mybatis 实体类使用的注解是jpa注解
注意事项:
1. 默认表名=类名,字段名=属性名
2. 表名可以使用 @Table(name = “tableName”) 进行指定
3. @Column(name = “fieldName”) 指定
4. 使用 @Transient 注解标识的字段不进行映射
不需要做任何配置就可以使用,不需要写mapper映射文件,只需要写mapper接口继承tk mybatis给我们提供的Mapper接口,我们就可以使用tk mybatis提供的内置方法完成增删改查操作。
mapper接口
一旦继承了Mapper,继承的Mapper就拥有了Mapper所有的通用方法:
当然,我们也可以自定义sql映射,那么上面的mapper接口中就需要写接口方法了,而且自定义的sql映射是不支持字段下划线user_name转驼峰userName的,而且字段名和属性名不一致时需要在sql中通过起别名,变成一致,而tk mybatis提供的内置方法是支持下划线转驼峰的,另外如果字段和属性不一致,只需要在属性上userName添加一个注解Column(name = “user_name”)即可。
Select
Insert
Update
Delete
Example
注意要把MapperScan类改成tk-mybatis构件的类
测试
上述只有第一个方法使用的是自定义的sql,要是没有自定义sql,那么映射文件就不需要了,而且mapper接口只需要实现tk mybatis的mapper接口就可以,也不需要写接口方法了。
Thymeleaf 是一个跟 FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下特点:
动静结合:
Thymeleaf 在有网络和无网络的环境下皆可运行,无网络显示静态内容,有网络用后台得到数据替换静态内容
springboot默认整合thymeleaf
编写接口
编写 UserService ,调用 UserMapper 的查询所有方法
编写一个 controller ,返回一些用户数据,放入模型中,等会再页面渲染
直接引入启动器:
SpringBoot会自动为Thymeleaf注册一个视图解析器:
与解析 JSP 的 InternalViewResolver 类似, Thymeleaf 也会根据前缀和后缀来确定模板文件的位置:
默认前缀: classpath:/templates/
默认后缀: .html
所以如果我们返回视图: users ,会指向到 classpath:/templates/users.html
一般我们无需进行修改,默认即可
静态页面
模板默认放在 classpath 下的 templates 文件夹,我们新建一个 html 文件放入其中
编写html模板,渲染模型中的数据:
语法
${} :这个类似与el表达式,但其实是ognl的语法,比el表达式更加强大
th- 指令: th- 是利用了Html5中的自定义属性来实现的。如果不支持H5,可以用 data-th- 来代替
th:each :类似于 c:foreach 遍历集合,但是语法更加简洁
th:text :声明标签中的文本 例如 1 ,如果user.id有值,会覆盖默认的1 如果没有值,则会显示td中默认的1。这正是thymeleaf能够动静结合的原因,模板解析失败不影响 页面的显示效果,因为会显示默认值!
首页
Thymeleaf会在第一次对模板解析之后进行缓存,极大的提高了并发处理能力。但是这给我们开发带来了不便,修 改页面后并不会立刻看到效果,我们开发阶段可以关掉缓存使用
1. 变量表达式
2. 选择或星号表达式
3. URL表达式
6.3.1.1.变量表达式
变量表达式即OGNL表达式或Spring EL表达式( 在Spring中用来获取model attribute的数据)。如下所示:
以HTML标签的一个属性来表示
${session.user.name}
${text}
你好 thymleaf
6.3.1.2.选择(星号)表达式
th:object中定义星号表达式上下文,这样可以简化变量表达式的语法,不需要通过对象打点获取属性,直接通过星号表达式的上下文中获取对应的属性的值。
6.3.1.3.URL表达式
URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。
@{/order/list} 没有参数
URL还可以设置参数:
@{/order/details(id=${orderId}, name=*{name})}
相对路径: @{…/documents/report}
三种不同的使用形式
url表达式
删除
文本替换
修改
字符串拼接
审核
字面(Literals)
文本文字(Text literals): ‘one text’, ‘Another one!’,…
数字文本(Number literals): 0, 34, 3.0, 12.3,…
布尔文本(Boolean literals): true, false
空(Null literal): null
文字标记(Literal tokens): one, sometext, main,…
文本操作(Text operations)
字符串连接(String concatenation):
文本替换(Literal substitutions): |The name is ${name}|
算术运算(Arithmetic operations)
二元运算符(Binary operators): +, -, *, /, %
减号(单目运算符)Minus sign (unary operator): -
布尔操作(Boolean operations)
二元运算符(Binary operators): and, or
布尔否定(一元运算符)Boolean negation (unary operator): !, not
比较和等价(Comparisons and equality)
比较(Comparators): >, <, >=, <= (gt, lt, ge, le)
等值运算符(Equality operators): ==, != (eq, ne)
条件运算符(Conditional operators)
If-then: (if) (then)
If-then-else: (if) (then) : (else)
Default: (value) : (defaultvalue)
1. 赋值、字符串拼接
字符串拼接还有另外一种简洁的写法
修改
审核
2. 条件判断 If/Unless
Thymeleaf中使用th:if和th:unless属性进行条件判断
th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容
也可以使用 (if) (then) : (else) 这种语法来判断显示的内容
查询结果存在
查询结果不存在
3. for 循环
status称作状态变量,属性有
index:当前迭代对象的index(从0开始计算)
count: 当前迭代对象的index(从1开始计算)
size:被迭代对象的大小
current:当前迭代变量
even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个
4. 内联文本
内联文本:[[…]]内联文本的表示方式,使用时,必须先用th:inline=”text/javascript/none”激活
th:inline=“text” 内联文本
th:inline=“javascript” 内联js
th:inline=“none” 关闭内联
th:inline可以在 父级标签内使用,甚至作为body的标签
内联文本尽管比th:text的代码少,不利于原型显示
在thymeleaf指令中显示
使用内联文本显示model attribute
5. 内联js
6. 内嵌变量
为了模板更加易用,T
hymeleaf还提供一系列Utility对象(内置Context中),可以通过#直接访问:
dates : java.util.Date**的功能方法类。
calendars : 类似#dates,面向java.util.Calendar
numbers : 格式化数字的功能方法类
strings : 字符串对象的功能类,
contains,startWiths,prepending/appending等等。
objects: 对objects的功能类操作。
bools: 对布尔值求值的功能方法。
arrays:对数组的功能类方法。
lists: 对lists功能类方法
sets
maps …
比如
在/resources/templates/目录下创建footer.html,内容如下
编写一个片段,在其他页面引用
在页面任何地方引入:
th:insert :保留自己的主标签,保留th:fragment的主标签。
th:replace :不要自己的主标签,保留th:fragment的主标签。
th:include :保留自己的主标签,不要th:fragment的主标签。(官方3.0后不推荐)
这几个标签的区别,查看源码
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,避免了我 们重复CRUD语句。
4.0.0
org.example
spring-boot-mybatis-plus
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.0.0.RELEASE
UTF-8
UTF-8
11
3.3.2
true
org.springframework.boot
spring-boot-starter
com.h2database
h2
runtime
com.baomidou
mybatis-plus-boot-starter
${mybatisplus.version}
org.assertj
assertj-core
test
org.projectlombok
lombok
provided
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
在Springboot中,推荐使用properties或者YAML文件来完成配置,但是对于较复杂的数据结构来说,YAML又远远 优于properties。
看一个Springboot中的properties文件和对应YAML文件的对比
可以明显的看到,在处理层级关系的时候,properties需要使用大量的路径来描述层级(或者属性),比如 environments.dev.url和environments.dev.name。其次,对于较为复杂的结构,比如数组(my.servers),写起 来更为复杂。而对应的YAML格式文件就简单很多:
数据库脚本文件/db/data-h2.sql和/db/schema-h2.sql
h2数据库是一个基于内存的数据库,在jvm启动时,自动执行脚本加载相应的数据,常用于开发时测试使用。
springboot 中使用h2数据库直接按照上面配置,配置schema表结构脚本和data数据脚本即可
这里用户名密码可以省略不写,或者随意设定
测试
注解解决表与实体类不一致问题
MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹 配
mybatis plus注解策略配置
1.如果mysql自增主键注解策略设置如下
@TableId(type = IdType.AUTO)
private Long id;
2.默认主键策略
ASSIGN_ID(3), //采用雪花算法生成全局唯一主键
3.排除实体类中非表字段
使用 @TableField(exist = false) 注解
通过分页拦截器拦截查询语句的执行,使用count语句解析器对left join这种一对一关联的sql进行优化,当分页查询的时候,将count查询和left join查询拆开,先select count查询总数,然后再查询当页数据
优化left join count场景
在一对一 join 操作时,也存在优化可能,下面是用户-账号一对一关系的left join查询
当对这条sql查询结果进行分页查询,执行的sql语句是这样的
加入count语句解析器后进行分页查询,最终执行的sql语句是
先配置分页 生成PaginationInterceptor分页拦截器
测试
生成分页拦截器
UserMapper.xml文件
UserMapper接口
application.yml
测试
引入 pageHelper 依赖
mybatis plus 整合 pageHelper 的配置类
映射文件
接口方法
测试