模拟Spring(一)

学过Java的人对SSH都不会陌生,其中Spring备受青昧除了它的"轻"之外,还因为他的IOC与AOP两大功能的强大。IOC是Spring的核心概念,全称“Inversion Of Control”,翻译成中文是“控制反转”,很多人都把它叫做“依赖注入(Dependency Injection)”。而它的抽象概念是“[color=red]依赖关系的转移[/color]”。转移是相对于过去不良的应用程序设计来说的,象“[color=red]高层模块不应该依赖于低层模块,而模块必须都依赖于抽象[/color]”是IOC的一种表现,“[color=red]实现必须依赖于抽象,而不是抽象依赖于实现[/color]”是IOC的另一种表现。
举一个例子,控制层调用业务逻辑组件,应该只知道业务逻辑组件的接口,而不知道其具体实现类,同样业务逻辑组件调用DAO组件时也应该只知道DAO组件的接口,这称为“依赖于抽象”。把他们原来的高层依赖于低层的关系转移到xml配置文件与一个大的工厂中(Spring本身就是一个工厂),由它们进行实例化与属性的注入,当要修改他们的依赖关系时,无需修改客户端,只需要修改xml配置文件即可。符合OCP原则的“开放-封闭”。

下面来模拟Spring如何把几层模块之间的依赖关系转移到xml文件中去。
[b][color=green]首先设计一个VO--Person.java[/color][/b]

import java.io.Serializable;
import java.sql.Date;

public class Person implements Serializable {

private String name; //名字

private int age; //年龄

private Date birthday; //出生年月

private double salary; //月薪

//对应的get,set方法
public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}
}


[color=green][b]DAO组件接口与DAO的实现类,只是简单模拟一下,并没有真的与数据库进行交互[/b][/color]

public interface PersonDao {

public void save(Person p);

public void delete(Person p);
}


public class PersonDaoImpl implements PersonDao {

public void delete(Person p) {
System.out.println("现在进行删除的操作...姓名:" + p.getName()
+ ",年龄:" + p.getAge()+",出生年月:"+p.getBirthday()+",月薪:"+p.getSalary());
}

public void save(Person p) {
System.out.println("现在进行保存的操作...姓名:" + p.getName() + ",年龄:"
+ p.getAge()+",出生年月:"+p.getBirthday()+",月薪:"+p.getSalary());
}
}


[b][color=green]业务逻辑组件接口与实现类,这里只是简单地调用DAO组件的方法[/color][/b]

public interface PersonService {

public void save(Person p);

public void delete(Person p);
}


public class PersonServiceImpl implements PersonService{

private PersonDao personDao; //依赖于抽象

public void delete(Person p) {
personDao.delete(p);
}

public void save(Person p) {
personDao.save(p);
}

public PersonDao getPersonDao() {
return personDao;
}

public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
}

[b]
[color=green]简单模拟Spring工厂的代码[/color][/b]

import java.io.File;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class SpringFactory {

//存储Bean的实例
private Map<String, Object> appMap = new HashMap<String, Object>();

//储存xml配置文件中Bean实例的信息
private Map<String, String> beans = new HashMap<String, String>();

//存储xml配置文件中Bean的属性的信息
private Map<String, String> properties = new HashMap<String, String>();


//工厂采用单例模式
private static SpringFactory df;

//私有构造函数
private SpringFactory() throws Exception {
//采用Dom4j对xml文件进行解析
Document doc = new SAXReader().read(new File("beanContext.xml"));
Element root = doc.getRootElement();
List el = root.elements();
for (Iterator it = el.iterator(); it.hasNext();) {
Element em = (Element) it.next();
String id = em.attributeValue("id");
String impl = em.attributeValue("class");
beans.put(id, impl);
// 开始第2次遍历
List e2 = em.elements();
// 储存属性的内容
StringBuilder s = new StringBuilder();
boolean flag = false;
for (Iterator i = e2.iterator(); i.hasNext();) {
Element em2 = (Element) i.next();
String name = em2.attributeValue("name");
String ref = em2.attributeValue("ref");
String value = em2.attributeValue("value");
if (ref != null) {
s.append("ref,").append(name + ",").append(ref + ";");
} else if (value != null) {
s.append("value,").append(name + ",").append(value + ";");
}
flag = true;
}
if (flag == true) {
properties.put(id, s.toString());
}
}
//实例化Bean并注入Bean的属性
initBeans();
}

private final void initBeans() throws Exception {
// 初始化Bean
for (String id : beans.keySet()) {
try {
Object o = Class.forName(beans.get(id)).newInstance();
appMap.put(id, o);
} catch (Exception e) {
e.printStackTrace();
}
}

//这里按照配置文件注入Bean中的属性
for (String id : properties.keySet()) {
String[] property = properties.get(id).split(";");
for (int i = 0; i < property.length; i++) {
String[] part = property[i].split(",");
String type = part[0];
String name = part[1];
String value = part[2];
// 从已经实例化好的appMap里面取出要设置属性的Object
Object bean = appMap.get(id);
// 获取对应的set方法名称
String methodName = "set" + name.substring(0, 1).toUpperCase()
+ name.substring(1, name.length());

Method[] methods = bean.getClass().getMethods();
for (Method m : methods) {
if (m.getName().equals(methodName)) {
Class[] typeParam = m.getParameterTypes();
// 获取非对象类型的属性转换为对应类型后的值
Object param = null;
if (type.equals("ref")) {
param = appMap.get(value);
} else {
param = getParameter(typeParam[0], value);
}
//捕捉一下参数类型不正确的异常
try {
m.invoke(bean, param);
} catch (IllegalArgumentException e) {
System.out.println("参数<"+name+">类型不正确,依赖注入失败!");
}
}
}
}
}
}


// 输入参数的源对象与值(String类型),转换为正确类型的值(只列出常用的几种)
public Object getParameter(Class c, String value) {
String typeName = c.getName();
if (typeName.equals("int") || typeName.equals("java.lang.Integer")) {
return Integer.valueOf(value);
} else if (typeName.equals("java.lang.String")) {
return value;
} else if (typeName.equals("java.lang.Boolean")||typeName.equals("boolean")) {
return Boolean.valueOf(value);
} else if(typeName.equals("java.lang.Long")||typeName.equals("long")){
return Long.valueOf(value);
}
else if(typeName.equals("java.lang.Double")||typeName.equals("double")){
return Double.valueOf(value);
}
else if(typeName.equals("java.lang.Float")||typeName.equals("float")){
return Float.valueOf(value);
}
//捕捉SimpleDateFormat转换日期失败的异常
else if(typeName.equals("java.util.Date")){
try{
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
return formatter.parse(value);
}catch(ParseException e){
System.out.println("日期参数转换类型失败!请输入\"yyyy-MM-dd\"的格式");
return null;
}
}
捕捉ava.sql.Date转换日期失败的异常
else if(typeName.equals("java.sql.Date")){
try{
return java.sql.Date.valueOf(value);
}
catch(IllegalArgumentException e){
System.out.println("日期参数转换类型失败!请输入\"yyyy-MM-dd\"的格式");
return null;
}
}
else{
return null;
}
}

// 返回SpringFactory的实例
public static synchronized SpringFactory getInstance() throws Exception {
if (df == null) {
df = new SpringFactory();
}
return df;
}

// 获取Map中实例的方法
public Object getBean(String id) {
return appMap.get(id);
}
}


