spring 略介绍
第一章 Spring 入门
1.1为什么要学spring
spring是java语言中必须要掌握的框架,它涵盖了java的各个领域,基本上是全能的。
spring的核心是依赖注入(DI),spring所有的技术方案都是基于DI发展来的。
1.2 maven入门(上)
maven:一个项目管理和自动化工具
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYGMOOXz-1604221023961)(https://style.youkeda.com/img/ham/course/j4/mvn.svg)]
注:如果说spring是java必须要学习的框架,那么maven就是必须要掌握的自动化工具。
借助maven的中央仓库,我们可以把任意代码分享给别人
maven使用惯例优于配置的原则,在没有要求定制之前,所有项目都遵循以下结构,如果代码放错位置将无法正确运行
目录 目的
${basedir} 存放pom.xml和所有子目录
${basedir}/pom.xml maven 项目的配置文件
${basedir}/src/main/java 项目java的源代码
${basedir}/src/main/resources 项目的资源
${basedir}/src/test/java 项目的测试类
${basedir}/src/test/resources 测试使用的资源
maven 安装
可以参考网上的安装教程
maven命令
mvn clean compile //编译命令 扫描java的源码并完成编译,完成后在根目录下生产target文件
mvn clean package //编译并打包命令 先编译然后把所有的java文件和资源打包为java的一个压缩格式jar
mvn clean install //执行安装命令 先编译然后打包最后安装到本都的maven仓库目录中
mvn compile exec:java -Dexec.mainClass=${main} //先编译然后再执行某一个类的命令 ${main}是一个变量 类名
1.3maven入门(中)
上图的5个概念都会运用到maven的配置文件 pom.xml中
POM:project object model
一个项目的所有配置都放在pom文件中,主要包括:定义项目的类型和名字
管理依赖关系
定制插件
maven坐标:必须指定,表示这个maven工程部署再maven仓库的文件位置
<groupId>com.youkeda.course</groupId> //像一个文件夹,一般都要命名
<artifactId>app</artifactId> //类似于文件,不能使用中文和特殊字符
<packaging>jar</packaging> //打包成的文件工程 一般默认jar 也可以是war ear pom
<version>1.0-SNAPSHOT</version> //版本号,后面加SNAPSHOT表示不稳定,随时可以修改 不加或者加RELEASE表示稳定版本号一般从1.0.0开始 第一位数字表示主版本号 第二位数字表示新增功能次数 第三位数字表示修复bug的次数 一般数字都不超过100
maven 属性配置:用来做参数设置
<properties><java.version>1.8</java.version> //表示设置一个参数,它的值是1.8<maven.compiler.source>${java.version}</maven.compiler.source> //这个参数表示指定maven编译时源代码的jdk版本<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>//工程代码源文件的文件编码格式 一般默认utf-8<maven.compiler.target>${java.version}</maven.compiler.target> //按照这个值来编译源代码 这是是指1.8
</properties>
1.4 maven入门(下)
依赖管理dependencies:用于指定当前工程依赖其他代码库
一般我们声明了依赖,会现在本地.m2文件夹内查找文件,没有找到就会去中央仓库下载,下载之后存放在.m2文件内
<dependencies><dependency> //可以有多个<dependency> <dependency>标签 内容就是maven坐标<groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency>
</dependencies>
一般我们把别人写的叫做三方库,自己和团队写的叫做二方库。
中央仓库:maven会把所有的jar都存放在中央仓库中,我们可以访问阿里云的镜像服务器查看(https://maven.aliyun.com/mvn/search)
间接依赖:如果一个remote工程依赖了 okhttp 库,而当前工程locale依赖了 remote工程,这个时候locale工程也会自动依赖了 okhttp
插件体系plugins:了解插件格式即可
<build><plugins><plugin> //内容也是maven坐标<groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version></plugin></plugins>
</build>
1.5简单实例
此处略去依赖配置,接口和实现类
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import com.youkeda.service.MessageService;/*** Application*/
@ComponentScan //注解
public class Application {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); //spring式的面向接口只关心接口,不关心实现类MessageService msgService = context.getBean(MessageService.class); //spring beanSystem.out.println(msgService.getMessage());}
}
第二章 spring 依赖注入
2.1 java注解 (annotation)
例子
@Service //这个就是注解
public class MessageServiceImpl implements MessageService{public String getMessage() {return "Hello World!";}}
annotation:它是java推出的一种注释机制,和普通注释不同,他可以可以在编译,运行阶段读取。它也可以看成一个特殊的类,可以完成一些自定义的行为。
我们来看看service的源代码来帮助理解注解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m7uZz6HV-1604221023967)(https://style.youkeda.com/img/ham/course/j4/service1.svg)]
可以看出一个注解是由多个注解组合而成的
target:它自身也是一个注解,拥有数组属性,用于设定该注解的作用目标范围,因为是数组,所以可以作用多个范围
我们来列举几个常用的范围:ElementType.TYPE 表示可以作用于类,接口类,枚举类上
ElementType.FIFLD 表示可以作用于类的属性上
ElementType.METHOD表示可以作用于类的方法上
ElementType.PARAMETER表示可以作用于类的参数
例如
@Target({ElementType.TYPE,ElementType.METHOD}) //这个表示可以作用于类和方法上@Service //作用于类上
public class MessageServiceImpl implements MessageService{public String getMessage() {return "Hello World!";}}public class MessageServiceImpl implements MessageService{@ResponseBody //作用于方法上public String getMessage() {return "Hello World!";}}
retention
它自身也是一个注解,用于声明该注解的生命周期,即是在编译和运行的哪个阶段有效,它有三个值可以选择
1:SOURCE:纯注释作用
2:CLASS:编译阶段有效
3:RUNTIME:运行时有效
例子:
@Retention(RetentionPolicy.RUNTIME) //在运行阶段有效
document
它自身也是一个注解,它的作用是将注解中的元素包含到javaDoc文档中,一般都需要添加这个注解。
@interface
作用是声明当前的Java类型是注解,固定语法
annotation属性
String value() default ""; //String 类型 value名称
Annotation 的属性有点像类的属性一样,它约定了属性的类型(这个类型是基础类型:String、boolean、int、long),
和属性名称(默认名称是 value ,在引用的时候可以省略),default 代表的是默认值。有了这个属性,我们就可以正式的引用一个 Annotation 啦,比如import org.springframework.stereotype.Service;@Service //也可以写成 @Service(value="Demo") 或@Service("Demo")
public class Demo {}
annotation属性可以有多个
例如
@AliasFor("name") //name表示别名,表示用这个别名 也可以访问到这个属性String value() default "";@AliasFor("value")String name() default "";
2.2spring bean
Ioc(inversion of control,控制反转)容器是spring框架最核心的组件,没有ioc容器就没有spring框架。它可以减少计算机代码之间的耦合度
在spring框架中,通过依赖注入(DI)来实现ioc
在spring的世界中,所有的java对象都被称为bean,构成应用程序主干和由ioc容器管理的对象都被称作beans,beans和它们之间的依赖关系反应在容器
配置的元数据中,几乎所有的bean都是由接口+实现类完成的,用户想要获取bean的实例直接从ioc容器获取就可以了,不需要关心实现类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZvmLW5p-1604221023970)(https://style.youkeda.com/img/ham/course/j4/ioc.svg)]
spring配置元数据的方式有两种: 基于xml
基于annotation (主流)
我们以主流annotation方案来做讲解:
我们可以通过一个接口很容易的从ioc容器获得bean对象
org.springframework.context.ApplicationContext
annotation类型的ioc容器对应的类是
org.springframework.context.annotation.AnnotationConfigApplicationContext
我们在启动java程序之前必须先启动ioc容器
ApplicationContext context = new AnnotationConfigApplicationContext("fm.douban"); //启动ioc容器,并且加载fm.douban下的bean(只要引用了spring注解的类都会被加载)
AnnotationConfigApplicationContext这个类的构造函数有两种
1AnnotationConfigApplicationContext(String …basePackages)根据包名实例化(常用)
2AnnotationConfigApplicationContext(Class clazz)根据自定义包名扫描行为实例化
spring bean的注解有以下几种
org.springframework.stereotype.Service //@Service代表 service bean
org.springframework.stereotype.Component //@Component 通用的bean注解 其余三个注解都扩展于Component
org.springframework.stereotype.Controller //@Controller 作用于web bean
org.springframework.stereotype.Repository //@Repository 作用于持久化相关bean
2.3 spring bean (下)
ioc容器可以看成一个大工厂,我们不关心如何生产产品,只需要知道如何使用产品即可
依赖注入第一步是启动ioc容器:(ApplicationContext context =
new AnnotationConfigApplicationContext(“fm.douban”);)
第二步是完成依赖注入行为
依赖注入是一种编程思想,简单来说就是一种获取其他实例的规范
我们用一个实例来理解依赖注入的方法和作用:在SujectServiceImpl实现类中引入SongService实例,调用方法
不使用依赖注入时的引用
public class SubjectServiceImpl implements SubjectService {private SongService songService;//缓存所有专辑数据private static Map<String, Subject> subjectMap = new HashMap<>();static {Subject subject = new Subject();//... 省略初始化数据的过程subjectMap.put(subject.getId(), subject);}@Overridepublic Subject get(String subjectId) {Subject subject = subjectMap.get(subjectId);//调用 SongService 获取专辑歌曲List<Song> songs = songService.list(subjectId);subject.setSongs(songs);return subject;}public void setSongService(SongService songService) { this.songService = songService;}
}使用依赖注入时引用
import fm.douban.model.Song;
import fm.douban.model.Subject;
import fm.douban.service.SongService;
import fm.douban.service.SubjectService;
import org.springframework.beans.factory.annotation.Autowired; //对比2
import org.springframework.stereotype.Service; //对比1 import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service //对比1 新增了@Service 注解
public class SubjectServiceImpl implements SubjectService {@Autowired //对比2 在变量上新增了@Autowired注解private SongService songService;//缓存所有专辑数据private static Map<String, Subject> subjectMap = new HashMap<>();static {Subject subject = new Subject();subject.setId("s001");//... 省略初始化数据的过程subjectMap.put(subject.getId(), subject);}@Overridepublic Subject get(String subjectId) {Subject subject = subjectMap.get(subjectId);//调用 SongService 获取专辑歌曲List<Song> songs = songService.list(subjectId);subject.setSongs(songs);return subject;} //对比3 删除了setSongService方法
}
依赖注入总结:依赖注入让我们得到其他bean实例变得十分简单,只需要添加注解就可以了
我们渐渐可以发现spring的理论十分复杂,但是它的应用却十分的简单
2.4 spring resourse(上)
我们先来了解一下在java工程中文件的三种情况:1 文件在电脑的某个位置 例如d:/mywork/a.doc
2 文件在工程目录下 例如:mywork/toutiao.png
3 文件在工程的src/main/resources目录下,我们知道这是maven工程存放文件的地方
对于第一第二种情况,我们可以使用file对象直接读写,但是第三种情况file对象读取不到,因为文件已经在jar里了。
计算机系统和java系统是有差别的,maven工程编译后会自动去掉src/main/java、src/main/resources 目录
classpath:java内部的文件路径
java内部的文件路径指定的文件可以被解析为inputstream对象,可以借助java io读取
例子
<dependency> //添加依赖 commons-io库<groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version>
</dependency>public class Test {public static void main(String[] args) {// 读取 classpath 的内容InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");//经常会用到 代码表示从java的类加载器的实例中查找文件 Test.class表示test.java编译后的 java class文件 // 使用 commons-io 库读取文本try {String content = IOUtils.toString(in, "utf-8");System.out.println(content);} catch (IOException e) {// IOUtils.toString 有可能会抛出异常,需要我们捕获一下e.printStackTrace();}}}
2.5spring resource(下)
我们都知道spring最擅长的就是封装,spring resource也不例外,它可以把本地文件,classpath文件,远程文件都封装在resource对象里面然后统一加载
我们来具体看一下
在spring 中定义了一个org.springframework.core.io.Resource类来封装文件,这个类的优势在于它可以支持普通的file,也可以支持class.path文件
并且通过org.springframework.core.io.ResourceLoader服务来完成任意文件的读写,你可以在任意的spring bean中引入ResourceLoader
@Autowired
private ResourceLoader loader;
我们来演示一下spring如何读取文件:
1 先创建一个接口 fileservice
public interface FileService {String getContent(String name);}2创建它的实现类
import fm.douban.service.FileService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; //这个类用来封装文件
import org.springframework.core.io.ResourceLoader; // 这个类读写文件
import org.springframework.stereotype.Service;import java.io.IOException;
import java.io.InputStream;@Service
public class FileServiceImpl implements FileService {@Autowiredprivate ResourceLoader loader; //读写文件@Overridepublic String getContent(String name) {try {InputStream in = loader.getResource(name).getInputStream();return IOUtils.toString(in,"utf-8");} catch (IOException e) {return null;}}
}3 调用这个接口
FileService fileService = context.getBean(FileService.class);String content = fileService.getContent("classpath:data/urls.txt"); //读取classpath内的文件System.out.println(content); String content2 = fileService.getContent("file:mywork/readme.md"); //读取工程目录下的文件System.out.println(content2); String content2 = fileService.getContent("https://www.zhihu.com/question/34786516/answer/822686390");//加载远程文件
System.out.println(content2);
2.6 spring bean 的生命周期
spring bean 提供的生命周期管理极大的提高了工程化的能力,更好的管理了bean.
什么是生命周期?生命周期是生命从开始到结束的整个流程 状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NC841GXA-1604221023972)(https://style.youkeda.com/img/ham/course/j4/beaninstance.svg)]
其中init(初始化)方法是我们必须要掌握的,我们以SubjectServiceImpl为例,通过注解来声明init
import javax.annotation.PostConstruct; // @PostConstruct 注解的路径@Service
public class SubjectServiceImpl implements SubjectService {@PostConstruct //使用这个注解就表示这个方法在spring bean 启动后自动执行public void init(){System.out.println("启动啦");}}
有了init方法 我们就可以把静态代码块的内容放在init里面了
@Service
public class SubjectServiceImpl implements SubjectService {@PostConstructpublic void init(){Subject subject = new Subject();subject.setId("s001");subject.setName("成都");subject.setMusician("赵雷");subjectMap.put(subject.getId(), subject);}}
小结:spring声明周期可以让我们更轻松的初始化一些行为和维护数据
第三章 spring mvc
3.1 spring boot介绍
spring boot是spring团队打造的一个新的面向服务的框架,它让java web工程的创建 运行和部署变得非常容易,我们必须要掌握
spring boot 多了一些工程方案比如下面4点:
1 java web容器的嵌入集成,默认集成了tomcat服务器,不需要再额外部署
2 自定义了工程打包格式,通过一个main方法就可以把工程启动
3 默认集成了你能想到的第三方框架和服务 比如数据库连接
4 提供了标准的属性配置文件,支持应用的参数动态配置
创建spring boot工程
我们可以从官方网站(https://start.spring.io/)完成创建,
参考视频(https://qgt-document.oss-cn-beijing.aliyuncs.com/PY1/11/fm%E5%88%9B%E5%BB%BA%E5%B7%A5%E7%A8%8B.mp4)
注意:创建工程的时候要选择web依赖
3.2 spring controller入门
spring mvc 是java web的一种(说明java web 还是有很多框架的)实现框架
java web的规范就是servlet技术,所有的java web 都实现了 servlet api的定义
我们来了解web服务做了哪些事情
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aN5XkJCW-1604221023973)(https://style.youkeda.com/img/ham/course/j4/springmvc1.svg)]
我们来理一下流程:当一个网页请求到了服务器之后,然后它就会进入spring boot 应用,最后匹配到一个controller(这也是一个bean),然后路由到
一个具体的bean方法,执行完后返回结果,输出给客户端。
可以看出,如果我们能够掌握spring controller就可以自己提供java web 服务了
spring controller(控制器)有三个核心点:
1 bean 的配置:controller注解运用
2 网络资源的加载:加载网页
3 网址路由的配置:requestmap 注解运用
1 controller注解
spring controller本身也是一个 spring bean,只是多了web能力,所以我们只需要在类上添加一个注解@Controller就可以了
import org.springframework.stereotype.Controller;@Controller //注意添加注解 一般不需要特意实现接口
public class HelloControl {}
2 加载网页
在spring boot应用中,网页一般存放在目录src/main/resources/static下
controller一般会自动加载static下的html内容,所以创建网站十分简单
import org.springframework.stereotype.Controller;@Controller
public class HelloControl {public String say(){ //定义返回的类型为Stringreturn "hello.html"; //表示自动加载src/main/resources/static/hello.html文件 ,我们只要把hello.html文件写好即可}}
static子目录 如果hello.html的路径为src/main/resources/static/html/hello.html,
那返回的应该是(return "html/hello.html)这样 才能正确加载
RequestMapping注解
对于web 服务器来说必须要实现的一个功能就是路由:解析url并提供资源给调用者
我们一般把controller类存放在control子包里面
配置路由,我们只需要添加一个注解RequestMapping即可
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; //引入注解@Controller
public class HelloControl {@RequestMapping("/hello") //添加注解public String say(){return "html/hello.html";}}
3.3 Get Request(一)
我们都知道网络中常用两个协议get 和post 一般我们上网看视频图片都是get协议包括我们上节课学的spring controller也是get协议
通过get协议,我们可以动态的渲染网页,还可以掌握参数的解析,通过获取参数我们可以获取特定的代码逻辑
例如https://www.baidu.com/s?wd=test 可以知道我们在百度中搜索了test
我们来看看网址格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6OGjupa-1604221023974)(https://style.youkeda.com/img/ham/course/j4/url.svg)]
url解析非常简单而且重要,url由四个部分组成 协议+域名+路径+参数
获取url 参数
每个url都可以设定自定义的参数,就像百度自定义的参数wd一样
需求:我们现在以id来作为歌单的参数,那么歌单的url应该是这样的
https://域名/songlist?id=xxxx
http://localhost:8080/songlist?id=xxxx //本地电脑开发的url
定义这个参数
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; //引入注解 spring mvc的注解都在这个包里面 直接应用@Controller
public class SongListControl {@RequestMapping("/songlist")public String index( @RequestParam("id") String id){ //定义参数十分简单,只需要添加注解即可 return "html/songList.html";}}
使用这个参数来查找
例子
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class SongListControl {@RequestMapping("/songlist")public String index( @RequestParam("id") String id ){if ("38672180".equals(id)) { //如果有这个参数,就返回songList这个界面return "html/songList.html";} else {return "html/404.html"; //没有就返回我们定义的404界面} }}
获取多个参数
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class SongListControl {@RequestMapping("/songlist")public String index(@RequestParam("id") String id, @RequestParam("pageNum") int pageNum){ //添加多个参数即可return "html/songList.html";}
}
注意点:如果设置了多个参数,那么url访问的时候必须要传递这些参数
小知识:所有的网站和程序都是通过request请求来搭建的。
3.4 get request(二)
@GetMapping
我们在controller那一节学习了@RequestMapping注解来解析url请求路径,这个注解默认支持所有的http method,放开所有的http method这样不是
很安全,我们可以用@GetMapping代替@RequestMappin注解。
import org.springframework.web.bind.annotation.*;@GetMapping("/songlist") //替换掉注解@RequestMapping
public String index(@RequestParam("id") String id,@RequestParam("pageNum") int pageNum){return "html/songList.html";
}我们可以使用下面的url进行访问
http://xxxx/songlist?id=xxx&pageNum=1 //多个参数用&隔开
非必须传递参数
如果想要某个参数不是一定要传递,那么我们可以参数修改注解
@GetMapping("/songlist")
public String index(@RequestParam(name="pageNum",required = false) int pageNum,@RequestParam("id") String id){//参数名称为pageNum required = false表示非必须传递return "html/songList.html";
}我们可以通过下面两种的url进行访问
http://xxxx/songlist?id=xxx&pageNum=1http://xxxx/songlist?id=xxx
输出json数据
我们上面的例子返回的都是html页面,但现在web通用的数据是json,那我们要如何在spring中配置json数据呢?
@GetMapping("/api/foos")
@ResponseBody //添加此注解即可
public String getFoos(@RequestParam("id") String id) {return "ID: " + id;
}
使用以下url进行访问
https://xxxx/api/foos?id=100
小知识:spring mvc会自动的把对象转化为json字符串输出到网页中,我们把这种输出json数据的方法叫做api。
第四章 spring template 入门
4.1 thymeleaf 入门
它是spring选择的默认模板方案,它可以支持多种格式内容的动态渲染
它的原理:数据+模板+引擎渲染出真实的页面
例如
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCnwWTJd-1604221023975)(https://style.youkeda.com/img/ham/course/j4/template1.svg)]
如何初始化thymeleaf?
1添加maven依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
数据传递
因为spring mvc 把页面数据封装的十分完善,我们在方法参数里面引入一个model对象,就可以通过这个model对象传递数据到页面中了。
导入这个model类
import org.springframework.ui.Model;@Controller
public class SongListControl {@Autowiredprivate SongListService songListService;@RequestMapping("/songlist")public String index(@RequestParam("id")String id,Model model){ //引入一个model对象SongList songList = songListService.get(id);//传递歌单对象到模板当中//第一个 songList 是模板中使用的变量名// 第二个 songList 是当前的对象实例model.addAttribute("songList",songList);return "songList";}
}
模板文件
模板文件一般存放在工程的src/main/resources/templates目录下
所以上面的return "songList"会去查找src/main/resources/templates/songList.html文件,且自动匹配后缀.html
注意点:只要工程src/main/resources/templates目录下面放置了文件,那么这些文件就是模板,而且表示被使用
src/main/resources/static 这个不是模板,是静态文件,如果使用了模板就一定要添加依赖
4.2thymeleaf变量
thymeleaf模板语法非常强大,它可以看作一个动态编程语言,它支持许多语言的特性,包括 变量 循环条件等。
th:text属性是模板自定义的html标签属性,th是thymeleaf的缩写,看到th就表示使用了thymeleaf模板语法。
举例:
<span th:text="${msg}">Hello</span> //表示用msg替代了span 标签内的hello内容
<span>msg/span>
注意变量的格式${xxx}
要调用模板的变量需要先设置模板变量
model.addAttribute("msg",str); // 变量名为msg 赋值为str
model.addAttribute("songList",songList); //变量名为songList 变量值为songList
对象变量
我们还可以设置对象变量,然后用.把属性调用出来
model.addAttribute("sl",songList); //在控制器中 设置变量为sl,变量值为songList对象<span th:text="${sl.id}"></span> //在模板中用.调用对象的属性<span th:text="${sl.name}"></span>
4.3thymeleaf循环语句
在thymeleaf模板语法中 th:each 就表示for循环
<ul th:each="song : ${songs}"> //${songs} 是设置的变量 song是遍历后的每一个对象<li th:text="${song.name}">歌曲名称</li> //读取每个歌曲的名称
</ul>
打印列表的索引值
th:each 还有另外一种写法
<ul th:each="song,it: ${songs}"> //it作为可选参数 需要统计数据的时候可以使用<li><span th:text="${it.count}"></span><span th:text="${song.name}"></span></li>
</ul>我们来看看it有哪些统计结果
it.index //从0开始显示行数
it.count //从1开始显示行数
it.size //被迭代对象的大小,获取列表长度时可以用
it.current //表示当前变量,上面等同于song
it.even/odd //布尔值,当前循环是否时偶数 奇数
it.first //布尔值,当前循环是否是第一个
it.last //布尔值,当前循环是否是最后一个
4.4thymeleaf表达式
thymeleaf对动态数据的处理非常方便,它常用于两种场景:
1 字符串处理
2 数据转化
字符串处理:我们举一个时间的例子
<span th:text="'00:00/'+${totalTime}"></span> //我们可以使用' '来包住文本 然后使用+连接变量 否则会报错<span th:text="00:00/+${totalTime}"></span> //报错<span th:text="|00:00/${totalTime}|"></span> //也可以使用|包围住字符串
数据转化:
thymeleaf默认集成了大量的工具类可以方便的进行数据的转化
一般我们使用最多的便是dates,如果想处理LocalDate和LocalDateTime类,可以添加依赖
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId><version>3.0.4.RELEASE</version>
</dependency>此库会默认添加一个工具类temporals
变量和工具类的表达是不同的,变量使用的是${变量名},而工具类是用的是#{工具类}
如果是data
Date dateVar = new Date(); //date型 模板中的使用方法
<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>
如果是LocalDate/LocalDateTime
LocalDateTime dateVar = LocalDateTime.now();<p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd')}"></p><p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日')}"></p><p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p><p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>
一些其他的方法:例如#strings
${#strings.toUpperCase(name)} //把字符串改成全大写
${#strings.toLowerCase(name)} //把字符串改为全小写
${#strings.arrayJoin(array,',')} //把字符串数组拼接为字符串,并以,连接
${#strings.arraySplit(str,',')} //把字符串分隔成一个数组,并以,作为分隔符,比如a,b执行后会变成["a","b"];如果abc没有匹配到,执行后会变成["abc"]
${#strings.trim(str)} //把字符串去空格,左右空格都会去掉
${#strings.length(str)} //得到字符串的长度,也支持获取集合类的长度
${#strings.equals(str1,str2)} //比较两个字符串是否相等
${#strings.equalsIgnoreCase(str1,str2)} //忽略大小写后比较两个字符串是否相等
完整的内置对象
#messages
#urls/uris
#conversions
#dates
#calendars
#numbers
#strings
#objects
#bools
#arrays
#lists
#sets
#maps
#aggregates
#ids
内联表达式可以把变量之间写在html中
<span>Hello [[${msg}]]</span> //[[变量]]这种格式就是内联表达式
th:text和[[]]两种写法都是允许的,看个人习惯
4.5 thymeleaf条件语句
thymeleaf同样也是支持条件语句if/else的
th:if的使用
<span th:if="${user.sex == 'male'}">男</span> //if表达式的值为true时渲染
th:unless代表的是否定条件,表示unless表达式的false时渲染
<span th:unless="${user.sex == 'male'}">女</span>
thymeleaf对下面的表达式都判定为true
1值非空
2值是非0数字
3值是字符串但不是false, off or no
strings逻辑判断
我们常常使用#strings来做逻辑判断和数据处理
检验是否为空
${#strings.isEmpty(name)} //检验字符串
${#strings.arrayIsEmpty(name)} //检验数组
${#strings.listIsEmpty(name)} //检验集合类
contains 检查是否含有某个字符串片段
${#strings.contains(name,'abc')} //检查是否含有abc这个字符串
<p th:if="${#strings.contains(str1,'人')}">匹配到人这个字啦</p> //检查是否含有人这个字
#strings判断语法的其他几个常用方法
${#strings.containsIgnoreCase(name,'abc')} //先忽略大小写字母,然后去判断是否包含指定的字符串
${#strings.startsWith(name,'abc')} //判断字符串是不是以 abc 开头的
${#strings.endsWith(name,'abc')} //判断字符串是不是以 abc 结束:::的
第五章 spring template进阶
5.1 thymeleaf表单
实例演示:
1图书模型
public class Book{// 主键private long id; //可以使用long类型 这样id更容易搜索,但也更容易被爬取数据// 图书的名称private String name;// 图书的作者private String author;// 图书的描述private String desc;// 图书的编号private String isbn;// 图书的价格private double price;// 图书的封面图片private String pictureUrl;// 省略 getter、setter
}
2页面开发 :我们现在thymeleaf模板里面创建一个名为addBook.html的模板,然后再创建一个BookControl的控制器
<form><div><label>书的名称:</label><input type="text" /></div><div><label>书的作者:</label><input type="text" /></div><div><label>书的描述:</label><textarea></textarea></div><div><label>书的编号:</label><input type="text" /></div><div><label>书的价格:</label><input type="text" /></div><div><label>书的封面:</label><input type="text" /></div><div><button type="submit">注册</button></div>
</form>配置control
package com.bookstore.control;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;@Controller
public class BookControl {// 当页面访问 http://localhost:8080/book/add.html 时// 渲染 addBook.html 模板@GetMapping("/book/add.html")public String addBookHtml(Model model){return "addBook";}
}
3 保存书籍 新增一个control来处理保存书籍的逻辑
@PostMapping("/book/save") //它只接受http method为post请求的数据public String saveBook(Book book){books.add(book); //接受book数据并储存到books对象中return "saveBookSuccess"; //返回模板下的saveBookSuccess页面}新增一个saveBookSuccess 页面
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>添加书籍</title>
</head><body><h2>添加书籍成功</h2>
</body></html>
4 form 表单:对html页面需要做一些修改
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>添加书籍</title>
</head><body><h2>添加书籍</h2><form action="/book/save" method="POST"> // 1 action属性设置为后端的请求路径 method设置为post<div><label>书的名称:</label><input type="text" name="name"> //2 input 的name属性后面的值要和模型类book保持一致</div><div><label>书的作者:</label><input type="text" name="author"> //保持一致</div><div><label>书的描述:</label><textarea name="desc"></textarea></div><div><label>书的编号:</label><input type="text" name="isbn"> //保持一致</div><div><label>书的价格:</label><input type="text" name="price"> //保持一致</div><div><label>书的封面:</label><input type="text" name="pictureUrl"> //保持一致</div><div><button type="submit">注册</button></div></form>
</body></html>
5.2 spring validation(一)
我们可以借助spring validation来完成表单数据的验证
jsr是java规范提案,它向jcp提出新增一个标准化技术规范的请求。
jsr380是bean(实例化后的pojo类)验证的规范,下面是它的依赖,都已经自动配置好了,平时不需要我们手动配置
<dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>2.0.1</version>
</dependency>
validation注解:jsr380的注解可以直接作用到bean的属性上
@NotNull //不允许为 null 对象
@AssertTrue //是否为 true
@Size //约定字符串的长度
@Min //字符串的最小长度
@Max //字符串的最大长度
@Email //是否是邮箱格式
@NotEmpty //不允许为null或者为空,可以用于判断字符串、集合,比如 Map、数组、List 常用
@NotBlank //不允许为 null 和 空格
示例
package com.bookstore.model;import javax.validation.constraints.*;public class User {@NotEmpty(message = "名称不能为 null") //注解private String name;@Min(value = 18, message = "你的年龄必须大于等于18岁")@Max(value = 150, message = "你的年龄必须小于等于150岁") //注解可以不止一条private int age;@NotEmpty(message = "邮箱必须输入")@Email(message = "邮箱不正确")private String email;// standard setters and getters
}
完成验证:我们先写好pojo类user ,再创建一个user/addUser.html 模板文件,用于管理员添加成员
然后写一个control完成验证 如下
package com.bookstore.control;import com.bookstore.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult; //BindingResult的路径
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;import javax.validation.Valid; //@Valid的路径@Controller
public class UserControl {@GetMapping("/user/add.html")public String addUser() {return "user/addUser";}@PostMapping("/user/save")public String saveUser(@Valid User user, BindingResult errors) { //增加两个参数 注意顺序if (errors.hasErrors()) {// 如果校验不通过,返回用户编辑页面return "user/addUser"; }// 校验通过,返回成功页面return "user/addUserSuccess";}}
最后创建一个user/addUserSuccess.html模板文件,用于返回成功页面
5.3 spring validation(二)
如果验证错误怎么把错误结果返回到模板中?要解决上面这个问题,我们需要解决两个关键点。
1 如何传递数据?
2 如何显示具体的字段错误?
control改造
如果我们想显示具体的字段信息,就需要结合模型来传输,例如上节课的user
@GetMapping("/user/add.html")
public String addUser(Model model) {User user = new User(); //先创建一个user实例model.addAttribute("user",user); //传递到模板中return "user/addUser";
}
user/add/html模板改造,我们取处理错误的状态,增加错误的样式和文案
th:object :让表单验证状态生效
<form action="/user/save" th:object="${user}" method="POST"> //在form标签中新增一个th:object="${user}"属性用于替换对象,使用了这个就不需要每次都编写 user.xxx,可以直接操作 xxx...
</form>
th:classappend :错误提示
必须要现有一个错误的样式
.error {color: red;}然后可以使用th:classpath支持动态管理样式
<div th:classappend="${#fields.hasErrors('name')} ? 'error' : ''"></div> //使用了三元表达式 ${#fields.hasErrors('key')}专门为了验证场景而提供,key 就是对象的属性名称,比如User 对象的 name、age、email 等
<div class="error"> </div> //上面错误信息含有name之后就会变成此标签
th:errors :显示错误信息
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></p> //th:errors="*{age}"属性可以自动取出错误信息
th:field :显示上一次输入的内容
<div th:classappend="${#fields.hasErrors('age')} ? 'error' : ''"><label>年龄:</label><input type="text" th:field="*{age}" /><p th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></p>
</div>
5.4 thymeleaf布局 (layout)
在一个网站里面经常会出现重复的导航底部等内容,我们可以把导航和底部做成布局组件,然后每个页面都套用上
使用th:include + th:replace方案可以帮助我们完成布局的开发
这个方案很简单分成两步:
第一步 :创建布局组件
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>布局</title><style>.header {background-color: #f5f5f5;padding: 20px;}.header a {padding: 0 20px;}.container {padding: 20px;margin:20px auto;}.footer {height: 40px;background-color: #f5f5f5;border-top: 1px solid #ddd;padding: 20px;}</style>
</head>
<body><header class="header"><div><a href="/book/list.html">图书管理</a><a href="/user/list.html">用户管理</a></div>
</header> //这里是头部组件<div class="container" th:include="::content">页面正文内容</div> //这里会被替换为正文 ::content选择器,表示加载当前页面的th:fragment的值<footer class="footer"> //这是底部组件<div><p style="float: left">© youkeda.com 2017</p><p style="float: right">Powered by 优课达</p></div>
</footer></body>
第二步 正文内容页面 我们会把组件加在正文上
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"th:replace="layout"> //要用的布局的名称 不要写错 这里用的是templates/layout.html
<div th:fragment="content"> //fragment(片段) 就是让上面的选择器选择这个片段 注意和选择器值保持一致<h2>用户列表</h2><div><a href="/user/add.html">添加用户</a></div><table><thead><tr><th>用户名称</th><th>用户年龄</th><th>用户邮箱</th></tr></thead><tbody><tr th:each="user : ${users}"><td th:text="${user.name}"></td><td th:text="${user.age}"></td><td th:text="${user.email}"></td></tr></tbody></table>
</div></html>
总结一下 即在布局中添加一个选择器,然后在正文中使用这个布局并选择这个选择器
第六章 spring boot入门
6.1 spring boot ComponentScan
当我们自动注入bean实例的时候,有时候会出现如下报错
Field subjectService in fm.douban.app.control.SongListControl required a bean of type'fm.douban.service.SubjectService' that could not be found.
这是因为spring 在扫描的时候,只会扫描启动类的包及之下的子包,如果所需要的实例不在启动类的包内就会报错。
我们如何来解决这个问题?
有两种方法解决这个问题,第一:
@SpringBootApplication(scanBasePackages={"fm.douban.app", "fm.douban.service"}) //为启动类的注解添加一个参数,告诉系统有需要额外扫描的包
第二种:
@ComponentScan({"fm.service", "fm.app"}) //如果不是启动类,可以使用单独的注解@ComponentScan,同样指定需要额外扫描的包
public class SpringConfiguration {... ...
}
额外知识点:使用@RestController 注解的类,所有的方法都不会渲染thymeleaf模板,而是返回数据。相当于使用@Controller 的类的方法
上面加上@ResponseBody 注解
6.2 spring boot logger运用
在很多情况下,spring打印出的内容具体输出在什么地方我们是不能确定的,所以我们要使用日志系统。
日志系统有两个优势:1日志系统可以轻松的控制日志是否输出
2日志系统可以灵活的设置日志的细节
使用日志系统有两大步骤:1 配置
修改spring boot系统的标准配置文件 application.properties ,增加日志级别配置
logging.level.root=info //表示所有的日志root都为info级别 不输出低级别debug的日志信息
logging.level.fm.douban.app=info //为不同的包定义不同的级别 fm.douban.app 包及其子包中的所有的类都输出 info 级别的日志。
常用的日志级别
优先级 级别 含义及作用
最高 error 错误日志信息
高 warn 暂时不出错,但高风险的警告信息日志
种 info 一般的提示语,普通数据等不要紧的信息日志
低 debug 开发阶段需要注意的调试信息日志
2 编码:实例化日志对象
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;@RestController
public class SongListControl {private static final Logger LOG = LoggerFactory.getLogger(SongListControl.class); //固定写法,定义一个类变量@PostConstructpublic void init(){LOG.info("SongListControl 启动啦"); //在参数种输入日志内容,注意保持级别的一致}
}
6.3 spring boot properties
spring boot框架已经有许多配置,但是部分配置还是要我们自己设置,对于一些特定的情况,我们会在application.properties文件中进行配置
配置文件格式,和上节课一样
配置项名称=配置项值
logging.level.root=info
logging.level.fm.douban.app=info //等号两边不加空格它的一些默认规则:配置项名称能够准确表达意义,作用以及.分隔符相同前缀名的配置项名称放在一起不同前缀名的配置项之间空一行
配置的意义:避免硬编码,解耦
把可变的内容从代码中剥离出来,做到在不修改代码的情况下,修改可变或者常变的内容
自定义配置项
在application.properties文件中设置自定义配置项
song.name=God is a girl //自定义配置项使用自定义配置项
import org.springframework.beans.factory.annotation.Value;public class SongListControl {@Value("${song.name}") //使用@Value注解即可private String songName;
}
第7章 spring session
7.1 cookie
cookie是网络编程中一种最常用的技术,它主要用来识别用户身份,它是临时放在客户端的
cookie有读写两种操作,服务端会返回cookie给客户端,也会读取客户端提交的cookie数据
读cookie:在control类方法中添加一个HttpServletRequest参数,通过request.getCookies()取得cookie数组,然后遍历循环数组。
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;@RequestMapping("/songlist")
public Map index(HttpServletRequest request) { //系统会自动传入方法参数所需要的HttpServletRequest 对象Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("author", songAuthor);Cookie[] cookies = request.getCookies();returnData.put("cookies", cookies);return returnData;
}
使用注解读取cookie
如果知道cookie的名字,就可以通过注解直接读取,不需要再遍历数组
control 类的方法增加一个 @CookieValue(“xxxx”) String xxxx 参数即可,注意使用时要填入正确的 cookie名字
import org.springframework.web.bind.annotation.CookieValue;@RequestMapping("/songlist")
public Map index(@CookieValue("JSESSIONID") String jSessionId) { // 添加一个注解参数 ,填入正确的名字 名字填错了会报错Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("author", songAuthor);returnData.put("JSESSIONID", jSessionId);return returnData;
}
写cookie
control 类的方法增加一个 HttpServletResponse 参数,调用 response.addCookie() 方法添加 Cookie 实例对象即可
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;@RequestMapping("/songlist")
public Map index(HttpServletResponse response) { //系统会自动传入方法参数所需要的HttpServletResponse对象Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("name", songName);Cookie cookie = new Cookie("sessionId","CookieTestInfo");// 设置的是 cookie 的域名,就是会在哪个域名下生成 cookie 值cookie.setDomain("youkeda.com");// 是 cookie 的路径,一般就是写到 / ,不会写其他路径的cookie.setPath("/");// 设置cookie 的最大存活时间,-1 代表随浏览器的有效期,也就是浏览器关闭掉,这个 cookie 就失效了。cookie.setMaxAge(-1);// 设置是否只能服务器修改,浏览器端不能修改,安全有保障cookie.setHttpOnly(false);response.addCookie(cookie);returnData.put("message", "add cookie successfule");return returnData;
}
7.2 spring session api
cookie是临时放在客户端的,有可能会被拦截或者伪造,所以我们一般用session(服务端)来存放重要的信息(用户id,登录状态等)
使用session会话机制时,Cookie 作为 session id 的载体与客户端通信。上一节课演示代码中,Cookie 中的 JSESSIONID 就是这个作用.
JSESSIONID是专门用来纪录用户session的
读操作:
与cookie 相似,从 HttpServletRequest 对象中取得 HttpSession 对象,使用的语句是 request.getSession()。
但不同的是,返回结果不是数组,是对象。在 attribute 属性中用 key -> value 的形式存储多个数据。
假设存储登录信息的数据 key 是 userLoginInfo,那么语句就是 session.getAttribute(“userLoginInfo”)
登录信息类:
登录信息实例对象因为要在网络之中传输,就必须实现序列化接口Serializable,不实现的话就会报错
演示两个属性
import java.io.Serializable;public class UserLoginInfo implements Serializable {private String userId;private String userName;
}
读操作
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@RequestMapping("/songlist")
public Map index(HttpServletRequest request, HttpServletResponse response) {Map returnData = new HashMap();returnData.put("result", "this is song list");// 取得 HttpSession 对象HttpSession session = request.getSession();// 读取登录信息UserLoginInfo userLoginInfo = (UserLoginInfo)session.getAttribute("userLoginInfo"); //读取信息的方法getAttributeif (userLoginInfo == null) {// 未登录returnData.put("loginInfo", "not login");} else {// 已登录returnData.put("loginInfo", "already login");}return returnData;
}
写操作:
登录成功之后,使用setAttribute()方法纪录登录信息到session
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@RequestMapping("/loginmock")
public Map loginMock(HttpServletRequest request, HttpServletResponse response) {Map returnData = new HashMap();// 假设对比用户名和密码成功// 仅演示的登录信息对象UserLoginInfo userLoginInfo = new UserLoginInfo();userLoginInfo.setUserId("12334445576788");userLoginInfo.setUserName("ZhangSan");// 取得 HttpSession 对象HttpSession session = request.getSession();// 写入登录信息session.setAttribute("userLoginInfo", userLoginInfo);returnData.put("message", "login successfule");return returnData;
7.3 spring session配置
cookie 作为session id的载体,也可以修改属性
前置知识点:配置
spring boot 也提供了编程式的配置方式,用于配置bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration //在类上添加@Configuration注解,表示这是一个配置类,系统会自动扫描处理
public class SpringHttpSessionConfig {@Bean //在方法上添加@Bean注解,表示把此方法返回的对象实例注册成bean public TestBean testBean() {return new TestBean();}
}
session配置
依赖库,在pom.xml添加此依赖库
<!-- spring session 支持 -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-core</artifactId>
</dependency>
配置类
在类上额外添加一个注解@EnableSpringHttpSession ,开启 session 。然后,注册两个 bean
CookieSerializer:读写Cookies中的SessionId信息
MapSessionRepository:Session信息在服务器上的存储仓库
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;import java.util.concurrent.ConcurrentHashMap;@Configuration
@EnableSpringHttpSession
public class SpringHttpSessionConfig {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName("JSESSIONID");// 用正则表达式配置匹配的域名,可以兼容 localhost、127.0.0.1 等各种场景serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");serializer.setCookiePath("/");serializer.setUseHttpOnlyCookie(false);// 最大生命周期的单位是分钟serializer.setCookieMaxAge(24 * 60 * 60);return serializer;}// 当前存在内存中@Beanpublic MapSessionRepository sessionRepository() {return new MapSessionRepository(new ConcurrentHashMap<>());}
}
7.4 spring request 拦截器
Spring 提供了 HandlerInterceptor(拦截器)解决当有大量页面页面功能需要判断用户是否登录的情况。
实现拦截器
1 创建拦截器:实现HandlerInterceptor接口,可以在三个地方进行拦截
一 controller方法执行之前,这是最常用的,例如:是否登录的验证就需要在preHandle()方法中处理
二 controller方法执行之后,例如纪录日志 统计方法执行时间等就需要在postHandle()方法中处理
三 整个请求完成之后,不经常用。例如统计整个请求的执行时间的时候就需要在afterCompletion()中处理
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;public class InterceptorDemo implements HandlerInterceptor {// Controller方法执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 只有返回true才会继续向下执行,返回false取消当前请求return true;}//Controller方法执行之后@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}// 整个请求完成后(包括Thymeleaf渲染完毕)@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {}
}
2 实现 webmvcconfigurer
创建一个类实现WebMvcConfigurer,并实现 addInterceptors() 方法。这个步骤用于管理拦截器。
实现类上面要添加一个@Configuration 注解,让框架能自动扫描并处理。
管理拦截器,最重要的是设置拦截范围,常用addPathPatterns("/**") 表示拦截所有的 URL 。
也可以调用excludePathPatterns() 方法排除某些 URL,例如登录页本身就不需要登录,需要排除。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebAppConfigurerDemo implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 多个拦截器组成一个拦截器链// 仅演示,设置所有 url 都拦截registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**");}
}
通常拦截器会放在一个包里,而用于管理拦截器的配置类通常会放在另一个包。
第八章 spring data 入门
###8.1 专属mongodb服务器
mongodb是一个基于分布式文件存储的数据库,特点是可扩展,高性能为web应用提供强大的支持。
1 购买服务器 在滴滴云上(https://www.didiyun.com)购买服务器
2 登录服务器
执行命令
ssh dc2-user@xxx.xxx.xxx.xxx //@ 符号之后输入自己购买的云主机的公网地址
3 安装docker
Docker 是近年来非常流行的虚拟化技术,这节课就不细讲了,主要用 Docker 来安装 MongoDB,自动化程度较高、非常简便。
使用命令行终端登录服务器后,依次输入命令(每一行是一条命令,一行输入完毕立即回车)执行:
sudo yum -y update
sudo yum -y install epel-release
sudo yum -y install docker-io
有的同学购买的云服务器不是用 dc2-user 账户登录的,是用 root 账户登录的。 ssh root@ 登录后,输入命令可以省略掉 sudo
,直接输入 yum -y … 即可。下同。
4 安装mongodb
sudo systemctl start docker
sudo docker version //先启动docker
sudo docker pull mongo:latest
sudo docker images //下载镜像
sudo docker run -itd --name mongo -p 27017:27017 mongo --auth //启动mongodb
sudo docker ps //检查mongodb是否启动成功
sudo docker exec -it mongo mongo admin //登录数据库db.createUser({ user:'admin',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'}]});
db.auth('admin', '123456') //创建一个用户 admin 密码自己设置
5创建数据库
use practice //切换数据库db.createUser({ user:'xxxx',pwd:'xxxxxx',roles:['root']}); //创建读写用户db.auth('xxxx', 'xxxxxx') //认证数据库
6 退出登录
exit
8.2 spring data mongodb配置
pom.xml依赖
如果已经创建工程,可以手动添加依赖库
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency
配置
在src/main/resources/application.properties 文件,增加配置项:# 购买的云服务器的公网 IP
spring.data.mongodb.host=xxx.xxx.xxx.xxx
# MongoDB 服务的端口号
spring.data.mongodb.port=27017
# 创建的数据库及用户名和密码
spring.data.mongodb.database=practice
spring.data.mongodb.username=pppp
spring.data.mongodb.password=111aaa
别忘了替换成自己购买的的云主机 IP 地址,以及 MongoDB 服务的用户名和密码
云服务器安全设置:打开27017端口
8.3 spring data crud
对数据库的操作一定要放在@Service类中,而不能放在@Controller类中。
@Controller 类可以调用 @Service 类的方法,反之则不行,这是 spring mvc 经典设计理念
@Service 类主要用于不易变的核心业务逻辑。
@Controller 类与前端页面紧密配合,调用 @Service 服务读写数据,从而响应前端请求,
1新增数据
import org.springframework.data.mongodb.core.MongoTemplate;@Autowired //使用 @Autowired 注解可以让系统自动注入实例 这里为MongoTemplate实例private MongoTemplate mongoTemplate;public void test() {Song song = new Song();song.setSubjectId("s001");song.setLyrics("...");song.setName("成都");mongoTemplate.insert(song);}
2 查询数据
mongoTemplate.findById(songId, Song.class) //第一个参数为主键id 第二个参数为类名.class
3 修改数据:修改那条数据?
哪个字段修改为什么值?
// 修改 id=1 的数据
Query query = new Query(Criteria.where("id").is("1")); //使用条件对象Criteria构建条件对象Query的实例// 把歌名修改为 “new name”
Update updateData = new Update();
updateData.set("name", "new name"); //调用修改对象Update的方法.set()设置需要修改的字段// 执行修改,修改返回结果的是一个对象
UpdateResult result = mongoTemplate.updateFirst(query, updateData, Song.class); //第三个参数是具体的类
// 修改的记录数大于 0 ,表示修改成功
System.out.println("修改的数据记录数量:" + result.getModifiedCount());
4 删除数据
调用mongoTemplate.remove() 方法即可删除数据,参数是对象,表示需要删除哪些数据。
通过主键来删除数据,是比较好的方式,不容易误删
Song song = new Song();
song.setId(songId);// 执行删除
DeleteResult result = mongoTemplate.remove(song);
// 删除的记录数大于 0 ,表示删除成功
System.out.println("删除的数据记录数量:" + result.getDeletedCount());
8.4 spring data query
只根据主键查询是不能满足需求的,我们会使用多条件查询来进行复杂的查询操作
核心方法
List<Song> songs = mongoTemplate.find(query, Song.class); //第一个参数 查询对象Query实例 第二个参数类名.class表示查询什么样的对象查询方法简单,但查询条件却十分复杂
Query query = new Query(criteria);
Criteria 条件对象一般有两种情况
1 单一条件 返回一个条件对象实例
Criteria criteria1 = Criteria.where("条件字段名").is("条件值")
2 组合条件 根据or或者and将多个字条件组合为复合条件
使用or关系
Criteria criteria = new Criteria();
criteria.orOperator(criteria1, criteria2);
使用and关系
Criteria criteria = new Criteria();
criteria.andOperator(criteria1, criteria2);
orOperator()和andOperator()的参数,都可以输入多个子条件,也可以输入子条件数组
当然,组合条件下也可以多层组合,子条件也可以由组合得来
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Criteria;public List<Song> list(Song songParam) {// 总条件Criteria criteria = new Criteria();// 可能有多个子条件List<Criteria> subCris = new ArrayList();if (StringUtils.hasText(songParam.getName())) {subCris.add(Criteria.where("name").is(songParam.getName()));}if (StringUtils.hasText(songParam.getLyrics())) {subCris.add(Criteria.where("lyrics").is(songParam.getLyrics()));}if (StringUtils.hasText(songParam.getSubjectId())) {subCris.add(Criteria.where("subjectId").is(songParam.getSubjectId()));}// 必须至少有一个查询条件if (subCris.isEmpty()) {LOG.error("input song query param is not correct.");return null;}// 三个子条件以 and 关键词连接成总条件对象,相当于 name='' and lyrics='' and subjectId=''criteria.andOperator(subCris.toArray(new Criteria[]{}));// 条件对象构建查询对象Query query = new Query(criteria);// 仅演示:由于很多同学都在运行演示程序,所以需要限定输出,以免查询数据量太大query.limit(10);List<Song> songs = mongoTemplate.find(query, Song.class);return songs;
}
8.5 spring data分页
分页功能的实现十分简单,只需要调用PageRequest.of() 方法构建一个分页对象,然后注入到查询对象即可。
pagerequest.of()方法第一个参数是页码,从0开始,第二个页码是每页的内容数量
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;Pageable pageable = PageRequest.of(0 , 20); //第一个参数为页码,第二个参数为每页的内容数量
query.with(pageable);
对于分页这个操作,我们要知道总数之后,计算出一共要分多少页。所以还有两个步骤要做:
1 调用count(query, XXX.class)方法查询总数,第一个参数是查询条件,第二个参数表示查询什么样的对象。
2 根据结果,分页条件,总数三个数据,构建分页器对象。
import org.springframework.data.domain.Page;
import org.springframework.data.repository.support.PageableExecutionUtils;// 总数
long count = mongoTemplate.count(query, Song.class);
// 构建分页器
Page<Song> pageResult = PageableExecutionUtils.getPage(songs, pageable, new LongSupplier() {、//第一个参数是查询结果 第二个参数是分页条件对象 第三个参数比较复杂,实现一个LongSupplier 接口的匿名类,在匿名类的 getAsLong() 方法中返回结果总数。方法返回值是一个 Page 分页器对象,@Overridepublic long getAsLong() {return count;}
});
spring 略介绍相关推荐
- spring事务介绍
一.spring事务介绍 spring事务优点 对不同的api进行统一编程模型,如JTA,JDBC,Hibernate,JPA,JDO... 支持声明式事务 简化编程式事务api 对spring数据层 ...
- Spring入门介绍:
Spring入门介绍 Spring诞生: 创建Spring的目的就是用来替代更加重量级的的企业级Java技术 简化Java的开发 基于POJO轻量级和最小侵入式开发 通过依赖注入和面向接口实现松耦合 ...
- Spring之旅—Spring模块介绍
1.0 Spring模块介绍 核心容器(Spring Core) 核心容器提供Spring框架的基本功能.Spring以bean的方式组织和管理Java应用中的各个组件及其关系.Spring使用Be ...
- spring框架介绍_Spring框架介绍
spring框架介绍 这是Spring框架和Spring核心概念的简介. 在本教程中,我们将介绍Spring Framework的主要优点和功能. 在随后的教程中,我们将学习有关Spring和Spri ...
- AOP和Spring AOP介绍
AOP和Spring AOP介绍 文章目录 AOP和Spring AOP介绍 一.AOP简介 二. 传统开发存在的弊端 三. AOP实现原理 四.Spring AOP 五.AOP相关术语 一.AOP简 ...
- Spring Boot 介绍、入门
一,Spring Boot 介绍 1,如果使用 Spring 开发一个"HelloWorld"的 web 应用: • 创建一个 web 项目并且导入相关 jar 包.SpringM ...
- Spring Actuator介绍及使用
Spring Actuator 介绍 我们的微服务在云上部署以后都需要对其进行监控.追踪.审计.控制等.SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应 ...
- DI的概念和实现原理—Spring系列介绍
DI的概念和实现原理-Spring系列介绍 DI和AOP是Spring中的两个核心概念,要学习DI和AOP,首先就需要了解清楚什么是DI,什么是AOP,这篇文章会讲解一下DI的概念和实现原理,不足之处 ...
- Spring框架介绍——第一章
文章目录 1: Spring的进化 2:Spring的功能 3:Spring模块介绍 在所有的框架体系中,Spring 框架是其中最明亮的一颗星星.Spring框架在Java的发展过程中是不可或缺的, ...
最新文章
- asp.net夜话之五:Page类和回调技术
- OpenGL ES 如何能看到一个物体内部和象3dmax中能只显示网格线
- navicate 导出批量插入语句
- android6.0权限管理工具EasyPermissionUtil
- html5实现拖拽上传头像
- GIS案例练习-----------第六天
- Qt 之 QToolBox
- VoLTE呼叫SIP信令分析
- 手机怎么调整照片分辨率?照片清晰度dpi怎么调整?
- 脱水马铃薯的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 二面深信服前端,秋招面经分享
- 微信小程序参数传递获取当前页面的url和参数
- vue修改vant自带的样式_@jie
- linux计划任务1
- Docker 使用--link出现Cannot link to /xxx, as it does not belong to异常
- smtp java 抓包_[Wireshark]_003_电子邮件抓包分析
- 将Chrome插件Momentum背景图片设为桌面壁纸
- 爱壁纸hd电脑版|爱壁纸hd电脑版下载
- 隐私计算技术实现数据要素安全可信流通
- Problem : 12306