简单实现spring框架的ioc

简单实现spring框架的ioc

首先复习一下依赖倒转原则

 依赖倒转原则(ioc)

 高层模块不应该依赖于底层模块,二者都应该依赖其抽象

  抽象不应该依赖于细节,细节应该依赖于抽象

  类A直接依赖于类B,假如未来有可能改成类A依赖于类C,这种场景下

  类A就是高层模块,负责复杂的业务逻辑;

  类B和类C是低层模块,负责基本的原子操作

 * 面向对象 -> 面向接口

在项目中的体现:(service层调用dao层方法,那么就需要在service层创建dao层对象,这个dao层对象是成员属性存在于service层)

为了解决三层架构之间的依赖问题,降低耦合度,推出了这样一个工具类

本类采用IoC/di设计原则通过调用xml映射文件和反射的方式实例化对象

ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态

还没有学到spring框架的ioc容器部分,采用java se 所学知识模拟。

ioc的概念

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。创建对象的控制权交给第三方容器,需要使用时向第三方容器申请对象。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

我们可以把IoC模式看做是工厂模式的升华,把IoC看做是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

工厂模式

 在工厂模式中, 我们在创建对象时不会对用户暴露创建逻辑

 并且是通过一个共同的接口来指向新创建的对象

 优点:

  1. 调用者项创建一个对象, 只需要知道名字就可以了

  2. 拓展性高, 如果想增加一种产品, 只要拓展一个工厂类就可以

  3. 屏蔽了产品的具体实现, 用户只关心产品的接口

 缺点:

每次增加一种产品, 必然要增加一种工厂. 会导致系统中的类越来越多,

  提高系统的复杂性, 有可能不是一件好事

 DI——Dependency Injection   依赖注入

采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。 

例:

Userservicrimpl类中很多方法需要调用userdaoimpl方法,

所以设置了全局变量private userdao ud以及对应的set方法

 

配置文件xml格式

若采用ioc依赖倒转原则,配置的xml文件如何实现

<?xml version ="1.0" encoding="UTF-8"?>
<beans>
	<bean name="UserService" class="com.bwf.service.impl.UserServiceImpl">
		<property name="ud" value="UserDao" />
		<property name="ad" value="AdminDao" />
		<property name="ld" value="Lottery_infoDao" />
		<property name="uld" value="User_lotteryDao" />

	</bean>
	<bean name="AdminService" class="com.bwf.service.impl.AdminServiceImpl">
		
		<property name="ld" value="Lottery_infoDao" />
		<property name="ud" value="UserDao" />
		<property name="ad" value="AdminDao" />
		<property name="uld" value="User_lotteryDao" />
	</bean>
	<bean name="UserDao" class="com.bwf.dao.impl.UserDaoImpl" />
	<bean name="AdminDao" class="com.bwf.dao.impl.AdminDaoImpl" />
	<bean name="Lottery_infoDao" class="com.bwf.dao.impl.Lottery_infoDaoImpl" />
	<bean name="User_lotteryDao" class="com.bwf.dao.impl.User_lotteryDaoImpl" />

</beans>


<!-- 1. 再配置一个user对象在xml文件中 在java代码中分别获取2个不同的user对象, 并打印输出 2. 在xml文件中配置一条记录对象, 
	并赋值属性 在java代码中获取这个记录对象, 并打印输出(可能会出问题, 试着解决一下) -->

Userdao,AdminDao,Lottery_infoDao,User_lotteryDao分别作为bean节点和property节点,因为这4个接口是作为userserviceimpl类的成员属性存在,userserviceimpl依赖于这4个接口

而Userdao,AdminDao,Lottery_infoDao,User_lotteryDao并不是基本数据类型,是引用数据类型,和userservice一样需要进行实例化

service层中的dao层成员属性关联:

	private Lottery_infoDao ld;
	private UserDao ud;
	private AdminDao ad;
	private User_lotteryDao uld;
	private List<String> list = new ArrayList<>();

	public void setLd(Lottery_infoDao ld) {
		this.ld = ld;
	}

	public void setUd(UserDao ud) {
		this.ud = ud;
	}

	public void setUld(User_lotteryDao uld) {
		this.uld = uld;
	}
	
	public void setAd(AdminDao ad) {
		this.ad = ad;
	}

 

在项目中用到applicationcontext的地方为服务器的读线程(实例化service层对象后根据传入的请求类型调用不同方法):

//成员属性:

private UserService us;
private AdminService as;

//run方法中的成员属性实例化:

us = (UserService) ApplicationContext.getBean("UserService");
as = (AdminService) ApplicationContext.getBean("AdminService");

 

Applicationcontext类的结构

加载外部jar包:dom4j-1.6.1(用于读取xml文件的类)

