Spring/MyBatis

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring/MyBatis相关的知识,希望对你有一定的参考价值。

Spring

BeanFactory是Spring工厂,IOC工厂
ApplicationContext是Spring容器,IOC容器
SpringMVC是基于Spring来实现的
SpringBoot是方便启动的,让整个项目快速运行

SpringBoot演示

新建Spring项目,一般在网页start.spring.io建立,不用在idea中建:

然后genrate,会生成一个压缩包,下载到本地

然后导入IDEA中,看一下maven设置,这里有两个特别的设置,可以改变目录的展现形式
关键是pom.xml

新建一个子包,controller
新建一个类AlphaController,用来处理前端请求

做以下注释:

然后运行main方法:

在网页上显示:

为什么可以启动这么快?
可见项目依赖了很多包,但是并没有一个个去依赖
怎么实现的?因为刚才在start里填写了web,最终会映射到pom.xml里,然后spring帮我们把包根据应用场景整合在一起


独立运行

自动配置后面说
里面内嵌了tomcat,tomcat是以包的形式存在这里面的

Spring优点(IOC)

IOC的好处:如果没有IOC,对象得自己创建,对象依赖得自己设置,对象得自己管理,自己管理很不靠谱。
IOC可以使对象做到可插拔

dao是数据访问层的代码

IOC叫控制反转,是将bean的管理权力交给Spring来做,Spring做这个事有什么好处,通俗来说就是降低bean之间的耦合度,使bean做到可插拔,撤换掉某个bean很方便

单例的用IOC来管理,实体的数据变化多,可复用,每个用户都不一样,一般不用IOC管理

Controller、Service、Repository功能是一样的, 只是语义有区别,controller用在控制层,service是服务,repository是数据层

要调谁就叫Autowired,而且调的是接口,具体要注入谁是Spirng决定的
Controller一般没有接口,因为Controller是页面调的,要换的话就重写一个Controller

PathVariable的意思是从路径里,把id解析出来,然后传给这个方法的参数id
ResponseBody表示要把最后的结果做成字符串返回给页面
返回的是json格式,就是map的形式,{id:1,username:’’,password:’’}

IOC不止能把bean注入给别人,还能管理bean的生命周期和作用域
可以看到这里,创建了个init方法,然后加了一个注解 PostConstruct,表示在初始化后(构造方法之后)执行这个方法
创建了一个destroy方法,加了注解PreDestroy,表示在销毁前执行这个方法

怎么体现是单例的:
默认创建的是单例的
在两个controller中都依赖UserService,但是运行只会生成一个对象
如果加了Scope(“prototype”)注解,表示作用域,就会创建两个对象,因为两个Controller都装配了Service。
prototype表示多个实例
Scope作用域默认是singleton单例,一般情况都是单例

IOC管理bean就是通过BeanFactory管理的
BeanFactory是比较底层的API,是开发框架的时候,用的
ApplicationContext是子接口,应用相关,功能比较全,开发者一般用这个

ApplicationContext核心的功能就是获取bean

创建spring项目会自带一个“项目名词” + Application的类,整个项目从这里启动
main方法中传入这个类的对象class,表示配置文件,让spring自动配置

底层做的事:
1.加载配置文件,加载resources下的application.properties配置文件
2.启动Tomcat
3.创建Spring容器
4.自动扫描bean并将其装配到容器里

AOP

面向切面编程,面向方面编程
解决公用的需求,比如很多组件要做同样的事情,以一个低耦合、可插拔的方式解决,比如五个bean都要记录日志,都要事务管理,可以以AOP统一去做

因为要加一个注解Aspect
虽然现在已经有了aop的包了,可以用spring原生的方式去调用,也可以用方面的注解,但是aspect的注解不是自带的,默认是没有的,所以需要pom.xml里添加一个包:

AOP:
首先是目标组件,影响哪些bean,要对哪些bean做处理
第二个是连接点,JoinPoint,Spring里就是方法,目标是对象,连接点就是对象的方法。例如哪些个方法需要记录日志
这两个东西需要在写程序前分析好
目标组件和连接点表示程序中定位的问题

