在Spring-IoC简介中简单介绍了什么是IoC以及一些相关的概念,这里将详细介绍如何通过IoC容器提供的方法进行注入和如何进行注入配置。
依赖注入
IoC提供的依赖注入方法主要有属性注入、构造函数注入和工厂方法注入,这里依次介绍如何通过各个方法注入Bean
。
属性注入
属性注入要求Bean
提供一个默认的构造器,并为需要注入的属性提供对应的setter
方法。Spring会检查Bean
中是否有setter
方法,但不会检查是否有对应的属性成员。
Bean
的代码如下。
public class Car{
private int maxSpeed;
private String brand;
//如果要通过属性注入则必须实现setter方法
public void setXX(<T> XX){
this.XX=XX;
}
}
我们这里通过XML文件对Bean
进行设置,后面会详细介绍所有的配置方式和配置内容。
<bean id="car" class="Car">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>红旗CA72</value></property>
</bean>
这样设置完成后,当Spring启动后,就会自动生成id
为car
,class
为Car
,属性分别为200
和红旗CA72
的Bean
。
对于属性的命名,要求变量的前两个字母要么全部大写,要么全部小写,iCCard
、iDCode
是非法的。如果取非法的变量名,在试图启动Spring时,将会失败。因此命名的建议是像QQ、MSN、ID等正常以大写字母出现的专业术语,在Java中一律使用小写形式。
构造函数注入
当想要通过构造函数注入时,需要在Bean
中实现相应的构造函数,并以想要注入的属性为参数。
Bean
的代码如下。
public class Car{
private int maxSpeed;
private String brand;
public Car(int maxSpeed,String brand){...};
}
然后我们在XML文件中进行配置。
<bean id="car" class="Car">
<constructor-arg type="java.lang.String">
<value>红旗</value>
</constructor-arg>
<constructor-arg type="double">
<value>200</value>
</constructor-arg>
</bean>
在通过构造器注入时,配置文件中arg
的顺序与构造器中的参数顺序无关,当只有一个构造器的情况下上述配置文件才会生效。因此建议使用索引匹配入参,即<constructor-arg index="0" value="红旗">
。
当两个构造器仅有int
和double
的区别(如下),则此时还需要在配置文件中加上参数类型以作区分,即<constructor-arg index="0" type="int" value="200"/>
。
public class Car{
private int maxSpeed;
private double averageSpeed;
private String brand;
public Car(int maxSpeed,String brand){...};
public Car(double averageSpeed,String brand){...};
}
如果构造函数的参数的类型是可以辨别的(非基础数据类型且入参类型各异),则可以不提供类型和索引的信息。
构造器注入可能会出现循环依赖的问题,即A
类需要注入B
类,B
类需要注入A
类,在这种情况下,使用属性注入就可以解决了。
工厂方法注入
在使用框架的过程中很少会使用到工厂方法注入,以下仅举2个例子。
- 非静态工厂类
public class CarFactory{
public Car createHongQiCar(){}
}
<bean id="carFactory" class="CarFactory"/>
<bean id="car" factory-bean="carFactory" factory-method="createHongQiCar"/>
由于工厂类不是静态的,因此需要先定义工厂类的bean。
- 静态工厂类
public class CarFactory{
public static Car createHongQiCar(){}
}
<bean id="car" class="CarFactory" factory-method="createCar"/>
Bean的配置方式
配置Bean
可以通过XML、注解、Java类和Groovy DSL这四种方式,下面来详细介绍各个配置方法的细节。
基于XML的配置
对于基于XML的配置,Spring2.0以后采用Schema格式,使得配置文件更具扩展性,但文件头声明会复杂一些,下面是一个例子。
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
>
<!-- 默认命名空间的配置 -->
<bean id="foo" class="com.smart.Foo"/>
<!-- aop命名空间的配置 -->
<aop:config>
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
</aop:config>
</beans>
在上面的代码中定义了3个命名空间:
-
xmlns="http://www.springframework.org/schema/beans"
,默认命名空间:没有空间名,用于Spring Bean的定义。 -
xsi
标准命名空间:这个命名空间用于指定自定义命名空间的Schema样式文件,是W3C定义的标准命名空间。 -
xmlns:aop="http://www.springframework.org/schema/aop"
,aop命名空间:这个命名空间是Spring配置aop的命名空间,即一种自定义的命名空间。
命名空间的定义分为两个步骤:
- 第一步指定命名空间的名称,如
xmlns:aop="http://www.springframework.org/schema/aop"
。aop
为命名空间的别名,而"http://www.springframework.org/schema/aop"
是命名空间的全限定名,习惯上用文档发布机构的官方网站和相关网站目录作为全限定名。如果命名空间的别名为空,则表示该命名空间为文档默认的命名空间。 - 第二步指定命名空间的Schema文档格式文件的位置,用空格或回车换行进行分隔。定义的语法如下
<命名空间1> <命名空间1Schema文件> <命名空间2> <命名空间2Schema文件>
(注意之间的分隔符可以为空格或换行符)。Schema地址有2个用途:一是XML解析器可以获取Schema文件并对文档进行格式合法性验证;二是IDE可以引用Schema文件对文档编辑提供自动补全功能。
以上就是一个XML配置文件的结构介绍,接下来将介绍如何对Bean
进行定制。
-
字面值
对于基本类型或其封装类,我们在XML中可以直接进行设置。对于字符串来说,如果其中包含了&、<、>、"、'这5种特殊字符,可以通过下面的![CDATA[]]
进行包装,使得XML解析器将其中的内容当成纯文本;或者可以通过转义序列来表示&、<、>、"、&apos。<bean id="car" class="Car"> <property name="maxSpeed"> <value>200</value> </property> <property name="brand"> <value><![CDATA[红旗&CA72]]></value> </property> </bean>
-
引用其他bean
可以通过以下3种标签对其他的bean进行引用。- bean:可以引用同一容器或父容器总的Bean,这是最常见的形式。
- local:只引用同一配置文件中的Bean。
- parent:只引用父容器中的Bean。
<!--通过bean--> <bean id="car" class="Car"/> <bean id="boss" class="Boss"> <property name="car"> <ref bean="car"/> </property> </bean> <!--通过local与通过bean的方式类似--> <!--通过parent--> <!--在父容器中定义car--> <bean id="car" class="Car"/> <!--在子容器中定义另一个car--> <bean id="car" class="Car"/> <bean id="boss" class="Boss"> <ref parent="car"><!--将引用父容器中的car--> </bean>
-
内部bean
我们也可以通过隐藏类那样定义bean。<bean id="boss" class="Boss"> <property name="car"> <bean class="Car"> <property name="brand" value="红旗"/> </bean> </property> </bean>
-
null值
如果想要注入null值,需要显示的将值设为<null/>
,即<property name="brand"><value><null/></value></property>
。 -
级联属性
如果想要通过这种方式直接对<bean id="boss" class="Boss"> <property name="car.brand" value="红旗"/> </bean>
boss
中的car
的属性进行设置,则需要在Boss
类中声明一个初始化对象并实现getter()
方法。public class Boss{ private Car car=new Car(); public Car getCar(){return car;} }
-
集合类
- List
如果List的属性类型可以通过字符串字面值进行配置,那么就可以使用这种方式,如<bean id="boss" class="Boss"> <property name="favorites"> <list> <value>看报</value> <value>滑雪</value> </list> </property> </bean>
String[]
、int[]
等。
此外,List还可以通过<ref>注入容器中其他的Bean。- Set
<bean id="boss" class="Boss"> <property name="favorites"> <set> <value>看报</value> <value>滑雪</value> </set> </property> </bean>
- Map
<bean id="boss" class="Boss"> <property name="jobs"> <map> <entry> <key><value>AM</value><key> <value>会见客户</value> </entry> <entry> <key><value>PM</value><key> <value>开会</value> </entry> <!--如果键值对都是对象--> <entry> <key><ref bean="keyBean"/><key> <ref bean="valueBean"/> </entry> </map> </property> </bean>
-
Properties
Properties属性可以看成键值都是字符串的Map类型。<bean id="boss" class="Boss"> <property name="jobs"> <props> <prop key="mail">123@gmail.com</prop> </props> </property> </bean>
-
强类型集合
Spring配置强类型和非强类型集合相同,会将值自动转换为目标类型。public class Car{ private Map<Integer,String> passengers=new HashMap<Integer,String>(); }
-
集合合并
<bean id="boss1" class="Boss"> <property name="favorites"> <set> <value>看报</value> </set> </property> </bean> <bean id="boss2" class="Boss" parent="boss1"> <property name="favorites"> <set merge="true"> <!--合并父类中的同名set--> <value>看报</value> </set> </property> </bean>
-
配置集合类型的Bean
如果想要配置集合类型的Bean,而不是属性类型的集合,可以通过在Spring配置文件中引入util命名空间的声明,然后进行配置。<beans xmlns:util="http://www.springframework.org/schema/util"> xsi:shcemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd" <util:list id="favorite" list-class="java.util.LinkedList"> <value>喝茶</value> <value>看报</value> </util:list> </beans>
简化XML配置方式
- 字面值属性
类型 | 简化前 | 简化后 |
---|---|---|
字面值属性 | <property name="maxSpeed"><value>200</value></property> | <property name="maxSpeed" value="200"/> |
构造函数参数 | <constructor-arg index="0"><value>200</value></constructor-arg> | <constructor-arg index="0" value="200"/> |
集合元素 | <map> <entry> <key><value>AM</value></key> <value>见客户</value> </entry> </map> |
<map> <entry key="AM" value="见客户"/> </map> |
- 引用对象属性
类型 | 简化前 | 简化后 |
---|---|---|
字面属性值 | <property name="car"> <ref-bean="car"/></property> | <property name="car" ref="car"> |
构造函数 | <constructor-arg> <ref-bean="car"></property> | <constroctor-arg ref="car"/> |
集合元素 | <entry> <key><ref bean="keyBean"/></key> <ref bean="valueBean"> </entry> |
<entry key-ref="keyBean" value-ref="valueBean"/> |
<ref>的简化形式对应于<ref bean="xxx">,而<ref local>和<ref parent>没有对应的简化形式。
- 使用p命名空间
<beans xmlns:p="http://www.springframework.org/schema/p"><!--声明p命名空间-->
<bean id="car" class="Car"
p:brand="红旗"
p:maxSpeed="200"/>
<bean id="boss" class="Boss"
p:car-ref="car"/>
</beans>
基于注解的配置
采用基于注解的配置文件,则Bean
定义信息通过在Bean
实现类上标注注解实现。
//这里定义了一个Dao的Bean
/*
与@Component功能相似的还有:
@Repository:用于对DAO实现类进行标注
@Service:用于对Service实现类进行标注
@Controller:用于对Controller实现类进行标注
*/
@Component("carDao")
public class CarDao{
//..
}
对于通过注解定义的bean需要在Spring配置文件中进行额外设置。
<beans
xmlns:context="http://www.springframework.org/schema/context"
xsi:"http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd"
>
<!--扫描类包以应用注解的bean,可以通过resource-pattern进行过滤 -->
<!-- 其余过滤的方法还有include-filter和exclude-filter -->
<context:component-scan base-package="package" resource-pattern="./*.class">
</beans>
对Bean
完成配置后,还需要通过注解进行自动注入
@Service
public class LoginService{
//注入LogDao的bean
//对required=false设置,当Spring启动时如果未找到对应的bean则不会报错
//通过Qualifier可以指定对应bean的名称
@Autowired(required=false)
@Qualifier("logDao")
private LogDao logDao;
//对类方法进行标注
@Autowired
@Qualifier("logDao")
public void setLogDao(LogDao logDao){}
//或者通过这种形式
public void setLogDao(@Qualifier("logDao")LogDao logDao){}
//如果对类中集合类的变量或方法入参进行标注,那么Spring会将容器中类型匹配的所有Bean都注入
@Autowired
private List<Plugin> plugins;
//Spring如果发现变量是一个list和一个map,会将容器中匹配集合元素类型的所有bean都注入
//这里将会把plugins的bean注入map集合,key是bean的名字,value是所有实现了plugin的bean。
@Autowired
private Map<String,Plugin> pluginMaps;
//如果Plugin有多个实现类,那么可以在不同的实现类前用@Order(value=1)对注入顺序进行设置,越小越先加载
}
基于Java类的配置、基于Groovy DSL的配置
一般来说,使用XML和标注的方式就能解决几乎所有的任务。建议使用XML配置DataSource、SessionFactory等资源bean,在XML中利用aop、context命名空间进行相关主题的配置,其余所有项目中开发的Bean都通过基于注解配置的方式进行配置。
Bean基本配置
Bean的命名
Bean
的id属性命名与Java变量的命名要求相同,而name属性命名没有任何字符上的限制。可以通过不设置id属性命名来实例化匿名Bean
。
<bean class="Car"/>
<bean class="Car"/>
<bean class="Car"/>
这样就实例化了3个匿名Bean
,第一个Bean
通过getBean("Car")
获得,第二个通过getBean("Car#1")
获得。
依赖注入
Spring支持属性注入和构造函数注入,除此之外还支持工厂法注入方式。