天天看点

spring之IOC总结

spring中,对于IOC(控制反转)和DI(依赖注入)。

控制反转和依赖注入,说的都是一个东西。控制反转是说java对象中依赖对象的创建,由原来通过new创建,变成由第三方spring容器创建,用时直接在代码中声明,容器会主动把创建好的对象动态的注入到代码中(依赖注入)。不管是创建一个依赖另一个对象的对象,还是创建一个简单的对象,spring容器都可以做到。  用控制反转的好处当然就是实现代码间的松耦合啦,现实的例子就是:我需要一把斧子,以前的方式是自己去生产一个,而现在就是由斧子工厂生产,我直接从工厂里拿来用,这样达到了人和斧子对象之间很好的解耦。因为控制反转不容易理解,所以后来改为依赖注入,因为依赖注入说明了这样思想的实现方式。

为了实现这样的功能,在代码中又是怎呀实现的呢?首先,我们需要考虑两个问题:1.在spring容器中的bean是怎样创建的?2.创建好的bean又是怎样动态注入到代码中的?

如果这两个问题解决了,那么就可以实现上面说的那种控制反转的思想了。

首先,讲bean的创建前先说spring容器,spring框架提供给我们一个spring容器,也叫IOC容器。这个容器就是用来创建bean的,不仅可以创建对象,还维护对象间的关系,管理对象的生命周期。代码中由BeanFactory来表示,但是我们开发一般用它的子接口ApplicationContext,因为它里面的方法更多一些,比如更易 与Spring AOP集成、资源处理(国际化处理)、事件传递及各种不同应用层的context实现 (如针对web应用的WebApplicationContext)。简而言之,BeanFactory提供了配制框架及基本功能,而 ApplicationContext 则增加了更多支持企业核心内容的功能。除了功能不同外,还有个区别,ApplicationContext是在初始化容器时就直接创建在里面注册的bean,而BeanFactory容器是延迟加载的,即容器初始化时,里面的bean不是立即创建,而是代码中用到bean时才创建实例化。而对于接口ApplicationContext,他的实现类有两种ClassPathXmlApplicationContext(从类路径下加载配置文件)和FileSysytemApplicationContext(从文件系统加载配置文件)

代码实现:1.创建一个spring容器

//从类路径下加载配置文件,配置文件里就是一些需要注册进容器的bean。此句表示创建一个spring容器并初始化里面的bean。	
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); 
         //从容器中取出bean
	Food food=(Food) context.getBean("food22");  //food22已经在容器中注册过才能拿来用,后面会讲bean如何注册进容器
           

spring容器中的bean是怎样创建的呢?---有3种。1.通过构造函数(实质就是通过反射机制实现的),一般我们用的是这种方式。2.通过静态工厂方法。3.通过实例工厂方法。

1.通过构造器,可以用默认的无参构造器,也可以用自定义的有参构造器

package com.yxj.spring.entity;

public class Food {
	private String name;
	private int price;
	private String type;
	
	//1.此处用默认构造器创建一个bean对象
	public Food() {
		super();
	}
	
	//2.同样是构造器创建一个bean,可以根据自己需求设置一个有参构造器
	public Food(String name, int price, String type) {
		super();
		this.name = name;
		this.price = price;
		this.type = type;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}

	@Override
	public String toString() {
		return "Food [name=" + name + ", price=" + price + ", type=" + type + "]";
	}
}
           

web.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


           
<!--spring容器通过java代码中的默认构造器(上1),在容器中创建一个实例bean(反射机制实现) 。 -->
<bean id="food" class="com.yxj.spring.entity.Food"/>

<!--通过自定义构造器参数(上2),实例化一个bean。注意!此处属性的顺序要和自定义构造函数的参数的一样,否则会报错。 此处写法表示已经给对象初始化了 -->
<bean id="food22" class="com.yxj.spring.entity.Food">
			<constructor-arg value="巧克力"></constructor-arg>
			<constructor-arg value="120"></constructor-arg>
			<constructor-arg value="sweet"></constructor-arg>
</bean>
</beans>
           

2和3,通过工厂方式创建bean就不作多说明,想要了解的请自行百度。

上面只是注入一个简单的java对象,对于对象需要依赖另一个对象的情景,则需要用到依赖注入来实现另一个对象注入到对象中。比如,User这个对象,依赖食物Food这个对象,怎样把食物对象注入到人对象中呢?----3种方式。1,通过setter方法注入 ;2,通过构造器注入 3.基于注解的注入

package com.yxj.spring.entity;

public class User {
	private String name;
	private Food food;
	
	public User() {
		super();
	}