Aspect方面组件,表示写的这个类有特殊的含义
Aspect是一个切面,是AOP的组件,能影响很多个bean
哪些bean的哪些方法是我们的目标,用PointCut切点表示,service..*(…)表示service包下的所有类的所有方法
PointCut表示用来影响谁,表示行为发生的地点

pointcut拦截完以后,需要处理的逻辑是什么时候执行?
Advice表示通知,比如记日志这个事情,是在执行逻辑之前记还是之后,还是抛出异常时记录,表示行为发生的时机
但是怎么影响到目标组件呢,需要将Aspect的这些代码织入到连接点,让代码生效

织入有三种方式,如下

但是在方面里写的代码为什么就在连接点调用了呢,关键就是织入运行的时候,Spring中的AOP给目标Service生成一个代理对象,然后在代理对象里怎么操作都可以,在任何位置上添加代码都可以。所以底层执行的时候不是我们写的service,而是一个代理类
面试问,但是用的比较少,因为都被事务的组件实现了

切点位置:

也可以把这些日志写在一个方法里,但是一般不建议这样写,容易犯错:

SpringMVC

Servlet是java针对web开发制定的一个标准,服务器Tomcat是支持这个标准的
我们开发一个Servlet是要注册给Tomcat,Tomcat去调这个东西

前端发请求访问后台代码,所有的请求在SpringMVC框架下都是交给DispatcherServlet去统一处理。它接收到一个请求,请求中包含一个路径,比如要访问user/detail/…访问item什么什么。需要解析这个路径背后是由哪个组件,哪个Controller去处理
那么怎么去找呢,它依赖于HandlerMapping,这个组件是用来扫描代码中哪些Bean上是带有Controller注解的,然后把它们先存储起来;因为扫描那个注解就知道了requestmap路径是什么,对应的方法是什么,这里面存储的是路径和方法的对应关系

DispatcherServlet询问HandlerMapping路径应该用什么方法去处理,HandlerMapping给出一个结果
HandlerMapping在启动的时候就去扫描,将路径方法对应存储起来,用的时候返回一个处理这个请求的链,能处理这个请求的往往不是一个组件,而是多个组件。这个链中包含了Controller和拦截器

DispatherServlet现在知道请求是由谁来处理,但是还要得到Controller的实例,这时需要HandlerAdapter适配器,能够处理这个问题一个适配的组件,它里面持有了对Controller的引用,可以理解为Controller被它调用,或者被它持有
所以DispatcherServlet通过HandlerAdapter去调Controller,正常情况下Controller返回html,Spring里面默认是通过ModelAndView返回的html
现在有了Model 和View,那么它去找谁把模板和视图组织在一起呢,找另外一个核心组件ViewResolver,视图的解析器,它是依赖于ModelAndView的
ViewResolver通过ModelAndView知道了视图在哪里,数据是什么,合成一个前端需要的html,html用一个View组件做封装,由View组件给前端做一个相应,即response

DispatcherServlet起到一个协调的作用
第一步,通过HandlerMapping获取路径对应的组件Controller
第二步,通过HandlerAdapter去调用Controller,得到ModelAndView
第三步,得到的结果由ViewResolver处理,由View封装,向前台响应具体内容

有的时候用了拦截器能够对Controller形成拦截的话,这个拦截器会在三个地方,产生作用
1.在调HandlerAdapter之前拦截,
2.在调HandlerAdapter之后拦截,
3.视图响应数据以后,整个流程执行回DispatcherServlet过程中,做最后一层拦截(finally中)

拦截器可有可无,如果有的话,和AOP类似,能力比较强

但是在前后端分离的项目中,不用返回html,返回的是json,不用ViewResolver去处理,数据由HandlerAdapter直接处理返回,因为字符串简单;所以如果返回的json,那么HandlerAdapter直接向前台做响应就可以

那么什么时候直接做响应呢,是在Controller方法上加上了@ResponseBody注解的时候,由HandlerAdapter直接做响应

DispatherServlet是自动注册给容器的,已经被注册给Tomcat了,看doDispatch方法,派发请求,可以把它当做一个路由器
tomcat会把请求和响应都给这个方法

