自定义Spring--DI依赖注入

1.框架思想

IoC(Inversion of Control,控制反转,反向控制),或者成为DI(Dependency Injection,依赖注入).

在传统程序中,相互的依赖性是固定在程序中的.程序的运行也是一步一步的,完全按照程序代码执行,根据代码就能知道程序怎样运行.

在Spring中程序间的依赖关系并不是直接写在程序中,而是配置在Spring文件中,有Spring在运行时配置的.在编译阶段,既没有实例化对象,也没有设置依赖关系,而是把依赖关系交给Spring,有Spring在运行阶段进行实例化,并组装对象.这种做法颠覆了传统的写代码,实例化,组装对象,然后一步一步执行的做法,因此被称为反向控制.

2.实现步骤

a.分析

传统创建对象的方式:

接口=new 实现类()的方式 

IBusinessService  businessService = new BusinessServiceImpl ();

使用Spring创建对象的方式:

接口 = BeanFactory.getBean(“配置文件中配置的Bean的Id属性的值”); 

IBusinessService businessService = BeanFactory.getBean("businessService");

b.配置文件与使用时注意

    1)如何通过接口的标识找到对应的实现类?

         需要在XML文件对每个名字对应的实现类进行配置,得到实现类的类名之后通过反射的方式获取实现类的字节码,再通过字节码创建实现类对象.

   <bean id="businessService" class="com.itheima.ebookstore.service.impl.BusinessServiceImpl" >

   2)对于实现类中成员变量为对象类型时,如何在创建实现类对象时保证成员变量已经被实例化?

         接下来对实现类中成员进行配置,name属性对应实现类中成员变量的标识,成员变量在声明时依然采用接口的方式,以方便当接口对应的实现类发生变化时,进行扩展,ref表示成员变量声明时采用的接口的标识,通过这个标识可以找到对应的实现类.

<property name="userDao" ref="userDao" value=""/>

        具体的配置文件如下:

   文件名:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="categoryDao" class="com.itheima.ebookstore.dao.impl.CategoryDaoImpl" scope="prototype"/>
    <bean id="userDao" class="com.itheima.ebookstore.dao.impl.UserDaoImpl" scope="prototype"/>
    <bean id="bookDao" class="com.itheima.ebookstore.dao.impl.BookDaoImpl" scope="prototype"/> 

<!-- 配置需要执行setter方法,及需要设置的内容的bean id

使用<property>标签进行配置

name="" 属性名,用于确定指定setter方法

ref="" 另一个bean实例的引用 id

-->

    <bean id="businessService" class="com.itheima.ebookstore.service.impl.BusinessServiceImpl" >
        <property name="userDao" ref="userDao" value=""/>
        <property name="bookDao" ref="bookDao" value=""/>
        <property name="categoryDao" ref="categoryDao" value=""/>
    </bean>
</beans>

  存放位置的配置:

     可以在当前项目的web.xml中进行配置(通过ServletContext对象getInitParameter方法获得)

<context-param>

<param-name>contextConfigLocation</param-name>

< ---配置文件的存放位置:前缀classpath:指的是配置文件存放在src路径下,生成Web项目对应在WEB-INF/classes路径下,无前缀指的是在放在WEB-INF路径下-- >

<param-value>classpath: applicationContext.xml</param-value>

<param-value>applicationContext.xml</param-value>

<param-value>classpath: applicationContext.xml</param-value>

</context-param>

   3)使用时注意:

当接口对应实现类中有成员变量希望框架自动进行实例化,需要为该变量提供Set方法

其中接口IBusinessService 的实现类如下:

public class BusinessServiceImpl implements IBusinessService {
    private ICategoryDao categoryDao;
    private IUserDao userDao ;
    private IBookDao bookDao ;
    public void setCategoryDao(ICategoryDao categoryDao) {
        this.categoryDao = categoryDao;
    } 

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    } 

    public void setBookDao(IBookDao bookDao) {
        this.bookDao = bookDao;
    } 

}

