Spring模拟——BeanFactory

续上篇,在解决完配置文件的解析之后,写了一个简单addUser模块准备测试

model代码:

package com.l.model;

public class User {
	
	private String name;
	private String password;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

 DAO(接口代码未贴出)

package com.l.impl;

import com.l.dao.UserDAO;
import com.l.model.User;

public class UserDAOImpl implements UserDAO {

	@Override
	public void save(User u) {
		System.out.println("Success!");
	}

}

UserService代码:

package com.l.service;

import com.l.dao.UserDAO;
import com.l.model.User;

public class UserService {
	        private UserDAO dao;//标红
		public UserDAO getUserDAO() {
			return dao;
		}
		public void setUserDAO(UserDAO dao) {
			this.dao = dao;
		}
		public void add(User user) {
			dao.save(user);
		}

}
 

 下面进行测试:

package com.l.service;

import java.io.IOException;

import org.junit.Test;

import com.l.impl.UserDAOImpl;
import com.l.model.User;

public class UserServiceTset {
	@Test
	public void addTest() throws IOException, Exception{	
		UserService service = new UserService();
		User u = new User();
		service.add(u);
	}
}

 当你测试完,会报一个空指针错误,这是因为UserService中没有new一个UserDAO对象,这就是spring的好处,不用自己进行手动创建对象。是怎么实现的呢?

在spring中有这样一个配置文件,叫beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<bean id="u" class="com.l.impl.UserDAO">
        </bean>
</beans>

 使用上篇的方法对其进行解析

package com.l.spring;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

public class ClassPathXMLApplicationContext implements BeanFactory{
	
	private Map<String, Object> beans = new HashMap<String, Object>();
	
	
	public ClassPathXMLApplicationContext() throws Exception, IOException{
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(this.getClass()
				.getClassLoader().getResourceAsStream("beans.xml"));
		Element root = doc.getRootElement();
		List<Element> elements = root.getChildren("bean");
		
		for (int i = 0; i < elements.size(); i++) {
			Element element = elements.get(i);
			
			String id = element.getAttributeValue("id");
			String clazz = element.getAttributeValue("class");//获取UserDAO的类名
			System.out.println(id + " : " + clazz);
			
			Object o = Class.forName(clazz).newInstance();//为UserDAO创建一个对象
			
			beans.put(id, o);
		}
		
	}
	@Override
        //通过getBean获取为UserDAO创建的对象
	public Object getBean(String id) {
		return beans.get(id);
	}
	
}

通过解析beans.xml中的类名,为其创建对象,实现BeanFactory中的getBea()方法,让外界获取该对象

BeanFactory接口:

package com.l.spring;

public interface BeanFactory {
	public Object getBean(String name);
}

  再次对其进行测试:

package com.l.service;

import java.io.IOException;

import org.junit.Test;

import com.l.impl.UserDAOImpl;
import com.l.model.User;
import com.l.spring.BeanFactory;
import com.l.spring.ClassPathXMLApplicationContext;

public class UserServiceTset {
	@Test
	public void addTest() throws IOException, Exception{
		BeanFactory factory = new ClassPathXMLApplicationContext();
		
		UserService service = new UserService();
		UserDAO userDAO = (UserDAO) factory.getBean("u");

		service.setUserDao(userDAO);
		User u = new User();
		u.setName("nimA");
		u.setPassword("heh");
		service.add(u);
	}
}

 通过测试

通过BeanFactory来实例化,管理对象,使用它的实现类ClassPathXmlApplicationContext来新建对象,将配置文件中对应的id和对象放入Map中。

再想一下,既然UserDAO可以这样来实现,那么UserService也是可以的:

在beans.xml中加入再添加一个bean标签<bean id="userService" class="com.l.service.UserService">

测试代码:

package com.l.service;

import java.io.IOException;

import org.junit.Test;

import com.l.impl.UserDAOImpl;
import com.l.model.User;
import com.l.spring.BeanFactory;
import com.l.spring.ClassPathXMLApplicationContext;

public class UserServiceTset {
	@Test
	public void addTest() throws IOException, Exception{
		BeanFactory factory = new ClassPathXMLApplicationContext();
		
                UserDAO userDAO = (UserDAO) factory.getBean("u");
		UserService service = (UserService)factory.getBean("userService");//标红
		service.setUserDao(userDAO);
		User u = new User();

		service.add(u);
	}
}
代码中标红部分, 分两次调用了getBean方法,然而却是没有这个必要的。在bean.xml中<bean>下添加一个子标签<property>
<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<bean id="u" class="com.l.impl.UserDAOImpl"/>
	<bean id="userService" class="com.l.service.UserService">
		<property name="userDAO" bean="u"/>
	</bean>
</beans>
 property是告诉你,“userService”中有一个setUserDAO的方法,当调用该方法时,将后面指定的bean传进去,这种方法叫做注入:

注入代码:

List<Element> property = element.getChildren("property");
			for(Element propertyElement : property) {
		    	   String name = propertyElement.getAttributeValue("name"); //userDAO
		    	   String bean = propertyElement.getAttributeValue("bean"); //u
		    	   Object beanObject = beans.get(bean);//UserDAOImpl instance
		    	   
		    	   String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
		
		    	   Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
		    	  // System.out.println(m.getName());
		    	   m.invoke(o, beanObject);
		       }
 将这段代码加到解析bean.xml的java文件的for循环中,此处用到的是反射机制,通过解析配置文件,Map中存有<"u", 相应对象><"userService", 相应对象>,配置文件中第一个<bean>标签下没有<property>,循环至第二个,即"userService" 取出”userService“中的setUserDAO方法,最后通过invoke(obj,args)实现注入,参数obj为调用该方法的对象(此处为o),参数args为取出方法的参数setUserDAO的参数为为一个UserDAO对象,而beanObject就是该类型。

test代码:

package com.l.service;

import java.io.IOException;

import org.junit.Test;

import com.l.impl.UserDAOImpl;
import com.l.model.User;
import com.l.spring.BeanFactory;
import com.l.spring.ClassPathXMLApplicationContext;

public class UserServiceTset {
	@Test
	public void addTest() throws IOException, Exception{
		BeanFactory factory = new ClassPathXMLApplicationContext();
		
		UserService service = (UserService)factory.getBean("userService");

		User u = new User();
		service.add(u);
	}
}
 

 测试通过,从原理上了解IOC更好的开始学习spring

相关推荐