@[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>
代码
传送门