调用getHandler方法返回HandlerExecutionChain,封装了路径和bean的关系

接下来通过getHandlerAdapter获取了HandlerAdapter

每个HandlerMapping都能返回一个HandlerExcutionChain

然后调用handle方法,返回ModelAndView

ModelAndView在这里被处理

在这个方法中,如果mv不为空,会去渲染

MyBatis

引入依赖,数据库连接池是用的阿里的druid,因为是做电商


连接池怎么连接数据库,需要把连接参数给它
第一行是mybatis的配置,声明sql文件在哪里
classpath是target目录,是编译后的代码
源代码在编译后在target下是以一定形式组织的,classpath访问的是编译后的文件
xml不会编译,是搬运过来的

连接池需要配的是驱动类,mysql连接的url,用户名,密码,连接池的核心类(type)

在pom.xml中配置划线的插件

配置文件

这里是对数据库增删改查的操作(生成增删改查的代码)
table标签,指定表名是什么
domainObjextName 希望生成的实体类是什么
后面一行很多false,是说许多生成的东西不需要,就不要生成
generatedKey 主键是什么,自增的
字段有特殊的处理,columnOverride,这里price是double类型,计算精度会有损失,所以这里映射成BigDecimal

如何让这个代码生效,可以在命令行或者终端,用maven去构建:
(用这句话去生成代码)

然后生成这样一个实体

在DAO下生成了一个ItemMapper,这里生成了一些增删改查的方法(根据主键)
如果需要别的方法,根据这些方法添加别的方法就行了
这个接口不需要实现,也是用动态代理实现的,在运行的时候会帮你生成一个实现类

但是生成实现类的时候,需要知道底层的sql怎么写,查询增加怎么写?
是在一个配置文件中写的,在这个配置文件中指出相关联的接口
这个文件也是生成的,里面的sql语句也是生成的,可以在这个基础上修改

可以看到这个配置文件中有delete,insert标签
id就是刚刚接口中的方法,然后在标签中,增加了对应的sql语句

这里用 # 可以预编译,会变成?,编译以后再执行,底层是用的PreparedStatement
如果使用$就不会编译,就会直接拼接,就会有SQL注入的风险

这里需要在接口上加一个@Mapper,方便Spring去管理这个bean,这是MaBatis的要求
和之前的Repository是同义的

MaBatis的原理

第一个核心组件:SqlSessionFactory,通俗点就是session工厂
在mabatis体系中,session相当于数据库的连接
我们执行增删改查等数据库的操作,就是通过sqlSession来执行的
sqlSession是通过SqlSessionFactory来创建的,sqlSession依赖于SqlSessionFactory去构建

sqlSession封装的是底层的数据库连接connection,所以创建连接的时候,它需要访问到DataSource,获取连接,封装于其中
那么DataSource在哪里,从哪里读DataSource呢,是从配置类中Configuration读取,配置类的信息是从配置文件中加载的

那么我们需要写的Mapper接口(其实是应该我们写,只不过是借用了工具自动生成了)
Mapper接口和一个Mapper.xml配置文件有对应关系,一个mapper对应一个xml

mapper是一个接口,它的实现类是程序运行的时候,由MaBatis动态创建的,是一个动态代理的机制,
那么谁去做这个代理,谁来实例化这个接口,是MapperProxy,是一个代理类,它去调配置文件,明确要执行什么样的sql语句
实现接口的时候,需要获取 sqlSession,利用这个sqlSession执行增删改查的方法,所以它依赖于SqlSession

那么SqlSessionFactory是什么时候去创建SqlSession呢,这里是自动配置的,启动服务器,就自动创建好了
自动配置的类叫做MyBatisAutoConfiguration,在程序启动的时候,自动的去初始化,创建SqlSessionFactory

1.所以整个过程的第一个环节是自动配置,一启动,自动配置类生效,创建SqlSessionFactory
2.SqlSessionFactory要想创建sqlSession,必须读取数据源,需要依赖于配置类Configuration,这个配置类其实读取的是application.properties这个文件
3.Configuration也会去看mapper.xml都有哪些,加载Mapper.xml的配置信息,如下图
sqlSessionFactory去加载和matabis配置有关的内容

