想必已经对BeanFactory已经轻车熟路。
这里我先演示下BeanFactory的演变过程:
1.早期的三成思想中的解耦
①早期三层思想中我们创建了servlet,service和dao三个目录。
②servlet负责接收前端的请求
③service负责业务逻辑的处理
④dao负责与数据库对接并完成数据库的CURD
后来我们发现用户需求会频繁发生改动,所以说service中的逻辑代码也会经常改动,项目代码可以增加,删除和修改都是下下策
当然为了解决这个问题,便采用了XxxService--->XxxServiceImpl的形式
一旦XxxService的需求发生变动,我们只需要增加一个YyyServiceImpl即可,这样就避免了修改原来的XxxServiceImpl文件了
这里你又可能产生疑问,XxxServiceImpl和YyyServiceImpl都是XxxService的实现类,工程运行时到底使用了哪个实现类呢?
解决这个问题,我们就是将XxxService和其当前对应的实现类的关系配置在bean.properties文件中,大致如下:
AreaService=com.whw.service.impl.AreaServiceImpl
同理,dao层也采取了这样的配置方式:
AreaDao=com.whw.dao.impl.AreaDaoImpl
说道这里,我们的BeanFactory就诞生了,它回去解析bean.properties文件并获取映射关系,从而生成对应的实现类对象:
public class BeanFactory {
/**
* 静态方法,接收接口,返回实现类的对象->properties文件解析
* @param clazz
* @param <T>
* @return
*/
public static <T> T getInstance(Class<T> clazz){
//读取配置文件,接口名是键,值是实现类
//Class类中方法 getSimpleName()获取此class对象的类名
String simpleName = clazz.getSimpleName();
ResourceBundle bundle = ResourceBundle.getBundle("bean");
//获取键对应的这,值是类全名
String className = bundle.getString(simpleName);
try {
//反射创建对象
return (T)Class.forName(className).newInstance();
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
}
Servlet中获取Service实现类对象的方式如下:
private AreaService AreaServiceImpl = BeanFactory.getInstance(AreaService.class);
Service中获取Dao实现类对象的方式如下:
private AreaDao AreaDaoImpl = BeanFactory.getInstance(AreaDao.class);
这里通过BeanFactory以及bean.properties主要实现了三层之间的降耦
2.原生JDBC转向连接池
①更早以前操作数据库是通过原生JDBC来进行的,需要注册驱动、获取连接、获取sql语句执行者对象、执行sql、收集结果
②Apache对JDBC进行了封装从而变为DbUtils,我们也进入了QreryRunner的时代
③后来又考虑到每次与数据库的连接都需要三次握手,耗费大量时间,从而产生了连接池的概念,但市面上同类产品居多
④为了标准化如何获取连接池,各连接池厂家都需要实现PooledDataSource这个接口
⑤C3P0连接池是这么获取连接池的:
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
⑥当然我们也自己封装了C3P0的工具类
package com.whw.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class C3P0Utils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(ResultSet rs, Statement stat, Connection con) {
if (rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (stat != null)
try {
stat.close();
} catch (Exception ex) {
}
if (con != null)
try {
con.close();
} catch (Exception ex) {
}
}
}
数据的表我们会将其映射为我们的实体类,比如说商品表product,我们就创建一个实体类叫Product
我们获取数据库执行结果的方式也变成了下面的样子:
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
String sql = "select * from product where pid=?";
Objects[] params = {1};
List<Product> products = qr.query(sql,new BeanListHandler<>(Product.class),params);
3.半自动框架Mybatis的引入
①前期使用DbUtils进行数据库查询时,SQL语句全部写在代码中
②引入Mybatis后我们将SQL语句全部写在XxxMapper.xml配置文件中,并且与XxxMapper接口文件进行映射
③完成了mapper接口文件与SQL语句的分离后,至此我们进入了SqlSession的时代
④Mybatis需要我们将数据库连接信息与XxxMapper.xml的扫描信息配置在SqlMapConfig.xml中
⑤我们在BeanFactory中完成对SqlMapConfig.xml的解析,并获取SqlSession对象,进而完成数据操作
package com.whw.utils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.ResourceBundle;
/**
* 创建对象
* 传递我接口,返回接口的实现类对象
* 读取配置文件
* 参数:接收的是接口的class文件对象
* 任何一个类的class文件对象,都是Class对象
*/
public class BeanFactory {
private static SqlSession sqlSession;
static {
//①加载SqlMapConfig.xml获取sqlSession
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
try {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory ssf = ssfb.build(is);
sqlSession = ssf.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取mapper接口的实例
* @param clazz
* @param <T>
* @return
*/
public static <T> T getMapperImpl(Class<T> clazz){
return sqlSession.getMapper(clazz);
}
/**
* 延迟加载
* 静态方法,接收接口,返回实现类的对象->properties文件解析
* @param clazz
* @param <T>
* @return
*/
public static <T> T getInstance(Class<T> clazz){
//读取配置文件,接口名是键,值是实现类
//Class类中方法 getSimpleName()获取此class对象的类名
String simpleName = clazz.getSimpleName();
ResourceBundle bundle = ResourceBundle.getBundle("bean");
//获取键对应的这,值是类全名
String className = bundle.getString(simpleName);
try {
//反射创建对象
return (T)Class.forName(className).newInstance();
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
}
⑥在BeanFactory加载的时候SqlSession对象就已经创建完毕,提供一个对外获取SqlSession对象的方法即可
⑦根据动态代理,在service中由SqlSession来回去对应mapper接口的实现类对象并完成方法的调用:
private static AreaMapper areaMapperImpl = BeanFactory.getMapperImpl(AreaMapper.class);
4.对象创建的延迟加载与立即加载
①至此,我们的bean.properties中只剩下了Service的配置(**dao层中采用了mapper动态代理的形式获取对象)
AreaService=com.whw.service.impl.AreaServiceImpl
.........
MovieService=com.whw.service.impl.MovieServiceImpl
②获取Service的实现对象仍采取BeanFactory的getInstace()来获取
③新的问题便产生了,getInstace()方法是在需要XxxServiceImpl对象时便会执行去生成一个该实现类对象
④这种有需要就需要去马上生成一个的方式被称之为延迟加载,貌似非常浪费资源和时间
⑤有没有一种方式是将所有需要被创建的对象全部创建出来存储到集合中,需要时直接从集合中取出即可,不必再创建了呢?
⑥答案是ClassPathXmlApplicationContext,它要求我们创建一个ApplicationContext.xml文件,用于存储所有对象的映射:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置对象创建信息 singleton:单例/prototype:多例-->
<bean id="AreaService" class="com.whw.service.impl.AreaServiceImpl" scope="prototype"/>
<bean id="CarouselService" class="com.whw.service.impl.CarouselServiceImpl" scope="prototype"/>
<bean id="FeaturesService" class="com.whw.service.impl.FeaturesServiceImpl" scope="prototype"/>
<bean id="GenreService" class="com.whw.service.impl.GenreServiceImpl" scope="prototype"/>
<bean id="MovieService" class="com.whw.service.impl.MovieServiceImpl" scope="prototype"/>
</beans>
⑦我们可以修正BeanFactory如下:
package com.whw.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
public class BeanFactory {
private static SqlSession sqlSession;
private static ClassPathXmlApplicationContext context;
static {
//①加载SqlMapConfig.xml获取sqlSession
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
try {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory ssf = ssfb.build(is);
sqlSession = ssf.openSession();
} catch (IOException e) {
e.printStackTrace();
}
//②加载ApplicationContext.xml并创建所有的对象
context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
}
/**
* 获取mapper接口的实例
* @param clazz
* @param <T>
* @return
*/
public static <T> T getMapperImpl(Class<T> clazz){
return sqlSession.getMapper(clazz);
}
/**
* 立即加载
* 通过spring容器在服务器启动时完成对象的创建
* @param beanName
* @param clazz
* @param <T>
* @return
*/
public static <T> T newInstance(String beanName, Class<T> clazz){
return context.getBean(beanName,clazz);
}
}
⑧当BeanFactory执行的时候会加载ApplicationContext.xml并创建所有对象存储于集合中,取出方式见newInstance()
5.Spring的依赖注入取代BeanFactory