模拟Spring属性的注入
1、创建场景代码,配置spring属性注入
新建包dao,创建接口IPersonDao
/** * Huisou.com Inc. * Copyright (c) 2011-2012 All Rights Reserved. */package com.chenzehe.spring.dao;/** * @description * * @author chenzehe * @email hljuczh@163.com * @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 hljuczh@163.com * @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" /><bean id="helloWorld" 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 hljuczh@163.com * @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() {// 循环所有beanfor (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}}}}??