成员属性:

hashmap 存放类名和该类名实例化的对象,对应xml文件的bean节点的属性name和class

静态方法:

init 方法:再调用2个静态方法遍历

第一次遍历:

加载xml文件,实现控制反转

遍历bean节点,把name对应的属性值(类名)采用反射的方式实例化对象存放到map中

第二遍遍历:

加载xml文件,注入依赖

遍历bean节点,若里面有依赖关系(property节点),那么采用反射的方式注入依赖(给成员属性赋值)

getbean方法

当某个类需要外部对象时调用静态getbean方法传入类名,从map中获取对象即可

具体代码:

public class ApplicationContext {

	private static Map<String, Object> beanmap = new HashMap<>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 初始化,加载xml文件
		init();
		Record r1 = (Record) getBean("record");
	}
	public static void init() {
		// 初始化,用jar包加载xml文件
		SAXReader reader = new SAXReader();
		Document dc;
		try {
			dc = reader.read(ApplicationContext.class.getClassLoader()
					.getResourceAsStream("applicationcontext.xml"));
			// 获得根节点
			Element re = dc.getRootElement();
			// 第一遍遍历xml将bean的类名实例化存入map
			paresxml(re);
			// System.out.println(beanmap);
			// 第二遍遍历map,把xml的属性值赋值给bean的成员属性,
			// 若某个类依赖于另一个类,则通过map再次实例化被依赖的类,依赖注入
			di(re);
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	private static void di(Element re) {
		// TODO Auto-generated method stub
		// 第二遍遍历xml文件,注入依赖
		// bean节点
		List<Element> list = re.elements();
		try {
			// 遍历bean节点
			for (Element element : list) {
				// String name=element.getName();
				String name = element.attributeValue("name");
				// 通过bean的name属性值,根据第一遍加载xml文件获得的map找到对应的对象
				Object object = beanmap.get(name);
				// System.out.println(beanmap);
				// 拿到并遍历property节点
				List<Element> propertys = element.elements();
				for (Element element2 : propertys) {
					// 拿到对象的成员属性名name(被依赖的接口对象名)
					String p_name = element2.attributeValue("name");
					// System.out.println(p_name);
					// 拿到对象的成员属性名name(被依赖的接口类名)
					String p_value = element2.attributeValue("value");
					// System.out.println(p_value);
					Field df = object.getClass().getDeclaredField(p_name);
					// 通过反射,调用set方法获得被依赖的类的对象
					// us实例化的对象,us的成员属性,us的成员属性名name(被依赖的接口类名)
					getElement(object, df, p_value);
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private static void paresxml(Element re) {
		// 第一遍遍历,把所有的beans实例化并放入map
		// TODO Auto-generated method stub
		try {
			List<Element> list = re.elements();
			for (Element element : list) {
				// String name=element.getName();
				String name = element.attributeValue("name");
				String classname = element.attributeValue("class");
				// System.out.println(classname);
				Class<?> forName = Class.forName(classname);
				Object object = forName.newInstance();
				// 存放 接口名和实例化的对象,若有接口要实例化对象直接调用getElement方法,传入类名即可返回对象
				beanmap.put(name, object);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
	}
	private static void getElement(Object object, Field df, String p_value)
	throws NoSuchFieldException, SecurityException, NoSuchMethodException,
			IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, ParseException {
		Class<?> class1 = object.getClass();
		// 组装set方法名
		String setter = "set" + df.getName().substring(0, 1).toUpperCase()
				+ df.getName().substring(1);
		// System.out.println(setter);
		// 反射获得set方法,参数为set方法名和成员属性类型
		Method dm = class1.getDeclaredMethod(setter, df.getType());
		Object value = null;
		// 因为xml读取的数据类型只能是string,
		// 但是成员属性不一定为string类型,所以反射调用set方法传非string参数会报错
		// 所以根据df成员属性的属性类型进行强制转换类型
		switch (df.getType().getSimpleName()) {
		case "Double":
			value = Double.parseDouble(p_value);
			break;
		case "Integer":
			value = Integer.parseInt(p_value);
			break;
		// 成员属性date为string类型
		// case"Date":
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		// value= sdf.parse(p_value);
		// break;
		case "String":
			value = p_value;
			break;
		default:
			// 若非基本数据类型和string,那么一定是被依赖的某个类
			// 这时根据成员属性的接口名从map取出相应的实例调用反射赋值给成员属性
			value = beanmap.get(p_value);
			// System.err.println(p_value);
			break;
		}
		// System.out.println(dm+" "+value);
		// 调用set方法赋值
		dm.invoke(object, value);
	}
	public static Object getBean(String name) {
		return beanmap.get(name);
	}
}

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值