标签:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 将其他资源文件引入到当前配置文件中 -->
<import resource="resouce.xml"/>
<!--为id为hello的Bean,取一个别名叫hello1 -->
<alias name="hello" alias="hello1"/>
<!-- id表示组件的名字,class表示组件的实现类 -->
<bean id="hello" class="com.lizhenhua.test.HelloImpl"></bean>
</beans>
1.bean标签主要用来进行Bean定义
2.alias用于定义Bean别名
3.import用于导入其他配置文件的Bean定义。这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组,new String[] {"config1.xml","config2.xml"}传给ApplicationContext实现进行加载多个配置文件,哪个更适合由用户决定;这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。
4.import标签可以放在任何位置,没有顺序关系
SpringIoc容器目的就是管理Bean,这些Bean将根据配置文件中的Bean定义进行创建,而Bean定义在容器内部由BeanDefinition对象表示,该定义主要包含以下信息:
全限定类名FQN:用于定义Bean的实现类
Bean行为定义:作用域(单例、原型创建)、是否惰性初始化及生命周期
Bean创建方式定义:通过构造器还是工厂方法创建Bean
Bean之间关系定义:对其他Bean的引用,也就是依赖关系定义,这些引用bean也可以称之为依赖bean,也就是依赖注入
Bean定义只有全限定类名在当使用构造器或静态工厂方法进行实例化bean时是必须的,其他都是可选的定义。
HelloI接口:用于测试
package com.lizhenhua.test;
public interface HelloI {
public void sayHello();//用于测试
}
HelloI接口的实现类HelloImpl
package com.lizhenhua.test;
public class HelloImpl implements HelloI {
@Override
public void sayHello() {
System.out.println("你好,世界");
}
}
HelloI接口的实现类HelloImpl2
package com.lizhenhua.test;
public class HelloImpl2 implements HelloI {
private String message;
public HelloImpl2(){
this.message = "Hello World!";
}
public HelloImpl2(String message){
this.message = message;
}
@Override
public void sayHello() {
System.out.println(message);
}
}
配置Bean
<!--定义HelloI的接口实现类HelloImpl的Bean的配置-->
<bean class="com.lizhenhua.test.HelloImpl"></bean>
<!--定义HelloI的接口实现类HelloImpl2的Bean的配置-->
<bean class="com.lizhenhua.test.HelloImpl2"></bean>
测试代码:
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据类型获取bean,注意该HelloI是一个接口
HelloI helloI = beanFactory.getBean(HelloI.class);
//3.执行业务逻辑
helloI.sayHello();
}
此代码运行HelloI helloI = beanFactory.getBean(HelloI.class);会报错,为什么呢?因为在该配置文件中有两个该接口的实现类。因此不知道返回哪个结果,所以报错。如何解决呢?
解决方法一:通过修改测试代码。
//解决方法一:指定是哪个具体的实现类,而不是接口
HelloI helloI = beanFactory.getBean(HelloImpl.class);
测试代码:
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据类型获取bean,注意该HelloI是一个接口
HelloI helloI = beanFactory.getBean(HelloImpl.class);
//3.执行业务逻辑
helloI.sayHello();
}
解决方法二:通过修改配置文件,去掉其中一个Bean的配置。原来测试代码不变。
<bean id="hello" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据id获取bean
HelloI helloI = beanFactory.getBean("hello",HelloI.class);
//3.执行业务逻辑
helloI.sayHello();
}
<bean name="hello" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据name获取bean
HelloI helloI = beanFactory.getBean("hello",HelloI.class);
//3.执行业务逻辑
helloI.sayHello();
}
<bean id="hello" name="alias" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据id获取bean
HelloI helloI = beanFactory.getBean("hello",HelloI.class);
//3.执行业务逻辑
helloI.sayHello();
//根据name获取bean
HelloI helloI = beanFactory.getBean("alias",HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
注意:
<!-- 如果id和name一样,Ioc容器能检测到,并消除冲突 -->
<bean id="hello" name="hello" class="com.lizhenhua.test.HelloImpl"></bean>
<bean name="hello,hello2,hello3" class="com.lizhenhua.test.HelloImpl"></bean>
<bean name="beanName" class="com.lizhenhua.test.HelloImpl"></bean>
<alias name="beanName" alias="alias1"/>
<alias name="beanName" alias="alias2">
SpringIoc容器如何实例化Bean呢?传统应用程序可以通过new和反射方式进行实例化Bean.而Spring Ioc容器则需要根据Bean定义里的配置元数据使用反射机制来创建Bean。在SpringIoC容器中根据Bean定义创建Bean主要有以下几种方式:
1.使用空构造器进行定义,使用此种方式,class属性指定的类必须有空构造器
2.使用有参数构造器进行定义,使用此种方式,可以使用<constructor-arg>标签指定构造器参数值,其中index表示位置,value表示常量值,也可以指定引用,指定引用使用ref来引用另一个Bean定义。
<bean name="bean2" class="com.lizhenhua.test.HelloImpl">
<!-- 指定构造器参数 -->
<constructor-arg index="0" value="hello"></constructor-arg>
</bean>
(1)准备Bean class,该类有一个空构造器和一个有参构造器
package com.lizhenhua.test;
public class HelloImpl2 implements HelloI {
private String message;
public HelloImpl2(){
this.message = "Hello World!";
}
public HelloImpl2(String message){
this.message = message;
}
@Override
public void sayHello() {
System.out.println(message);
}
}
(2)在配置文件配置Bean定义。
<!-- 使用默认构造器 -->
<bean id="bean1" class="com.lizhenhua.test.HelloImpl2"></bean>
<!-- 使用有参数的构造器 -->
<bean id="bean2" class="com.lizhenhua.test.HelloImpl2">
<!-- 指定构造器参数 -->
<constructor-arg index="0" value="Hello Spring"></constructor-arg>
</bean>
(3)测试
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据类型获取bean ,使用无参构造器
HelloI helloI = beanFactory.getBean("bean1",HelloI.class);
//3.执行业务逻辑
helloI.sayHello();
//使用有参数构造器,
helloI = beanFactory.getBean("bean2", HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
输出结果:
Hello World!
Hello Spring
(1)先编写静态工厂类代码
package com.lizhenhua.test;
public class HelloIStitaicFactory {
//工厂方法
public static HelloI newInstance(String message){
//返回需要的Bean实例
return new HelloImpl2(message);
}
}
(2)配置Bean定义
<!-- 使用静态工厂方法 -->
<bean id="bean3" class="com.lizhenhua.test.HelloIStitaicFactory" factory-method="newInstance">
<constructor-arg index="0" value="静态工厂方法"></constructor-arg>
</bean>
(3)测试
package com.lizhenhua.test;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloTest {
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用静态工厂获取Bean
HelloI helloI = beanFactory.getBean("bean3", HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
①实例工厂代码
package com.lizhenhua.test;
public class HelloIInstanceFactory {
public HelloI newInstance(String message){
return new HelloImpl2(message);
}
}
②在配置文件中定义Bean
<!-- 1.定义实例工厂Bean -->
<bean id="beanInstanceFactory" class="com.lizhenhua.test.HelloIInstanceFactory"></bean>
<!-- 2.使用实例工厂Bean创建Bean -->
<bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance">
<!-- 指定带参数构造器 -->
<constructor-arg index="0" value="实例工厂方法"></constructor-arg>
</bean>
③测试
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用实例工厂创建bean
HelloI helloI = beanFactory.getBean("bean4", HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
测试输出结果
实例工厂方法
通过以上例子我们已经基本掌握了如何实例化Bean了。
这三种方式只是配置不一样,从获取方式看完全一样,没有任何不同。这也是SpringIocDE MEILI .
SpringIoc帮我们创建Bean,只管使用就可以了。
1.1依赖和依赖注入
传统应用程序设计中所说的依赖一般指类之间的关系。
复习类之间的关系
泛化: 表示类与类之间的继承关系、接口与接口之间的继承关系
实现: 表示类对接口的实现
依赖: 当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种“相识关系”,只在某个特定地方(比如某个方法体内)才有关系
关联:表示类与类或类与接口之间的依赖关系,表现为“拥有关系”;具体到代码可以使用实例变量表示。
聚合:是关联的特殊情况,体现部分-整体关系,是一种弱拥有关系;整体和部分可以有不一样的生命周期;是一种弱关联
组合:是关联的特殊情况,也行了部分-整体关系,是一种强拥有关系,整体与部分由相同的生命周期,是一种强关联
Spring Ioc容器的依赖有两层含义:Bean依赖容器和容器注入Bean的依赖资源
Bean依赖容器: 也就是说Bean要依赖于容器,这里的依赖是指容器负责创建Bean并管理Bean的生命周期,正是由于容器来控制创建Bean并注入依赖,也就是控制权被反转了,这也正是Ioc名字的由来,此处的有依赖是指Bean和容器之间的依赖关系
容器注入Bean的依赖资源:容器负责注入Bean的依赖资源,依赖资源可以是Bean、外部文件、常量数据等,在JAVA中都反映为对象,并且由容器负责组装Bean之间的依赖关系。此处的依赖是指Bean之间的依赖关系,可以认为是传统类与类之间的关联、聚合、组合关系。
为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?
动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件。
更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现
更好实践优先使用对象组合,而不是类继承:因为Ioc容器采用注入依赖,也就是组合对象,从而更好的实践对象组合
采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能
而如果采用类继承,Bean没哟偶依赖Bean ,而是采用继承方式添加新功能,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。
增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单
降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合。
代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。
从以上我们可以看出,其实依赖注入只是一种装配对象的手段,设计的类结构才是基础,如果设计的类结构不支持依赖注入,Spring Ioc容器也注入不了任何东西,从而从根本上说"如何设计好类结构才是关键,依赖注入只是一种装配对象手段"。
前边Ioc一章我们已经了解了Bean依赖容器,那容器如何注入Bean的依赖资源,SpringIoc容器注入依赖资源主要有两种基本方式实现:
构造器注入:就是容器实例化Bean时注入那些依赖,通过在Bean定义中指定构造器参数进行注入依赖,包括实例工厂方法参数注入依赖,但静态工厂方法参数不允许注入依赖。
setter注入:通过setter方法进行注入依赖
方法注入:能通过配置方式替换掉Bean方法,也就是通过配置改变Bean方法功能
使用构造器注入通过配置构造器参数实现,构造器参数就是依赖。除了构造器方式,还有静态工厂,实例工厂方法可以进行构造器注入。
构造器注入可以根据参数索引注入、参数类型注入或Spring3支持的参数名注入,但参数名注入是有限制的,需要使用在编译程序时打开调试模式(即在编译时使用javac -g:vars在产拉丝少文件中生成变量调式信息,默认是不包含变量调试信息的,从而能获取参数名字,否则获取不到参数名字)或在构造器上使用@ConstructorProperties注解来指定参数名。({"message","index"})
一、根据参数索引注入,使用<constructor-arg index="1" value="1"/>来指定注入的依赖,其中index表示索引,从0开始,value来指定注入的常量值。
二、根据参数类型进行注入,使用标签<constructor-arg type="java.lang.String" value="nihao"/>来注入依赖。type表示需要匹配的参数类型,可以是基本类型也可以是其他类型
三、根据参数名进行注入<constructor-arg name="message" value="nihao"/>name表示需要匹配的参数名字,value来指定注入的常量值
四、静态工厂方法注入
(1)先编写静态工厂类代码
package com.lizhenhua.test;
public class HelloIStitaicFactory {
//工厂方法
public static HelloI newInstance(String message){
//返回需要的Bean实例
return new HelloImpl2(message);
}
}
(2)配置Bean定义
<!-- 使用静态工厂方法 -->
<bean id="bean3" class="com.lizhenhua.test.HelloIStitaicFactory" factory-method="newInstance">
<constructor-arg index="0" value="静态工厂方法"></constructor-arg>
</bean>
(3)测试
package com.lizhenhua.test;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloTest {
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用静态工厂获取Bean
HelloI helloI = beanFactory.getBean("bean3", HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
五、实例工厂方法注入
①实例工厂代码
package com.lizhenhua.test;
public class HelloIInstanceFactory {
public HelloI newInstance(String message){
return new HelloImpl2(message);
}
}
②在配置文件中定义Bean
<!-- 1.定义实例工厂Bean -->
<bean id="beanInstanceFactory" class="com.lizhenhua.test.HelloIInstanceFactory"></bean>
<!-- 2.使用实例工厂Bean创建Bean -->
<bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance">
<!-- 指定带参数构造器 -->
<constructor-arg index="0" value="实例工厂方法"></constructor-arg>
</bean>
③测试
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用实例工厂创建bean
HelloI helloI = beanFactory.getBean("bean4", HelloI.class);
//执行业务逻辑
helloI.sayHello();
}
测试输出结果
实例工厂方法
注意:静态工厂方式和实例工厂方式根据参数名注入的方式只支持通过在class文件中添加变量调式信息方式才能运行,注解方式不能工作,因此,不建议使用根据
参数名进行构造器注入。。
setter注入,是通过在通过构造器、静态工厂或实例工厂实例好Bean后,通过调用Bean类的setter方法进行注入依赖。
setter注入方式只有一种根据setter名字进行注入
<!--setter方法注入-->
<property name="message" value="hello world" />
setter注入的方法名要遵循JavaBean getter/setter方法命名约定
JavaBean:本质就是一个POJP类
该类必须要有公共的无参构造器
属性为private访问级别
属性必要时通过一组setter和getter方法来访问
setter方法,以set开头,后跟首字母大写的属性名
getter方法,一般属性以get开头,对于boolean类型一般以is开头,后跟首字母大写的属性名
还有一些特殊情况,比如属性有连续两个大写字母开头,URLsetter/getter方法为,setURL和getURL
<property name="message" value="Hello"></property>
或
<property name="index">
<value>1</value>
</property>
注意:Spring类型转换对于boolean类型进行了容错处理除了可以使用true/fasle,还能使用yes/no 、on/off、 1/0
用于注入Bean的ID,ID是一个常量不是引用,且类似于注入常量,但提供错误验证功能,配置方式如下所示
<property name="id"><idref bean="bean1"></property>
<property name="id"><idref local="bean1"></property>
两种方式都可以,上述配置本质上在运行时等于如下方式
<bean id="bean1" class="" />
<bean id="idrefBean" calss="">
<property name="id" value="bean1"/>
</bean>
第一种方式可以在容器初始化时校验引用的Bean是否存在,如果不存在将抛出异常,
而第二种方式只有在Bean实际使用时才能发现传入的Bean的ID是否正确,可能发生不可预料的错误
测试
package com.lizhenhua.test2;
public class IdRefTestBean {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
<bean id="bean1" class="java.lang.String">
<constructor-arg index="0" value="test"></constructor-arg>
</bean>
<bean id="bean2" class="java.lang.String">
<constructor-arg index="0" value="test2"></constructor-arg>
</bean>
<bean id="idrefBean1" class="com.lizhenhua.test2.IdRefTestBean">
<property name="id">
<idref bean="bean1"/>
</property>
</bean>
<bean id="idrefBean2" class="com.lizhenhua.test2.IdRefTestBean">
<property name="id">
<idref local="bean2"/>
</property>
</bean>
@Test
public void testHelloWorld(){
//1.获取BeanFactory
BeanFactory beanFactory = new ClassPathXmlApplicationContext("/com/lizhenhua/test2/applicationContext.xml");
IdRefTestBean helloI = beanFactory.getBean("idrefBean1", IdRefTestBean.class);
//执行业务逻辑
System.out.println(helloI.getId());
helloI = beanFactory.getBean("idrefBean2", IdRefTestBean.class);
//执行业务逻辑
System.out.println(helloI.getId());
}
测试结果
bean1
bean2
为什么是bean1和bean2 而不是 test test1
List类型
<property name="values">
<!-- value-type:条目的类型 merge用于父子Bean情况是否合并list条目 -->
<list value-type="java.lang.String" merge="default">
<!-- value指定列表条目值 -->
<value>nihao1</value>
<value>nihao2</value>
</list>
</property>
1、可选的value-type属性,表示列表中条目的数据的类型,比如value-type="java.lang.String"
2.也可以采用泛型,Spring能根据泛型数据自动检测到List里条目的数据类型,比如java.util.List<String>,Spring能自动识别列表需要条目为String数据类型;
3.如果既没有指定value-type属性List也不是泛型的则默认就是String类型
Set类型、Collection类型都是与List的规则一样
只是将标签改为对应的标签
二维数组
<array>
<array>
<value>1</value>
</array>
<array>
<value>4</value>
</array>
</array>
方式一
<property name="values">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="22" value="22"></entry>
</map>
</property>
方式二
<map key-type="java.lang.String" value-type="java.lang.String">
<entry>
<!-- 注入key的值 -->
<key><value>333</value></key>
<!-- 注入值的值 -->
<value>2222</value>
</entry>
</map>
<props value-type="java.util.String" merge="default">
<prop key="22">22ss</prop>
<prop key="33">33ss</prop>
</props>
注意:props的键和值必须是String类型,否则无效
使用ref属性代替value属性或者<ref>标签代替value标签,与常量一样
其他引用方式:
(1)<ref local="bean1"/>用于引用通过<bean id="beanName"/>方式中通过id属性指定的Bean,它能利用XML解析器的验证功能在读取配置文件时来验证引用的Bean是否存在。因此如果在当前配置文件中有相互引用的Bean可以采用ref local 方式。如果配置错误能在开发调式时就发现错误。
(2)<ref parent="bean2"/>配置方式:用于引用父容器中的Bean,不会引用当前容器中的Bean,当然父容器中的Bean和当前容器的Bean是可以重名的,获取顺序是先从父容器找Bean,找不到在找当前容器中的Bean
内部Bean就是在<property>或 <constructor-arg>内通过bean标签定义的bean,该bean不管是否指定id或name,该bean都会有唯一的匿名标识符,而且不能指定别名,该内部bean对其他外部Bean不可见
用<bull/>标签注入null值
所谓对象图导航是指类似a.b.c这种点缀访问形式的访问或修改值。Spring支持对象图导航方式依赖注入。对象图导航依赖注入有一个限制就是比如a.b.c对象导航图注入中,a和b必须为非null值才能注入c,否则将抛出空指针异常支持数组、列表、字典、Properties数据类型的导航
对于set类型无法支持,因为无法导航
例如:
array[0] list[1]
不过需要注意数组越界
一、构造器注入
1.常量值
简写<constructor-arg index="0" value="常量"></constructor-arg>
全写<constructor-arg index="0" ><value>常量</value></constructor-arg>
2.引用
简写<constructor-arg index="0" ref="引用"></constructor-arg>
全写<constructor-arg index="0" ><ref bean="引用"></constructor-arg>
二、setter注入
1.常量值
简写:<property name="message" value="常量"/>
全写:<property name="message" ><value>常量</value></property>
2.引用
简写:<property name="message" ref="引用"/>
全写:<property name="message" ><ref bean="引用"></property>
3.数组
4.列表
5.集合
6.字典
简写:
<map>
<entry key="键常量" value="值常量">
<entry key-ref="键引用" value-ref="值引用">
</map>
全写:
<map>
<entry>
<key><value>键常量</value></key>
<value>值常量</value>
</entry>
<entry>
<key><ref bean="键引用"/></key>
<ref bean="值引用"/>
</entry>
</map>
三、使用p命名空间简化setter注入
首先指定:xmlns:p="http://www.springframework.org/schema/p"
<bean id="" class="" p:id="value"/>
常量setter注入方式,其等价于<property name="id" value="value"/>
<bean id="" class="" p:id-ref="bean1"/>:引用setter注入方式,等价于<property name="id" ref="bean1">
什么是循环依赖
循环依赖就是循环引用。就是两个或多个Bean相互之间的持有对方。
延迟初始化Bean
延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。
配置方式很简单,只需在Bean标签上指定lazy-init="true"即可延迟初始化bean
<bean id="bean" class="com.lizhenhua.Test" p:id="value" lazy-init="true"/>
Spring容器会在创建容器时提前初始化singleton作用域的Bean。singleton就是单例的意思即整个容器每个Bean只有一个实例。
Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。
延迟初始化的Bean通常会在第一次使用时被初始化,或者在被非延迟初始化Bean作为依赖对象注入时会随着初始化该Bean时被初始化。
容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的Bean定义上配置即可,比编程方式更简单,而且是无侵入代码的
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 配置bean id是bean的名字 -->
<bean id="helloWorld" class="com.lizhenhua.spring.beans.HelloWord">
<!-- name对应setter方法名,value是setter方法的默认值 -->
<property name="name" value="Spring"></property>
</bean>
<!-- 使用构造器注入属性值可以指定参数的位置和参数的类型!以区分重载的构造器 -->
<bean name="car2" class="com.lizhenhua.spring.beans.Car">
<!-- 如果字面值包含特殊字符可以使用![CDATA[]]包裹起来 -->
<!-- 属性值可以使用value子节点设值 -->
<constructor-arg value="奔驰" type="java.lang.String"></constructor-arg>
<constructor-arg type="java.lang.String">
<value><![CDATA[<ShangHai^>]]></value>
</constructor-arg>
<!-- 如果是数值类型的字面值可以使用value子节点设值 -->
<constructor-arg type="int">
<value>100</value>
</constructor-arg>
</bean>
<bean id="person" class="com.lizhenhua.spring.beans.Person">
<property name="name" value="Tom"></property>
<property name="age" value="24"></property>
<!-- ref 设置引用之间的关系 -->
<!-- 方式一 -->
<property name="car" ref="car2"></property>
<!-- 方式二 -->
<!-- <property name="car">
<ref bean="car2"/>
</property>
-->
<!-- 方式三:内部bean :内部bean不能够再外部被引用。-->
<!--
<property name="car">
<bean class="com.lizhenhua.spring.beans.Car">
<constructor-arg>
<null></null>
</constructor-arg>
<constructor-arg value="changsha"></constructor-arg>
<constructor-arg value="2000" type="int"></constructor-arg>
</bean>
</property>
-->
<!-- 为级联属性赋值,必须先初始化 -->
<property name="car.speend" value="222"></property>
</bean>
</beans>
depends-on是指Bean初始化及销毁时的顺序, 意思是:depends-on 属性中定义的依赖bean 要在定义该属性的Bean之前销毁。 但是实际是错误的,定义depends-on 属性的Bean会首先被销毁,然后才是depends-on指定的Bean被销毁。
测试depends-on
定义类
package com.lizhenhua.spring.beans;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ResourceBean {
private FileOutputStream fos;
private File file;
//初始化方法
public void init(){
System.out.println("ResouceBean:----初始化");
//加载资源,在此只是演示
System.out.println("ResouceBean:----加载资源,执行一些预操作");
try {
this.fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//销毁资源
public void destroy(){
System.out.println("ResouceBean:----销毁");
//释放资源
System.out.println("ResouceBean:----释放资源,执行一些清理操作");
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public FileOutputStream getFos(){
return fos;
}
public void setFile(File file) {
this.file = file;
}
}
package com.lizhenhua.spring.beans;
import java.io.IOException;
public class DependentBean {
ResourceBean resourceBean;
public void write(String ss) throws IOException{
System.out.println("DependentBean:-----写资源");
resourceBean.getFos().write(ss.getBytes());
}
//初始化方法
public void init() throws IOException{
System.out.println("DependentBean:-----初始化");
resourceBean.getFos().write("DependentBean:-----初始化".getBytes());
}
//销毁方法
public void destroy() throws IOException{
System.out.println("DependentBean:-----销毁");
resourceBean.getFos().write("DependentBean:-----销毁".getBytes());
}
public void setResourceBean(ResourceBean resourceBean) {
this.resourceBean = resourceBean;
}
}
配置Bean
<!-- init-method:"init" :指定初始化方法,在构造器注入和setter注入完毕后执行,destroy-method="destroy" 指定销毁方法,只有“singleton”作用域能销
毁,prototype作用域的一定不能,其他作用域不一定能; -->
<bean id="resouceBean" class="com.lizhenhua.spring.beans.ResourceBean" init-method="init" destroy-method="destroy">
<!-- Spring容器能够自动把字符串转换为java.io.File -->
<property name="file" value="D:/test.txt"></property>
</bean>
<!-- depends-on:resouceBean 因为dependentBean必须先初始化resouceBean类,才能执行dependentBean类中的方法 -->
<bean id="dependentBean" class="com.lizhenhua.spring.beans.DependentBean" init-method="init" destroy-method="destroy" depends-on="resouceBean">
<property name="resourceBean" ref="resouceBean"></property>
</bean>
测试类
public static void main(String[] args) throws IOException {
//1.创建Spring的IOC容器对象
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//注册销毁回调,否则我们定义的销毁方法不执行
ctx.registerShutdownHook();
DependentBean dependentBean =
ctx.getBean("dependentBean", DependentBean.class);
dependentBean.write("nihao");
}
运行结果
ResouceBean:----初始化
ResouceBean:----加载资源,执行一些预操作
DependentBean:-----初始化
DependentBean:-----写资源
DependentBean:-----销毁
ResouceBean:----销毁
ResouceBean:----释放资源,执行一些清理操作
自动装配是指由Spring来自动地注入依赖对象,无需人工参与。
目前Spring3.0 支持“no”、“byName”、“byType”“constructor”四种自动装配,默认是“no” 指不支持自动装配的。
其中Spring3.0已不推荐使用之前版本的autodetect自动装配,推荐使用java5支持的@Autowired注解方式代替;如果想支持autodetect自动装配,请将schema改为spring-beans-2.5.xsd或去掉
①减少构造器注入和setter注入配置,减少配置文件的长度。
一、default: 表示使用默认的自动装配,默认的自动装配需要在<beans>标签中使用default-autowire属性指定。
二、no:意思是不支持自动装配,必须明确指定依赖
三、byName:通过设置Bean定义属性 autowire="byName",意思是根据名字进行自动装配,只能用于setter注入。
例如:我们有方法setHelloApi 则byName 方式 Spring容器将查找名字为helloApi的Bean并注入,如果找不到指定的Bean,将什么也不注入。
如果一个bean有很多setter注入,通过byName方式能够减少很多property配置。
注意:在根据名字注入时,将把当前Bean自己排除在外。
四、byType:根据类型注入,用于setter注入,比如如果指定自动装配方式为byType,而setHelloApi方法需要注入HelloApi类型数据,则Spring容器将查找
HelloApi类型数据,如果找到一个则注入该Bean,如果找不到将什么也不注入,如果找到多个Bean将优先注入bean标签primary属性为true的Bean,否则抛出异常来表明有多
个Bean发现但不知道使用哪个。
注意:在根据类型注入时,将把当前Bean自己排除在外。
自动装配候选者中的首选本案:提升byType自动装配的优先权通过<bean primary="true">进行设置,因为byType找到的符合类型的不止一个
从自动装配候选者中去除: <bean autowire-candidate="false">进行设置
五、constructor:功能和byType功能一样 ,根据类型注入构造器参数,只是用于构造器注入方式。
六、autodetect:自动检测是使用constructor还是byType自动配置方式,已不推荐使用。如果有空构造器那么将采用byType自动装配方式,否则使用constructor自动装配方式
可以采用<beans>标签中通过default-autowire属性指定全局的自动装配方式。
不是所有类型都能自动装配:
不能自动装配的数据类型:Object、基本数据类型(Date、CharSequence、Number、URL、URI、Class、int)等
通过<beans>标签的default-autowire-candidates属性指定的匹配模式,不匹配的将不能作为自动装配的候选者,例如:指定*Service,*Dao,将只把匹配这些模式的Bean作为候选者,而不匹配的不会作为候选者。
通过bean标签的autowire-candidates属性设置为false,从而该bean将不会作为依赖注入的候选者
数组、集合、字典类型的根据自动装配和普通类型的自动装配是有区别的
数组类型、集合接口类型:将根据泛型获取匹配的所有候选者并注入到数组或集合中,如List<HelloApi> list将选择所有的HelloApi类型Bean并注入到list中,而对于集合的具体类型将只选择一个候选者,如ArrayList<HelloApi> list 将选择一个类型为ArrayList的Bean注入,而不是选择所有的HelloApi类型Bean注入
字典Map接口类型:同样根据泛型信息注入,键必须为String类型的Bean名字,值根据泛型信息获取。如:Map<String,HelloApi> map 将选择所有的HelloApi类型Bean并注入到map中,而对于具体字典类型如 HashMap<String,HelloApi> map 将只选择类型为HashMap的Bean注入,而不是选择所有的HellApi类型Bean注入
注意:
首先,自动装配确实减少了配置文件的量,其次,byType自动装配能在相应的Bean更改了字段类型时自动更新,即修改Bean类不需要修改配置,确实简单了
缺点:超找注入错误时麻烦。
配置注入的数据会覆盖自动装配注入的数据
采用自动装配方式时如果没有找到合适的Bean时什么也不做,这样的程序中莫名其妙的发生一些空指针异常,而且是在程序运行期间才能发现,有没有办法能提前发现这些错误呢?
用于检查bean定义的属性都注入数据了,不管是自动装配还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。
Spring3也不推荐配置方式依赖检查了,建议采用java5 @Required注解方式。
测试时XMLschema降低2.5版本的,和自动装配中autodetect配置方式的xsd一样。
dependency-check: none|simple|objects|all
一、none:默认方式,不检查
二、objects:检查除基本数据类型外的依赖对象。
三、simple:对基本类型进行依赖检查,包括数据类型,其他依赖不报错;
四、all:对所有类型进行依赖检查
依赖检查也可以通过<beans>标签中default-dependency-check属性来指定全局依赖配置检查
通过配置方式覆盖或拦截指定的方法,通常通过代理模式实现。
Spring提供两种方法注入:查找方法注入和方法替换注入
因为Spring是通过CGLIB动态代理方式实现方法注入,也就是通过动态修改类的字节码来实现的,本质就是生成需方法注入的类的子类方式实现。
在进行测试之前,我们需要确保将com.springsouce.cn.sf.cglib-2.2.0.jar放到lib里并添加到javabuildpath中的libararies中。否则报错。
一、查找方法注入:又称为Lookup方法注入,用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。
使用<lookup-method name="方法名" bean="bean名字"/>配置
其中name属性指定方法名,bean属性指定方法需要返回的Bean。
方法定义格式:访问级别必须是public 或 protected ,保证能被子类重载,可以是抽象方法,必须有返回值,必须是无参数方法,查找方法的类和被重载的方法必
须为非final:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
因为Singleton Bean在容器中只有一个实例,而prototype Bean是每次获取容器都返回一个全新的实例,所以如果singletonBean在使用prototypeBean情况时,那么
prototypeBean由于是singleton Bean 的一个字段属性,所以获取的这个prototype Bean 就和它所在的singleton Bean具有同样的生命周期,所以不是我们所期待的结果。
因此查找方法注入就是用于解决这个问题
首先定义我们需要的类,Printer类 是一个有状态的类,counter字段记录访问次数
package com.lizhenhua.spring.bean;
public class Printer {
private int counter = 0 ;
public void print(String type){
System.out.println(type + " printer:" + counter++) ;
}
}
HelloImpl类用于打印欢迎信息,其中包括setter注入和方法注入,此处特别需要注意的是该类是抽象的,充分说明了需要容器对其进行子类化处理,还定义了一个
抽象方法createPrototypePrinter用于创建prototype Bean ,createSingletonPrinter方法用于创建singleton Bean,此处注意方法会被Spring拦截,不会执行方法体代码。
package com.lizhenhua.spring.bean;
/**
* 打印欢迎信息
* @author Administrator
*HelloImpl类是抽象化的,如果需要创建,必须有实现类。这里的实现类是由SpirngIoc容器对齐进行子类化处理。
*/
public abstract class HelloImpl implements HelloI {
private Printer printer;
@Override
public void sayHello() {
printer.print("setter");
createPrototypePrinter().print("prototype");
}
/**
* 该方法用于创建prototype Bean ,在此处也就是与printer属性相对应。
* @return
*/
public abstract Printer createPrototypePrinter();
/**
* 创建单例printer
* @return
*/
public Printer createSingletonPrinter(){
System.out.println("该方法不会被执行,如果输出就错了");
return new Printer();
}
public void setPrinter(Printer printer) {
this.printer = printer;
}
}
配置
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 配置一个prototype的Printer Bean -->
<bean id="prototypePrinter" class="com.lizhenhua.spring.bean.Printer" scope="prototype" />
<!-- 配置一个singleton的Printer Bean 单例 -->
<bean id="singletonPrinter" class="com.lizhenhua.spring.bean.Printer" scope="singleton" />
<!-- 配置一个singleton的HelloImpl Bean -->
<bean id="hello1" class="com.lizhenhua.spring.bean.HelloImpl" scope="singleton">
<property name="printer" ref="prototypePrinter"></property>
<lookup-method name="createPrototypePrinter" bean="prototypePrinter"></lookup-method>
<lookup-method name="createSingletonPrinter" bean="singletonPrinter"></lookup-method>
</bean>
<!-- 配置prototype的HelloImpl Bean -->
<bean id="hello2" class="com.lizhenhua.spring.bean.HelloImpl" scope="prototype">
<property name="printer" ref="prototypePrinter"></property>
<lookup-method name="createPrototypePrinter" bean="prototypePrinter"></lookup-method>
<lookup-method name="createSingletonPrinter" bean="singletonPrinter"></lookup-method>
</bean>
</beans>
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("singleton sayHello===");
HelloI helloI = context.getBean("hello1",HelloI.class);
helloI.sayHello();
helloI = context.getBean("hello1",HelloI.class);
helloI.sayHello();
System.out.println("prototype sayHello===");
HelloI helloI2 = context.getBean("hello2",HelloI.class);
helloI2.sayHello();
helloI2 = context.getBean("hello2",HelloI.class);
helloI2.sayHello();
}
}
singleton sayHello===
setter printer:0
prototype printer:0
setter printer:1
prototype printer:0
prototype sayHello===
setter printer:0
prototype printer:0
setter printer:0
prototype printer:0
二、替换方法注入: 也叫MethodReplacer注入,和查找注入方法不一样的是,他主要用来替换方法体。
scope:singleton|prototype|request|session
singleton:单例
prototype:原型。每次向Spring容器请求获取Bean都返回一个全新的Bean。相对于singleton来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean
Web应用中的作用域:
request、session、globalSession
request作用域:表示每个请求需要容器创建一个全新Bean
session作用域:表示每个会话需要容器创建一个全新Bean
globalSession:类似于session作用域,只是其用于portlet环境的web应用。如果非portlet环境将设为session作用域
自定义作用域
标签:
原文地址:http://blog.csdn.net/li286487166/article/details/51225764