需求:
把用户信息进行持久化。
分析:
1、提取需求中的实体类:用户信息,定义类:User,包含name和age两个field
2、分层。根据需求可分为用户服务和持久化层;
一、传统方法实现:
1、写持久层测试用例
package com.gll.spring.ioc.dao;
import com.gll.spring.ioc.model.User;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Created by Administrator on 2015/4/4.
*/
public class UserDaoTest {
@Test
public void should_return_true_when_save_user() throws Exception {
UserDao userDao = new UserDao();
User user = new User();
assertThat(userDao.save(user),is(true));
}
}
3、消除测试用例中的错误,创建实体类User和持久化类DaoUser,并运行测试用例并通过:
package com.gll.spring.ioc.model;
/**
* Created by Administrator on 2015/4/4.
*/
public class User {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
package com.gll.spring.ioc.dao;
import com.gll.spring.ioc.model.User;
/**
* Created by Administrator on 2015/4/4.
*/
public class UserDao {
public boolean save(User user) {
return true;
}
}
4、先写服务层测用例,消除用例中的错误并使用例通过,代码如下:
package com.gll.spring.ioc.service;
import com.gll.spring.ioc.dao.UserDao;
import com.gll.spring.ioc.model.User;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Created by Administrator on 2015/4/4.
*/
public class UserServiceTest {
@Test
public void should_return_true_when_add_user() throws Exception {
UserService userService = new UserService();
userService.setUserDao(new UserDao());
User user = new User();
assertThat(userService.add(user),is(true));
}
}
package com.gll.spring.ioc.service;
import com.gll.spring.ioc.dao.UserDao;
import com.gll.spring.ioc.model.User;
/**
* Created by Administrator on 2015/4/4.
*/
public class UserService {
private UserDao userDao;
public boolean add(User user) {
return userDao.save(user);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
5、完成需求,问题:
需求变更:用户信息存储到多个数据库或文件中时,则需要修改代码,由开发者控制实现。
二、模拟spring实现
1、考虑在配置文件中配置Dao的具体实现类,增加一个接口BeanFactory:
package com.gll.spring.simulation;
/**
* Created by Administrator on 2015/4/4.
*/
public interface BeanFactory {
Object getBean(String var);
}
2、定义配置文件simulation.xml,格式如下:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="userDao" class="com.gll.spring.ioc.dao.UserDao"></bean> <bean id="userService" class="com.gll.spring.ioc.service.UserService"> <property name="userDao" ref="userDao"></property> </bean> </beans>
3、写一个解析simulation.xml文件的测试用例
package com.gll.spring.simulation;
import com.gll.spring.ioc.dao.UserDao;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
/**
* Created by Administrator on 2015/4/4.
*/
public class ClassPathXmlApplicationContextTest {
@Test
public void should_return_class_UserDao_when_get_bean_by_userDao() throws Exception {
BeanFactory ctx = new ClassPathXmlApplicationContext();
assertThat(ctx.getBean("userDao").getClass().getName(),is(UserDao.class.getName()));
}
}
4、消除测试用例中的错误,运行测试用例,此时用例不通过,实现解析simulation.xml文件直到用例通过:
package com.gll.spring.simulation;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator on 2015/4/4.
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
private final Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder builder = new SAXBuilder();
Document build = builder.build(this.getClass().getClassLoader().getResourceAsStream("simulation.xml"));
Element rootElement = build.getRootElement();
List<Element> elements = rootElement.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");
Object instance = Class.forName(clazz).newInstance();
beans.put(id, instance);
}
}
@Override
public Object getBean(String var) {
return beans.get(var);
}
}
5、写第二个测试用例,把UserDao注入到UserService中:
@Test
public void userDao_not_null_when_get_userDao_in_userService() throws Exception {
UserService userService = (UserService) ctx.getBean("userService");
assertThat(userService.getUserDao().getClass().getName(), is(UserDaoImpl.class.getName()));
}
该过程需要把UserDao提取成一个接口,实现setter注入,运行所有测试用例,直到所有测试通过:
package com.gll.spring.simulation;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator on 2015/4/4.
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
private final Map<String, Object> beans = new HashMap<String, Object>();
@Override
public Object getBean(String id) {
return beans.get(id);
}
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder builder = new SAXBuilder();
Document build = builder.build(this.getClass().getClassLoader().getResourceAsStream("simulation.xml"));
Element rootElement = build.getRootElement();
List<Element> elements = rootElement.getChildren("bean");
buildObjectDI(elements);
}
private void buildObjectDI(List<Element> elements) throws Exception {
for (int i = 0; i < elements.size(); i++) {
Element element = elements.get(i);
String id = element.getAttributeValue("id");
String clazz = element.getAttributeValue("class");
Object instance = Class.forName(clazz).newInstance();
beans.put(id, instance);
List<Element> properties = element.getChildren("property");
BuildSetterDI(instance, properties);
}
}
private void BuildSetterDI(Object instance, List<Element> properties) throws Exception {
for (int j = 0; j < properties.size(); j++) {
Element property = properties.get(j);
String name = property.getAttributeValue("name");
String ref = property.getAttributeValue("ref");
Object field = beans.get(ref);
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method method = instance.getClass().getMethod(methodName, field.getClass().getInterfaces()[0]);
method.invoke(instance, field);
}
}
}
6、如果用户信息写到文件中,则增加写文件类FileUserDaoImpl.java,修改配置simulation.xml则可。
说明:例子采用先写测试后写实现的方式开发但用例跨度有点大,整个过程还伴随了重构。