Spring的IOC(控制反转)和DI(依赖注入)

2 篇文章 0 订阅

1、Spring简介

Spring框架的主要作用是降低应用开发的复杂性,详细介绍参见百度百科Spring介绍,这里记录一下其中的一段话,

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

2、IOC:控制反转

什么是控制反转?传统Javaee编程中,当我们要获取一个对象时,通常通过new关键字来创建类的对象,这是一种主动的获取对象的方法,而在Spring框架中,Spring通过IOC容器来加载对象,将需要加载的所有对象放在容器中,当应用程序需要获取这一对象时,直接从IOC容器中获取。

即原本当我们获取一个对象时,是以主动创建的方式来获取的,现在为我们获取对象时需要依赖于IOC容器对类的加载,需要容器来获取或加载类,从主动转换为被动,这一思想即称为IOC控制反转思想。这其中IOC对类的加载是通过工厂模式+反射+配置文件来完成的。

那么,既然要通过容器来加载类,就需要通过配置文件来实现,下面具体描述IOC入门案例的搭建实现过程:

a、准备Spring框架的开发包,解压,目录分三层,分别为:doc:API和开发规范,libs:jar包和源码,schema:约束

从上面对Spring的描述可以看出,Spring底层的核心容器包括beans、core、context、Expression Language,了解IOC需要首先导入这四部分对应的四个jar包,此外,还需要导入日志包,Spring中默认使用的是common-logging.jar,此外当我们导入了log4j的jar包的时候,common-logging会主动使用log4jar包,也就是说common必须导入,log4j可选择性导入。

b、创建需要被Spring加载的类,在该类的根目录下创建任意名称的xml配置文件,建议的命名格式是:applicationContext.xml,这个xml需要使用的约束头可以在\spring-framework-4.2.4.RELEASE-dist\spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html目录下的最后一个html文件,xsd-configuration.html中找到,对应bean部分,该html还包括了很多其他需要使用的约束头信息。

c、配置xml文件,<bean id="user" class="cn.itcast.domain.User"></bean>

这样就能够实现Spring的控制反转,当我们需要使用User类的对象时,通过

ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");这种方式来加载配置文件,再通过context.getBean("user")来获取,即可获取到User对象。此外,也可以通过new FileSystemXmlApplicationContext(String configLocation);的方式,从系统路径中加载配置文件。

IOC的bean标签和管理对象细节

bean标签的各个属性值描述如下:

	<!-- 
	
		1: 基本配置
			bean标签: 指定要创建的实体类
				id属性: 可以为任意值  但是在整个配置文件中唯一
				class属性: 要实例化类的全限定名 反射 
	 -->
<!-- 	 <bean id="user" class="cn.itcast.domain.User"></bean>  -->

	<!-- 
		2: scope属性: 范围
			    singleton: 单实例 默认值
			    	 如果是单实例,配置文件文件只要一加载  就会创建对象 放在spring容器 (map<id,对象>)
			    	当所有人过来问spring容器要的时候(getBean),所用人用的都是同一个对象
			    prototype: 多实例
				         如果是多实例,配置文件加载,不创建对象
				         当每个人过来getbean的时候,getbean一次创建一次 放在容器中
		<bean id="user" class="cn.itcast.domain.User" scope="singleton" ></bean>
		<bean id="user" class="cn.itcast.domain.User" scope="prototype" ></bean>
		什么时候用默认值singleton(单实例)? 什么时候用prototype(多实例)?
		action: prototype
		service/dao: singleton
		
		3 了解
		singleton的对象什么时候销毁? prototype创建出来的对象什么时候销毁?
			singleton的对象当spring容器关闭 对象销毁
			prototype的对象 长时间不用自动被垃圾回收机制给回收了
			init-method:指定初始化方法
			destory-method:指定销毁方法	
	 -->
	<!-- <bean id="user" class="cn.itcast.domain.User" scope="prototype" init-method="init" destroy-method="destory"></bean> -->
   
   	<!-- 
   		 3  import:导入外部的配置文件
   		 	resource:外部配置文件的地址
   			web: 所有在web层创建的对象   applicationContext_web.xml
   			service:所有在service层创建的对象   applicationContext_service.xml
   			dao:所有在dao层创建的对象   applicationContext_dao.xml

   			<import resource="applicationContext_web.xml"/>
   			<import resource="applicationContext_service.xml"/>
   			<import resource="applicationContext_dao.xml"/>	
   			
   			
   			<import resource="applicationContext_user.xml"/> 	
   	 -->

此外,Bean有三种创建方式,分别为无参数构造方式、静态工厂方式、实例工厂方式

无参数构造方式:

         <!-- 无参构造方式 
             <bean id="user" class="cn.itcast.domain.User"></bean>
         -->    
public class User 
{
	// 构造器
	public User()
	{
		System.out.println("我被创建了...");
	}
	
//	public void run()
//	{
//		System.out.println("run");
//	}
//	
//	// 指定一个方法为初始化方法
//	public void init()
//	{
//		System.out.println("我要初始化了...");
//	}
//	// 指定一个方法为销毁方法
//	public void destory()
//	{
//		System.out.println("我挂了..66666666");
//	}
}

