问题描述:
最近在学习Java Spring框架,关于程序解耦方法中遇到的问题。
- 在表现层通过使用BeanFactory类获取一个业务层serviceImpl对象。
- 在BeanFactory中使用静态代码块去预先读取properties配置文件,将所有键值对的对应对象创建出来放入Map容器。
- 最后在BeanFactory中创建一个get方法,通过传入的name参数,获取BeanFactory类中Map容器里对应name的对象。
- 但是由于在业务层serviceImpl中同样调用了BeanFactory类去创建持久层daoImpl,导致在BeanFactory类的静态代码块中根据properties文件读取的键值对信息去反射创建对象时,先读取并创建的是业务层serviceImpl,而业务层serviceImpl的创建必然会导致其中的成员变量持久层daoImpl的创建,但是此时BeanFactory类的Map容器还未填入任何对象,因此当业务层serviceImpl调用BeanFactory类的get方法获取Map容器内的对象时,得到的持久层daoImpl为null。
通过new关键字创建对象会导致程序耦合度过高,运行时可能在编译期就出现错误;而通过properties配置文件的方法可以保障编译期通过,程序出错发生在缺类的错误上。但是在实验使用beanfactory进行多个bean的创建和储存时,完成单例创建并用Map进行储存,以实现以后调用service实现类仍然是同一个。在这里遇到了nullpointer的exception,debug后发现map中dao的信息为Null。
public class BeanFactory {
private static Properties props;
//存放我们要创建的对象,也就是容器
private static Map<String,Object> beans;
static{
try {
props = new MyProper();//props = new Properties();
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
beans = new HashMap<String,Object>();
//取出所有的key
Enumeration keys = props.keys();
//遍历枚举
while(keys.hasMoreElements()){
String key = keys.nextElement().toString();
String beanPath = props.getProperty(key);
Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
System.out.println(value);
beans.put(key,value);
}
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
// public static Object getBean(String beanName){
// Object bean = null;
// try {
// String beanPath = props.getProperty(beanName);
// System.out.println(beanPath);
// bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();
// } catch (Exception e) {
// e.printStackTrace();
// }
// return bean;
// }
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
bean.properties
accountDao=com.itheima.dao.impl.AccountDaoImpl
accountService=com.itheima.service.impl.AccountServiceImpl
public class AccountServiceImpl implements IAccountService {
//模拟业务层调用持久层
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
public void saveAccount() {
accountDao.saveAccount();
}
}
//模拟表现层用于调用业务层
public class Client {
public static void main(String[] args) {
// IAccountService as = new AccountServiceImpl();
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
as.saveAccount();
}
}
原因分析:
由 尝试将配置文件中的service和dao换一个位置(将service放在dao之前)
但是失效,仍然空指针。
解决方案:
由
在bean.properties文件中,键值对信息明明就是daoImpl在先,serviceImpl在后,但是读取到的顺序却是反的。
即在创建service的实现类的时候,beanfactory还没来得及创建dao的bean,导致service实现类对象中的dao属性为空。
解决方法就是使beanfactory先创建dao的bean 再创建service的bean。
使用Myproper.class
import java.util.*;
public class MyProper extends Properties {
//LinkedHashSet有序,可以保证读取出来顺序不变
private final LinkedHashSet<Object> keys = new LinkedHashSet<Object>();
/**
* 读取key集合
*/
@Override
public Set<String> stringPropertyNames() {
Set<String> set = new LinkedHashSet<String>();
for (Object key : keys) {
set.add((String) key);
}
return set;
}
@Override
public Set<Object> keySet() {
return keys;
}
/**
* 枚举可以直接进行遍历,但是和iterator一样,遍历过程中不能进行修改删除等操作<br/>
* 若要在遍历过程中进行修改擦除等操作,建议使用stringPropertyNames方法
*/
@Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(keys);
}
@Override
public synchronized Object put(Object key, Object value) {
keys.add(key);
return super.put(key, value);
}
/**
* 若要移除元素,要重写remove方法
*/
@Override
public Object remove(Object o) {
keys.remove(o);
return super.remove(o);
}
}