Spring的实现回顾
当然我们还需要配置相关Spring的内容,例如我们通过Service来控制Dao层的实现,并且在调用方法前后增加切面控制增加访问日志:
1 创建Service层
创建PersonService及其实现类
PersonService
package com.example.spring_mybatis.service; import com.example.spring_mybatis.model.Person; import java.util.List; public interface PersonService { List<Person> getPersonList(); }
PersonService
package com.example.spring_mybatis.serviceImpl; import com.example.spring_mybatis.daoImpl.PersonDaoImpl; import com.example.spring_mybatis.model.Person; import com.example.spring_mybatis.service.PersonService; import lombok.Data; import java.util.List; @Data public class PersonServiceImpl implements PersonService { private PersonDaoImpl personDaoImpl; @Override public List<Person> getPersonList() { return personDaoImpl.getPersonList(); } }
2 创建Dao层
Dao层的接口我们已经在MyBatis部分实现了,接下来只要编写其实现类即可:
package com.example.spring_mybatis.daoImpl; import com.example.spring_mybatis.dao.PersonDao; import com.example.spring_mybatis.model.Person; import java.util.List; public class PersonDaoImpl implements PersonDao { @Override public List<Person> getPersonList() { return null; } }
这里我们先不编写其实现,这里的实现其实就是我们今天的主题。
3 增加AOP类
增加Aop类关于日志的,扫描service包,给这个包里的方法前后都加日志:
package com.example.spring_mybatis.aop; import java.time.LocalDateTime; /** * * @Name LogProxy * * @Description * * @author tianmaolin * * @Data 2021/8/24 */ public class LogAop { public void beforeLog() { System.out.println("日志记录开始"+ LocalDateTime.now()); } public void afterLog() { System.out.println("日志记录结束"+ LocalDateTime.now().plusMinutes(5)); } }
4 applicationContext.xml配置文件编写
我们先编写目前用到的一些配置,包括Service对Dao的调用:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 使用aop注解 --> <aop:aspectj-autoproxy/> <!-- 定义目标对象:定义被代理者 --> <bean id="personServiceImpl" class="com.example.spring_mybatis.serviceImpl.PersonServiceImpl"> <property name="personDaoImpl" ref="personDaoImpl"/> </bean> <bean id="personDaoImpl" class="com.example.spring_mybatis.daoImpl.PersonDaoImpl"> </bean> <!-- 定义切面,切面内包含通知要执行的方法--> <bean id="logAop" class="com.example.spring_mybatis.aop.LogAop"></bean> <!--aop的配置--> <aop:config proxy-target-class="true"> <!--切面配置--> <aop:aspect ref="logAop"> <!--切点--> <aop:pointcut id="logPointCut" expression="execution(* com.example.spring_mybatis.service..*.*(..))"/> <!--切点-通知--> <aop:before pointcut-ref="logPointCut" method="beforeLog"/> <aop:after pointcut-ref="logPointCut" method="afterLog"/> </aop:aspect> </aop:config> </beans>
Spring整合MyBatis
Spring整合用到一个依赖,也就是我们上述坐标中的,其官方文档介绍:mybatis-spring
<!-- Mybatis-Spring相关 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
什么是 MyBatis-Spring?MyBatis-Spring 会帮助我们将 MyBatis 代码无缝地整合到 Spring 中。Spring整合MyBatis, 主要目的就是把MyBatis核心配置文件及Mapper文件中的内容交给Spring来处理,通俗的说,就是Spring把SqlSession的创建以及Mapper对象的创建都代理了,事实上就是我们不用再编写相关的配置文件以及配置文件读取类了,也就是我们上述的8个步骤中4-6步都不用做了,Spring完全代工。
1 SqlSession注入实现
那么首先来看第一种方式,我们通过这种方式可以干掉mybatis-config.xml
,完全通过Spring来实现.
1-1 调整配置文件applicationContext.xml
由Spring来构建SqlSession,同时MybatisUtil也不需要了,SqlSession的构建完全交给Spring来实现,调整后配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 使用aop注解 --> <aop:aspectj-autoproxy/> <!-- 定义目标对象:定义被代理者 --> <bean id="personServiceImpl" class="com.example.spring_mybatis.serviceImpl.PersonServiceImpl"> <property name="personDaoImpl" ref="personDaoImpl"/> </bean> <bean id="personDaoImpl" class="com.example.spring_mybatis.daoImpl.PersonDaoImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> <!--DataSource: 使用Spring的数据源替换Mybatis的配置, 可以使用c3p0,dbcp,druid,或者spring提供的jdbc--> <!-- 加载数据库配置信息 --> <context:property-placeholder location="properties/db.properties" system-properties-mode="NEVER"/> <!-- 连接池对象 --> <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <!--配置日志输出--> <bean id="configuration" class="org.apache.ibatis.session.Configuration"> <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> </bean> <!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <!--关联Mybatis--> <!--<property name="configLocation" value="mybatis-config.xml"/>--> <property name="mapperLocations" value="mappers/*.xml"/> <property name="configuration" ref="configuration"/> </bean> <!--注册sqlSessionTemplate , 关联sqlSessionFactory--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能通过构造器注入sqlSessionFactory,因为没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!-- 加载日志配置信息 --> <context:property-placeholder location="log4j.properties" system-properties-mode="NEVER"/> <!-- 定义切面,切面内包含通知要执行的方法--> <bean id="logAop" class="com.example.spring_mybatis.aop.LogAop"></bean> <!--aop的配置--> <aop:config proxy-target-class="true"> <!--切面配置--> <aop:aspect ref="logAop"> <!--切点--> <aop:pointcut id="logPointCut" expression="execution(* com.example.spring_mybatis.service..*.*(..))"/> <!--切点-通知--> <aop:before pointcut-ref="logPointCut" method="beforeLog"/> <aop:after pointcut-ref="logPointCut" method="afterLog"/> </aop:aspect> </aop:config> </beans>
上述配置中我们用到了这几个概念:
- 在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory ,而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建
- SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替代码中已经在使用的 SqlSession
也就是原生Mybatis的SqlSession获取过程被Spring给接管了。
1-2 调整PersonDaoImpl类实现
由上边的配置我们发现,SqlSession被构建好后注入到了PersonDaoImpl中,这样实现类就可以打开一个session并执行相关逻辑了,然后具体的实现使用SqlSession通过personMapper.xml配置文件去构建代理对象然后调用方法实现。
package com.example.spring_mybatis.daoImpl; import com.example.spring_mybatis.dao.PersonDao; import com.example.spring_mybatis.model.Person; import lombok.Data; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; @Data public class PersonDaoImpl implements PersonDao { private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<Person> getPersonList() { return sqlSession.getMapper(PersonDao.class).getPersonList(); } }
1-3 测试实现方式
最后我们来编写个单元测试类来测试下实现过程:
import com.example.spring_mybatis.service.PersonService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceTest { @Test public void getPersonListTest(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); PersonService personService = (PersonService) applicationContext.getBean("personServiceImpl"); personService.getPersonList(); } }
打印结果如下:
2 SqlSessionFactory注入实现
让Dao继承Support类 , 直接利用 getSqlSession()
获得SqlSessionTemplate , 然后直接注入SqlSessionFactory . 比起方式1 不需要管理SqlSessionTemplate
, 而且对事务的支持更加友好
再向下追溯,可以发现SqlSessionTemplate有属性sqlSessionFactory,所以按照依赖链正常注入使用:
2-1 调整配置文件applicationContext.xml
我们调整配置文件,让SqlSessionFactory直接注入到PersonDaoImpl,不再使用中间的SqlSessionTemplate作为媒介:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 使用aop注解 --> <aop:aspectj-autoproxy/> <!-- 定义目标对象:定义被代理者 --> <bean id="personServiceImpl" class="com.example.spring_mybatis.serviceImpl.PersonServiceImpl"> <property name="personDaoImpl" ref="personDaoImpl"/> </bean> <bean id="personDaoImpl" class="com.example.spring_mybatis.daoImpl.PersonDaoImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!--DataSource: 使用Spring的数据源替换Mybatis的配置, 可以使用c3p0,dbcp,druid,或者spring提供的jdbc--> <!-- 加载数据库配置信息 --> <context:property-placeholder location="properties/db.properties" system-properties-mode="NEVER"/> <!-- 连接池对象 --> <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <!--配置日志输出--> <bean id="configuration" class="org.apache.ibatis.session.Configuration"> <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> </bean> <!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <!--关联Mybatis--> <!--<property name="configLocation" value="mybatis-config.xml"/>--> <property name="mapperLocations" value="mappers/*.xml"/> <property name="configuration" ref="configuration"/> </bean> <!-- 加载日志配置信息 --> <context:property-placeholder location="log4j.properties" system-properties-mode="NEVER"/> <!-- 定义切面,切面内包含通知要执行的方法--> <bean id="logAop" class="com.example.spring_mybatis.aop.LogAop"></bean> <!--aop的配置--> <aop:config proxy-target-class="true"> <!--切面配置--> <aop:aspect ref="logAop"> <!--切点--> <aop:pointcut id="logPointCut" expression="execution(* com.example.spring_mybatis.service..*.*(..))"/> <!--切点-通知--> <aop:before pointcut-ref="logPointCut" method="beforeLog"/> <aop:after pointcut-ref="logPointCut" method="afterLog"/> </aop:aspect> </aop:config> </beans>
2-2 调整PersonDaoImpl类实现
当然相应的也要调整PersonDaoImpl类的实现方式:
package com.example.spring_mybatis.daoImpl; import com.example.spring_mybatis.dao.PersonDao; import com.example.spring_mybatis.model.Person; import lombok.Data; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; @Data public class PersonDaoImpl extends SqlSessionDaoSupport implements PersonDao { @Override public List<Person> getPersonList() { return getSqlSession().getMapper(PersonDao.class).getPersonList(); } }
2-3 测试实现方式
测试类还使用之前的:
import com.example.spring_mybatis.service.PersonService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceTest { @Test public void getPersonListTest(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); PersonService personService = (PersonService) applicationContext.getBean("personServiceImpl"); personService.getPersonList(); } }
可以看到打印结果同上:
3 Mapper对象自动扫描注入实现
前两种的整合实现方式都创建了Dao接口的实现类,通过SqlSession来获取mapper对象。使用第三种方式我们可以告诉Spring让其帮我们自动创建mapper的代理对象,也就是我们不再需要PersonDaoImpl了,它也由Spring代为生成:
3-1 调整配置文件applicationContext.xml
我们调整配置文件,干掉personDaoImpl的相关配置,直接使用映射类生成器生成:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 使用aop注解 --> <aop:aspectj-autoproxy/> <!-- 定义目标对象:定义被代理者 --> <bean id="personServiceImpl" class="com.example.spring_mybatis.serviceImpl.PersonServiceImpl"> <property name="userMapper" ref="userMapper"/> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> <!--给哪个接口创建代理对象--> <property name="mapperInterface" value="com.example.spring_mybatis.dao.PersonDao"/> </bean> <!--DataSource: 使用Spring的数据源替换Mybatis的配置, 可以使用c3p0,dbcp,druid,或者spring提供的jdbc--> <!-- 加载数据库配置信息 --> <context:property-placeholder location="properties/db.properties" system-properties-mode="NEVER"/> <!-- 连接池对象 --> <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <!--配置日志输出--> <bean id="configuration" class="org.apache.ibatis.session.Configuration"> <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> </bean> <!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <!--关联Mybatis--> <!--<property name="configLocation" value="mybatis-config.xml"/>--> <property name="mapperLocations" value="mappers/*.xml"/> <property name="configuration" ref="configuration"/> </bean> <!-- 加载日志配置信息 --> <context:property-placeholder location="log4j.properties" system-properties-mode="NEVER"/> <!-- 定义切面,切面内包含通知要执行的方法--> <bean id="logAop" class="com.example.spring_mybatis.aop.LogAop"></bean> <!--aop的配置--> <aop:config proxy-target-class="true"> <!--切面配置--> <aop:aspect ref="logAop"> <!--切点--> <aop:pointcut id="logPointCut" expression="execution(* com.example.spring_mybatis.service..*.*(..))"/> <!--切点-通知--> <aop:before pointcut-ref="logPointCut" method="beforeLog"/> <aop:after pointcut-ref="logPointCut" method="afterLog"/> </aop:aspect> </aop:config> </beans>
3-2 调整PersonServiceImpl类实现
同时我们需要在PersonServiceImpl中替换方法的实现方式,使用我们自动生成的userMapper,而不是对应创建的:
package com.example.spring_mybatis.serviceImpl; import com.example.spring_mybatis.dao.PersonDao; import com.example.spring_mybatis.daoImpl.PersonDaoImpl; import com.example.spring_mybatis.model.Person; import com.example.spring_mybatis.service.PersonService; import lombok.Data; import java.util.List; @Data public class PersonServiceImpl implements PersonService { private PersonDao userMapper; @Override public List<Person> getPersonList() { List<Person> personList=userMapper.getPersonList(); for (Person person : personList) { System.out.println(person); } return personList; } }
3-3 测试实现方式
测试类还使用之前的:
import com.example.spring_mybatis.service.PersonService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceTest { @Test public void getPersonListTest(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); PersonService personService = (PersonService) applicationContext.getBean("personServiceImpl"); personService.getPersonList(); } }
打印结果如下:
总结一下
完成了上述的实现方式之后,我们确定最终的项目整体结构如下:
本篇Blog讨论了三种Spring整合MyBatis的方式,随着实现方式的递进,我们越来越多的工作交给了Spring去做,第一种整合方式我们配置了sqlSessionFactory(myDataSource,configuration),sqlSession
,然后把sqlSession注入personDaoImpl类,通过这个步骤,我们摆脱了mybatis-config.xml
核心配置文件的编写、MybatisUtils辅助类的编写。第二种整合方式我们抛弃了sqlSession
,直接用sqlSessionFactory
注入personDaoImpl
实现。第三种整合方式我们则干脆抛弃了实现类personDaoImpl
的编写,实现类的生成由Spring控制,其实继续演化下去,我们连mapper配置文件都可以抛弃,那就是使用注解,这些内容在Spring Boot
中体现吧,所以框架就是先给出配置规则,再持续简化,让程序员逐渐变成傻瓜,俗称傻瓜式编程,但是如果原理不知道,出了错误可就不好排查了,所以我们还是不要做傻瓜好了。