1、IOC
(1)、概念:把对象的创建、初始化、销毁等工作交给Spring来做
(2)、Hello World
首先导入必须的jar包
接着创建bean容器配置文件applicationContext.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">
<!-- hello world -->
<bean id="helloworld" class="ff.helloworld.HelloWorld"></bean>
</beans>
<beans>元素中属性是一些必要的约束,<bean>元素描述某个类,id属性是bean唯一标识,class属性是类的全限定名
然后创建HelloWorld类和测试类,进行测试
public class HelloWorld {
public void say() {
System.out.println("Hello World -Spring");
}
}
public class TestHelloWorld {
private ApplicationContext applicationContext;
@Before
/**
* 加载context
*/
public void init() {
applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void testSay() {
HelloWorld hw = (HelloWorld) applicationContext.getBean("helloworld");
hw.say();
}
}
加载配置文件applicationContext.xml生成ApplicationContext对象,再用它的getBean()方法从bean容器中获取helloworld对象,调用say()方法。
(3)、Spring创建对象的三种方式
a、构造器(用的最多)
通过类的构造器创建对象,上面helloworld案例就是通过构造器创建
b、静态工厂
applicationContext.xml中不配置具体的某个bean,而是配置一个工厂bean,在创建工厂bean的时候调用工厂方法(factory-method)创建具体bean。
<!-- 静态工厂创建bean -->
<bean
id="beanDemoFactory_static" class="ff.beanCreate.BeanDemoFactory_Static"
factory-method="getInstance" lazy-init="true"></bean>
public class BeanDemo {
public BeanDemo() {
System.out.println("i am BeanDemo");
}
public void say() {
System.out.println("i am created!");
}
}
public class BeanDemoFactory_Static {
public static BeanDemo getInstance() {
return new BeanDemo();
}
}
c、实例工厂
application中不配置具体某个bean,而是配置一个工厂bean同时配置一个第三方bean,在创建第三方bean的时候通过factory-bean属性找到工厂bean,再调用工厂方法(factory-method属性)创建具体bean。
<!-- 实例工厂创建bean -->
<bean
id="beanDemoFactory_instance" class="ff.beanCreate.BeanDemoFactory_instance"></bean>
<!-- 第三方bean -->
<bean
id="beanDemo_instance" factory-bean="beanDemoFactory_instance"
factory-method="getInstance"></bean>
public class BeanDemo {
public BeanDemo() {
System.out.println("i am BeanDemo");
}
public void say() {
System.out.println("i am created!");
}
}
public class BeanDemoFactory_instance {
public BeanDemo getInstance() {
return new BeanDemo();
}
}
d、为什么要用工厂方式创建对象?
实现bean创建和使用分离,将bean创建的工作交由工厂来完成。这样可以统一管理bean的创建,如可以在各个bean创建前做一些初始化工作。此外,便于集成其它框架的bean创建管理方法。
(4)、Spring创建对象的时机
Spring默认在启动IoC容器时自动创建对象,但可以通过配置<bean>元素的lazy-init属性自定义创建时机。
lazy-init属性有三种值:default、false、true。默认值为default,效果等同于值false,在启动时创建对象,若将其设置为true则在获取bean的时候创建对象。
注意:一般不设置为true,原因是这样设置后Spring在启动不会创建这个bean,如果这个bean有bug,那么启动时不会报错,导致存在安全隐患。若一个bean的属性中含有大量数据,过早的放入内存会影响程序性能,这时候将lazy-init设置为ture。
(5)、bean的scope(作用域)
a、 默认情况
默认情况下Spring创建的bean是单例的,每次获取bean的时候返回同一个对象,这个bean的属性自然也被多处引用中共享,这样很不安全。所以默认情况下不能将数据定义到bean属性中。
b、修改bean的scope
<bean>元素中的scope属性可以修改scope。scope属性有四个值singleton(单例)、prototype(多例)、request、session,其中prototype为原型模式,每次获取bean返回不同对象。
c、选择单例还是多例?
Spring IoC容器在启动时会创建applicationContext.xml文件中配置的所有bean,实际开发中大多数的bean只会使用到1次,所以默认配置为单例,对特殊bean配置成多例。
(6)、scope与创建时机的结合
a、当scope为property时,不管lazy-init值是什么,都在获取bean的时候创建对象。因为property是多例模式,每次获取bean的时候都会返回不同的对象。
b、当scope为singleton时,在什么时候创建对象由lazy-init决定,且都以单例模式创建。
(7)、bean初始化,销毁
<bean>元素的init-method和destory-method定义初始化和销毁方法。
(8)、bean创建、初始化、销毁的顺序
当Spring启动(或代码中获取bean)时创建bean对象,接着Spring内部调用初始化方法(如果配置),当Spring容器关闭时执行这个bean对象的销毁方法。
说明:1、初始化和销毁方法由Spring内部执行。 2、销毁方法在Spring容器关闭时执行,但Spring容器一般不会关闭,所以容器里的对象会一直存在。3、如果<bean>元素的scope属性值为property,Spring容器不负责销毁。
2、 DI(依赖注入)
(1)、概念:给bean对象属性赋值
(2)、三种赋值方式
a、通过setter方法赋值
配置<bean>元素内<property>子元素,在bean被创建时调用对应的setter方法给属性注入值。
<bean id="persion" class="domain.Persion">
<property name="student" ref="student"></property>
<property name="string" value="this is a string"></property>
<property name="list">
<list>
<value>index1</value>
<value>index2</value>
<ref bean="student"/>
</list>s
</property>
</bean>
public class Persion {
private Student student; // Student对象
private String string;
private List list;
public void say(Student student) {
student.say();
}
public void setStudent(Student student) {
this.student = student;
}
public void setString(String string) {
this.string = string;
}
public void setList(List list) {
this.list = list;
}
}
当Person对象被创建时,Spring通过<property>元素找到对应的setter方法,将配置的值注入。
<property>元素给属性注入值,value为基本类型以及String类型值,ref为引用属性的bean的id,array、list、map为集合属性
b、通过构造方法赋值
在<bean>元素内配置<constructor-arg>元素,当Persion类被创建时调用对应的构造器为属性赋值。
<bean id="student" class="ff.ing.pojo.Student">
<constructor-arg index="0" value="盖伦"></constructor-arg>
</bean>
<bean id="persion" class="ff.ing.pojo.Persion">
<constructor-arg index="0" value="007"></constructor-arg>
<constructor-arg index="1" value="赵信"></constructor-arg>
<constructor-arg index="2" ref="student"></constructor-arg>
</bean>
public class Persion {
private int id;
private String name;
private Student student;
public Persion() {
}
public Persion(int id, String name, Student student) {
this.id = id;
this.name = name;
this.student = student;
}
public void say() {
String str = "id:" + id + " name:" + name + " student:" + student.getName();
System.out.println(str);
}
}
index属性为构造器参数列表中参数的序号。
c、通过注解赋值
1、依赖注入注解
(1) 首先需要修改applicationContext.xml文件:添加注解命名空间约束,启动依赖注入注解解析器
<!-- 添加注解命名空间约束 -->
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.xsd">
<!-- 启动依赖注入注解解析器 -->
<context:annotation-config/>
<!-- bean -->
<bean>........</bean>
(2) 在类中使用注解
public class Persion {
private int id;
private String name;
@Resource(name="student")
private Student student;
………
………
}
@Resource会到IoC容器中寻找id与name匹配的bean,赋值给student属性。
@Resource是java内置注解,Spring框架定义了自己的注解。@Autowired按照类型进行匹配;@Qualifier(value="xxx")按照id进行匹配,且只能与@Autowired组合使用,无法单独使用。
注解只能注入引用类型值,无法注入基本类型、String类型以及集合类型值
注解方式比xml配置方式效率低。
2、类扫描注解
(1)、首先需要修改applicationContext.xml文件:添加注解命名空间约束,启动类扫描注解解析器,配置要扫描的包
<!-- 添加注解命名空间约束 -->
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.xsd">
<!-- 启动类扫描注解解析器,配置要扫描的包 -->
<context:component-scan base-package="ff.ing.pojo"></context:component-scan>
(2)在类中使用注解
@Component(value="student")
public class Student {
private String name;
public Student() {
}
……
……
}
@Component(value="persion")
public class Persion {
private int id;
private String name;
@Resource(name="student")
private Student student;
public Persion() {
}
……
……
}
@Component配置了这个类的在spring容器中的id,以Persion类为例@Component(value="persion")相当于<bean id="persion" class="……"></bean>
Spring还提供了更细分的组件注解:@Repository
、@Service
和 @Controller,分别代表dao层、业务层和控制层。他们的用法以及功能与@Component一致,只是方便了代码的阅读而已。
类扫描注解方式比依赖注入注解方式效率还低。
● IoC、DI中的类继承问题
当使用setter和构造器方法给属性输入值时,子类无法继承父类中属性的值,这时需要在<bean>元素中添加parent属性。使用注解不存在这样的问题
● IoC与DI的意义
● IoC与DI最生动的解释(摘抄他人)
首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、号码………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的信息,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
● 意义
实现了完全面向接口编程(程序设计依赖于抽象的接口而非具体实现类),在代码中不需要关心具体是哪个实现类在工作。