SSM-Spring(二)-数据类型转化、整合MyBatis、bean生命周期
数据类型转化Converter
spring内部封装了一个自定义日期转换格式的转化器
- Spring已经内置了基本的类型转换功能,比如
- String转int、String转Date(支持yyyy/MM/dd格式)
<bean id="service" class="com.zh.service.PersonServiceImpl"> <!--字符串自动转化为int--> <property name="age" value="25"/> <!--"2011/09/10"自动转化为Date--> <property name="birthday" value="2011/09/10"/> </bean>
- Spring默认的日期转化仅仅支持yyyy/MM/dd格式,那么如果我们要自己定义转化格式,该怎么办呢?
自定义Converter
-
自定义Converter类,实现Converter接口
//继承Converter接口,String->Date public class DateConverter implements Converter<String, Date> { private List<String> formats; public void setFormats(List<String> formats) { this.formats = formats; } @Override public Date convert(String s) { //根据传过来的所有格式进行遍历 for (String format : formats) { try { SimpleDateFormat fmt = new SimpleDateFormat(format); return fmt.parse(s); } catch (ParseException e) { // e.printStackTrace(); } } return null; } }
-
applicationContext.xml中注册
<!--自动转化,但是注意必须是yyyy/MM/dd格式--> <!-- <bean id="person" class="com.zh.domain.Person" p:birthday="2011/09/10"/>--> <!--其他格式的转化,自定义转换器--> <bean id="person" class="com.zh.domain.Person" p:birthday="09_10_2111"/> <!-- 配置FactoryBean 注意:id值必须是conversionService,因为这个id是给spring内部使用的,内部会根据这个指定的id来创建转换对象 1. 创建Spring内置对象conversionService 2. 给内置对象设置属性converters--所有自定义的转换器 3. 将自定义转换器设置的所有格式进行设置 --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 给上面对象设置属性--> <property name="converters"> <set> <!-- 设置自定义转化器--> <bean class="com.zh.converter.DateConverter"> <property name="formats"> <list> <!-- 同时兼容各种格式--> <value>yyyy-MM-dd</value> <value>MM_dd_yyyy</value> </list> </property> </bean> </set> </property> </bean>
-
测试
@Test public void test() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = ctx.getBean("person1", Person.class); System.out.println(person); }
整合MyBatis
- 使用MyBatis的前期几个特点:
1.数据源的配置在核心配置文件mybatis-config.xml,如果使用第三方数据源要实现一个UnpooledDataSourceFactory的子类,并且实现其无参的构造方法,设置数据源属性
2.所有的mapper文件都需要在核心配置文件mybatis-config.xml一一导入
3.需要事先创建好SqlSessionFactory对象,封装一个工具类MyBatises,每次都需要调用openSession方法(close)
pom添加依赖
<dependencies>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--spring整合mybatis,需要添加的2个依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
数据源设置
-
只需要在Spring的配置文件applicationContext.xml中配置即可
<!-- 1. 导入配置文件db.properties --> <context:property-placeholder location="db.properties"/> <!-- 2. 定义数据源 --> <!-- 使用Spring内置的数据源(Spring的DriverManagerDataSource) --> <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">--> <!-- 使用MyBatis内置的数据源(MyBatis的PooledDataSource) --> <!--<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">--> <!--<property name="driver" value="${jdbc.driverClass}"/>--> <!-- 使用druid数据源(Druid) --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
-
注意: 这里配置的连接池,默认会自动提交事务
SqlSessionFactoryBean配置
- 通过xml文件配置一个sqlSessionFactory的bean对象,类似原来的mybatis,用来操作mapper文件中的内容
- 主动设置数据源属性,之前的mybatis的SqlSessionFactory实例创建的时候会根据核心配置文件中配置的数据源内部自动设置。
-
设置mapperLocations属性,类似之前的核心配置文件中一一导入maper
<!-- 创建SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 1. 主动设置数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 这个包底下的类会自动设置别名(一般是领域模型) 这个包下面所有的别名就是类名 比如com.zh.domain.Skill;别名是Skill 设置完成之后具体的mapper文件比如skill.xml中就可以用别名Skill了 --> <property name="typeAliasesPackage" value="com.zh.domain"/> <!-- 2. 映射文件的位置 --> <property name="mapperLocations"> <array> <value>mappers/*.xml</value> </array> </property> </bean>
MapperScannerConfigurer
- 封装一个全局的扫描对象,用来创建mybatis相关联的所有bean,该对象有spring内部自动创建
- 设置sqlSessionFactoryBeanName属性,获取sqlsession
- 设置basePackage,获取dao层
<!-- 扫描dao
这个bean没有设置id值的原因是,spring在扫描到这个bean时直接创建对象,后序不会再通过id来使用这个对象,因此不需要id值
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 设置MapperScannerConfigurer的sqlSessionFactoryBeanName属性值-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 设置dao的包 ,通过上面的属性可拿到sqlsession,然后可通过getMapper方法实现所有dao接口对应的impl-->
<property name="basePackage" value="com.zh.dao"/>
</bean>
使用
-
上面配置完之后,直接可以通过容器的getBean方法获取dao的代理对象,bean的id是dao类名的小驼峰形式
//com.zh.dao.SkillDao的id是skillDao ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); skillDao = ctx.getBean("skillDao", SkillDao.class);
-
SkillTest如下:
public class SkillTest { private ApplicationContext ctx; private SkillDao skillDao; @Before public void before() { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //根据id创建dao的代理对象 skillDao = ctx.getBean("skillDao", SkillDao.class); } @Test public void list() { List<Skill> skills = skillDao.list(); System.out.println(skills); } @Test public void save() { System.out.println(skillDao.save(new Skill("123", 456))); } }
-
Skill模型类如下
package com.zh.domain; public class Skill { private Integer id; private String name; private Integer level; public Skill() {} public Skill(String name, Integer level) { this.name = name; this.level = level; } //属性get、set方法,省略。。。。 }
-
dao接口
package com.zh.dao; import com.zh.domain.Skill; import org.apache.ibatis.annotations.Insert; import java.util.List; public interface SkillDao { //xml List<Skill> list(); //注解 @Insert("INSERT INTO skill(name, level) VALUES (#{name}, #{level})") boolean save(Skill skill); }
-
resources/mappers中的skill.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zh.dao.SkillDao"> <select id="list" resultType="Skill"> SELECT * FROM skill </select> </mapper>
使用分析(applicationContext.xml文件从下往上分析)
- 创建MapperScannerConfigurer对象,然后根据属性依赖创建以下对象
- 该对象是扫描所有dao的接口类,然后拿到SqlSession,通过getMapper获取到所有dao接口类对应的impl实例
- 创建sqlSessionFactory对象
- 需要设置数据源
- 需要知道所有mapper映射文件的位置
- 设置所有dao的存放路径
整合分析
- 原来的mybaits的mybatis-config.xml中的信息都整合到了applicationContext.xml中
- mappers内容规则不变,dao层不需要实现impl
- mybaitis最终的使用方式是拿到SqlSession,然后通过
session.getMapper()
获取对应的接口创建出对应的dao的impl实例 - spring本质是通过MapperScannerConfigurer拿到SqlSession
IDEA直接操作数据库
- 点击IDEA右边列的Database工具菜单,展开
- 点击左上角的加号“+”,选择DataSource->MySQL
- 如果是第一次打开这个界面,在这个页面底部有download提示(download missing driver files),需要下载对应的驱动之后才能使用,点击下载即可
- 该界面可以设置数据库的名字name(假设命名为test)、地址(Host)、端口(Port)、用户名(User)、密码(password)、Database(需要连接的数据库,可以先不填,先连上Mysql服务器即可)
- 点击test Connect测试连接,如果没问题点击OK,就会连接数据库
- 会展示一个列表,有个schemas文件夹,下面是所有数据库的名称
- 如果之前Database没有指定哪个数据库,这里将显示一个数据
- 右击最上面根目录test->properties->选择Schemas->勾选All schemas,点击OK
- 则此次schemas文件夹下显示的是mysql服务器所有的数据库了
- 再次点击Database工具菜单折叠后,点击顶部的
<schemas>
(也可以在Database展开的情况下,点击左边“+”->Query Console,选择对应的数据库),然后在展开的列表里面选择你要操作的数据库,然后在里面写SQL语句,点击顶部的执行即可。
bean的生命周期
-
一个bean从出生到死亡, 经历的生命周期方法是
1. 构造方法 2. setter 1. 如果通过属性注入肯定会调用setter方法,除非是通过构造方法注入 3. BeanNameAware协议的setBeanName 1. 获取当前bean 的名字,bean对象要实现BeanNameAware接口 2. 让你知道一下bean的名字(id、name) 4. ApplicationContextAware协议的setApplicationContext 1. 获取当前IoC容器的名字,bean对象要实现ApplicationContextAware接口 2. 让你知道一下你在哪个容器里面 5. BeanPostProcessor协议的postProcessBeforeInitialization 1. 初始化方法调用之前调用 6. InitializingBean协议的afterPropertiesSet 1. 监听对象初始化完成之后调用,效果跟init-method一样 2. 构造、注入完毕之后调用①(初始化,加载资源) 7. init-method 1. bean标签属性,对象初始化"完成"的时候调用 2. 初始化代表:构造方法、注入方法(setter) 3. 构造、注入完毕之后调用②(初始化,加载资源) 8. BeanPostProcessor的postProcessAfterInitialization 1. 初始化方法调用之后调用 9. 业务方法 1. 拿到bean对象去做一些相关业务 10. DisposableBean的destroy 1. 监听bean对象销毁时调用,效果跟destory-method一样 2. 销毁之前调用①(释放资源) 11. destroy-method 1. 对象呗销毁的时候调用 2. 销毁之前调用②(释放资源)
- 当bean的scope为singleton,IoC容器关闭时,才会调用10、11方法
- 因为scope为singleton,此时在容器中是一个单例,归容器管理,因此容器销毁,单例也销毁
- 如果是prototype,则生命周期不归容器管理
- 参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lifecycle
生命周期举例
-
applicationContext.xml中注册类
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- init-method:对象初始化"完成"的时候调用,初始化代表:构造方法、注入方法 destroy-method:对象销毁的时候调用 scope:默认为singleton,生命周期跟Ioc容器一致 --> <bean id="userService" class="com.zh.service.impl.UserServiceImpl" p:age="20" init-method="init" destroy-method="dealloc"/> <!--监听所有的bean,相当于一个第三方监控对象,监听每个bean的初始化之前、之后的时刻--> <bean class="com.zh.processor.MyProcessor"/> </beans>
-
service类
//接口UserService public interface UserService { boolean login(String username, String password); } //实现 public class UserServiceImpl implements UserService, BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean { private Integer age; //构造方法 public UserServiceImpl() { System.out.println("01 - UserServiceImpl"); } //setter方法,注入 public void setAge(Integer age) { this.age = age; System.out.println("02 - setAge - " + age); } //业务方法 @Override public boolean login(String username, String password) { System.out.println("09 - UserServiceImpl - login - " + username + "_" + password); return false; } public void init() { System.out.println("07 - init-method"); } public void dealloc() { System.out.println("11 - destroy-method"); } //BeanNameAware接口带的方法:获取bean的名字 @Override public void setBeanName(String name) { System.out.println("03 - BeanNameAware - setBeanName - " + name); } //ApplicationContextAware接口带的方法,获取IoC容器的名称 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("04 - ApplicationContextAware - setApplicationContext - " + applicationContext); } //DisposableBean接口的方法,监听bean对象的销毁 @Override public void destroy() throws Exception { System.out.println("10 - DisposableBean - destroy"); } //InitializingBean接口的方法,监听bean对象初始化完毕之后调用 @Override public void afterPropertiesSet() throws Exception { System.out.println("06 - InitializingBean - afterPropertiesSet"); } }
-
MyProcessor
/** * com.zh.processor.MyProcessor,可以统一处理所有的Bean */ public class MyProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //对所有的bean对象做一个初始化前拦截 System.out.println("05 - BeanPostProcessor - postProcessBeforeInitialization - " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //对所有的bean对象做一个初始化后拦截 System.out.println("08 - BeanPostProcessor - postProcessAfterInitialization - " + beanName); return bean; } }
-
UserTest
@Test public void test() { // 创建容器 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService service = ctx.getBean("userService", UserService.class); service.login("123", "456"); // 关闭容器 ctx.close(); }