续上篇,在解决完配置文件的解析之后,写了一个简单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