SSM基础学习笔记
SSM框架:
1、界面层---servlet类---SpringMVC
2、业务逻辑层---service类---Spring
3、数据访问层(持久层)---dao类---MyBatis
一、MyBatis框架
MyBatis是JDBC与AOP动态代理相结合的一种SQL映射框架,通过实现SqlSession对象,执行SQL语句。
SQL映射文件的使用:
1、参数传入:
(1)、简单类型参数传入:
@Param("自定义参数名称")
(2)、引用类型参数传入:
2、自定义映射resultMap:
(1)、id:指定主键列的映射关系,
(2)、result:设置普通字段的映射关系
(3)、association:设置多对一的映射关系
(4)、collection:设置一对多的映射关系
3、动态SQL片段:
mybatis提供动态标签:
(1)、<if>:判断条件
(2)、<where>:用来包含多个<if>,即: <where> <if> <if>...</where>
(3)、<foreach>:对数组与集合遍历,foreach使用: List<Integer>
(4)、choose、when、otherwise(相当于于if...else if..else):选择条件
(5)、<trim>:用于去掉或添加标签前后内容
<trim 属性="关键字" >标签的常用属性:
prefix |
在trim标签中的内容的前面添加某些内容 |
prefixOverrides |
在trim标签中的内容的前面去掉某些内容 |
suffix |
在trim标签中的内容的后面添加某些内容 |
suffixOverrides |
在trim标签中的内容的后面去掉某些内容 |
Mybatis使用#{}与${}获取参数值的区别:
1、${}的本质就是字符串拼接,#{}的本质就是占位符赋值
2、${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;在SQL语句中不使用占位符,同时使用Statement进行编译,执行效率低,有sql注入的风险,缺乏安全性。
3、#{}使用占位符赋值的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号;在SQL语句中相当于使用?做占位符,同时使用PreparedStatement进行编译,执行效率高,能够避免sql注入,更安全。
Mybatis的一级缓存和二级缓存:
1、一级缓存是SqlSession级别的缓存。Mybatis默认开启一级缓存。
在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是它只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。
(1)、一级缓存失效的情况:
1)、不同的sqlSession,使用不同的一级缓存;只有在同一个sqlSession期间查询到的数据会保存在这个sqlSession的缓存中;
2)、同一个SqlSession但是查询条件不同;
3)、同一个SqlSession两次查询期间执行了任何一次增删改操作,增删改操作会把缓存清空;
4)、同一个SqlSession两次查询期间手动清空了缓存;
2、二级缓存是mapper级别的缓存,Mybatis默认是关闭二级缓存的。
多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
(1)、开启二级缓存需要注意的问题:
1)、对该表的操作与查询都在同一个namespace(命名空间)下,其他的namespace如果有操作,就会发生数据的脏读。
2)、对关联表的查询,关联的所有表的操作都必须在同一个namespace。
MyBatis缓存查询的顺序:
1、先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
2、如果二级缓存没有命中,再查询一级缓存
3、如果一级缓存也没有命中,则查询数据库
4、SqlSession关闭之后,一级缓存中的数据会写入二级缓存
MyBatis的分页方式:
逻辑分页与物理分页
1、逻辑分页:使用MyBatis自带的RowBounds进行分页,是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
2、物理分页:使用分页插件PageHelper或者自己写sql分页(limit),是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
RowBounds分页方式是否一次性查询全部结果:
RowBounds表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为mybatis是对JDBC的封装,在JDBC驱动中有一个Fetch Size的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行next()的时候,去查询更多的数据。(有效防止内存溢出)
MyBatis的延时加载:
MyBatis支持延时加载,延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫做按需查询。通过设置lazyLoadingEnabled=true即可开启延时加载。
MyBatis的执行器(Executor):
在应用层通过sqlSession执行的各类selectXXX和增删改操作在做了动态sql和参数相关的封装处理后,都被委托给具体的执行器去执行,包括一、二级缓存的管理,事务的具体管理,Statement和具体JDBC层面优化的实现等等。所以执行器比较像是sqlSession下的各个策略工厂实现,用户通过配置决定使用哪个策略工厂。
MyBatis提供了两种类型的执行器,缓存执行器(cacheEnabled)与非缓存执行器(BaseExecutor)
1、非缓存执行器
非缓存执行器分为三种,这三种类型的执行器都基于基础执行器(BaseExecutor)
BaseExecutor实现了大部分通用功能本地缓存管理、事务提交、回滚、超时设置、延迟加载等
(1)、简单执行器SimpleExecutor:
每执行一次update或select就开启一个Statement对象,用完立刻关闭Statement对象;
(2)、可重用执行器ReuseExecutor:
重复使用Statement对象。
(3)、批处理执行器BatchExecutor:
批量更新(update),且必要地方分开其中的select语句,确保动作易于理解
2、缓存执行器
缓存执行器(CachingExecutor)相对于其他执行器的差别在于,首先是在query()方法中判断是否使用二级缓存(也就是mapper级别的缓存)。虽然mybatis默认启用了CachingExecutor,但是如果在mapper层面没有明确设置二级缓存的话,就退化为SimpleExecutor。二级缓存的维护由TransactionalCache(事务化缓存)负责。
半自动ORM(对象/关系数据库)映射工具——MyBatis:
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
全自动与半自动的区别:
1、Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
2、Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
Mybatis常用插件:
1、MyBatisCodeHelperPro
2、Pagehelper分页插件
二、Spring框架:
代码参考:提取码:k34k
1、Spring是容器,用于创建与管理对象(解耦合)
2、核心技术:IOC(控制反转)与AOP(面向切面编程)
Spring的核心技术:
1、IOC(控制反转):
IOC是一种理论与思想,由容器代替开发人员完成对象的管理,IOC技术实现使用依赖注入(DI),底层是反射机制。
2、AOP(面向切面编程):
AOP是动态代理的规范化,在不修改源代码情况下进行功能增强,使用AOP要求在分析项目功能时,找出切面(@aspect),并合理安排切面的执行时间与执行位置。(运用切入点表达式)
IOC控制反转的实现方式:
1、基于XML的DI实现:
在Spring的配置文件中使用<bean>完成属性(set注入与构造注入)赋值。
2、基于注解的DI实现:
在Spring的类上使用注解(@Component),完成属性(@Value简单类型与@Autowired引用类型)赋值。
Spring支持bean的作用域:
Spring beans:即Spring容器所管理的Java 对象
Spring框架支持五种不同的作用域:
属性 |
详解 |
singleton |
在Spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在。 |
prototype |
一个bean可以定义多个实例。 |
request |
每次HTTP请求都会创建一个新的Bean。该作用域仅适用于WebApplicationContext环境。 |
session |
一个HTTP Session定义一个Bean。该作用域仅适用于WebApplicationContext环境。 |
globalSession |
同一个全局HTTP Session定义一个Bean。该作用域同样仅适用于WebApplicationContext环境 |
bean默认的scope属性是“singleton”。
BeanFactory和ApplicationContext的区别:
1、BeanFactory
BeanFactory是spring的原始接口,针对原始结构的实现类功能比较单一,BeanFactory接口实现的容器,特点是在每次获取对象时才会创建对象。
2、ApplicationContext
继承了BeanFactory接口,拥有BeanFactory的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。
主要区别:
BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean 。
Spring框架中bean的生命周期:
ApplicationContext容器中,Bean的生命周期流程如图所示,流程大致如下:
1、首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化,
2、按照Bean定义信息配置信息,注入所有的属性,
3、如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id,
4、如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
5、如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
6、如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法,
7、如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法,
8、如果Bean配置了init-method方法,则会执行init-method配置的方法,
9、如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法,
10、经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,期生命周期就交给调用方管理了,不再是Spring容器进行管理了
11、容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
12、如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。
Resource的查找与加载:
Resource接口是Spring资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成(每个实现类代表一种资源访问策略)。
Spring为Resource接口提供如下实现类:
1、UrlResource:访问网络资源的实现类。
2、ClassPathResource:访问类加载路径里资源的实现类。
3、FileSystemResource:访问文件系统里资源的实现类。
4、ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
5、InputStreamResource:访问输入流资源的实现类。
6、ByteArrayResource:访问字节数组资源的实现类。 这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。
XML的自动装配机制:
根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
1、属性名称自动注入(byName):
Java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。
2、属性类型自动注入(byType):
Java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性是同源关系的,这样的bean能够赋值给引用类型。
同源关系:
(1)、java类中引用类型的数据类型和bean的class的值是一样的。
(2)、java类中引用类型的数据类型和bean的class的值是父子类关系的。
(3)、java类中引用类型的数据类型和bean的class的值是接口和实现类关系的
Spring创建对象提供的注解:
1、@Component:创建对象;
2、@Repository:用于数据访问层即持久层,表示创建DAO对象,能访问数据库
3、@Service:用于业务逻辑层,表示创建Service对象,做业务、事务处理等
4、@Controller:用于界面层即控制层,表示创建控制器对象,接收请求,返回响应
注:@Repository,@Service,@Controller是给项目的对象分层的,都能够创建对象。
基于注解方式实现属性注入:
1、简单类型的DI注入
@Value("属性值")
2、引用类型的DI注入
(1)、默认使用的是byType自动注入:
@Autowired
(2)、使用byName方式:
@Autowired @Qualifier(value="bean的id") //限定词,用于指定注入bean
注:
对于采用@Qualifier("bean的id")注解,需要在多个实现类上分别加上各自的声明:@Service("bean的id")
(3)、默认是byName(先使用byName自动注入,如果byName赋值失败,再使用byType):
@Resource
3、@value使用的注意事项:
(以下问题都会造成,无法注入的问题)(1)、不能作用于静态变量(static)
(2)、不能作用于常量(final)
(3)、不能在非注册的类中使用(类需要被注册在spring上下文中,如用@Service,@RestController,@Component等;
(4)、使用这个类时,只能通过依赖注入的方式,用new的方式是不会自动注入这些配置的。
AOP底层——动态代理:
1、JDK动态代理:
通过反射机制,实现reflect接口(有接口时使用)
具体实现步骤:
(1)、通过实现 InvocationHandler 接口创建自己的调用处理器;
(2)、通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
(3)、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
(4)、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
2、CGLIB动态代理:
通过继承第三方的工具库(无接口时使用)
AOP功能通知(增强):
1、前置通知@Before:在一个方法之前执行的通知
2、后置通知@AfterReturning:在某连接点正常完成后执行的通知。
3、环绕通知@Around:在方法调用前后触发的通知。
4、异常通知@AfterThrowing:在方法抛出异常退出时执行的通知。
5、最终通知@After:当某连接点退出的时候执行的通知(无论是否正常退出)
Spring的声明式事务管理处理方案:
Spring事务是实现方式:编程式事务管理与声明式事务管理
事务要求同时成功,同时失败(提交事务commit,回滚事务rollback)
基于声明式事务管理的处理方案:
1、注解方案(小项目):
Spring框架中使用AOP的环绕机制,利用@Transactional注解增加事务功能。
2、XML配置文件方案(大项目):
使用aspectj框架功能,在Spring配置文件声明事务(管理器对象与类型),实现业务与事务完全分离。
@Transactional实现原理
1、事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令。
(不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚)
2、事务结束时,回滚在第一步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象。
(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)
@Transactional(属性)详解:
1、Isolation类的事务隔离级别详解:
@Transactional(isolation = Isolation.READ_UNCOMMITTED) |
读取未提交数据 |
@Transactional(isolation = Isolation.READ_COMMITTED) |
读取已提交数据 |
@Transactional(isolation = Isolation.REPEATABLE_READ) |
可重复读 |
@Transactional(isolation = Isolation.SERIALIZABLE) |
串行化读 |
2、Propagation类的事务传播行为详解:
@Transactional(propagation= Propagation.传播属性)
传播属性 |
描述 |
REQUIRED |
支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY |
支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED |
支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 |
Spring5新功能:
1、整合日志框架
2、@Nullable注解
3、函数式注册对象
4、整合JUnit5单元测试框架
5、SpringWebflux编程模型的使用:注解式与函数式
三、SpringMVC框架
SpringMVC是容器,基于原生的Servlet,用于界面层,通过前端控制器(DispatcherServlet),对请求和响应进行统一处理。
SpringMVC注解式开发:
1、创建控制器对象:
@Controller
用于界面层,表示创建控制器对象,接收请求,返回响应
2、建立请求映射关系:
@RequestMapping
将请求和处理请求的控制器方法关联起来,建立映射关系。(相关参考 )
(1)、@RequestMapping属性:
属性 |
解释 |
备注 |
value/path |
通过请求的请求地址匹配请求映射 |
属性为字符串类型的数组,且必须设置 |
method |
通过请求的请求方式(GET、PUT、POST、DELETE 、PATCH)匹配请求映射 |
属性为RequestMethod类型的数组: 例:RequestMethod.GET等 |
produces |
指定返回的内容类型 |
了解 |
consumes |
指定处理请求的提交内容类型 |
了解 |
header |
通过请求的请求头信息匹配请求映射 |
根据header请求头信息去过滤请求映射所匹配的请求 (了解) |
params |
通过请求的请求参数匹配请求映射 |
根据param请求参数信息去过滤请求映射所匹配的请求 (了解) |
(2)、@RequestMapping派生注解:
用于处理指定请求方式的控制器方法
注解 |
解释 |
@GetMapping |
处理get请求的映射,通常对应查询操作(select) |
@PostMapping |
处理post请求的映射,通常对应新增操作(insert) |
@PutMapping |
处理put请求的映射(更新整体),通常对应修改操作(update) |
@DeleteMapping |
处理delete请求的映射,通常对应删除操作(drop) |
@PatchMapping |
Patch方式是put方式的一种补充(更新局部) |
3、获取请求参数
@RequestParam
将请求参数和控制器方法的形参创建映射关系
(1)、@RequestParam属性
属性 |
备注 |
value |
指定为形参赋值的请求参数的参数名 |
required |
设置是否必须传输此请求参数,默认值为true,若设置为false,没有传输时注解所标识的形参的值为null |
defaultValue |
不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值 为""时,则使用默认值为形参赋值 |
(2)、@RequestParam相同用法的注解:
注解 |
解释 |
备注 |
@RequestHeader |
将请求头信息和控制器方法的形参创建映射关系 |
三个属性:value、required、defaultValue |
@CookieValue |
cookie数据和控制器方法的形参创建映射关系 |
三个属性:value、required、defaultValue |
4、报文信息转换:
(1)、将请求报文转换为Java对象
1)、@RequestBody注解:
获取请求体
2)、RequestEntity类:
封装请求报文的类,通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
(2)、将Java对象转换为响应报文
1)、@ResponseBody注解
将java对象转为Json格式的响应数据
//可采用: @RestController = @Controller + @ResponseBody
2)、ResponseEntity类
返回值就是响应到浏览器的响应报文
5、集中异常处理机制:
//将当前类标识为异常处理的组件 @ControllerAdvice //用于设置所标识方法处理的异常 @ExceptionHandler
过滤器与拦截器的区别
1、创建方式不同:
(1)、过滤器是servlet中tomcat服务器创建的对象,
(2)、拦截器是框架中SpringMVC容器中创建的对象
2、实现方式不同:
(1)、过滤器实现Filter接口对象,
(2)、拦截器实现HandleInterceptor接口对象
3、执行时间不同:
(1)、过滤器是一个执行时间点(请求处理前)
(2)、拦截器有三个执行时间点(请求处理前preHandle(),控制器方法执行后postHandle(),请求处理完成后afterCompletion())
多个拦截器的执行顺序:
preHandle()返回值为boolean类型:
返回true表示放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
1、若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关: preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
2、若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false 的拦截器之前的拦截器的afterComplation()会执行;
SpringMVC执行流程:
1、浏览器提交请求到中央调度器
2、中央调度器(也叫前端控制器)直接将请求转给处理器映射器。
3、处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
4、中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
5、处理器适配器调用执行处理器。
6、处理器将处理结果及要跳转的视图封装到一个对象ModelAndView中,并将其返回给处理器适配器。
7、处理器适配器直接将结果返回给中央调度器。
8、中央调度器调用视图解析器,将ModelAndView中的视图名称封装为视图对象。
9、视图解析器将封装了的视图对象返回给中央调度器
10、中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
11、中央调度器响应浏览器。