自定义Spring工厂类BeanFactory
Spring的概述
概述:
Spring是一个轻量级开源框架,整合了大量的第三方框架。
优势:
- 方便解耦,简化开发
- Aop编程的支持
- 声明式事务的支持
- 方便程序的测试
- 方便集成各种优秀框架
解耦:
耦合:程序间的依赖关系,类之间的解耦,方法之间的解耦。
解耦:降低程序间的依赖。
类之间的解耦:
1.使用反射来创建对象,而避免使用new关键字。
2.通过读取配置文件来获取要创建对象的全限定名。
实际开发中:编译期不依赖,运行时才依赖。
通过jdbcDemo来分析代码间的耦合度
- 导入依赖:
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- 创建数据库
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
- 测试工程
/**
* 通过jdbc查询数据库
* 耦合度太高
*/
public class JDBCDemo {
public static void main(String[] args) throws Exception {
//1注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//通过反射来创建对象,避免使用new来创建 减少代码间的耦合度
Class.forName("com.mysql.jdbc.Driver");//字符串可以通过配置文件的方式来声明更大的减少耦合度
//2获取链接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/my_spring", "root", "root");
//3获取操作数据库的预处理对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
//4执行sql,得到结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5遍历结果集
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
//6释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
模拟耦合度高的代码
//dao层接口
public interface AccountDao {
public void saveAccount();
}
//dao层实现类
public class AccountDaoImpl implements AccountDao {
public void saveAccount() {
System.out.println("用户保存成功");
}
}
//service接口
public interface AccountService {
/**
* 模拟保存数据到数据库
*/
void saveAccount();
}
//service实现类
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
//模拟表现层
public class ControllerTest {
public static void main(String[] args) {
AccountService accountService = new AccountServiceImpl();
accountService.saveAccount();
}
}
通过自定义工厂类BeanFactory解决上述代码中的耦合
/**
* 创建一个Bean对象的工厂
* Bean:在计算机英语中被定义为可重用组件
* JavaBean: 用java语言编写的可重用组件,它就是创建我们的service和dao对象
* javaBean > 实体类
* 如何创建javaBean:
* 第一步需要一个配置文件来配置我们的service和dao
* 配置内容:唯一标识=全限定类名(key=value)
* 第二步通过配置文件中配置的内容,反射创建对象
* 配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个properties对象
private static Properties properties;
//使用静态代码块为Properties对象赋值 静态代码块只会随着类的加载仅加载一次
static{
//通过配置文件的方式来配置反射的全限定名
try {
//实例化properties对象
properties=new Properties();
//从properties中获取流对象
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
//读取流
properties.load(inputStream);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean的名称获取bean对象
* @param beanName
* @return
*/
//方法想要被类名直接调用需要添加static 如果不加static就必须通过这个类的对象进行调用
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = properties.getProperty(beanName);
//通过反射获取全限定名下的类的class对象并实例化
//通过反射的方式进行创建对象
bean = Class.forName(beanPath).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
}
}
修改service和Controller代码
- controller
/**
* 模拟一个表现层,用于调用业务层
*/
public class ControllerTest {
public static void main(String[] args) {
//AccountService accountService = new AccountServiceImpl();
//通过工厂类进行调用service层
AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
accountService.saveAccount();
}
}
-service
public class AccountServiceImpl implements AccountService {
//private AccountDao accountDao = new AccountDaoImpl();
private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
//通过工厂调用dao层
public void saveAccount() {
accountDao.saveAccount();
}
}
再次分析自定义BeanFactory是否有耦合
每次通过BeanFactory创建的对象都是不同的也就是多例的,对象被多次创建,执行效率也就没有那么高,创建出来的对象不存储起来根据java的垃圾回收机制对象会在长时间不用时被回收,当你下次用的时候就没有了。可以只用一次new创建一个对象然后马上存起来,将对象存到一个容器中,下次创建对象就会从容器中拿到这个对象,不用重新创建对象。
- 创建一个容器Map<String,Object>,key为配置文件的beanName,values为反射创建的对象,在静态代码块中实现,再类加载的时候只加载一次。它是通过将需要创建的对象放入到map集合中,每次需要创建对象的时候都会到map容器中获取对象,而不是新建一个对象,从而到达每次拿到的都是同一个对象。
public class BeanFactory {
//定义一个properties对象
private static Properties properties;
//定义一个容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值 静态代码块只会随着类的加载仅加载一次
static{
//通过配置文件的方式来配置反射的全限定名
try {
//实例化properties对象
properties=new Properties();
//从properties中获取流对象
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
//读取流
properties.load(inputStream);
//实例化容器
beans=new HashMap<String, Object>();
//取出配置文件中的所有key
Enumeration<Object> keys = properties.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个key
String key = keys.nextElement().toString();
//根据key获取beanPath
String beanPath = properties.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//将对象保存到beans容器中
beans.put(key,value);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据beans容器获取对象 获取到的都是同一个对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
/* *//**
* 根据bean的名称获取bean对象
* @param beanName
* @return
*//*
//方法想要被类名直接调用需要添加static 如果不加static就必须通过这个类的对象进行调用
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = properties.getProperty(beanName);
//通过反射获取全限定名下的类的class对象并实例化
//通过反射的方式进行创建对象
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}*/
}
- controller测试层
public class ControllerTest {
public static void main(String[] args) {
//AccountService accountService = new AccountServiceImpl();
//通过工厂类进行调用service层
for (int i = 0; i < 5; i++) {
AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
System.out.println(accountService);//获取的都是同一个com.itheima.spring.service.impl.AccountServiceImpl@14ae5a5
accountService.saveAccount();
}
}
}