Spring框架一刷:关于IOC手动装配,配置XML方式实现。

Spring框架使用的前提与理解

使用Spring框架需要下载对应的jar包,事实上我的理解就是spring框架的创始人创建好了对应的包给使用者使用,初学者不必在意spring底层实现细节,导包即用即可。

Spring中有一个重要的概念,即IOC容器,(名字叫控制反转)。

什么是IOC?

IOC事实上是为了项目解耦出现的,将对象的创建以及对应属性对象的注入交由Spring进行管理。

在web项目的初期,对象之间大量使用new方法来进行创建,使得层与层之间耦合严重,牵一发而动全身,具体例子就是:service层需要DAO层对象来对数据库进行操作,如果直接new一个DAO层对象的话,后续如果想对DAO进行如移动class类到其他包,改名等操作,会疯狂爆红(报bug)。

为了项目后期的维护更新以及升级,IOC容器应运而生,在XML中进行对象的配置(在XML中对象统称为bean),IOC通过读取XML中的配置来进行对象的统一创建(默认是单例模式创建),如果对象中引用到其他bean对象,IOC会在统一创建所有bean对象后,在一一根据配置的属性进行依赖注入。(这种是读取XML文件进行手动配置)。

简易IOC的实现原理

之前博主曾跟随一个视频进行过手写简易版ioc容器,所以大概能够明白ioc容器的实现过程:大致上就是通过特定的类,读取到你配置的bean中id与class属性,通过反射的方式用class的全类名创建对象,然后将id作为key值,bean对象作为value值封装到一个BeanMap集合中,所有对象创建完毕之后,再重头读取bean节点的子节点中的属性值,通过name属性作为key值在BeanMap中得到对应的bean对象,此时有两种方法可以进行属性注入,Spring中使用xml配置的话默认是通过该类设置的set方法进行注入,如果没设置到set方法会报错,配置失败;而博主的手写简易IOC容器是使用反射进行暴力破解注入,这种方法不安全,也不需要使用到该类方法配置的set方法。 (具体视频是尚硅谷的最新版Javaweb视频,讲师是张益桃老师)

	private Map<String,Object> beanMap = new HashMap<>();
    private String path = "applicationContext.xml" ;
    public ClassPathXmlApplicationContext(){
        this("applicationContext.xml");
    }
    public ClassPathXmlApplicationContext(String path){
        if(StringUtil.isEmpty(path)){//StringUtil是手写的工具类,作用只是判断字符串是否为空
            throw new RuntimeException("IOC容器的配置文件没有指定...");
        }
        try {
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path);
            //1.创建DocumentBuilderFactory
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //2.创建DocumentBuilder对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;
            //3.创建Document对象
            Document document = documentBuilder.parse(inputStream);

            //4.获取所有的bean节点
            NodeList beanNodeList = document.getElementsByTagName("bean");
            for(int i = 0 ; i<beanNodeList.getLength() ; i++){
                Node beanNode = beanNodeList.item(i);
                if(beanNode.getNodeType() == Node.ELEMENT_NODE){
                    Element beanElement = (Element)beanNode ;
                    String beanId =  beanElement.getAttribute("id");
                    //读取到对应id的属性值
                    String className = beanElement.getAttribute("class");
                    //读取到对应class的全类名
                    Class beanClass = Class.forName(className);
                    //创建bean实例
                    Object beanObj = beanClass.newInstance() ;
                    //将bean实例对象保存到map容器中
                    beanMap.put(beanId , beanObj) ;
                    //到目前为止,此处需要注意的是,bean和bean之间的依赖关系还没有设置
                }
            }
            //5.组装bean之间的依赖关系
            for(int i = 0 ; i<beanNodeList.getLength() ; i++){
                Node beanNode = beanNodeList.item(i);
                if(beanNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element beanElement = (Element) beanNode;
                    String beanId = beanElement.getAttribute("id");
                    //先得到是哪个bean需要被注入属性依赖
                    NodeList beanChildNodeList = beanElement.getChildNodes();
                    for (int j = 0; j < beanChildNodeList.getLength() ; j++) {
                        Node beanChildNode = beanChildNodeList.item(j);
                        //判断这个bean中的子节点是否是设置的property节点
                        if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){
                            Element propertyElement = (Element) beanChildNode;
                            String propertyName = propertyElement.getAttribute("name");
                            String propertyRef = propertyElement.getAttribute("ref");
                            //1) 找到propertyRef对应的实例
                            Object refObj = beanMap.get(propertyRef);
                            //2) 将refObj设置到当前bean对应的实例的property属性上去
                            Object beanObj = beanMap.get(beanId);
                            Class beanClazz = beanObj.getClass();
                            Field propertyField = beanClazz.getDeclaredField(propertyName);
                            propertyField.setAccessible(true);
                            propertyField.set(beanObj,refObj);
                        }
                    }
                }
            }

IOC容器中基于XML的属性注入的格式要求

第一种,常用类属性通过set方法注入

<bean id="book" class="com.atguigu.spring5.Book">
 <!--使用 property 完成属性注入
 name:类里面属性名称
 value:向属性注入的值
 -->
 <property name="bname" value="易筋经"></property>
 <property name="bauthor" value="达摩老祖"></property>
</bean>

第二种,通过有参构造函数注入

public class Orders {
 //属性
 private String oname;
 private String address;
 //有参数构造
 public Orders(String oname,String address) {
 this.oname = oname;
 this.address = address;
 }
}
<bean id="orders" class="com.atguigu.spring5.Orders">
 <constructor-arg name="oname" value="电脑"></constructor-arg>
 <constructor-arg name="address" value="China"></constructor-arg>
</bean>

而引用类型注入的话,又分为外部bean对象注入与内部bean对象注入
其中,外部bean对象注入:

public class UserService {
 //创建 UserDao 类型属性,生成 set 方法
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void add() {
 System.out.println("service add...............");
 userDao.update();
 }
}
<!--1 service 和 dao 对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
 <!--注入 userDao 对象
 name 属性:类里面属性名称
 ref 属性:创建 userDao 对象 bean 标签 id 值
 -->
 <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

内部bean对象注入:

//部门类
public class Dept {
 private String dname;
 public void setDname(String dname) {
 this.dname = dname;
 }
}
//员工类
public class Emp {
 private String ename;
 private String gender;
 //员工属于某一个部门,使用对象形式表示
 private Dept dept;
 public void setDept(Dept dept) {
 this.dept = dept;
 }
 public void setEname(String ename) {
 this.ename = ename;
 }
 public void setGender(String gender) {
 this.gender = gender;
 }
}
<!--内部 bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
 <!--设置两个普通属性-->
 <property name="ename" value="lucy"></property>
 <property name="gender" value=""></property>
 <!--设置对象类型属性-->
 <property name="dept">
 <bean id="dept" class="com.atguigu.spring5.bean.Dept">
 <property name="dname" value="安保部"></property>
 </bean>
 </property>
</bean>

后面还有一些细节方面的东西,比如关于数组,集合类型的注入该如何实现,此处博主不想作多赘述,全都是所见即所得的东西,该用时查询使用即可。

还有一种方式是通过注解进行配置,我还没学完。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值