1、 创建场景代码,配置spring 属性注入
新建包dao ,创建接口 IPersonDao
/** * Huisou.com Inc. * Copyright (c) 2011-2012 All Rights Reserved. */ package com.chenzehe.spring.dao; /** * @description * * @author chenzehe * @email [email protected] * @create 2012-4-17 下午08:56:39 */ public interface IPersonDao { void save(); }
创建IPersonDao 的实现类:
/** * Huisou.com Inc. * Copyright (c) 2011-2012 All Rights Reserved. */ package com.chenzehe.spring.dao.impl; import com.chenzehe.spring.dao.IPersonDao; /** * @description * * @author chenzehe * @email [email protected] * @create 2012-4-17 下午08:57:33 */ public class PersonDao implements IPersonDao { public void save() { System.out.println("PersonDao save()..."); } }
创建 Service包,新建接口 IHelloWorld
package com.chenzehe.spring.service; public interface IHelloWorld { void sayHelloWorld(); void save(); }
创建新接口的实现类Helloworld ,该类中有属性 IPerson 类型:
package com.chenzehe.spring.service.impl; import com.chenzehe.spring.dao.IPersonDao; import com.chenzehe.spring.service.IHelloWorld; public class HelloWorldImpl implements IHelloWorld { private IPersonDao personDao; public void save() { personDao.save(); } public HelloWorldImpl() { System.out.println("实例化!"); } public void sayHelloWorld() { System.out.println("Hello World!"); } public IPersonDao getPersonDao() { return this.personDao; } public void setPersonDao(IPersonDao personDao) { this.personDao = personDao; } }
在spring 配置文件 applicationContext.xml 中配置 benan :
<bean id="personDao" class="com.chenzehe.spring.dao.impl.PersonDao" /> <bean id="helloWorld" class="com.chenzehe.spring.service.impl.HelloWorldImpl"> <property name="personDao" ref="personDao" /> </bean>
创建单元测试类HelloWorldTest :
package com.chenzehe.spring.test.junit; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.chenzehe.spring.service.IHelloWorld; public class HelloWorldTest { @Test public void instanceApplicationContext() { ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml"); IHelloWorld helloWorld = (IHelloWorld) cxt.getBean("helloWorld"); helloWorld.sayHelloWorld(); helloWorld.save(); } }
2、定义 Bean 属性描述类 PropertyDefinition
/** * Huisou.com Inc. * Copyright (c) 2011-2012 All Rights Reserved. */ package com.chenzehe.spring.myspring; /** * @description * * @author chenzehe * @email [email protected] * @create 2012-4-17 下午08:39:22 */ public class PropertyDefinition { private String name; private String ref; public PropertyDefinition() { } public PropertyDefinition(String name, String ref) { this.name = name; this.ref = ref; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getRef() { return this.ref; } public void setRef(String ref) { this.ref = ref; } }
3 、 把该属性描述对象添加到 Bean 对象描述定义类 BeanDefinition 中,一个 Bean 可以有多个属性,所以对象属性为集合类型。
package com.chenzehe.spring.myspring; import java.util.ArrayList; import java.util.List; public class BeanDefinition { private String id; private String className; private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>(); public BeanDefinition() { } public BeanDefinition(String id, String classPath) { this.id = id; this.className = classPath; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public List<PropertyDefinition> getPropertys() { return this.propertys; } public void setPropertys(List<PropertyDefinition> propertys) { this.propertys = propertys; } }
4、 在原先代码 模拟spring 生成 bean 基础上增加注入功能
在解析xml 文件生成 bean 描述对象时解析描述描述对象,并添加到 bean 对象的 properys 属性中。然后再添加注入方法 injectObject() 。
package com.chenzehe.spring.myspring; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; public class ClassPathXmlApplicationContext { // 保存从配置文件中解析出来的bean属性 private List<BeanDefinition> beans = new ArrayList<BeanDefinition> () ; // 保存实例化好的bean private Map<String, Object> beansClass = new HashMap<String, Object> () ; /** * 根据Bean名称取得Bean实例 */ public Object getBean ( String name ) { return beansClass .get ( name ) ; } /** * 传入配置文件初始化 */ public ClassPathXmlApplicationContext ( String xmlFilePath ) { initBeansFromXML ( xmlFilePath ) ; initBeansClass () ; injectObject () ; } /** * 为Bean对象的属性注入值 */ private void injectObject () { // 循环所有bean for ( BeanDefinition beanDefinition : beans ) { Object bean = beansClass .get ( beanDefinition.getId ()) ; if ( bean != null ) { try { PropertyDescriptor [] ps = Introspector. getBeanInfo ( bean.getClass ()) .getPropertyDescriptors () ; for ( PropertyDefinition propertyDefinition : beanDefinition.getPropertys ()) { for ( PropertyDescriptor propertyDescriptor : ps ) { if ( propertyDefinition.getName () .equals ( propertyDescriptor.getName ())) { Method setterMethod = propertyDescriptor.getWriteMethod () ; // 获取setter方法 if ( setterMethod != null ) { setterMethod.setAccessible ( true ) ; Object value = beansClass .get ( propertyDefinition.getRef ()) ; setterMethod.invoke ( bean, value ) ; // 把引用对象注入到属性中 } break ; } } } } catch ( Exception e ) { // TODO : handle exception } } } } /** * 从beans中读取Bean属性,使用反射实例化Bean对象 */ private void initBeansClass () { for ( BeanDefinition bean : beans ) { if ( StringUtils. isNotBlank ( bean.getClassName ())) { try { beansClass .put ( bean.getId () , Class. forName ( bean.getClassName ()) .newInstance ()) ; } catch ( Exception e ) { e.printStackTrace () ; } } } } /** * 使用 Jsoup 解析配置文件,把bean属性存到beans */ private void initBeansFromXML ( String xmlFilePath ) { try { Document doc = Jsoup. parse ( new File ( xmlFilePath ) , "UTF-8" ) ; Elements beanElements = doc .getElementsByTag ( "bean" ) ; for ( Element element : beanElements ) { String id = element.attr ( "id" ) ; String classPath = element.attr ( "class" ) ; BeanDefinition bean = new BeanDefinition ( id, classPath ) ; // 取得所有属性元素 Elements propertyElements = element.getElementsByTag ( "property" ) ; for ( Element propertyElement : propertyElements ) { String propertyName = propertyElement.attr ( "name" ) ; String propertyRef = propertyElement.attr ( "ref" ) ; PropertyDefinition propertyDefinition = new PropertyDefinition ( propertyName, propertyRef ) ; // 把属性元素定义添加到bean定义中 bean.getPropertys () .add ( propertyDefinition ) ; } beans .add ( bean ) ; } } catch ( Exception e ) { e.printStackTrace () ; } } }
在单元测试中使用该注入器:
package com.chenzehe.spring.test.junit; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.chenzehe.spring.service.IHelloWorld; public class HelloWorldTest { @Test public void instanceApplicationContext() { ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml"); IHelloWorld helloWorld = (IHelloWorld) cxt.getBean("helloWorld"); helloWorld.sayHelloWorld(); helloWorld.save(); com.chenzehe.spring.myspring.ClassPathXmlApplicationContext mycxt = new com.chenzehe.spring.myspring.ClassPathXmlApplicationContext( "E:\\chenzehe\\study\\Spring\\eclipse\\workspace\\com.chenzehe.spring\\src\\main\\resources\\applicationContext.xml"); IHelloWorld myHelloWorld = (IHelloWorld) mycxt.getBean("helloWorld"); myHelloWorld.sayHelloWorld(); myHelloWorld.save(); } }
5、 内部Bean 注入
以上模拟注入对于内部Bean 的注入依然生效,即 Bean 的配置文件改成下面格式:
< bean id = "helloWorld" class = "com.chenzehe.spring.service.impl.HelloWorldImpl" > < property name = "personDao" > < bean class = "com.chenzehe.spring.dao.impl.PersonDao" /> </ property > </ bean >
6、 一般属性的注入
给bean 对象 HelloWorldImpl 添加一个 String 类型的属性 name ,一个 Integer 类型的属性 id ,添加 set 和 get 方法,添加一个三个属性的构造函数。
修改spring 配置文件为:
< bean id = "personDao" class = "com.chenzehe.spring.dao.impl.PersonDao" /> < bean id = "helloWorld" class = "com.chenzehe.spring.service.impl.HelloWorldImpl" > < property name = "personDao" ref = "personDao" /> < property name = "name" value = "chenzehe" /> < property name = "id" value = "25" /> </ bean >
修改属性描述类PropertyDefinition ,添加一个描述属性 value ,设置 get 、 set 方法。
修改注入实现核心代码ClassPathXmlApplicationContext ,先加入 commons-beanutils 包的依赖,用其取得一般类型的属性。
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
修改解析xml 文件类,把一般类型的属性信息也加进去:
private void initBeansFromXML(String xmlFilePath) { try { Document doc = Jsoup.parse(new File(xmlFilePath), "UTF-8"); Elements beanElements = doc.getElementsByTag("bean"); for (Element element : beanElements) { String id = element.attr("id"); String classPath = element.attr("class"); BeanDefinition bean = new BeanDefinition(id, classPath); // 取得所有属性元素 Elements propertyElements = element.getElementsByTag("property"); for (Element propertyElement : propertyElements) { String propertyName = propertyElement.attr("name"); String propertyRef = propertyElement.attr("ref"); String propertyValue = propertyElement.attr("value"); PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyRef, propertyValue); // 把属性元素定义添加到bean定义中 bean.getPropertys().add(propertyDefinition); } beans.add(bean); } } catch (Exception e) { e.printStackTrace(); } }
修改注入方法:
private void injectObject() { // 循环所有bean for (BeanDefinition beanDefinition : beans) { Object bean = beansClass.get(beanDefinition.getId()); if (bean != null) { try { PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); for (PropertyDefinition propertyDefinition : beanDefinition.getPropertys()) { for (PropertyDescriptor propertyDescriptor : ps) { if (propertyDefinition.getName().equals(propertyDescriptor.getName())) { Method setterMethod = propertyDescriptor.getWriteMethod();// 获取setter方法 if (setterMethod != null) { Object value = null; if (StringUtils.isBlank(propertyDefinition.getValue())) { value = beansClass.get(propertyDefinition.getRef()); } else { value = ConvertUtils.convert(propertyDefinition.getValue(), propertyDescriptor.getPropertyType()); } setterMethod.setAccessible(true); setterMethod.invoke(bean, value);// 把引用对象注入到属性中 } break; } } } } catch (Exception e) { // TODO: handle exception } } } }