	public User(String name, Food food) {
		super();
		this.name = name;
		this.food = food;
	}
           
public Food getFood() {
		return food;
	}


           
//通过setter方法,将Food这个对象注入进来
	public void setFood(Food food) {
		this.food = food;
	}

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

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           

Food类就是上面的。

xml配置。1.通过setter方法注入。通过在类中写依赖类的set方法。在xml配置中通过ref表示两个对象间的关联关系

<bean id="food" class="com.yxj.spring.entity.Food"/>  //要把food对象动态注入到User对象中,需在容器中创建一个food对象

<bean id="user" class="com.yxj.spring.entity.User">   //在容器中创建一个user对象
 	<!--通过property元素来表示对象的属性。-->
	<property name="name" value="这是name属性的值"></property>
	<!--两个对象间的关联,由ref指定 -->
	<property name="food" ref="food"></property>
</bean>
           

               2.通过构造器注入依赖对象,java代码中就可以不用写food的setter方法,加入相应的构造器即可。

<bean id="user" class="com.yxj.spring.entity.User">
	<constructor-arg value="Jack"></constructor-arg>	
        <constructor-arg ref="food" ></constructor-arg>
</bean>
           

3.注解方式注入。从spring2.5开始支持,现在很流行这种方式,注解使整个代码量减少了。主要由@Autowired   @Resource  @Inject  

在讲注解方式注入前,先说一下什么是自动装配(autowire)。自动装配是指,Spring 在装配 Bean 的时候,根据指定的自动装配规则,将某个 Bean 所需要引用类型的 Bean 注入进来。注意,目前只支持自动装配一个依赖对象,对于基本的属性类型如string,int等不支持。<bean> 元素提供了一个指定自动装配类型的 autowire 属性。autowire由几个属性,byName,byType等,之间区别就不详说了。主要是说autowire的功能。

对于上面的例子,可以简化为:

<bean id="food" class="com.yxj.spring.entity.Food"/> 
           
<bean id="user" class="com.yxj.spring.entity.User" autowire="byName"/> //使用autowire属性,则不用再配对象的依赖对象,当然对于基本属性还是可以继续配的
           
使用@Autowired @Resource @Inject等注解实现自动注入---替代setter方法或是构造器,用于类中依赖对象的自动注入。     3.1 @Autowired 注解进行自动装配,只能是根据类型进行匹配。@Autowired 注解可以用于 Setter 方法、构造函数、字段,甚至普通方法,前提是方法必须有至少一个参数。@Autowired 可以用于数组和使用泛型的集合类型。然后 Spring 会将容器中所有类型符合的 Bean 注入进来。 xml中:
<bean id="food" class="com.yxj.spring.entity.Food" />
<bean id="user" class="com.yxj.spring.entity.User"/>
           
java代码中:
public class User {
	private String name;
	
	@Autowired
	private Food food;   //使用@Autowired注解,不用写Food对象的set方法.则xml中配置user对象时也不用写            
//<property name="food" ref="food">来进行装配
           
public User() {
		super();
	}

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

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           
如果food类没有在容器中注册,那么会报错。解决这一问题,只需这样写@Autowired(required=false),这样不会报错,只是表示这个依赖对象为null。 如果在xml文件中出现多个类型相同的bean,比如注册了food1,food2等,则在使用@Autowired时也会出错,因为@Autowired是根据类型匹配的,当出现多个类型相同的bean时,容器会不知道匹配哪个bean。解决方案,用@Qualifier注解指明装配哪个bean。   3.2  @Resource 是根据bean的name名字进行自动装配的,而不是像@Autowired和@Qualifier结合根据类型装配。可以作用于带一个参数的 Setter 方法、字段,以及带一个参数的普通方法上。@Resource会先根据bean的名字装配,如果没有符合的bean则退居根据类型装配。   3.3  @Inject注解几乎可以完全替代Spring的@Autowired注解。所以除了使用Spring特定的@Autowired注解,我们可以选择@Inject。和@Autowired一样,@Inject可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。因此@Inject注解注入的依赖关系必须存在,否则报异常。 上面所说的@Autowired   @Resource  @Inject  都是用于实现对象中依赖对象的装配。而对于一个类的自动装配,可以用下面的注解。 @Service @Component @Controller @Repository,用他们放在类上,就不用在xml文件中配写这个类的<bean />了。容器会扫描到后自动注册进容器。(用于类的自动注入)
package com.yxj.spring.entity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component   //使用@Component注解后,就不用在xml中写<bean  />了,会自动把这个类注入到容器中
public class User {
	private String name;
	
	@Autowired
	private Food food;
	
	public User() {
		super();
	}

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

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<context:component-scan base-package="com.yxj.spring.entity"></context:component-scan>  
</beans>
           
还有其他功能的注解,后面会继续补充。