那么Mapper接口什么时候去实现,还是通过自动配置类去驱动的,底层还有一个组件,是扫描这个接口的组件,Mapper.xml是被Configuration配置类扫描的,Mapper接口是被MapperScannerConfigurer扫描的,它会去扫描Mapper,看哪些类有Mapper注解,生成对应的动态代理MapperProxy
4.配置类去调用MapperScannerConfigurer扫描带有Mapper注解的接口,然后去实现它

所以程序中,哪个地方Autowired注入了这个接口,它给我们传的是MapperProxy这个东西


底层代码

先看DateSourceAutoConfiguration,Spring会去扫描注解,看这些注解条件去加载Bean

//第一个注解,表示这是一个配置类,那么Spring在启动的时候就会实例化它,和component、controller类似,但是这是一个配置类,不是在什么层
@Configuration(
    proxyBeanMethods = false
)
//这个注解的意思是,在程序中有DataSource类的时候,这个配置才被加载
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
//没有这个东西的时候会加载
@ConditionalOnMissingBean(
    type = {"io.r2dbc.spi.ConnectionFactory"}
)
//启用这个配置文件,这个类就是刚才说的Configuration,配置文件的类,全局的配置类
//这个自动配置的bean会用到这个全局的配置类
@EnableConfigurationProperties({DataSourceProperties.class})
//当前bean在调用的时候,会先把这两个bean实例化好,再导入
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
    public DataSourceAutoConfiguration() {
    }

    static class EmbeddedDatabaseCondition extends SpringBootCondition {
        private static final String DATASOURCE_URL_PROPERTY = "spring.datasource.url";
        private final SpringBootCondition pooledCondition = new DataSourceAutoConfiguration.PooledDataSourceCondition();

        EmbeddedDatabaseCondition() {
        }
        .....

这个前缀叫spring.datasource,所以在配置文件里,以这个前缀开头的东西会被加载到这个bean里(如下图连接池的东西,会被这个DataSourceProperties读取到)

刚刚那个自动配置类是抽象的东西,druid是具体的东西
这个配置类会在DataSourceAutoConfiguration之前加载
并且启用了配置类druid的配置类和DataSource的配置类

在里面方法上有个注解Bean,Spirng扫描注解会调用这个方法,获得DataSource对象,把这个对象放到容器里,并且这Bean的名字和方法同名,就叫做dataSource
另一个注解ConditionalOnMissBean表示如果没有这个实例,就会去创建

druid的配置类

如果想要配置连接数,连接池的一些相关东西
需要在下面写spring.datasource.druid.(…)

MyBatisAutoConfiguration
第一个注解表示是一个配置类,Spring容器要加载它
第二个注解,如果程序中有SqlSessionFactory的时候,这个类就要被加载
第三个,如果只有一个DataSource的时候,加载它;如果有多个的时候,就需要自己搞定
第四个,读取mabatis的配置文件
第五个:在自动配置之后配置它,

myBatis的配置类,加载前缀是mybatis的参数,也就是下图的划线处

这里两个注释表示如果没有这个bean的时候,会调用这个方法,返回bean放在容器里
在这里可以看到是通过自动配置创建了SqlSessionFactory
先new,然后把配置文件给它

这里会读到那个文件中前缀为mabatis的参数

这里体现了MapperScannerConfiguration,但是这里没有注解,是怎么调用的呢

是在这里

这里注册这个bean,让他起作用
然后也能看出,这里Mapper就是注解的Mapper,所以在mybatis中注解就是这个

那么MapperProxy是由谁创建的?由MapperProxyFactory
MapperScanner扫描到的接口给了它,它会调newInstance,创建一个代理实现类

而这个类中,没有真正实现接口,最终执行的invoke方法,只有这个方法

以上是关于Spring/MyBatis的主要内容,如果未能解决你的问题,请参考以下文章

Spring---Mybatis和Spring整合

spring+mybatis的优缺点

Spring MVC+Spring+Mybatis实现支付宝支付功能(图文详解)

Spring+Mybatis之登录功能demo

spring-boot+mybatis整合简写

spring mybatis 整合 可以省略mybatis配置文件吗