首页 > 代码库 > 自定义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依赖注入