首页 > 代码库 > 自定义Spring--DI依赖注入

自定义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=http://www.mamicode.com/""/>

        具体的配置文件如下:

   文件名: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=http://www.mamicode.com/""> >@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 =http://www.mamicode.com/ propValue;     }     public Property() {         super();     }     public Property(String propName, String propRef, String propValue) {         super();         this.propName = propName;         this.propRef = propRef;         this.propValue =http://www.mamicode.com/ propValue;     } }

BeanFactory的实现:

public class BeanFactory {     // key : beanId ; value : <bean>标签封装对象     private static Map<String,Bean> data = http://www.mamicode.com/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 = http://www.mamicode.com/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--DI依赖注入