Spring-解决代码耦合问题的思路-20200504

需要解决的问题

考虑这么一种场景,传统编码方式中,Service层调用Dao层的时候,一般是在service的实现类中定义一个Dao层接口的成员属性,并直接new一个对象。

	public class TransferServiceImpl implements TransferService {
 		private AccountDao accountDao = new JdbcAccountDaoImpl();
 	}

此时Service层的TransferServiceImpl 就与Dao层的一个具体的实现类JdbcAccountDaoImpl产生了耦合,如果Dao层的实现要变化(例如从当前使用jdbc实现换成使用mybatis实现),那Service层的调用就需要修改代码。修改点多的时候特别不方便。
最完美的情况是:在TransferServiceImpl 中声明一个AccountDao的对象即可。

解决思路

  1. 创建对象不再使用直接new的方式,而使用反射
  2. 将实例化对象的工作使用工程模式处理,因为项目中可能很多地方都要用到实例化对象,工程模式比较合适,也能达到解耦的目的。

实现方式

1. 解决对象实例化的问题

为了避免硬编码,首先将需要实例化的类的全限定名称都配置到xml文件中去,由工厂类从xml配置文件中读取,并通过反射实例化所有配置的对象。

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
 	<bean id="transferService" class="com.xxx.service.impl.TransferServiceImpl"></bean>
 	<bean id="accountDao" class="com.xxx.dao.impl.JdbcAccountDaoImpl"></bean>
</beans>

比如配置文件命名为beans.xml,其中每个类建立一个bean标签,标签中给一个id的属性以方便查找,给一个class属性标识出类的全限定名称。
定义一个工程类BeanFactory,它应该具备以下形式:

	public class BeanFactory {
		// 缓存该工程类提供的所有对象
 		private static Map<String,Object> map = new HashMap<>();
 		// 提供获取实例化对象的方法,供外部获取实例化对象用
 		public static Object getBean(String id) {
 			return map.get(id);
 		}
 		// 完成从xml文件读取所有需要实例化的对象,并实例化。此处先不写,往下继续分析
 	}

通过dom4j读取解析xml配置文件,获取到所有的bean标签的内容,通过反射和类的全限定名来实例化对象。
通过beans.xml实例化对象代码实现片段:

	// 通过类加载器获取配置文件为输入流
	InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
	// 通过dom4j解析xml配置文件
	SAXReader saxReader = new SAXReader();
 	Document document = saxReader.read(resourceAsStream);
 	// 获取配置文件根节点
 	Element rootElement = document.getRootElement();
 	// 获取根节点下的所有bean标签内容
 	List<Element> list = rootElement.selectNodes("//bean");
 	// 循环所有的bean标签内容,实例化对象
 	for (int i = 0; i < list.size(); i++) {
 		Element element = list.get(i);
 		String id = element.attributeValue("id");
 		String clazz = element.attributeValue("class");
 		// 通过反射实例化对象
 		Class<?> aClass = Class.forName(clazz);
 		Object o = aClass.newInstance();
 		// 将实例化的对象全部放到map中缓存
 		map.put(id,o);
 	}

至此,对象的创建问题解决。

2.维护对象依赖关系

此时所有的对象我们已经创建完成了,要做的就是将被依赖的对象实例注入到依赖它的对象中去。首先要在有依赖对象的对象中提供出给被依赖对象赋值的set方法:

	public class TransferServiceImpl implements TransferService {
 		private AccountDao accountDao = new JdbcAccountDaoImpl();
		// 提供set方法,供外部注入依赖对象用
 		public void SetAccountDao(AccountDao accountDao) {
 			this.accountDao = accountDao;
 		}
 	}

其次,就是要知道什么对象依赖了什么对象,这样才能准确的将被依赖对象注入到依赖它的对象中去。这个问题,我们可以通过修改配置文件,增加属性来解决。在有依赖其他对象的bean中,新增property元素来配置依赖对象的信息:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
 	<bean id="transferService" class="com.xxx.service.impl.TransferServiceImpl">
 		<!--新增依赖对象的配置,name是属性名称,给外部调用set方法的时候拼接方法名称用,ref是依赖的bean的id-->
		<property name="AccountDao" ref="accountDao"></property>
	</bean>
 	<bean id="accountDao" class="com.xxx.dao.impl.JdbcAccountDaoImpl"></bean>
</beans>

配置文件做了如上改动之后,实例化对象的工厂代码中加上以下操作

	List<Element> propertyNodes = rootElement.selectNodes("//property");
	for (int i = 0; i < propertyNodes.size(); i++) {
 		Element element = propertyNodes.get(i);
 		// 处理property元素
 		String name = element.attributeValue("name");
 		String ref = element.attributeValue("ref");
 		// 获取property标签的父标签,也就是有依赖的bean
 		String parentId = element.getParent().attributeValue("id");
 		// 从map中拿到有依赖的bean对象
 		Object parentObject = map.get(parentId);
 		// 获取到所有的方法
 		Method[] methods = parentObject.getClass().getMethods();
 		// 遍历所有方法,如果有和配置中配置的方法名称一致的,就调用set方法把被依赖的ref的一个实例注入进去
 		for (int j = 0; j < methods.length; j++) {
 			Method method = methods[j];
 			if(("set" + name).equalsIgnoreCase(method.getName())) {
 				Object propertyObject = map.get(ref);
 				method.invoke(parentObject,propertyObject);
 			}
 		}
 		map.put(parentId,parentObject);

至此,对象中依赖的其他对象都被注入了一个实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值