df
作用
查看磁盘使用情况
使用说明
1 | # 格式:df [参数] [对象磁盘/分区] |
百 事 可 乐
查看磁盘使用情况
1 | # 格式:df [参数] [对象磁盘/分区] |
JSON是一种十分轻量级的数据交换的格式,我们来讲一下处理JSON字符串的两个库
1 | <dependency> |
将对象转为JSON字符串
1 | ObjectMapper mapper = new ObjectMapper(); |
这时候,页面再次出现乱码问题
第一种原生的解决方案是
1 | @GetMapping(value = "/json1",produces = "application/json;charset=utf-8") |
第二种使用springmvc的配置文件,这段配置只针对jackson库
1 | <mvc:annotation-driven> |
那我们怎么处理时间对象呢?
1 | @GetMapping("/datejson") |
下面是第一种方法
1 | @GetMapping("/datejson") |
这是第二种方法
1 | @GetMapping("/datejson2") |
1 | <dependency> |
主要的三个类
对应JSON对象,可以使用get方法获取JSON对象的值
代表JSON数据
主要用这个类的静态方法去转化JSON字符串和对象
和mybatis不同,在springmvc中,注解开发才是主流,所以接下来都会使用注解来开发。
被@Controller标注的类是一个控制器类,其中的每一个方法都是一个控制器,都各自映射一个url地址
在spring中我们说过,使用注解的时候需要在spring的配置文件中开启注解扫描
1 | <context:component-scan base-package="com.lizhi.controller"/> |
回顾spring中的常用注册bean的注解
1 | @Component 普通组件 |
因为我们配置的DispatcherServlet会拦截所有的请求,但是对于静态资源,我们不需要拦截对他做出处理,所以有了以下配置
1 | <!-- Springmvc不处理静态文件--> |
该注解可以作用于类上面或者方法上面
该方法映射的url地址即为注解中的值
当其作用在类上面的时候,该控制器类中所有的方法的映射路径的前面都会加上该注解中的值
@GetMapping 该注解代表请求是get请求
@PostMapping 该注解代表请求是post请求
@PutMapping 该注解代表请求是put请求
@DeleteMapping该注解代表请求是delete请求
当类上不是@Controller注解而是@RestController时候,该类中所有的方法返回的值,不会跳转视图,而是返回JSON字符串
如果不想让类中所有的方法都返回字符串
则类上面使用@Controller注解,方法上使用@ResponseBody标注此方法返回的结果是一个JSON字符串不要被视图解析器解析
概念:RestFul就是一个资源定位及资源操作的风格。
它不是标准也不是协议,它就只是一个style。基于这种风格设计的软件可以更加简洁,更有层次,并且安全,更易于实现缓存等机制
传统方式操作资源通过不用的参数来实现不同的功能,方法单一,post和get
使用RestFul风格操作资源
本质其实就是通过不同的提交方式走不同的方法
控制器最后直接return字符串的话默认请求转发
如果想要重定向字符串前面加“**redirect:**”即可
如果提交参数的名称和方法的参数名一致的话,
1 | @GetMapping("user/add") |
如果提交参数和方法参数不一致的话
提交数据:http://localhost:8080/user/add?username=lizhi&userage=20
1 | @GetMapping("user/add") |
如果提交的是一个对象
提交数据:http://localhost:8080/user/add
要求提交的表单要和属性的属性名一致,方法参数即可直接使用对象
1 | @PostMapping("user/add") |
该对象在初识mvc的时候已经见过,不再赘述
该对象在使用注解开发的时候也已经见过,这里也不再赘述
ModelMap实现了Model接口,继承了LinkedHashMap,所以它更强大,但是大部分情况下Model会更常用
所有请求直接转发到jsp然后在jsp编写业务代码,然后根据不同的结果跳转不同的页面
Controller
View
Model
SpringMVC的特点:
说白了学它就一个原因,因为它让我们开发变得简单了
原理
我们都学过java web,那时候我们对于前端的请求都是通过一个servlet来处理的,然后在servlet里面,处理前端传来的数据,调用业务逻辑,返回处理之后的数据,然后servlet根据这些数据,进行重定向或者请求转发
一个请求对应一个servlet,这就会导致我们在开发中写了大量的servlet,而且每次都需要在web.xml中注册,十分繁琐。
没有什么是加一层解决不了的,所以,springmvc在servlet之前加了一层来简化我们的开发
SpringMVC的web框架是以围绕DispatcherServlet(前端控制器,调度器,请求分发器)设计的。它的作用就是将请求分发到不同的处理器(方法或者类,下面的初识mvc是以类级作为处理器,但是一般都会使用方法级去做处理器)
下图是DispatcherServlet的结构图,我们可以看到一些在javaweb阶段比较眼熟的类,比如Servlet,GenericServlet,HttpServlet。因此我们可以得出,DispatcherServlet本质就是一个和我们当时写的servlet是一样的,只是他帮我们做了很多事情。
人到中年有三大件,枸杞大枣保温杯。
SpringMVC中也有三大件,分别是
我们来看看这三大件是如何在springmvc中发挥作用的,原理图如下:
核心类图
简要分析
其实此拦截器就是我们在javaweb阶段使用的过滤器,只不过是换了一个名字而已,换汤不换药,本质是一样的。
只不过拦截器是AOP思想的具体应用
过滤器:
拦截器:
实现HandlerInterceptor接口
1 | package com.lizhi.interceptor; |
在Spring核心文件中注册拦截器
1 | <mvc:interceptors> |
UserController
1 | package com.lizhi.controller; |
login.jsp
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
index.jsp
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
结果如下:
我们先尝试一下配置一个过滤器,这是第一种方案
1 | package com.lizhi.filter; |
记得去web.xml中注册
1 | <filter> |
我们尝试后发现,没有任何效果,那既然我们写的过滤器不行
那springmvc给我们写了一个专门处理乱码的过滤器,我们来配置一下,这是第二种方案,很稳妥
1 | <filter> |
测试后发现,还是乱码!!这时候我们可以去看一下tomcat的config目录下的server.xml,找到connector标签并修改属性
1 | <Connector port="8080" protocol="HTTP/1.1" |
发现还是不行,我们去找一个其他大佬自己写的过滤器试一试,这是第三种方案
1 | package com.lizhi.filter; |
结果测试发现,还是不对!其实乱码的问题到这里一般都会解决了,但是为什么还是不行呢?
其实是在注册filter的时候就出错了,接下来看下面的正确配置,大家可以看一下有什么区别,
1 | <filter> |
有的小伙伴可能在一开始就看出来了,没错,就是<<url-pattern>/*</url-pattern>>
这里的拦截路径我们应该使用/*
而不是/
两者有什么区别呢?
前者是拦截一切请求,包括jsp
而后者会拦截除了jsp的所有资源
相信吃了这次亏,大家之后就再也不会忘记这个坑了!而且也了解了大部分乱码问题的解决方案!
第一步:创建普通maven工程
第二步:导入所需依赖
1 | <dependency> |
第三步:添加web支持
第四步:在web.xml中配置DispatcherServlet
1 | <?xml version="1.0" encoding="UTF-8"?> |
第五步:在资源路径下创建springmvc-servlet.xml配置文件,配置处理器映射器,处理器适配器,视图解析器
1 | <?xml version="1.0" encoding="UTF-8"?> |
第六步:创建HelloController实现Controller接口,并注册,id为路径
1 | package com.lizhi.controller; |
第七步:在WEB-INF文件夹下创建jsp文件夹,在jsp文件夹中创建hello.jsp
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
第八步:启动tomcat测试
前四步如上
第五步:在springmvc-servlet中开启注解支持
1 | <?xml version="1.0" encoding="UTF-8"?> |
第六步:创建Controller
1 | package com.lizhi.controller; |
第七步:在WEB-INF文件夹下创建jsp文件夹,在jsp文件夹中创建login.jsp
第八步:启动tomcat测试
如果出现404或者500错误可,尝试以下两种解决方案
1 | <!--在build中配置resources,来防止我们资源导出出现的问题--> |
mybatis的真正强大之处就在于它的语句映射。
正是因为此映射器文件,才使我们减少了将近95%的JDBC代码,使我们更专注于书写SQL语句
映射器文件只有这些顶级元素
该标签对标SQL语句的DQL语句,也就是select语句
1 | <select id="getStudentById" resultType="student"> |
这个select语句映射namespace绑定的接口中的listTeachers方法
resultType定义返回值类型
#{id}是取到方法传入的参数
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
resultType |
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap |
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
useCache |
将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
这三个标签对标SQL语句的DML语句
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
该标签用来定义sql片段,一般用来定义一些可复用的SQL语句片段,使用include标签以在其他语句中使用
1 | <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql> |
如果传入一个复杂的对象,行为就会有点不一样了。比如:
1 | <insert id="insertUser" parameterType="User"> |
JDBC 要求,如果一个列允许使用 null 值,并且可能会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)
默认情况下,使用#{}获取参数的值的时候,mybatis会创建一个PreparedStatement对象,通过预编译,生成与#{}出现次数相同的占位符(即 ? )这样更安全并且更迅速。
但是有时候想直接在SQL语句中插入一个字符串,不需要转义此字符串(即不需要占位符),比如ORDER BY语句,这时候可以使用${}语句,这样的话,mybatis就不会转义该字符串了
当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。
举个栗子,如果你想
select
一个表任意一列的数据时,不需要这样写:
1 | @Select("select * from user where id = #{id}") |
而是可以只写这样一个方法:
1 | @Select("select * from user where ${column} = #{value}") |
其中 ${column}
会被直接替换,而 #{value}
会使用 ?
预处理。 这样,就能完成同样的任务:
1 | User userOfId1 = userMapper.findByColumn("id", 1L); |
可以自定义一些复杂的返回值对象
1 | @Data |
1 | <select id="getStudentById" resultMap="studentMap"> |
1 | @Data |
1 | <resultMap id="teacherMap" type="com.lizhi.pojo.Teacher"> |
resultMap的子标签
constructor
用于在实例化类时,注入结果到构造方法中
idArg
- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能arg
- 将被注入到构造方法的一个普通结果id
– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result
– 注入到字段或 JavaBean 属性的普通结果
association
一个复杂类型的关联,许多结果将包装成这种类型
resultMap
元素,或是对其它结果映射的引用collection
一个复杂类型的集合
resultMap
元素,或是对其它结果映射的引用有些情况下,你会使用一个不可变类,即那些很少改变或者基本不变的类,即可以使用构造方法注入
1 | <constructor> |
1 | <id property="id" column="post_id"/> |
这些元素是结果映射的基础。
id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。
这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两个元素都有一些属性:
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 |
column |
数据库中的列名,或者是列的别名。 |
javaType |
一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
jdbcType支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
BIT |
FLOAT |
CHAR |
TIMESTAMP |
OTHER |
UNDEFINED |
---|---|---|---|---|---|
TINYINT |
REAL |
VARCHAR |
BINARY |
BLOB |
NVARCHAR |
SMALLINT |
DOUBLE |
LONGVARCHAR |
VARBINARY |
CLOB |
NCHAR |
INTEGER |
NUMERIC |
DATE |
LONGVARBINARY |
BOOLEAN |
NCLOB |
BIGINT |
DECIMAL |
TIME |
NULL |
CURSOR |
ARRAY |
该标签处理一个对象聚合另一个对象的关系。
mybatis有两种不同方式去加载关联
1 | <resultMap id="StudentMap" type="student"> |
优点:SQL语句写起来方便
缺点:多层SQL嵌套,不易于维护,使用了mybatis的相关插件会导致select语句爆红,看起来不舒服
对于大型数据上,这里有一个“N+1查询问题”,问题描述如下
这样会导致运行成千上万条sql语句,大大增加系统开销,这是我们不希望看到的
1 | <resultMap id="studentMap" type="student"> |
优点:配置resultMap十分方便,逻辑十分清楚,方便查错和维护
缺点:连接查询的SQL语句写起来难度大,
collection可以说是多个类型的关联,所以与association的处理十分相似
1 | <resultMap id="teacherMap" type="teacher"> |
注意:这里需要使用ofType来指定集合的泛型
1 | <resultMap id="teacherMap" type="teacher"> |
首先我们先来看一下到底什么是缓存?
缓存这个词,相信大家对其不会陌生,因为我们在b站,腾讯视频这一类视频APP中都会缓存过一些视频。
那我们可以从中归纳出,缓存就是程序以某种形式将数据保存下来,方便下一次的使用。
那我们为什么需要使用缓存呢?
我们都知道,我们程序员要追求三高,当然不会是高血脂,高血糖,高血压。我们所说的三高是高并发,高可用,高性能。众所周知,对于数据库的操作是比较消耗资源的。因此,多次连接数据库不利于我们达到三高,那么缓存就是会帮助我们达到三高的一种技术。
我们将用户经常查询,并且很少发生改变的数据放入缓存中,当用户下一次查询同样的数据时,就不用再去连接数据库查询数据了,减少了访问数据库的次数,减少了系统开销,从而提高了性能。
在mybatis中,存在着两种缓存机制:一级缓存和二级缓存
该级别的缓存,mybatis是默认开启的,并且无法关闭。
该缓存是sqlSession级别的,也就是说作用域和我们之前谈过的sqlSession的作用域一致
因为用户大部分操作都是查询,但是如果出现了DML语句(即增删改)的时候,可能会对缓存中的数据造成了修改,为了安全起见,当发生增删改操作的时候,一级缓存即刻失效。
当然我们也可以手动清理缓存
该级别的缓存,是默认关闭的,需手动打开
1 | <cache/> |
1 | //可能有的小伙伴会说,该设置默认就是true,不需要写这一行代码 |
注:如果在cache标签中没有加入readOnly=“true”,则需要将模型类实现序列化接口(Serializable)
该缓存是namespace级别的,也就是和应用程序的生命周期一致
附上一张关于mybatis的原理图,仅供参考
AOP为Aspect Oriented Programming的缩写,意思是面向切面编程,是通过预编译和运行期动态代理实现程序功能的统一维护的一种技术。
Spring框架两大重点,AOP和IOC(Inversion Of Control)控制反转
AOP是OOP的延续,是软件开发中的一个热点, 也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行解耦合,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
作用及优势
实际上,AOP的底层是通过Spring提供的的动态代理技术实现的。
在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
常用代理技术
Adivce.java
1 | public class Advice { |
TargetInterface.java
1 | public interface TargetInterface { |
Target.java
1 | public class Target implements TargetInterface { |
ProxyTest.java
1 | public class ProxyTest { |
ProxyTest.java
1 | public class ProxyTest { |
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
Target (目标对象) :代理的目标对象
Proxy (代理) :一个类被AOP织入增强后,就产生一 个结果代理类
Joinpoint (连接点) :所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut (切入点) :所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice (通知/增強) :所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
Aspect (切面) :是切入点和通知(引介)的结合
Weaving (织入) :是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而Aspect采用编译期织入和类装载期织入
需要编写的内容
编写核心业务代码(目标类的目标方法)
编写切面类,切面类中有通知(增强功能方法)
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
AOP技术实现的内容
Spring框架监控切入点方法的执行。
一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
步骤
1 | <dependencies> |
创建目标类和目标接口
创建切面类(内部有增强方法)
将目标类和切面类的对象的创建权交给Spring
在applicationContext.xml中配置中织入关系
测试
1 | execution([修饰符]返回值类型 包名.类名.方法名(参数列表)) |
访问修饰符可以省略
返回值类型、 包名、类名、方法名可以使用星号*代表任意
包名与类名之间一 个点.代表当前包下的类,两个点.. 表示当前包及其子包下的类
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
1 | //代表切口是com.lizhi.aop.Target类的返回void的method的无参数方法 |
名称 | 通知类型 | 说明 |
---|---|---|
前置通知 | aop:before | 指定增强的方法在切入点之前执行 |
后置通知 | aop:after-returning | 指定增强的方法在切入点之后执行 |
环绕通知 | aop:around | 指定增强的方法在切入点之前和之后都会执行 |
异常抛出通知 | aop:throwing | 指定增强的方法在出现切点异常时执行 |
最终通知 | aop:after | 无论增强方式执行是否有异常都会执行 |
around
1 | //ProceedingJoinPoint指的是正在执行的连接点==切点 |
切点表达式的抽取
1 | <apo:config> |
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的对系统设计人员来讲就不那么重要了:
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。接口应有两类:
第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)
一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
面向对象编程指的是,我们考虑问题的时候,以对象为单位,考虑它的属性和方法
面向过程编程指的是,我们考虑问题的时候,以一个具体的流程(事务过程)为单位,考虑它的实现
接口设计与非接口设计是针对复用技术层面的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构