这里有几点要注意:
第一,其实这里主要就用到了[color=red]Java的反射[/color]+[color=red]xml解析[/color]的技术,xml解析用到Dom4j,因此项目要加入Dom4j的jar包才能执行。
第二,工厂是先解析完xml文件,然后再进行Bean与组件的实例化和注入属性,并非边解析边实例化,所以代码多了很多:)
第三,获取要注入属性的类型时,并没有依赖于该私有属性的类型去查找,而是按照该属性的set方法中的参数类型,这正是Spring的做法。因此在不按照Java规范书写时,Spring也能进行注入,如
private String name;

public void setYourName(String yourName) {
name = yourName;
}

[color=brown]当然在xml配置文件你要设置为yourName而并非name[/color]
第四,有没看到中间一大段很臃肿的代码,这里是进行属性的类型转换时的操作,因为从xml获取属性的值是String类型,要根据各个Bean不同的属性类型而进行正确的转换,这也是难点之一。本来想用策略模式改造,或者加入一个类似类型转换的系统,但又太麻烦,迟下再完善一下。Spring是用BeanUtils进行类型转换,但性能方面BeanUtil并不是最好的选择。对此大家有什么好的建议 :D
第五,Spring有“singleton”与“prototype”两种模式设置(针对Web应用还有request,session等),上面的例子只是仿照“singleton”模式产生单一实例,而“prototype”是每次请求到来的时候都产生单一实例。单例模式能节省更多的内容空间,但如果涉及到[color=red]保存数据[/color]与[color=red]需要维护状态[/color]的时候,就要采用prototype的方式。对于上述例子,把生成实例的代码放到getBean()中去就可以了。



<?xml version="1.0" encoding="GBK"?>
<beanContext>
<bean id="person1" class="spring.simulate.Person">
<property name="name" value="Jam"/>
<property name="age" value="18"/>
<property name="birthday" value="1985-8-8"/>
<property name="salary" value="10000"/>
</bean>

<bean id="person2" class="spring.simulate.Person">
<property name="name" value="Wjm"/>
<property name="age" value="20"/>
<property name="birthday" value="1985-9-9"/>
<property name="salary" value="9000"/>
</bean>

<bean id="personDao" class="spring.simulate.PersonDaoImpl">
</bean>
<bean id="personService" class="spring.simulate.PersonServiceImpl">
<property name="personDao" ref="personDao"/>
</bean>
</beanContext>



[color=green][b]客户端测试代码:[/b][/color]

public class Client {
public static void main(String[] args) throws Exception {
SpringFactory factory = SpringFactory.getInstance();
//实例化Service组件,Dao组件已经注入到Service的组件中
PersonService service = (PersonService) factory.getBean("personService");
//获取一些Person
Person p1 = (Person) factory.getBean("person1");
Person p2 = (Person) factory.getBean("person2");

service.save(p1);
service.delete(p2);
}
}


结果显示:
现在进行保存的操作...姓名:Jam,年龄:18,出生年月:1985-08-08,月薪:10000.0
现在进行删除的操作...姓名:Wjm,年龄:20,出生年月:1985-09-09,月薪:9000.0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值