自定义beans.xml文件实现Spring框架
经过一天的补习,学习文件加载,java反射,JDom等知识,到了晚上终于能够搭出一个基于配置文件的简单spring框架实现!
首先我们先看看这个问题:
下面是两副图左边是项目结构图,右边是UML图:
正常情况下我们会按照上图一行一行的写代码:
其中UserService的代码是这样的
public class UserService { //实现dao层的一个实例对象 private UserDAO userDAO=new UserDAOImpl(); //userDAO的setter和getter public UserDAO getUserDao() { return userDAO; } public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } //添加用户 public void add(User user) { if (userDAO == null) System.out.println("erro"); else { userDAO.add(user); } } }
正像UML图所画的那样,UserService对User,UserDao,UserDAOImpl都有单向关联!
这里我们思考一下:
这仅仅是一个对象的操作,如果有TeacherService,狗Service,猫Service.........那么我们是不是每个都得实现service手动的调动Dao,DAOImpl,User等等,何其麻烦,
我们再注意看看:
1.Dao层是专门 与数据库交互的,里面有很多方法,而service是用来调用dao层的方法,他们之间唯一的联系就是在方法调用时,
Service层通过 private UserDAO userDAO=new UserDAOImpl();来实现与DAO层的耦合,我们知道程序设计讲究”高内聚低耦合“,
2.我们再看private UserDAO userDAO=new UserDAOImpl(); 这句是得到UserDao这个接口对象实例,并且这个实例是由new UserDAOImpl来决定,
我们知道,一个接口可以有很多实现类,这样写就是证明这个类中UserDAO的实例只能是UserDaoImpl的实例,不能使其他实现接口类的实例,那么这个类就具备重用的灵活性
既然这样,有没有一种方式能够降低这种耦合,提高灵活性呢,答案是spring
Spring思路
我们的service和UserDAOImpl没有任何的联系,而是通过一个配置文件来关联各个类之间的关系
1.我们看看beans.xml
<beans> <bean id="u" class="com.spring.dao.impl.UserDAOImpl" /> <bean id="userService" class="com.spring.service.UserService" > <property name="userDAO" bean="u"/> </bean> </beans>
配置文件中又两个bean,所谓的bean就是java中的类,每个bean都有 id和class两个属性,id就是代表这个bean唯一标识,class就是这个类的(包+类)名
我们看到第二个bean是有<property>子节点的,<property>表明这个bean中时存在以其他bean作为参数的,
并且这个参数bean在这个类中实例对象名字为(userDAO),在所有beans中这个参数bean的id=u
2.加载beans.xml
我们先定义一个容器,通常是一个Map类型的容器,按照<bean>顺序依次初始化每个bean对应的类的对象,并把这个对象,和对象的id存放到Map容器中
我们先来实现这个容器:
package com.spring.buildframework; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; public class ClassPathXMLApplicationContext { private static Map<String, Object> beans = new HashMap<String, Object>(); public ClassPathXMLApplicationContext() { SAXBuilder sb = new SAXBuilder(); try { Document doc = sb.build(this.getClass().getClassLoader() .getResourceAsStream("beans.xml")); //获取根节点 Element root=doc.getRootElement(); //将根节点的下的孩子全部放到List中 List allList= root.getChildren("bean"); for(int i=0;i<allList.size();i++) { Element sigElement=(Element)allList.get(i); //获取每个<bean>节点的id和class属性的值 String id=sigElement.getAttribute("id").getValue(); String clazz=sigElement.getAttribute("class").getValue(); System.out.println(id+clazz); //根据获取的类名利用反射得到实例 Class<?> demo=Class.forName(clazz); Object obj=demo.newInstance(); //将id和类的实例放到beans中 beans.put(id, obj); //判断该bean下是否存在property的子节点 List propertyChild=sigElement.getChildren("property"); for(int j=0;j<propertyChild.size();j++) { //获取此bean下name和bean的属性 ,其中bean指的是配置文件中另一个bean Element propertyElement=(Element)propertyChild.get(j); String name=propertyElement.getAttributeValue("name"); //userDAO String bean=propertyElement.getAttributeValue("bean"); //u //拼接该bean(useDAO)的setter方法 setUserDAO String setterMethodName="set"+name.substring(0,1).toUpperCase()+name.substring(1); //调用setter方法是需要注入com.spring.dao.impl.UserDAOImpl类的 对象 Object fieldObj=beans.get(bean); //获取fieldObj对象的类的类型, Class<?> type=fieldObj.getClass().getInterfaces()[0]; Method setterMethod=obj.getClass().getMethod(setterMethodName, type); setterMethod.invoke(obj,fieldObj); System.out.println(type); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //提供给外界接口 public static Object getBean(String id) { return beans.get(id); } }
我们只需要初始化这个类,我们就能获得上图中的容器,而容器中就包含着我们需要的所有配置了的类的对象!
这时我们的UserService:
package com.spring.service; import com.spring.dao.UserDAO; import com.spring.dao.impl.UserDAOImpl; import com.spring.model.User; public class UserService { private UserDAO userDAO; public UserDAO getUserDao() { return userDAO; } public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } public void add(User user) { if (userDAO == null) System.out.println("erro"); else { userDAO.add(user); } } }
请注意和之前的不同,我们不再实例化userDAO而是通过 ClassPathXMLApplicationContext 类调用其set方法来实例化。
其他类:
public class UserDAOImpl implements UserDAO { @Override public void add(User user) { // TODO Auto-generated method stub System.out.println("user added successful"); } } package com.spring.dao; import com.spring.model.User; public interface UserDAO { public void add(User user); } package com.spring.model; public class User { private String username; private String age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
测试类:
public class ClassPathXMLApplicationContextTest { @Test public void test() { try { // Class<?> demo=Class.forName("com.spring.dao.impl.UserDAOImpl"); } catch (Exception e) { // TODO: handle exception } //得到转载容器类的实例 ClassPathXMLApplicationContext test=new ClassPathXMLApplicationContext();
//从容器中获取userService对象 UserService service=(UserService)ClassPathXMLApplicationContext.getBean("userService"); User user=new User();
//执行方法 service.add(user);
fail("Not yet implemented"); } }
需要说明的是:我们得从容器中拿到UserService对象才能进行调用
就这样:通过xml文件配置的简单spring框架就实现了
顺便讲讲IOC和DI的概念:
IOC(Inverse Of Control):1>就在UseService中我们UserDaO的初始化不是掌握在我们自己手中,而是掌握在容器中
2>原来我们的程序写实现,现在依赖于抽象,也就是从实现具体的东西反转到接口(抽象上),给人的感觉是在控制接口而不是控制事项 3>控制的实现在与接口而不是具体实现
DI(Dependency Inject) :向bean中注入参数