在过去,我们总是自己用new关键字新建一个对象。当两个类存在依赖关系时,我们必须考虑new的顺序,来保证正确性。这样一方面使得我们的设计很麻烦,另一方面使得两个类的依赖关系明显,增加了耦合度。为此,我们使用了Spring。Spring就相当于一个大容器,我们需要某个类时,就只需从里面获取,不用我们主动去new。两个类之间的依赖关系,Spring也会帮我们解决。这样可以降低耦合度,也使得设计更加方便。这就是Spring的控制反转,把新建对象的控制权交给Spring。
Spring的依赖注入是Spring的核心功能之一,通过依赖注入可以将类放入Spring容器中。Spring的依赖注入有四种方法,一种是设值注入,一种是构造注入,一种是实例工厂方式,一种是静态工厂方式。
- 设值注入
主要是在Java类中给相应的属性设置set方法,然后在配置文件中使用标签设置其为属性。 若该属性是Bean,则可以使用ref属性来指定某一个bean。若是非bean,则可以使用value属性来初始化属性。
public class TestAction {
private TestService testService;
//set方法
public void setTestService(TestService testService) {
this.testService = testService;
}
@ResponseBody
@RequestMapping(value="/testHello")
public Map<String,Object> testHello(){
testService.test();
Map<String,Object> result=new HashMap<String,Object>();
result.put("test", "testHello");
return result;
}
}
<bean class="com.test.service.impl.TestServiceImpl" name="testService" id="testService" ></bean>
<bean class="com.test.action.TestAction" id="testAction" name="testAction">
<property name="testService" ref="testService"></property>
</bean>
- 构造注入
主要是在Java类中提供该类的构造函数,形参为你需要注入的属性。然后在配置文件中使用多个标签来配置构造函数的形参。若该形参是一个bean,则可以使用ref属性来配置。否则使用value属性。存在多个形参时,可以使用index属性来标记形参位置,也可以使用type属性标记形参类型。
public class TestAction {
private TestService testService;
private String string;
public TestAction(TestService testService,String str) {
// TODO 自动生成的构造函数存根
this.testService=testService;
this.string=str;
}
@ResponseBody
@RequestMapping(value="/testHello")
public Map<String,Object> testHello(){
testService.test();
Map<String,Object> result=new HashMap<String,Object>();
result.put("test", "testHello");
return result;
}
}
<bean class="com.test.service.impl.TestServiceImpl" name="testService" id="testService" ></bean>
<bean class="com.test.action.TestAction" id="testAction" name="testAction">
<constructor-arg name="testService" ref="testService" index="0"></constructor-arg>
<constructor-arg name="string" value="hello" index="1"></constructor-arg>
</bean>
- 实例工厂注入
实例工厂方式指的是写一个工厂类,里面有各种对象的新建方法,但都是费静态方法,所以需要实例一个工厂出来。在配置文件中,先写出该工厂的bean,然后再写出工厂每一个类的bean,而使用属性factory-bean说明是来自哪个工厂,用属性factory-method说明哪个方法。
public class ServiceFactory {
public TestServiceImpl geTestServiceImpl() {
return new TestServiceImpl();
}
}
public class TestAction {
private TestServiceImpl testServiceImpl;
public void setTestServiceImpl(TestServiceImpl testServiceImpl) {
this.testServiceImpl = testServiceImpl;
}
@ResponseBody
@RequestMapping(value="/testHello")
public Map<String,Object> testHello(){
testServiceImpl.test();
Map<String,Object> result=new HashMap<String,Object>();
result.put("test", "testHello");
return result;
}
}
<bean class="com.test.action.ServiceFactory" name="serviceFactory"></bean>
<bean name="testServiceImpl" factory-bean="serviceFactory" factory-method="geTestServiceImpl"></bean>
<bean class="com.test.action.TestAction" id="testAction" name="testAction">
<property name="testServiceImpl" ref="testServiceImpl"></property>
</bean>
- 静态工厂方式
静态工厂与实例工厂类似,但里面的方法都是静态的,这样不用实例一个工厂出来。在配置文件中,不用写出工厂的bean。对于工厂的每一个类的bean,用class属性说明来源的工厂,用factory-method来说明哪个方法。
<bean name="testServiceImpl" class="com.test.action.ServiceFactory" factory-method="geTestServiceImpl"></bean>
<bean class="com.test.action.TestAction" id="testAction" name="testAction">
<property name="testServiceImpl" ref="testServiceImpl"></property>
</bean>
一般来说,设值注入和构造注入使用比较多。当这种都存在的时候,先设值注入再构造注入。其两种区别是:设值注入更加简单,易懂,同时使得依赖关系更加直观,自然。对于多参数的构造函数时,构造方式显得累赘,笨重。但构造注入方式可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。设值注入是先构造出本身,再初始化其依赖属性。而构造注入反过来。
获取bean的方式
Spring容器最基本的接口是BeanFactory。其负责配置,创建,管理Bean。它有一个子接口:ApplicationContext,称为Spring上下文。BeanFactory常用的实现类是DefaultListableBeanFactory,ApplicationContext常用实现类是:FileSystemXmlApplicationContext,(从文件系统中加载xml配置文件来创建bean),ClassPathXmlApplicationContext(从类加载路径中加载xml配置文件创建bean), AnnotationConfigWebApplicationContext(通过加载Java配置类【使用了@Configuration】来创建bean)。BeanFactory与ApplicationContext的区别:
使用BeanFactory来加载时,不会预初始化容器的bean,而ApplicationContext会预初始化。所以,系统在创建ApplicationContext有较大的开销,一旦初始化结束,获取bean就有很好的性能。(也可以使用lazy-init属性指定延时加载)。ApplicationContext还能提供国际化支持,资源访问,事件机制,同时加载多个xml文件,以声明式方式启动并创建Spring容器。