c.实现自定义框架

  对XML的bean标签封装到Bean类对象中,并将Bean对象存放在BeanFactory工厂类中,以便工厂在创建实例对象的时候参照,XML文件中有多个bean标签,对应多个接口的配置,需要使用容器将Bean对象保存起来,为了方便的使用接口标识直接能够得到Bean类对象,在工厂中定义成员private static Map<String,Bean> beanMap = new HashMap<String,Bean>();对XML的解析结果进行缓存,缓存的工作可以由ServletContext对象的监听器事件去完成.

  XML配置文件中,Bean标签对应的类实现:

/** 以下标签的封装对象
* <bean id="" class="" scope=""></bean>
* @author Administrator
*
*/
public class Bean {
    private String beanId;
    private String beanClass;
    private String beanScope;  //暂 不使用
    //一对多:一个bean 可以使用 【多个属性】--容器 Set(不重复、无序)
    private Set<Property> propSet = new HashSet<Property>() ; //注意:建议将容器进行实例化,方便操作。
    public String getBeanId() {
        return beanId;
    }
    public void setBeanId(String beanId) {
        this.beanId = beanId;
    }
    public String getBeanClass() {
        return beanClass;
    }
    public void setBeanClass(String beanClass) {
        this.beanClass = beanClass;
    }
    public String getBeanScope() {
        return beanScope;
    }
    public void setBeanScope(String beanScope) {
        this.beanScope = beanScope;
    }
    public Bean() {
        super();
    }
    public Bean(String beanId, String beanClass, String beanScope) {
        super();
        this.beanId = beanId;
        this.beanClass = beanClass;
        this.beanScope = beanScope;
    }
    public Set<Property> getPropSet() {
        return propSet;
    }
    public void setPropSet(Set<Property> propSet) {
        this.propSet = propSet;
    }
}

XML配置文件中,Property标签对应的类实现:

/**
* 用于封装 xml文件中 <property name="" ref="" value="">
* @author Administrator
*
*/
public class Property {
    private String propName;        //属性名
    private String propRef;            //属性引用bean的id值
    private String propValue;        //属性值(暂不用)
    public String getPropName() {
        return propName;
    }
    public void setPropName(String propName) {
        this.propName = propName;
    }
    public String getPropRef() {
        return propRef;
    }
    public void setPropRef(String propRef) {
        this.propRef = propRef;
    }
    public String getPropValue() {
        return propValue;
    }
    public void setPropValue(String propValue) {
        this.propValue = propValue;
    }
    public Property() {
        super();
    }
    public Property(String propName, String propRef, String propValue) {
        super();
        this.propName = propName;
        this.propRef = propRef;
        this.propValue = propValue;
    }
}

BeanFactory的实现:

public class BeanFactory {
    // key : beanId ; value : <bean>标签封装对象
    private static Map<String,Bean> data = new HashMap<String, Bean>();
    public static Map<String, Bean> getData() {
        return data;
    }
    /**
     * 通过bean标示符获得bean实例
     * @param beanId
     * @return
     */
    public static Object getBean(String beanId){
        //通过 id 获得 类的全限定类名,并使用反射进行实例化
        try {
            //1 通过id获得Bean对象
            Bean bean = data.get(beanId);
            if (bean == null) {
                throw new RuntimeException("[" + beanId + "]不存在,请检查配置文件");
            }
            //2 获得全限定类名
            String beanClass = bean.getBeanClass();
            //3 创建bean实例
            Class clazz = Class.forName(beanClass);
            Object beanObj = clazz.newInstance();  //new BusinessServiceImpl()
            //4 执行实例的setter,注意,需要在实现类中对相应属性提供Set方法
            // * 获得所有<property>内容,执行 setter方法,并将ref对应的实例进行设置
            for(Property prop:bean.getPropSet()){
                // 属性名:表示需要指定setter方法
                String propName = prop.getPropName();
                // 引用名称:表示使用getBean获得实例
                String propRef = prop.getPropRef();
                // 引用对应实例对象
                Object propRefObj = getBean(propRef);
                // 将对用的值设置相应的实例
                BeanUtils.setProperty(beanObj, propName, propRefObj);
            }
            //返回创建的实例
            return beanObj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T getBean(String beanId,Class<T> clazz){
        return (T)getBean(beanId);
    } 

}

在ServletContext对象的监听器中将XML文件中的标签解析为对应的Java类对象,并将解析的结果保存在BeanFactory中:

public class ContextLoaderListener implements ServletContextListener { 

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //1 获得web.xml文件配置 spring配置文件位置
        ServletContext sc = sce.getServletContext();
        String contextConfigLocation = sc.getInitParameter("contextConfigLocation");
        //2 获得资源流
        InputStream is = null;
        if(contextConfigLocation.startsWith("classpath:")){
            //xml在src下
            String fileName = contextConfigLocation.substring("classpath:".length());
            is = ContextLoaderListener.class.getClassLoader().getResourceAsStream(fileName);
        } else{
            //WEB-INF目录下
            is = sc.getResourceAsStream("/WEB-INF/" + contextConfigLocation);
        }
        //3 解析并封装
        parser(is);
    }
    /**
     * 使用dom4j解析
     * @param is
     */
    private void parser(InputStream is) {
        try {
            //1 获得doucument
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(is);
            //2 解析
            //2.1 获得根元素
            Element rootElement = document.getRootElement();
            //2.2 获得所有的<bean>元素
            List<Element> allBeanElement = rootElement.elements("bean");
            //2.3 遍历
            for (Element beanElement : allBeanElement) {
                //2.4.1 id属性
                String beanId = beanElement.attributeValue("id");
                //2.4.2 class属性
                String beanClass = beanElement.attributeValue("class");
                //2.4.3 scope属性
                String beanScope = beanElement.attributeValue("scope");
                //2.5 封装 Bean对象,添加BeanFactory的提供Map中
                Bean bean = new Bean(beanId, beanClass, beanScope);
                /**2.6 解析 <bean>中 <property> start*/
                // 2.6.1 获得所有的<property>元素
                List<Element> allPropElement = beanElement.elements("property");
                for(Element propElement : allPropElement){
                    //2.6.2 获得name属性
                    String propName = propElement.attributeValue("name");
                    //2.6.3 获得ref属性
                    String propRef = propElement.attributeValue("ref");
                    //2.6.4 获得value属性
                    String propValue = propElement.attributeValue("value");
                    //2.6.5 将获得内容,封装 JavaBean Property对象中,并添加到 bean实例
                    Property prop = new Property(propName, propRef, propValue);
                    bean.getPropSet().add(prop);
                }
                /**解析 <bean>中 <property> end*/
                BeanFactory.getData().put(beanId, bean);
            }
            System.out.println("--------------- 配置解析完成,加载了"+BeanFactory.getData().size()+"<bean>标签");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    } 

    @Override
    public void contextDestroyed(ServletContextEvent sce) { 

    }
}

时间: 2024-12-17 18:30:52

自定义Spring--DI依赖注入的相关文章

Spring DI - 依赖注入

1.IOC(DI) - 控制反转(依赖注入) 所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和生命周期的管理,而是在需要时由Spring框架提供,这个由spring框架管理对象创建和生命周期的机制称之为控制反转.而在创建对象的过程中Spring可以依据配置对对象的属性进行设置,这个过称之为依赖注入,也即DI. 2.set方法注入 通常的javabean属性都会私有化,而对外暴露setXxx()

spring -di依赖注入,seter方法

[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" xmlns:p="http://www.sprin

初识Spring框架实现IOC和DI(依赖注入)

学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的, IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想. 在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制.如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转

Spring的依赖注入(DI)三种方式

Spring依赖注入(DI)的三种方式,分别为: 1.  接口注入 2.  Setter方法注入 3.  构造方法注入 下面介绍一下这三种依赖注入在Spring中是怎么样实现的. 首先我们需要以下几个类: 接口 Logic.java 接口实现类 LogicImpl.java 一个处理类 LoginAction.java 还有一个测试类 TestMain.java Logic.java如下: package com.spring.test.di; public interface Logic {

Spring详解(三)------DI依赖注入

上一篇博客我们主要讲解了IOC控制反转,也就是说IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建.初始化.销毁等工作交给spring容器来做.那么创建对象的时候,有可能依赖于其他的对象,即类的属性如何赋值?这也是我们这篇博客讲解 Spring 另一个核心要点:DI依赖注入. PS:本篇博客源码下载链接:http://pan.baidu.com/s/1c2xVUDi密码:v1h3 1.什么是DI依赖注入? spring动态的向某个对象提供它所需要的其他对象.这一点

Spring:(二)DI依赖注入方式

DI 依赖注入 DI(Dependency Injection)依赖注入,说简单一点就将类里面的属性在创建类的过程中给属性赋值,即将对象依赖属性(简单值,集合,对象)通过配置设值给该对象. 属性注入的方式 构造方法的方式 set方法的方式 工厂方法注入 主要学习前两种方式 构造方法的方式 当是构造方法时注入Bean的属性值(简单值,集合,对象) 利用<constructor-arg>标签进行属性的注入 name:被设置属性的名 value:被设置属性的值 编写用构造方法的pojo 1 pack

Spring学习三、Spring配置与DI依赖注入

五.Spring配置 5.1别名 <!-- 别名--> <alias name="user" alias="user2"/> 5.2 Bean的配置 <!-- id : bean的唯一标识符,相当于对象名 class : 全限定名 包名 + 类名 name : 也是别名,name可以同时起多个别名 --> <bean id="userT" class="cn.imut.pojo.User&quo

【Spring】DI 依赖注入传递参数的方式

DI依赖注入传入参数的方式,这里介绍了基本数据类型,集合,符合数据类型的传递(String类型比较特殊,其传递值和基本数据类型的方法一致) 注入基本数据类型和String类型 通过setter方法注入基本数据类型与String 案例: <bean id="book" class="cn.xdl.bean.Book"> <!-- 对于基本数据类型 与 String的注入操作 通过set方法 ,完成注入 name: 属性的名称 value: 属性要赋的

第二十七天 春之细雨润物于无形 —Spring的依赖注入

6月11日,晴."夏条绿已密,朱萼缀明鲜.炎炎日正午,灼灼火俱燃." IT人习惯把具体的事物加工成的形状一致的类,正是这样的一致,加上合适的规范,才能彰显对象筋道的牙感和bean清香的味道.Spring比谁都清楚OO的奥妙,让组件之间的依赖关系由容器在运行时期决定,称作依赖注入(Dependency Injection). 下面用一通俗的例子,一探依赖注入奥妙. 设计模式中的一个原则:针对接口编程,不要针对实现编程. 一.设计两个接口: (1)奶制品接口-MilkProductInte

二、Spring的依赖注入

Spring的依赖注入 1.理解依赖注入 (1)A对象需要调用B对象的方法,这种情形被称为依赖注入,即A对象依赖B对象:依赖注入(DI)也被成为控制反转(IoC): (2)依赖注入的两种方式: 1)设值注入:IoC容器通过使用成员变量的setter方法来注入被依赖对象: 2)构造注入:IoC容器通过使用构造器来注入被依赖的对象: 2.设置注入 (1)Bean与Bean之间的依赖关系由Spring管理,Spring采用setter方法为目标Bean注入所需要的值,这种注入方式被称为设值注入: (2