静态工厂方式和实例工厂方式:

   	  <!-- 静态工厂方式(了解)
   	  		  条件:需要有一个工厂类 在这个工厂类中还需要有一个静态方法
   	  		 <bean id="user" class="cn.itcast.utils.BeanFactory" factory-method="createUser"/> 
   	   -->
   	  <!-- 实例工厂(了解) 
   	  		  条件:需要有一个工厂类 在这个工厂类中还需要有一个普通方法
   	  		  <bean id="beanFactory" class="cn.itcast.utils.BeanFactory"></bean>
   	   <bean id="user" factory-bean="beanFactory" factory-method="createUser"></bean>
   	   -->
public class BeanFactory 
{
	// 静态方法---静态工厂
	/*public static User createUser()
	{
		return new User();
	}*/
	
	// 普通方法---实例工厂
	public User createUser()
	{
		return new User();
	}
}

3、依赖注入DI(Dependency Injection)

什么是依赖注入?就是当IOC容器加载类的时候,这个类中的属性也会被一并加载,这一过程就被称为依赖注入。我们可以通过配置文件的方式,依赖注入的基础是控制反转机制。

依赖注入的几种方式(或者说给类的成员变量赋值的几种方式):

第一种:构造器属性注入(不重要)

   	<!-- 
   			DI的注入方式:
   				   1 构造器的方式
   				   	条件:需要有参构造方法
   	
   	 -->
   	<!--  <bean id="car" class="cn.itcast.serviceImpl.CarImpl">
   	 		构造器的方式 
   	 			 name:要赋值的属性名
   	 			 value:要赋的值 (针对的是基本类型和String类型)
   	 			 ref: 针对的是对象类型
   	 		
   	 		<constructor-arg name="name" value="BMW"></constructor-arg>
   	 		<constructor-arg name="price" value="1000000"></constructor-arg>
   	 </bean> -->

 

public class CarImpl implements Car
{
	
	private String name;
	private double price;
	public void setName(String name) {
		this.name = name;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	
	// 有参构造 --方便di的构造器注入方式
	public CarImpl(String name,double price)
	{
		this.name=name;
		this.price=price;
	}
	
	@Override
	public void run() {
		System.out.println("价格:"+price+"钱的"+name+"车跑起来了...");
		
	}
	@Override
	public String toString() {
		return "CarImpl [name=" + name + ", price=" + price + "]";
	}

}

第二种:set属性的方式

   	 <!-- 
   			DI的注入方式:
   				   2 set属性的方式
   				   	     条件:属性必须要有set方法
   				 name:要赋值的属性名
   	 			 value:要赋的值 (针对的是基本类型和String类型)
   	 			 ref: 针对的是对象类型   
   	 			 	       指向的是spring中bean的id名  
   				   	     
   	
   	 -->
   	 <!-- <bean id="person" class="cn.itcast.serviceImpl.PersonImpl">
   	 		set属性的方式
   	 		<property name="name" value="jack"></property>
   	 		<property name="car" ref="car"></property>
   	 </bean> -->
public class PersonImpl implements Person
{
	private String name;
	private Car car;
	public void setName(String name) {
		this.name = name;
	}
	public void setCar(Car car) {
		this.car = car;
	}
	@Override
	public String toString() {
		return "PersonImpl [name=" + name + ", car=" + car + "]";
	}

	
}

第三种:p名称空间的方式:略

此外,有的复杂属性注入方式需要掌握

	   	 <!-- DI:复杂属性注入 -->
	   	 <bean id="collBean" class="cn.itcast.domain.CollBean">
	   	 	 <property name="ss">
	   	 	 		<!-- 数组类型 -->
	   	 	 		<list>
	   	 	 			<value>aaa</value>
	   	 	 			<value>bbb</value>
	   	 	 			<value>ccc</value>
	   	 	 		</list>
	   	 	 </property>
	   	 	 
	   	 	 <property name="ll">
	   	 	 		<!-- list类型  -->
	   	 	 		<list>
	   	 	 			<value>111</value>
	   	 	 			<value>222</value>
	   	 	 			<ref bean="car"/>
	   	 	 		</list>
	   	 	 </property>
	   	 	 
	   	 	 <property name="mm">
	   	 	 	<!-- map -->
	   	 	 	<map>
	   	 	 		<entry key="k1" value="aaa"></entry>
	   	 	 		<entry key="k2" value="bbbb"></entry>
	   	 	 		<entry key="k3" value-ref="car"></entry>
	   	 	 	</map>
	   	 	 </property>
	   	 	 
	   	 	 <property name="properties">
	   	 	 	<!-- properties类型 -->
	   	 	 	<props>
	   	 	 		<prop key="hibernate.username">root</prop>
	   	 	 		<prop key="hibernate.password">1234</prop>
	   	 	 	</props>
	   	 	 </property>
	   	 </bean>
public class CollBean 
{
	public CollBean()
	{
		System.out.println(11111);
	}
	
	private String[] ss;
	private List ll;
	private Map mm;
	private Properties properties; //键值对
	
	public void setSs(String[] ss) {
		this.ss = ss;
	}
	public void setLl(List ll) {
		this.ll = ll;
	}
	public void setMm(Map mm) {
		this.mm = mm;
	}
	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	@Override
	public String toString() {
		return "CollBean [ss=" + Arrays.toString(ss) + ", ll=" + ll + ", mm=" + mm + ", properties=" + properties + "]";
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值