经过一天的补习,学习文件加载,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中注入参数

自定义beans.xml文件实现Spring框架的更多相关文章

  1. spring框架中beans.xml文件报错XmlBeanDefinitionStoreException

    第一次构建spring,实现简单的注入方式,就发生了beans.xml文件报错,报错信息如下图 org.springframework.beans.factory.xml.XmlBeanDefinit ...

  2. Spring配置文件的加载,及装载多个beans.xml文件

    applicationContext.xml 是spring的全局配置文件,用来控制srping的特性 1  手动加载自定义的beans.xml文件 @Test public void testAut ...

  3. Struts2学习:struts.xml引入自定义的xml文件

    随着项目代码的增多,用一个struts.xml来管理所有功能模块的Action未免显得臃肿且结构不清晰,因此可以根据实际的功能划分,将各模块的Action放在自定义的xml文件中,再引入struts. ...

  4. IoC COntainer Create Javabeans 可以通过读取beans.xml 文件来创建一个应用程序上下文对象 依赖反转

    Spring初学快速入门 - Spring教程™ https://www.yiibai.com/spring/spring-tutorial-for-beginners.html# pom <? ...

  5. mybatis 热部署xml文件(spring boot和springmvc两种方式)

    参考:http://thinkgem.iteye.com/blog/2304557 步骤:1.创建两个java类 (1)MapperRefresh.java   :用于刷新mapper (2)SqlS ...

  6. IDEA创建XML文件没有Spring Config选项

    我在resources目录下导入3个配置文件时,applicationContext-common.xml文件中有4处http地址红色报错,下图为修正后的图片 了解到可能是由于父工程的pom文件中没有 ...

  7. Spring框架的配置文件

    Spring框架的配置文件 (2014-12-18 20:43:42) 转载▼ 标签: 配置文件 例子 构造函数 成员 spring 分类: 专业知识 (注:文中的"<"均需 ...

  8. Spring框架配置beans.xml

    Spring学习笔记(一) 一.Spring 框架 Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 ...

  9. 死磕Spring之IoC篇 - BeanDefinition 的加载阶段(XML 文件)

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...

随机推荐

  1. hadoop之 hadoop 机架感知

    1.背景 Hadoop在设计时考虑到数据的安全与高效,数据文件默认在HDFS上存放三份,存储策略为本地一份,同机架内其它某一节点上一份,不同机架的某一节点上一份.这样如果本地数据损坏,节点可以从同一机 ...

  2. linux shell获取用户输入

    一.获取用户输入1.基本读取read命令接收标准输入的输入,或其它文件描述符的输入.得到输入后,read命令将数据输入放入一个标准变量中.[root@rac2 ~]# cat t8.sh #!/bin ...

  3. 什么是HBase(二) 关于HFile分割

    关于HFile的分割,是首先要从HFile的合并说起,上回书讲到memstore会不定期刷HFile,然后这些HFile将会被不定过期的被监控程序进行小合并+大合并(所有的文件,不分column fa ...

  4. CCFlow SDK模式开发(有比较详细的代码,以服务的形式与ccflow数据库进行数据交互)

    http://www.cnblogs.com/s0611163/p/3963142.html 需求: 1.业务数据要保存在我们自己的数据库里     2.CCFlow有保存草稿的功能,但是领导要求每个 ...

  5. 常用的acl规则

    一.常用的acl规则        haproxy的ACL用于实现基于请求报文的首部.响应报文的内容或其它的环境状态信息来做出转发决策,这大大增强了其配置弹性.其配置法则通常分为两步,首先去定义ACL ...

  6. .NET 4.5 HttpClient 中使用Cookie

    为了使用.NET 4.5的HttpClient从WIN2K3换成了WIN7.装VS2010,结果告诉我VS2010不支持.NET 4.5.又装VS2012,接着装.NET FRAMEWORK 4.5. ...

  7. intellij系列ide配置

    显示行号 搜索line number 在Editor,General,Appearance里面,勾选show line numbers 修改自体 sudo apt-get install fonts- ...

  8. H5页面获取openid,完成支付公众号(未关注公众号)支付

    一.页面授权 // 进入页面获取权限code function initAuthorizeCode() { var appid = $("#appid").val();//公众号a ...

  9. mysql master or master copy

    双主复制: 在两台server配置my.cnf [root@localhost mysql]# egrep -v "^$|^#" /etc/my.cnf datadir = /my ...

  10. pythonNetday06

    进程 Process(target,name,args,kwargs) p.pid : 创建的新的进程的PID号 p.is_alive() 判断进程是否处于alive状态 p.daemon = Tru ...