共计 7718 个字符,预计需要花费 20 分钟才能阅读完成。
@[TOC]
xml 形式自定义实现 Ioc 容器
应用 xml 实现自定义简略的 Ioc 容器
前言
平时开发过程中, 咱们都是应用 Spring 来进行开发,Spring 外围的 Ioc 容器帮忙咱们去创建对象这一过程被称作 管制反转
也叫 Ioc
在实例化一个对象时候, 这个对象中用到一个对象类型的属性, 容器把这个对象注入到实例化对象的过程被称作 依赖注入
简称 DI
Ioc 和 DI 说的是一个事件, 针对的侧重点不同,IOC 是站在容器角度创建对象,DI 是站在应用的角度, 注入应用对象;
没有 IOC 容器的时候
模仿银行转账例子
转账接口
public interface AccountDao {Account queryAccountByCardNo(String cardNo) throws Exception;
int updateAccountByCardNo(Account account) throws Exception;
}
接口实现类
public class JdbcAccountDaoImpl implements AccountDao {public void init() {System.out.println("初始化办法.....");
}
public void destory() {System.out.println("销毁办法......");
}
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
// 从连接池获取连贯
Connection con = DruidUtils.getInstance().getConnection();
String sql = "select * from account where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
//con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
// 从连接池获取连贯
// 革新为:从以后线程当中获取绑定的 connection 连贯
Connection con = DruidUtils.getInstance().getConnection();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
//con.close();
return i;
}
}
业务接口
public interface TransferService {void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
}
实现类
public class TransferServiceImpl implements TransferService {
// 1 原始的 new 办法创立 dao 接口实现类对象
private AccountDao accountDao = new JdbcAccountDaoImpl();
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
//int c = 1/0;
accountDao.updateAccountByCardNo(from);
}
}
controller 层
@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// 1. 实例化 service 层对象
private TransferService transferService = new TransferServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置申请体的字符编码
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// 2. 调用 service 层办法
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
// 响应
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
能够看进去在 controller 层 new
业务层的对象,new 业务层的对象中曾经 new 进去 dao 层的实现类
业务层和 dao 层通过 new 关键字连接起来, 耦合高
应用 Ioc 容器状况下
如图能够看到,Ioc 容器讲过 AB 对象的示例存储起来,main 函数应用 AB 对象的时候, 间接在 Ioc 容器中获取;
基于这个了解, 咱们能够实现本人的 Ioc 容器;
首先呢, 咱们新建本人的 xml, 用来配置本人的须要一些示例话的 bean 也就是对象
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="accountDao" class="com.udeam.edu.dao.impl.JdbcAccountDaoImpl">
</bean>
<!-- TransferServiceImpl service-->
<bean id="transferService" class="com.udeam.edu.service.impl.TransferServiceImpl">
</property>
</bean>
</beans>
创立 dao 层实现类和业务层实现类, 并且过本人的 Id 从容器中获取;
创立 BeanFactorys
类来解析 xml, 实例化配置的对象
/**
* 工厂 Bean
*/
public class BeanFactorys {private final static Map<String, Object> iocMap = new HashMap<>();
static {
// 1 读取解析 beans.xml 通过反射技术, 生产 bean 对象, 并将其存在 map 中
InputStream resourceAsStream = BeanFactorys.class.getClassLoader().getResourceAsStream("beans.xml");
// 失去一个 文档对象
try {Document read = new SAXReader().read(resourceAsStream);
// 获取跟对象
Element rootElement = read.getRootElement();
/**
* xpath 表达式 用法
* // 从匹配抉择的以后节点抉择文档中的节点, 而不思考他们的地位
* / 从根节点获取
* . 选取以后节点
* .. 选取以后节点的父节点
* @ 选取属性
*
*/
// // 示意读取任意地位的 bean 标签
List<Element> list = rootElement.selectNodes("//bean");
if (Objects.isNull(list) || list.size() == 0) {throw new RuntimeException("无此 bean 标签");
}
list.forEach(x -> {
// 获取 Id
String id = x.attributeValue("id"); //accountDao
// 获取权限定命名
String clasz = x.attributeValue("class"); //com.udeam.edu.dao.impl.JdbcAccountDaoImpl
System.out.println(id + "--->" + clasz);
// 通过反射创建对象
try {Object o = Class.forName(clasz).newInstance();
// 存入 ioc 容器
iocMap.put(id, o);
} catch (InstantiationException e) {e.printStackTrace();
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (ClassNotFoundException e) {e.printStackTrace();
}
});
} catch (DocumentException e) {e.printStackTrace();
}
// 2 对外提供获取示例对象接口
}
/**
* 对外提供获取 bean 接口
*
* @param id
* @return
*/
public static Object getBean(String id) {return iocMap.get(id);
}
}
能够看到在类加载的时候通过解析 xml 获取其中的 bean 的 class 权限定名, 通过反射创建对象, 存储在 map 中,id 作为 key;
而后咱们须要在实现类和管制层去替换这个 new 关键字创立的对象
private TransferServiceImpl transferService = (TransferServiceImpl) BeanFactorys.getBean("transferService");
private AccountDao accountDao = (AccountDao) BeanFactorys.getBean("accountDao");
然而这样子实现还是有点不优雅 还是有 =
连贯;
把 =
号去掉
private AccountDao accountDao
accountDao.queryAccountByCardNo(fromCardNo);
此时, 没有赋值操作, 这儿会默认 null 空呈现空指针异样;
为此, 咱们须要进行设置值, 能够通过 set, 结构参数等设置值;
能够在 xml 中应用属性 set 设置值
在 xml 中定义一个 对象属性 property
设置名字 name 和 ref 援用对象
<bean id="transferService" class="com.udeam.edu.service.impl.TransferServiceImpl">
<property name="AccountDao" ref="accountDao"></property>
</bean>
在方才的 BeanFactorys
中增加解析办法
在解析 xml 完 实例化对象到 Ioc 容器之后, 来解析 property
属性
// 获取所有 properties 属性 并且 set 设置值
List<Element> prList = rootElement.selectNodes("//property");
prList.forEach(y -> {
// 获取 property 属性 name 值
String name = y.attributeValue("name"); // <property name="setAccountDao" ref = "accountDao"></property>
String ref = y.attributeValue("ref");
// 获取父节点 id
Element parent = y.getParent();
// 获取父节点 id
String id = parent.attributeValue("id");
// 保护对象依赖关系
Object o = iocMap.get(id);
// 找到所有办法
Method[] methods = o.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
// 办法就是 set 属性反方
if (methods[i].getName().equalsIgnoreCase("set" + name)) {
try {
//set 设置对象
methods[i].invoke(o, iocMap.get(ref));
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (InvocationTargetException e) {e.printStackTrace();
}
//set 之后从新赋值
iocMap.put(id,o);
}
}
});
而后 TransferServiceImpl
类中增加一个 set 办法设置咱们须要设置的值
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}
xml 上中获取这个 set 办法, 而后利用 set + 获取的 property
属性 name 值 进行判断, 而后反射设置值
if (methods[i].getName().equalsIgnoreCase("set" + name)) {
try {
//set 设置对象
methods[i].invoke(o, iocMap.get(ref));
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (InvocationTargetException e) {e.printStackTrace();
}
//set 之后从新赋值
iocMap.put(id,o);
}
这个过程能够看做是简略的 set 注入形式, 相似于 Spring 中的 set 注入;
IoC 解决了什么问题
IoC 解决对象之间的耦合问题
咱们不⽤⾃⼰去 new 对象了,⽽是由 IoC 容器(Spring 框架)去帮忙咱们实例化对
象并且治理它,咱们须要使⽤哪个对象,去问 IoC 容器要即可
管制反转
管制:指的是对象创立(实例化、治理)的权力
反转:控制权交给外部环境了(spring 框架、IoC 容器)
用到的依赖
<!-- mysql 数据库驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!--druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jackson 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<!--dom4j 依赖 -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--xpath 表达式依赖 为了疾速定位 xml 元素 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
代码
传送门