这里出现了两个属性,scope和lazy-init:
1、scope表示的是bean的作用域,有prototype、request、session、singleton四种,其中singleton是默认的,表示单例。prototype表示每次创建都会产生一个bean实例。request和session只在web项目中才会用,其作用域就和web中的request和session一样
2、lazy-init表示的是bean的生命周期,默认为false。当scope=singleton时,bean会在装在配置文件时实例化,如果希望bean在产生时才实例化,可以把lazy-init设置为true。当scope=prototype时,在产生bean时才会实例化它。补充一点,如果希望该配置文件中所有的bean都延迟初始化,则应该在beans根节点中使用lazy-init="true"
关于依赖注入和控制反转,详见第七节
所谓注入即注入bean中的属性,Spring为用户提供了三种注入方式,settter注入、构造方法注入、接口注入(很少使用)
1、setter注入,bean.xml的写法为:
2、构造方法注入,bean.xml的写法为:
这里故意把family的定义写在person的定义上面,说明即使前面的beanA持有beanB的引用,把beanA定义在beanB前面也不影响
3、集合注入
除了简单属性的注入外,有时会用到集合注入,不过遇到的不多,简单了解一下即可
spring对于集合属性的支持非常好,以CollectionClass为例,看下如何配置bean.xml:
111
222
333
444
Spring虽然可以指定bean以单例的方式生成出来,但是每次都要用getBean方法获取类的实例非常麻烦,有办法像单例模式一样使用XXX.getInstance()就好了,不过还真有,以SingletonClass作为例子:
这样,我们就可以使用单例的方式去调用这个类了,如果类里面有一些私有属性,还可以注入的方式在生成这个bean的时候就注入进去,非常方便
init-method和destory-method
有时候我们希望,在某个bean加载的时候做一些事情,销毁的时候做一些事情(是不是想到了Servlet),所以我们可以自定义初始化和销毁的方法EmptyClass这个类,bean.xml的配置为:
注意两点:
1、实例化类的时候,几个方法的加载顺序为静态资源->构造方法->初始化方法
2、触发destory()方法的调用可以使用"((ClassPathXmlApplicationContext)ctx).close();",注意scope="prototype"是不会触发destory()的,没有为什么,设计就是这样
有时候我们有要求,一个类是某一个类/抽象类的子类,可以这么做:
public abstract class AbstractClass
{
}
public class ImplClass extends AbstractClass
{
}
此时bean.xml要这么写:
为什么Spring要支持Autowire(自动装配)
先写几个类,首先定义一个Animal接口表示动物:
public interface Animal {
public void eat();
}
写一个Animal接口的实现Tiger类:
public class Tiger implements Animal {
@Override
public void eat() {
System.out.println("Tiger.eat()");
}
@Override
public String toString() {
return "I'm a tiger";
}
}
写一个动物园类Zoo,持有Animal接口,表示动物园中有动物:
public class Zoo {
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
@Override
public String toString() {
if (animal == null) {
return null;
}
return animal.toString();
}
}
配置一下spring文件,由于这个功能研究的是Autowire,因此我命名为autowire.xml:
Spring引入Autowire(自动装配)机制就是为了解决
因此,为了解决使用
使用Autowire去除
下面来看一下使用Autowire去除
通常都是在
这里研究的是去除
首先看一下byName,byName意为在spring配置文件中查询beanName与属性名一致的bean并进行装配,若类型不匹配则报错,autowire.xml如果使用byName进行属性装配,那么将改成以下的形式:
看到Zoo中有一个名为animal的属性,我将Tiger这个bean也命名为animal,由于Tiger是Animal接口的实现类,因此Spring可以找到beanName为animal的bean并自动装配到Zoo的animal属性中,这就是byName的自动装配形式。
接着看一下byType的自动装配形式,byType意为在spring配置文件中查询与属性类型一致的bean并进行装配,若有多个相同类型则报错(下一部分讲),autowire.xml如果使用byType进行属性装配,那么将改成以下的形式:
将Tiger命名为tiger(将bean命名为类名首字母小写也比较符合规范),由于Tiger是Animal接口的实现类,因此Spring找到了Tiger并自动装配到Zoo的animal属性中,这就是byType的自动装配形式。
byType装配出现多个相同类型的bean及解决方案
前面演示了,byType的装配方式是在Spring配置文件中寻找属性类型与bean类型一致的bean,那么有一个问题,就是如果属性类型在Spring配置文件中有多个相同类型的bean会出现什么样的情况?为了探究一下这个问题,先定义另外一个Animal接口的实现类,叫做lion:
public class Lion implements Animal {
@Override
public void eat() {
System.out.println("Lion.eat()");
}
@Override
public String toString() {
return "I'm a lion";
}
}
接着,在Spring配置文件中定义一下Lion这个类:
运行一个测试类,结果为:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'zoo' defined in class path resource [spring/autowire.xml]: Unsatisfied dependency expressed through bean property 'animal': : No unique bean of type [org.xrq.action.by.Animal] is defined: expected single matching bean but found 2: [tiger, lion]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.xrq.action.by.Animal] is defined: expected single matching bean but found 2: [tiger, lion] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1166) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1058) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:516) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:455) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
意思很明显:想要通过byType方式为animal进行装配却找到了两个符合要求的bean,分别为tiger与lion,这导致了没有唯一的bean可以对animal进行装配。
这个问题有两种解决方案,假如现在我要装配的是lion这个bean,第一种解决方案是将不需要进行自动装配的bean进行排除,对不需要进行自动装配的bean设置属性autowire-candidate="false"即可:
candidate顾名思义,即候选人的意思,autowire-candidate="false"即这个bean我不想让它作为自动装配的候选者,既然tiger不是自动装配的候选者,那么animal类型在Spring容器中能自动装配的也就只有一个lion了,Spring自动装配lion,不会有问题。
第一种思路是排除那些不需要作为自动装配候选者的bean,第二种思路就从相反逻辑出发,设置当发现有多个候选者的时候优先使用其中的哪个候选者,对要作为自动装配候选者的bean设置primary="true"即可:
这种方式同样也可以将lion装配到animal属性中而不会报错。
1.控制反转 --> 谁控制谁? 控制什么? 为何叫反转(对应于正向)?哪些方面反转了?为何需要反转?
谁控制谁? --> IoC/DI容器控制应用程序
控制什么? --> IoC/DI容器控制对象本身的创建、实例化; IoC/DI容器控制对象之间的依赖关系
为何叫反转(对应于正向)? --> 因为现在应用程序不能主动去获取外部资源了,而是被动等待IoC/DI容器给它注入它所需要的资源,所以称之为反转.
哪些方面反转了? --> 1.创建对象 2.程序获取资源的方式反了
为何需要反转? --> 1.引入IoC/DI容器过后,体系更为松散,而且管理更有序; 2.类之间真正实现了松散耦合
2.依赖 --> 什么是依赖(按名称理解、按动词理解)? 谁依赖于谁? 为什么需要依赖? 依赖什么东西?
什么是依赖(按名称理解、按动词理解)? --> 依赖(按名称理解):依赖关系; 依赖(按动词理解):依赖的动作
谁依赖于谁? --> 应用程序依赖于IoC/DI容器
为什么需要依赖? --> 因为发生了反转,应用程序依赖的资源都是IoC/DI容器里面
依赖什么东西? --> 应用程序依赖于IoC/DI容器,依赖IoC/DI容器为它注入所需要的资源。(比如:依赖关系)
3.注入:谁注入于谁? 注入什么东西? 为何要注入?
谁注入于谁? --> IoC/DI容器注入于应用程序
注入什么东西? --> 注入应用程序需要的外部资源,比如依赖关系
为何要注入? --> 因为程序要正常运行需要这些外部资源
4.依赖注入和控制反转是同一概念吗?
不是同一概念, 其实它们两个描述的是同一件事件,但是是从不同的角度来说:控制反转是从IoC/DI容器的角度;依赖注入是从应用程序的角度
控制反转的描述: IoC/DI容器反过来控制应用程序,控制应用程序锁所需要的外部资源(比如:外部资源)
依赖注入的描述: 应用程序依赖IoC/DI容器,依赖它注入所需要的外部资源。
5.参与者都有哪些?
IoC/DI容器、应用程序
6.IoC/DI是什么?能做什么?怎么做?用在什么地方?
IoC/DI是什么?
IoC:就是使用IoC/DI容器反过来控制应用程序所需要的外部资源,这样的一种程序开发思想。
DI:就是应用程序依赖IoC/DI容器来注入所需要的外部资源,这样一种程序的开发思想。
能做什么? --> 松散耦合对象
怎么做? --> 使用Spring框架,里面有实现好了的IoC/DI容器
用在什么地方? --> 凡是程序里面需要使用外部资源的情况,都可以考虑使用IoC/DI容器
7.什么是外部资源
对于一个类来讲,所谓的外部资源,就是指在自己类的内部不能得到或实现的东西,比如说:在类里面需要读取一个配置文件,那么这个配置文件就相当于这个类的外部资源。又比如:A类里面要调用B类,那么对于A类来讲B类就是外部资源。
8. IoC容器
简单的理解就是:实现IoC思想,并提供对象创建、对象装配以及对象生命周期管理的软件就是IoC容器。
对IoC的理解:
a. 应用程序无需主动new对象,而是描述对象应该如何被创建
b. 应用程序不需要主动装配对象之间的依赖关系,而是描述需要哪个服务,IoC容器会帮你装配,被动接受装配
c. 主动变被动,是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则
9.使用IoC/DI容器开发需要改变思路
a. 应用程序不主动创建对象,但是要描述创建它们的方式
b. 在应用程序代码中不直接进行服务的装配,但是要描述哪一个组件需要哪一项服务,由容器负责将这些装配在一起。也就是说:所有的组件都是被动的,组件初始化和专供都是由容器负责,应用程序只是在获取相应的组件后,实现应用的功能即可。