Spring4-快速入门之在IOC容器中装配Bean
概述
Bean配置信息:
即Bean的元数据信息,包括Bean的实现类,属性信息,依赖关系和行为配置(生命周期及生命周期过程中的回调函数)
Spring容器内部协作接口
首先,容器会根据Bean的配置信息,在容器内部建立Bean定义注册表(一个个BeanDefinition对象),然后根据注册表实例化Bean,并建立Bean和Bean之间的依赖关系,最后将这些准备就绪的Bean放入缓存池中,供外部的应用程序使用
Bean基本配置
基于XML文件的配置方式如下
<bean id="foo1" class="com.spring4.chpter5.Foo">bean>
除了使用id为Bean命名,还可以使用name为Bean命名,name属性支持多个命名,可以使用空格,分号,或逗号分开
<bean name="foo1 foo2 foo3" class="com.spring4.chpter5.Foo">bean>
依赖注入
Spring支持3种方式的注入,分别是属性注入,构造函数注入和工厂方法注入
属性注入
通过属性的setter()方法进行注入,要求Bean必须提供一个默认的构造函数,并为需要注入的属性提供setter()方法
public class Car {private String brand;private String color;private String maxSpeed;public void setBrand(String brand) {this.brand = brand;}public void setColor(String color) {this.color = color;}public void setMaxSpeed(String maxSpeed) {this.maxSpeed = maxSpeed;}public void introduce() {System.out.println("brand:" + this.brand + ";color:" + this.color + ";maxSpeed:" + this.maxSpeed);}
}"car" class="com.spring4.chpter5.Car">"brand" value="影刺HT+"> "color" value="黑色"> "maxSpeed" value="300">
注意,Spring只会检查Bean中是否有对应的setter方法,并不关心是否有该属性,例如上面Car类中有setBrand()方法,但却不一定要有brand属性
构造函数注入
使用构造函数注入,要求Bean必须提供带参数的构造函数。例如上面的Car类,提供一个带参数的构造函数。
public Car(String brand,String color,String maxSpeed) {this.brand = brand;this.color = color;this.maxSpeed = maxSpeed;}
bean.xml配置如下:
<constructor-arg name="color" value="红色">constructor-arg><constructor-arg name="brand" value="游侠9">constructor-arg><constructor-arg name="maxSpeed" value="350">constructor-arg>
构造函数注入还提供按索引和按参数类型匹配入参的功能,可以使用constructor-arg标签下的type属性和index属性配置。
注意:循环依赖问题
考虑下面的例子:
Car类的构造函数
public Car(String brand,String color,String maxSpeed,Boss boss) {this.brand = brand;this.color = color;this.maxSpeed = maxSpeed;this.boss = boss;}
Boss类的构造函数
public Boss(String name,Car car) {this.name = name;this.car = car;}
bean.xml配置:
<bean id="car" class="com.spring4.chpter5.Car"><constructor-arg name="color" value="红色">constructor-arg><constructor-arg name="brand" value="游侠9">constructor-arg><constructor-arg name="maxSpeed" value="350">constructor-arg><constructor-arg name="boss" ref="boss">constructor-arg
bean><bean id="boss" class="com.spring4.chpter5.Boss"><constructor-arg name="name" value="SSS">constructor-arg><constructor-arg name="car" ref="car">constructor-arg> bean>
由于在使用构造函数注入时,Bean的入参引用的对象必须已经准备就绪,而这里的Car类和Boss类都使用了构造函数注入,而且都互相引用了对方,因此会发生循环依赖问题,两者都会等待对方实例化,就会出现类似线程死锁的问题,SpringIoC容器将不能启动成功。
工厂方法注入
非静态工厂方法:
public Car getCar() {Car car = new Car();return car;}
<bean id="carFactory" class="com.spring4.chpter5.CarFactory">bean><bean id="car2" factory-bean="carFactory" factory-method="getCar">bean>
静态工厂方法:
public static Car getCar2() {Car car = new Car();return car;}
<bean id="car3" class="com.spring4.chpter5.CarFactory" factory-method="getCar2">bean>
注入方式的考量
选择构造函数注入的理由:
1.构造函数注入可以保证一些重要的属性在Bean实例化好之前就设置好,避免因为一些重要属性没有提供而导致一个无用Bean实例的情况
2.不需要为每个属性都设置setter方法,减少方法的数量
3.可以更好的封装类变量,避免外部错误的调用
不选择构造函数注入的理由:
1.如果类的属性过多,bean标签下的constructor-arg标签也会增多,可读性差
2.灵活性不强,如果在有些属性是可选的情况下,通过构造函数注入需要传入null作为默认值
3.不利于类的继承和扩展,因为子类也需要引用父类的构造函数
4.会造成循环依赖问题
部分注入参数介绍
1.当注入的属性包含了XML的特殊字符时,可以使用
<property name="brand"><value>value>property>
2.引用其他Bean
通过ref标签引用其他的Bean,ref标签包含两个属性,bean和parent,bean属性表示引用当前容器中的Bean,如果没有,则引用父容器中的Bean;parent属性表示引用父容器中的Bean。
父容器bean.xml:
<bean id="car" class="com.spring4.chpter5.Car"><property name="brand"><value>value>property><property name="color" value="黑色">property><property name="maxSpeed" value="300">property>bean>
子容器bean2.xml:
<bean id="car" class="com.spring4.chpter5.Car"><property name="brand"><value>New棉花糖Black>value>property><property name="color" value="黑色">property><property name="maxSpeed" value="300">property>bean><bean id="boss" class="com.spring4.chpter5.Boss"><constructor-arg name="name" value="SSS">constructor-arg><constructor-arg name="car"> <ref parent="car"/> constructor-arg> bean>
3.内部Bean
内部Bean和Java的匿名内部类相似,没有名字,也不能被其他Bean引用,只能在声明处为外部Bean提供实例注入
<bean id="boss" class="com.spring4.chpter5.Boss"><bean class="com.spring4.chpter5.Car"><property name="brand"><value>New棉花糖>value>property><property name="color" value="绿色">property><property name="maxSpeed" value="280">property>bean>constructor-arg> bean>
4.null值
通过标签为属性注入null值
<bean id="boss" class="com.spring4.chpter5.Boss"><bean class="com.spring4.chpter5.Car"><property name="brand"><value>New棉花糖>value>property><property name="color" value="绿色">property><property name="maxSpeed"><null>null>property>bean>constructor-arg> bean>
5.级联属性
以圆点(.)的方式定义级联属性
<property name="car.brand" value="黑色甲虫9">property>
6.定义集合属性
例如:为Boss类添加一个List类型的favorites属性
public class Boss {private List favorites = new ArrayList();...
}
Spring配置如下:
<property name="favorites"><list><value>吃饭value><value>睡觉value><value>打豆豆value>list>
property>
注意:不仅仅是List类型的属性,数组类型(int[],String[])的属性也可以通过这种方式进行注入
Map类型和Set类型的注入方式与List类似
<property name="favorites"><set><value>吃饭value><value>睡觉value><value>打豆豆value>set>property><property name="jobs"><map><entry key="AM" value="MA">entry><entry key="WW" value="CC">entry>map>property>
除此之外,List,Set,Map也可以使用Bean作为注入的对象,可通过注入
<list><ref bean="beanName" />
list><set><ref bean="beanName"/>
set><map><entry key-ref="beanName" value-ref="beanName">entry>
map>
Properties类型的属性注入
Properties与Map的区别在于,Properties只支持key和value为字符串。
<property name="props"><props><prop key="key1">value1prop><prop key="key2">value2prop>props>
property>
通过util命名空间配置集合类型的Bean
如果希望配置一个集合类型的Bean,而不是一个集合类型的属性,可以使用util命名空间进行配置,需要在Spring配置文件中引入util命名空间的声明。
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
配置一个List类型的Bean,可以通过list-class属性显示的指定List的实现类
<util:list id="list1" list-class="java.util.LinkedList"><value>吃饭value><value>睡觉value><value>打豆豆value>
util:list>
配置一个Set类型的Bean,可以通过set-class属性显示的指定Set的实现类
<util:set id="set1" set-class="java.util.HashSet"><value>吃饭value><value>睡觉value><value>打豆豆value>
util:set>
配置一个Map类型的Bean,可以通过map-class属性显示的指定Map的实现类
<util:map id="map1" map-class="java.util.HashMap"><entry key="AM" value="MA">entry><entry key="WW" value="CC">entry>
util:map>
方法注入
1.lookup方法注入
声明一个MagicBoss接口,并声明一个getCar()方法,现在通过lookup方法注入,使每次调用getCar()方法都返回一个新的car Bean
public interface MagicBoss {public Car getCar();
}
<bean id="car" class="com.spring4.chpter5.Car" scope="prototype"><property name="brand"><value>New棉花糖Black>value>property><property name="color" value="黑色">property><property name="maxSpeed" value="300">property>
bean>
<bean id="magicBoss" class="com.mjf.spring4.chpter5.MagicBoss"><lookup-method name="getCar" bean="car"/>
bean>
2.方法替换
Bean实现MethodReplacer接口后,可以使用该接口的方法去替换目标Bean的方法
例如:
Boss1的getCar()方法返回Car1
public class Boss1 {public Car getCar() {Car car = new Car();car.setBrand("Car1");return car;}
}
Boss2实现了org.springframework.beans.factory.support.MethodReplacer接口,该接口返回Car2
public class Boss2 implements MethodReplacer{@Overridepublic Object reimplement(Object obj, Method method, Object[] args) throws Throwable {Car car = new Car();car.setBrand("Car2");return car;}
}
Spring配置如下
<bean id="boss1" class="com.mjf.spring4.chpter5.Boss1"><replaced-method name="getCar" replacer="boss2">replaced-method>
bean>
<bean id="boss2" class="com.mjf.spring4.chpter5.Boss2">bean>
Bean之间的继承和依赖
1.继承
通过继承,子bean会继承父bean的所有配置信息
<bean id="car" class="com.spring4.chpter5.Car" scope="prototype"><property name="brand"><value>New棉花糖Black>value>property><property name="color" value="黑色">property><property name="maxSpeed" value="300">property>bean><bean id="car2" class="com.spring4.chpter5.Car" scope="prototype" parent="car"><property name="color" value="红色">property>bean>
2.依赖
通过depends-on属性,显式的指定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好
<bean id="boss3" class="com.spring4.chpter5.Boss" depends-on="mycar">bean><bean id="mycar" class="com.spring4.chpter5.Car">bean>
整合多个配置文件
通过import标签将多个xml配置文件整合到一起
<import resource="classpath*:com/spring4/chpter5/bean.xml"/>
Bean的作用域
| 类型 | 说明 |
|---|---|
| singelton | Bean以单例的方式存在 |
| prototype | 与singelton相反,每次都会返回新的 Bean |
| request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
| session | 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean,该作用域仅适用于WebApplicationContext环境 |
| globalSession | 同一个全局Session共享一个Bean,一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
注意:当非web相关作用域的Bean引用web相关作用域的Bean时,需要与Spring的动态代理技术一起使用,如下:
<bean id="car5" class="com.spring4.chpter5.Car" scope="request"><aop:scoped-proxy/>bean><bean id="boss5" class="com.spring4.chpter5.Boss" scope="singleton"><property name="car" ref="car5">property>bean>
基于注解的配置
1.使用注解定义的Bean
将class定义为Bean的注解类别:
| 注解名 | 说明 |
|---|---|
| @Component | 将一个class定义为Bean |
| @Repository | 用于对Dao实现类进行标注 |
| @Service | 用于对Service实现类进行标注 |
| @Controller | 用于对Controller实现类进行标注 |
2.扫描注解定义的Bean
Spring提供了一个Context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式,如下:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.spring4.chpter5">context:component-scan>
3.自动装配Bean
使用@Autowired进行自动注入(默认使用byType的方式)
@Autowired
private Car car;
将@Autowired的required属性设置为false,即使Spring找不到匹配的Bean,也不会抛出异常
可以使用@Qualifier注解指定注入的Bean的名称
@Autowired
@Qualifier("car")//注入名为car的Bean
private Car car;
@Autowired和@Qualifier除了能注解属性外,还可以注解方法,如下:
@Autowired
public Boss(String name,@Qualifier("car") Car car) {System.out.println("Boss Constructor");this.name = name;this.car = car;
}
使用@Lazy注解指定延迟依赖注入,注意@Lazy注解必须同时标注在属性和目标Bean上,否则无效。
4.Bean作用范围和生命过程方法
使用@Scope注解指定Bean的作用范围,例如:@Scope(“prototype”)。
使用@PostConstruct和@PreDestory注解指定Bean的初始化及容器销毁前执行的方法,可以标注多个方法。
基于Java类的配置
1.使用Java类提供Bean定义信息
使用@Configuration将一个POJO标注定义为Bean的配置类(我的理解是,同xml配置文件类似,也可以作为bean标签使用)
使用@Bean注解标注方法,提供Bean的定义信息(和bean标签类似)
例如:
@Configuration
public class AppConf {@Beanpublic Boss1 getBoss1() {Boss1 boss1 = new Boss1();return boss1;}@Beanpublic Car getCar() {Car car = new Car();return car;}
}
上面的代码等同于
<bean id="AppConf" class="com.spring4.chpter5.AppConf">bean>
<bean id="getBoss1" class="com.spring4.chpter5.Boss1">bean>
<bean id="getCar" class="com.spring4.chpter5.Car">bean>
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
