1.springboot跨域配置(重要)
新建项目
删无用目录
pom.xml中spring-boot-starter-parent版本改为2.3.5.RELEASE,删除没用内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.javasm</groupId>
<artifactId>demo</artifactId>
<version>0.0.1</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件后缀改成yml(application.yml)
找到启动类Application(也是配置类,主配置类),在启动类所在包建东西。
新建config.BootAppConfig配置类(不需要导入主配置类,只要自己定义的配置类在主配置类的包下就不用。否则在主配置类上加@Import(BootAppConfig.class))--该类加@Configuration就相当于一个.xml配置文件,这里主要注册bean对象(@Bean的方式)。
在前后端分离开发模式下,服务端如何进行跨域设置,springboot中仍然是基于CorsFilter进行跨域设置.
①.方法1:从WebMvcConfigurer接口派生,重写addCorsMapping方法进行跨域配置.
(此方法不建议使用,因为这里是基于springmvc的跨域拦截器进行跨域响应头设置,此拦截器最后一个执行,如果与自定义拦截器一起使用,出现跨域异常.)
用户请求进来,先执行filter过滤器,再执行dispatcherServlet。dispatcherServlet根据url找到对应的拦截器和处理器方法,自定义的拦截器在最上方,最下方是跨域拦截器。所有拦截器通过后,执行处理器方法。若有一个拦截器将请求拦截,返回前端,跨域拦截器就不会执行。跨域拦截器不执行,跨域响应头就没有设置。不返回跨域响应头给前端,前端就报跨域异常。
(也可以解决,只要在自定义拦截器里,将opsession域检请求放行)
②.方法2:使用CorsFilter过滤器对象进行跨域配置
将自定义配置类改名CorsConfig,表示跨域配置。
@Configuration //相当于建了cors.xml配置文件
public class CorsConfig {
//注册Bean对象(之前注册CorsFilter对象到容器,然后在web.xml中配置过滤器代理对象,才能在容器中真正获取CorsFilter对象,使它生效)
//在springboot中没有web.xml了,如何配置Filter过滤器?通过FilterRegistrationBean过滤器注册器对象来配置Filter的信息(过滤器
//执行顺序,范围...)该对象相当于web.xml中filter标签
@Bean
public FilterRegistrationBean cors(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); //还是web.cors包,是跨域过滤器对象参数
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:8088"); //允许的客户端域名
config.setAllowCredentials(true); //允许客户端携带cookie
config.addAllowedHeader("*"); //允许的客户端请求头,这里表示所有
config.addAllowedMethod("*"); //允许的客户端请求方法
config.addExposedHeader("admin_token"); //若使用token,添加暴露出去的响应头
source.registerCorsConfiguration("/**",config); //注册跨域配置(对所有请求路径都进行跨域)
CorsFilter corsFilter = new CorsFilter(source); //web包下的CorsFilter,创建跨域过滤器对象
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(corsFilter);
filterRegistrationBean.setOrder(0); //指定过滤器执行顺序,这里设置第一个执行,任何请求进来,先执行跨域过滤器
return filterRegistrationBean;
}
}
但参数在代码里写死了,故将他们移到配置文件。
先建配置对象。这里先建一个公共包commons,并将刚才的config包也拉过去(只要在com.javasm包下都是可以的)。再在公共包下建properties.BootProperties
@ConfigurationProperties(prefix = "boot",ignoreUnknownFields = true) //获取指定前缀的数据,这里为boot。
//第二个参数为忽略无法识别的字段,默认true,故可省略
@EnableConfigurationProperties(BootProperties.class)
public class BootProperties {
private List<String> origin; //客户端地址(服务器接收到的客户端可能不止一个)
private Boolean credentials; //是否允许携带cookie
private List<String> methods; //可能限定几种方法,其他方法不让进来
private List<String> headers; //允许的客户端请求头
private List<String> exposedHeaders; //允许暴露的响应头
public List<String> getOrigin() {
return origin;
}
public void setOrigin(List<String> origin) {
this.origin = origin;
}
public Boolean getCredentials() {
return credentials;
}
public void setCredentials(Boolean credentials) {
this.credentials = credentials;
}
public List<String> getMethods() {
return methods;
}
public void setMethods(List<String> methods) {
this.methods = methods;
}
public List<String> getHeaders() {
return headers;
}
public void setHeaders(List<String> headers) {
this.headers = headers;
}
public List<String> getExposedHeaders() {
return exposedHeaders;
}
public void setExposedHeaders(List<String> exposedHeaders) {
this.exposedHeaders = exposedHeaders;
}
}
接下来在配置文件里配这些东西:(yml文件里不能写*)
boot:
origin: [http://localhost:8088]
credentials: true
methods: [GET,POST,PUT,DELETE,OPTIONS]
heheaders: []
exposedHeaders: [admin_token]
(集合为空,表示允许所有。)
然后数据就会被加载到BootProperties对象里,CorsConfig类里就可以注入该对象。同时修改方法,比如拿客户端域名不止一个(之前的Addxx方法是一个个往里添,现在是整个集合放进去)。
@Configuration //相当于建了cors.xml配置文件
public class CorsConfig {
@Resource //注入该对象
private BootProperties bp;
//注册Bean对象(之前注册CorsFilter对象到容器,然后在web.xml中配置过滤器代理对象,才能在容器中真正获取CorsFilter对象,使它生效)
//在springboot中没有web.xml了,如何配置Filter过滤器?通过FilterRegistrationBean过滤器注册器对象来配置Filter的信息(过滤器
//执行顺序,范围...)该对象相当于web.xml中filter标签
@Bean
public FilterRegistrationBean cors(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); //还是web.cors包,是跨域过滤器对象参数
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(bp.getOrigin()); //允许的客户端域名
config.setAllowCredentials(bp.getCredentials()); //允许客户端携带cookie
List<String> headers = bp.getHeaders();
if (headers==null || headers.size()==0){
config.addAllowedHeader("*"); //没配置请求头,全部加进去
}else //否则按照用户配置的请求头加进去
config.setAllowedHeaders(headers);
List<String> methods = bp.getMethods();
if(methods==null || methods.size()==0)
config.addAllowedMethod("*");
else
config.setAllowedMethods(bp.getMethods()); //允许的客户端请求方法
List<String> exposedHeaders = bp.getExposedHeaders();
if(exposedHeaders!=null && exposedHeaders.size()>0)
config.setExposedHeaders(bp.getExposedHeaders()); //若使用token,添加暴露出去的响应头
source.registerCorsConfiguration("/**",config); //注册跨域配置(对所有请求路径都进行跨域)
CorsFilter corsFilter = new CorsFilter(source); //web包下的CorsFilter,创建跨域过滤器对象
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(corsFilter);
filterRegistrationBean.setOrder(0); //指定过滤器执行顺序,这里设置第一个执行,任何请求进来,先执行跨域过滤器
return filterRegistrationBean;
}
}
(记住FilterRegistrationBean和CorsFilter两个对象,剩下的靠提示就可以写出来。)
2.Thymeleaf模板引擎(不重要,但最好记着有这么个东西)
①.在同步开发模式下,才会用到,类似jsp这套技术,故不重要,但每个程序员都要掌握一两套这个技术。
同步请求,每次操作都刷新页面;
异步请求,每次操作可以不刷新页面,而是局部修改页面中数据.
②.模板引擎这门技术主要用来做什么?
可以用在做视图模板,在html静态页中添加模板引擎语法,来解析服务端传递来的数据,生成不同效果的动态页. 可以用来做代码生成模板,类似easycode插件(生成的模板都是.vm后缀,该后缀是velocity的文件,velocity是基于java的模板引擎。此插件是velocity模板引擎的应用),若依代码生成也用到velocity模板。 可以用来做word文件生成模板;基于freemarker模板引擎来生成动态word文件.(用来生成word文件)
③.springboot框架中建议使用thymeleaf引擎作为视图模板技术.
jsp: 编写XX.jsp文件。用户第一次访问jsp文件,jsp引擎生效,解析jsp语法,把访问的jsp文件翻译成XX.java文件。虚拟机jvm再编译成XX.class文件,jvm再次把字节码文件加载到虚拟机内存,再实例化XX对象,再执行对象。service方法,该方法内再生成html文本字符串,输出给客户端。
thymeleaf:从性能以及开发便捷性上,thymeleaf比jsp优秀。编写XX.html(thymeleaf的后缀还是.html文件),用户第一次访问html模板文件,thymeleaf引擎解析语法,把模板文件直接解析到内存中,接收动态数据,生成静态html输出前端。(直接在内存中解析,执行流程少了很多)同时
thymeleaf的语法更丰富,jsp做不到的事,它可以做到。
④.thymeleaf引擎在springboot框架中使用
添加thymeleaf启动器,一旦引入该启动器,则spring-boot-starter-autoconfiguration包下的ThymeleafAutoConfiguration预配置类生效,该配置类中注册了ThymeleafViewResolver视图解析器对象.
在pom.xml文件中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在templates目录下新建login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页</h1>
<form action="/user/dologin" method="post">
用户名:<input name="uname">
<br>
密码:<input type="password" name="upwd">
<br>
<button type="submit">登录</button>
</form>
</body>
</html>
在templates目录下新建main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>主页。欢迎:这里显示登录用户</h1>
</body>
</html>
这两个页面前端无法直接访问,因为只有静态目录static下的内容前端才可以直接访问。
故需要后端处理器方法返回页面。在javasm包下新建sys.controller.UserController
//不要用@restController,它表示返回json数据给前端,所有的同步开发模式下都是返回视图给前端,故用@Controller
@Controller
@RequestMapping("user") //第一层url
public class UserController {
@PostMapping("dologin") //第二层url
public String dologin(String uname, String upwd){
if("admin".equals(uname) && "123".equals(upwd)){
System.out.println("suc");
return "main";
}else {
return "login"; //密码有误,还停留在登录页面
//thymeleafViewResolver中会拼接视图前缀和后缀 classpath:/templates/login.html
}
}
}
若希望在返回的视图加一些东西:
//不要用@restController,它表示返回json数据给前端,所有的同步开发模式下都是返回视图给前端,故用@Controller
@Controller
@RequestMapping("user") //第一层url
public class UserController {
@PostMapping("dologin")
public String dologin(String uname, String upwd, Model m){ //若向登录页面带一些数据,可加Model对象,向该对象加数据即可
if("admin".equals(uname) && "123".equals(upwd)){
System.out.println("suc");
m.addAttribute("loginUname",uname);
return "main";
}else {
m.addAttribute("msg","用户名密码错误");
return "login"; //密码有误,还停留在登录页面
//thymeleafViewResolver中会拼接视图前缀和后缀 classpath:/templates/login.html
}
}
}
现在还是访问不到,因为templates不是静态目录。
故还需要加方法
//不要用@restController,它表示返回json数据给前端,所有的同步开发模式下都是返回视图给前端,故用@Controller
@Controller
@RequestMapping("user") //第一层url
public class UserController {
@GetMapping("login") //用户访问user/login,进入这个方法,该方法又返回一个字符串视图路径,才会找到templates目录下的login.html
public String login(){
return "login";
}
@PostMapping("dologin")
public String dologin(String uname, String upwd, Model m){ //若向登录页面带一些数据,可加Model对象,向该对象加数据即可
if("admin".equals(uname) && "123".equals(upwd)){
System.out.println("suc");
m.addAttribute("loginUname",uname);
return "main";
}else {
m.addAttribute("msg","用户名密码错误");
return "login"; //密码有误,还停留在登录页面
//thymeleafViewResolver中会拼接视图前缀和后缀 classpath:/templates/login.html
}
}
}
这就是同步开发,每一次操作返回给前端的都是视图,带着数据和视图一起返回前端。
(“login” “main” 这些就是视图模板。)
测试:
登录失败,登录页面应该返回错误信息。获取服务端回传到视图模板里的数据需要用到thymeleaf的语法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页</h1>
<form action="/user/dologin" method="post">
用户名:<input name="uname">
<br>
密码:<input type="password" name="upwd">
<br>
<div>
<span style="color:red">[[${msg}]]</span>
</div>
<button type="submit">登录</button>
</form>
</body>
</html>
ctrl+F9重新发布,故意打错密码。
同理main页面在登录成功后也可以得到登录用户名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>主页。欢迎:[[${loginUname}]]</h1>
</body>
</html>
ctrl+F9,在登录页输入正确的用户名和密码,跳转到主页面
这样就使得静态的页面变成了动态的内容。
⑤.学习thymeleaf的语法:
1.thymeleaf表达式:
a.属性表达式:${}.---------必须配合th:text属性
作用:用来获取服务端request,session,servletContext域对象中的数据.
语法:request域数据获取:${key} session域数据获取:${session.key}或者${#session.getAttribute('key')} servletContext域数据获取:${#servletContext.getAttirubte('key')}
前端获取数据
(获取到request域里的数据,给双标签span中间的内容赋值,中间的a被覆盖)
登录成功后:
(当然,若key写错,获取null,相当于null覆盖中间的a)
其他几个:
测试:
(以上顺便讲了th:value th:text)
b.链接表达式:@{}------(th:href th:src th:action)
作用:为src/href等url链接属性补全项目根路径
注意点:url中如何传递参数 <a th:href="@{/user/del(id=1,uname=${loginUname})}">点我</a>
在static目录下新建imgs目录,放一张图,浏览器可直接访问
现在想在main页面显示该图片
图片太大,修改下尺寸。src里应为/imgs/a.jpg
现在部署路径为空,若手工指定部署路径为/boot
此时再访问,请求路径要加boot
输入用户名密码,还是报错
因为action写的/开头,表示绝对路径的写法。
绝对路径即端口后边开始算,即根开始算。
需要改为action="/boot/user/dologin",即加上部署路径。但这样写死了,部署路径发生改变后,就得修改。
怎么办?---写相对路径。action="dologin"
进入登录页面,写错误用户名密码后就可以停留在登录页面。
相对路径:没有/开头,相对当前路径。那就需要知道该页面当前是在哪个路径。
这个login是在user/login路径下。
或者也可以看这里
是在boot/user/login路径下。
相对当前路径,当前login在user下,故去user下找同级的dologin
以上容易搞晕,此时,thymeleaf出现:
链接表达式的地址必须/开头的根路径,即绝对路径,但不需要考虑项目部署路径,会自动补上。
访问图片时也不用加部署路径
<Br>
<img th:src="@{/imgs/a.jpg}" style="height: 150px;width: 150px">
后端再加个删除方法
前端对接后端的该方法,并传递参数:
后端该方法打断点,可以收到del:1,uname:null。但不建议这样用
此时若uname要传递的是request里的参数
会将${loginUname}整体作为参数传递,而${}作为特殊符号,不能传递,就报错了。
链接表达式里传递参数,不建议用?而是用()
这个主要用在修改里。
除了form表单标签,用link标签引入其他文件也会用到链接表达式
在Static目录下新建css目录,目录下新建main.css
body{
background-color: teal;
}
此时,写绝对路径引入
(部署路径改为了b)
若用链接表达式
引入js文件同理
在开发中thymeleaf是个神器!!!
c.文档表达式:~{}
作用:进行公共页的嵌套。
网站的顶部和底部都是公共页。可将顶部和底部的页面提出来成为公共页面。
在templates目录下新建commons目录,新建top.html
<div>
这里是顶部公共页
</div>
想将top页面嵌入到main页面
会自动拼接路径前缀和后缀,效果:
它是在th:insert所在标签里面放入div。(将top.html里的内容整个插入到标签里面)
另一种方法:
<!--/tempaltes/commons/top.html-->
<div th:replace="~{commons/top}"></div>
他是将top.html整个div放入到main.html。(将th:replace所在标签替换掉)
局部嵌套这样来:(fragment片段)-------引用页面当中的某个片段
在top.html中:
<div th:fragment="topDiv">
这里是顶部公共页
</div>
<div th:fragment="footDiv">
这里是底部内容
</div>
在main.html中:
body标签的最上面:
<!--/tempaltes/commons/top.html-->
<div th:replace="~{commons/top :: topDiv}"></div>
body标签的最下面:
<div th:replace="~{commons/top :: footDiv}"></div>
效果:
2.thymeleaf属性:写在html标签属性处.
th:value: 作用:结合属性表达式${}使用,为单标签指定内容
th:text 作用:结合属性表达式${}使用,为双标签指定内容 简写:[[${}]]
th:href th:src th:action 作用:这三个属性,结合链接表达式@{}使用,用在img,link,script,a,form标签上,补全项目根路径 示例:<a th:href="@{/user/del(id=1,uname=${loginUname})}">点我</a>
th:insert th:replace: 作用:这两个属性,结合文档表达式实现公共页嵌套. 实例:<div th:replace="~{commons/top}"></div>
th:fragment: 作用:结果文档表达式实现局部页嵌套 实例:公共内容:<div th:fragment="topDiv">这里是顶部公共</div> 页面:<div th:replace="~{commons/top :: footDiv}"></div>
th:each: 实例:<tr th:each="u,s : ${key}">
th:if: 实例:<tr th:each="u,s : ${ll}"
th:if="${s.count%2==0}">
循环与判断属性:
在进入main页面前,向Model对象加入集合数据:
在sys包下新建entity.Users
在main页面显示一个表格数据,需要用th:each遍历key为ll的集合数据,需要用到循环变量
效果:
也可以有两个循环变量,用逗号分隔。
u是s的一部分。s.current就是u。s.count可以用来做序号。这是前端的序号,和id是不一样的概念。之前说过,从来不会将用户id显示给前端看的。
id是用来做修改删除用的。比如现在有个操作列:
<table>
<thead>
<tr>
<td>seq</td>
<td>uname</td>
<td>uphone</td>
<td>uage</td>
<td>edit</td>
</tr>
</thead>
<tbody>
<tr th:each="u,s : ${ll}">
<td>[[${s.count}]]</td>
<td>[[${u.uname}]]</td>
<td>[[${u.uphone}]]</td>
<td>[[${u.uage}]]</td>
<Td>
<a th:href="@{/users/update(id=${u.id})}">修改</a>
<a th:href="@{/users/del(id=${u.id})}">删除</a>
</Td>
</tr>
</tbody>
</table>
th:if用来做判断,比如只显示偶数行的数据:(它里面必须是布尔表达式)
<table>
<thead>
<tr>
<td>seq</td>
<td>uname</td>
<td>uphone</td>
<td>uage</td>
<td>edit</td>
</tr>
</thead>
<tbody>
<tr th:each="u,s : ${ll}" th:if="${s.count%2==0}">
<td>[[${s.count}]]</td>
<td>[[${u.uname}]]</td>
<td>[[${u.uphone}]]</td>
<td>[[${u.uage}]]</td>
<Td>
<a th:href="@{/users/update(id=${u.id})}">修改</a>
<a th:href="@{/users/del(id=${u.id})}">删除</a>
</Td>
</tr>
</tbody>
</table>
3.log4j2整合
在项目中整合log4j2日志,与slf4j日志门面组件
在spring-boot-starter依赖中引入了spring-boot-starter-logging启动器,该启动器使用logback依赖.要把这个logback日志替换成log4j2.(同一个项目中绝对不能有两个日志组件,会报错,因为slf4j日志门面的问题,该日志门面会检测当前项目下的具体日志组件,如果有多个,直接给出异常。日志组件只能有一个。 )
①.排除logging启动器
操作后,该组件消失。
(必须在第一个引入该日志包的依赖里排除它。看左侧依赖包谁在最上面,就从谁的里面排除该包)
②.添加log4j2启动器
若想不起来咋添加,可在两个地方找:
去maven官网找(推荐)
搜索log4j2
也可去spring.io官网找(全英文,不好找)
复制相关代码到pom文件
③.添加log4j2.xml文件
直接拷贝之前的项目,将log4j2.xml复制到resources目录下
若使用它
④.如果要使用异步日志,需要引入disruptor组件,lmax公司开源异步非阻塞队列.
在pom文件加入:
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
同时在log4j2.xml中改变配置方式:(将同步更换为异步)
4.mybatis整合
①.添加mysql驱动包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
②.添加druid环境,是单个jar包还是有对应启动器呢?mysql驱动包为啥没有启动器?
druid是有启动器的。有启动器优先使用启动器,启动器里含着对应的jar包。
何时有启动器?何时没有? 即什么时候组件会封装启动器?
如果这个组件需要注册对象到spring容器,那么一般都会有对应的starter.
如果没有,只能手工添加依赖包,自己注册对象。如何注册?建一个Config类
大多数需要注册到容器的都会有启动器的。
组件的启动器一般都是组件名+AutoConfigure
添加druid启动器,查看DruidDataSourceAutoConfigure预配置类,该配置类早于DataSourceAutoConfiguration配置类.在Druid配置类中已经注册了DruidDataSourceWrapper连接池对象
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
指定用户名密码等信息(Driver的包是cj.jdbc.Driver,现在mysql驱动是8.0版本,之前是5.0版本)
(datasource下有username,druid下也有username,url等也是。用哪个都可以)
(新版的驱动类必须指定时区)
(要指定连接池的类型是DruidDataSource对象,可以不指,但引进mybatis启动器后,会引入新的数据库连接池,叫HikariCP:3.4.5连接池。故,不指定,就会创建HikariDataSource连接池(日本的)。)
(设置初始连接数为5)
(要想配置齐全,可以到课件里去找)
在application.yml中:
boot:
origin: [http://localhost:8088]
credentials: true
methods: [GET,POST,PUT,DELETE,OPTIONS]
heheaders: []
exposedHeaders: [admin_token]
server:
servlet:
context-path: /b
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/704a?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
测试:
更新仓库(引得jar包多了,就更新一下。对本地仓库所有jar包文件进行扫描,创建索引号,创建索引文件,便于查找仓库的东西)
启动后,初始会有5个连接对象,是druidDataSource
③.添加mybatis启动器,查看MybatisAutoConfiguration预配置类,在该配置类中依赖DataSource对象,注册SqlSessionFactory对象到spring容器.还注册了MapperScannerConfigurer对象.
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
该启动器还会引入jdbc启动器。
④.在yml文件中配置mybatis映射文件路径
在resource目录下建mapper包,再按照模块划分(sys系统管理模块,orders订单模块,cards卡管理模块...)
在application.yml中添加:
mybatis:
mapper-locations: classpath:/mapper/*/*.xml
type-aliases-package: com.javasm
configuration:
map-underscore-to-camel-case: true
(本地映射文件的路径,必须有。剩下的都可以不要)
(mybatis别名包的范围)
在src.main.java.com.javasm.sys下创建dao,service包。
dao包新建UserDao
public interface UserDao {
Users getById(String id);
}
在mapper.sys下创建映射文件user-mapper:
<?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.javasm.sys.dao.UserDao">
</mapper>
在UserDao的方法上alt+回车生成方法。
<?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.javasm.sys.dao.UserDao">
<select id="getById" resultType="com.javasm.sys.entity.Users">
select * from sys_user where id=#{id}
</select>
</mapper>
测试
想用mybatis,还要注入SqlSessionFactory,然后从它里面打开Session,再从Session得到dao接口的代理对象。
在ssm整合那里,注入SqlSessionFactory后,使用MapperScannerConfigurer dao接口扫描器对象把指定包下的dao接口全部创建代理对象注册容器。
这里mybatis预配置对象,底层把com.javasm包下的所有接口都创建代理对象。
service包下新建IUserService接口:
public interface IUserService {
Users getById(String id);
}
实现类
@Service将该接口注入容器,service依赖dao,@Resource将dao取出。
mybatis预配置对象会将IUserService创建代理对象注册到容器,对dao的接口也创建代理对象注册到容器。如果controller依赖于service
这里注入就不行了,因为类型为IUserService的对象容器里有两个。
开启@MapperScan 接口扫描
此时报没有唯一bean定义异常。
conrtoller依赖的service不能用(先注释掉),此时测试时,直接从容器取出dao,用就行。
@SpringBootTest
class ApplicationTests {
@Resource
private DataSource ds;
@Resource //从容器里拿到SqlSessionFactory对象
private SqlSessionFactory f;
@Resource
private UserDao ud;
@Test
void contextLoads() {
Users byId = ud.getById("1");
}
}
如何才能让service用起来,就是向ssm整合一样,指向dao接口所在包名,而不是com.javasm下的所有接口。
⑤.在配置类中启用接口扫描,并指定dao接口的具体包名
@MapperScan(basePackages = "com.javasm.*.dao") //启用接口扫描,并指定dao接口的具体包名
5.事务整合
这个非常简单,不用添加任何依赖,任何配置,因为在DataSourceTransactionManagerAutoConfiguration预配置类,已经注册了DataSourceTransactionManager对象,并依赖DataSource.
①.开启事务注解识别:
@SpringBootApplication
@MapperScan(basePackages = "com.javasm.*.dao")
@EnableTransactionManagement //启用事务注解识别
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
②.在服务层的方法上添加@Transactional注解即可.(哪个方法需要开启事务,就在类上加@Transactional注解)
@Service //注入容器
public class UserServiceImpl implements IUserService {
@Resource //service依赖dao,从容器获取
private UserDao ud;
//只对RunTimeException异常生效,进行回滚,如果方法抛出的编译时异常,并不能触发回滚.如果需要回滚
@Transactional(rollbackFor = Exception.class)
@Override
public Users getById(String id) {
return null;
}
@Override
public List<Users> list() {
return ud.list();
}
}
将注解放在Application,会使启动类很乱,在config目录下新建DaoConfig类,去承载这些注解。
6.分页整合
分页组件也有启动器,PageInterceptor拦截器对象需要注册进入SqlSessionFactory对象的plugins属性.
在这个启动器中,有PageHelperAutoConfiguration预配置类.此配置类实例化PageInterceptor,并把分页拦截器注册到了SqlSessionFactory.
(每个组件都必须找到配置类来看他!!若不找到配置类看他,用springboot就会很晕)
①.添加分页启动器
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
(小组件没有引过去,需要指定版本)
在controller包下新建UserController2类:
@RestController
@RequestMapping("u")
public class UserController2 {
@Resource
private IUserService us;
@GetMapping("list")
public ResponseEntity userslIst(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "2") Integer pageSize){
PageHelper.startPage(pageNum,pageSize);
List<Users> l = us.list();
PageInfo i = new PageInfo(l);
return ResponseEntity.ok(i);
}
}
public interface IUserService {
Users getById(String id);
List<Users> list();
}
@Service //注入容器
public class UserServiceImpl implements IUserService {
@Resource //service依赖dao,从容器获取
private UserDao ud;
//只对RunTimeException异常生效,进行回滚,如果方法抛出的编译时异常,并不能触发回滚.如果需要回滚
@Transactional(rollbackFor = Exception.class)
@Override
public Users getById(String id) {
return null;
}
@Override
public List<Users> list() {
return ud.list();
}
}
public interface UserDao {
Users getById(String id);
List<Users> list();
}
<?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.javasm.sys.dao.UserDao">
<select id="getById" resultType="com.javasm.sys.entity.Users">
select * from sys_user where id=#{id}
</select>
<select id="list" resultType="com.javasm.sys.entity.Users">
select * from sys_user order by update_time desc
</select>
</mapper>
dabug运行
②.在yml中设置分页合理化
pagehelper:
reasonable: true
此时访问-1
给的就是第一页
访问234
就是最后一页
不会出现空记录。
不要硬件怎么配置的,只要知道加了启动器,就去搜索它的配置类,去配置类看他用的配置对象是谁,去那里面找数据。
7.aop
①.添加aop启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
②.启用aop注解识别
commons包下新建aspect.LogAspect
@Aspect定义切面对象 @Pointcut切入点定义 @Around环绕通知
总结:
除了模板引擎以外,都在项目中使用。
跨域配置不需要死记。拷贝就行。
log4j2,拷贝就行
其他的必须阅读预配置类,关注组件内的核心对象创建过程,关注以来的XXXXXProperties配置对象。