自定义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) { 



    } 

}

你可能感兴趣的:(spring)