Bean 的配置中介绍的是 Bean 声明问题, 在哪声明怎么声明的问题. Bean 的注入是怎么实例化, 怎么注入的问题. Bean 注入的方式有两种, 一种是在 XML 中配置, 另一种则是使用注解的方式注入.
一, XML 方式注入
XML 方式注入一般有三种方式: 属性注入, 构造函数注入和工厂方法注入.
一, 属性注入
在传统的对象实例化时可以通过 new class(), 然后通过 setXXX()方法设置对象的属性值或依赖对象, 属性注入也是采用这种方式, 只是 Spring 框架会在内部完成这些操作, 它会先调用 Bean 的默认构造函数实例化 Bean 对象, 然后通过反射的方式调用 Setter 方法注入属性值. 它会使用默认的构造函数(无参数构造函数), 只需为注入的属性设置 set 方法, 可选择性和灵活性比较高, 所以也是比较常用的一种注入方式. 这里示例还是在 IOC 章节使用人和空气的基础上稍作修改来演示. IAir 接口和 CleanAir,DirtyAir 类不变, 这里就不贴了.
1. 新建 XMLInstance 类
- package com.demo.model;
- public class XMLInstance {
- private String name;
- public void setName(String name) {
- this.name = name;
- }
- private IAir air;
- public void setAir(IAir air) {
- this.air = air;
- }
- public void Breath()
- {
- System.out.println("Name:"+this.name+";Air:"+this.air.toString());
- }
- public XMLInstance(String name, IAir air) {
- super();
- this.name = name;
- this.air = air;
- }
- public XMLInstance() {
- }
- public void DestoryMethod()
- {
- System.out.println("DestoryMethod");
- }
- public void InitMethod()
- {
- System.out.println("InitMethod");
- }
- }
- View Code
在 XMLInstance 类中并未声明构造函数, 对于 air 属性只设置了 set 方法, get 方法也没设置.
- <bean id="CleanAir" class="com.demo.model.CleanAir">
- <qualifier value="cleanair"/>
- </bean>
- <bean id="xmlinstance" class="com.demo.model.XMLInstance">
- <property name="air" ref="CleanAir"></property>
- <property name="name" value="abc"></property>
- </bean>
Xml 中使用 property 类配置属性, name 是属性名, value 用来设置基本数据类型的属性值. Spring 配置文件中 bean 之间可以相互引用, 引用时可以用 < ref > 标签配置 bean 的 id 属性使用.<ref > 可以用在 < property > 属性中, 也可以用在 < construct-arg > 构造函数的参数值, 还可以用在其他地方, 通过引用能减少 bean 的声明.
二, 构造函数注入
在属性注入时先使用默认的构造函数 (无参数构造函数) 实例化, 然后通过 set 方法注入属性, 在传统实例化对象时可以自定义构造函数进行实例化, 构造函数注入就是通过自定义构造函数来进行对象的实例化. 这里在 XMLInstance 类的基础上增加了一个构造函数, 第一个参数是 String 类型的 name, 第二个参数是 IAir 类型的 air.
- public XMLInstance(String name, IAir air) {
- super();
- this.name = name;
- this.air = air;
- }
Xml 中使用使用 < construect-arg > 来设置构造函数的参数, index 属性设置参数的顺序, 参数顺序应该与构造函数的一致, ref 设置引用 bean 的 id,value 设置构造函数参数的值.
<bean id="xmlcontructinstance" class="com.demo.model.XMLInstance">
<constructor-arg index="1" ref="CleanAir"></constructor-arg>
<constructor-arg index="0" value="abc"></constructor-arg>
</bean>
三, 工厂方法注入
工厂方法注入参考的是工厂设计模式, 通过在工厂类中实现对象的实例化. 工厂类负责创建一个或多个目标类实例, 工厂类方法一般以接口或抽象类变量的形式返回目标类实例, 工厂类对外屏蔽了目标类的实例化步骤, 调用者甚至不用知道具体的目标类是什么. 工厂方法也分静态工厂方法和非静态工厂方法, 静态工厂方式不用实例化工厂类, 直接通过类名调用, 非静态工厂方法需要先实例化工厂类, 然后通过工厂类对象调用获取对象. 这里创建了一个工厂类 XMLFactory, 在类中定义了一个静态方法, 和一个实例方法用来实例化 bean 对象.
- package com.demo.model;
- public class XMLFactory {
- public XMLInstance CreateInstance()
- {
- return new XMLInstance("instance",new CleanAir());
- }
- public static XMLInstance CreateStaticInstance()
- {
- return new XMLInstance("static instance",new CleanAir());
- }
- }
1. 静态工厂方法
只需设置工厂方法对应的类, 以及对应的工厂方法.
<bean id="xmlfactorystaticinstance" class="com.demo.model.XMLFactory" factory-method="CreateStaticInstance"></bean>
2. 实例工厂方法
需要先实例化工厂类, 再通过工厂类对象调用实例方法获取 bean 对象.
<bean id="xmlfactoryinstance" factory-bean="xmlfactory" factory-method="CreateInstance" destroy-method="DestoryMethod" init-method="InitMethod"></bean>
四, 常见数据类型注入
(1)List 属性注入
使用 < list > 配置 java.util.List 类型的属性. List 属性中元素可以是任何数据类型的值, 如果是 Java 对象可以使用 ref 指定, 或使用 < bean > 定义新实例. 如果是基础数据类型可直接用字符串.<list > 中的元素会按配置的先后顺序排序.
<property name="lists">
<list>
<value>1</value>
<ref bean="CleanAir" />
<bean class="com.demo.model.CleanAir"/>
</list>
</property>
(2)Set 属性注入
使用 < set > 配置 java.util.Set 类型的属性. Set 属性中元素可以是任何数据类型的值, 如果是 Java 对象可以使用 ref 指定, 或使用 < bean > 定义新实例. 如果是基础数据类型可直接用字符串.<set > 中的元素没有先后顺序.
<property name="sets">
<set>
<value>1</value>
<ref bean="CleanAir" />
<bean class="com.demo.model.CleanAir"/>
</set>
</property>
(3)Map 属性注入
使用 < map > 配置 java.util.Map 类型的属性.<entry > 配置 Map 里的元素, Key 指定索引, value 指定值. 如果是 Java 对象可以使用 ref 指定, 或使用 < bean > 定义新实例.
<property name="maps">
<map>
<entry key="key1" value="1"></entry>
- <entry key="key2" value-ref="CleanAir"></entry>
- <entry key="key3">
- <bean class="com.demo.model.CleanAir"/>
- </entry>
- </map>
- </property>
(4)Properties 属性注入
使用 < props > 配置 java.util.Properties 类型的属性.<props > 配置一个 Properties 对象,<prop > 配置一条属性, 属性 Key 配置索引.
<property name="pros">
<props>
<prop key="prokey1">prokeyA</prop>
<prop key="prokey2">prokeyB</prop>
</props>
</property>
(5)自定义属性编辑器
对于有一些属性是没法注入的, 此时就需要自定义, 比如日期类型. 可以通过继承 PropertyEditorSupport 的类, 重写 setAsText 方法来实现注入. 这里定义了 CustomerProperty 继承 PropertyEditorSupport, 重写了 setAsText 方法, 并将该 bean 配置到 xml 中.
- package com.demo.model;
- import java.beans.PropertyEditorSupport;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- public class CustomerProperty extends PropertyEditorSupport {
- private String format="yyyy-MM-dd";
- public String getFormat() {
- return format;
- }
- public void setFormat(String format) {
- this.format = format;
- }
- @Override
- public void setAsText(String text) throws IllegalArgumentException {
- SimpleDateFormat sdf=new SimpleDateFormat(format);
- //super.setAsText(text);
- try {
- // 转换对象, 能过 setValue 方法重新赋值
- this.setValue(sdf.parse(text));
- } catch (ParseException e) {
- e.printStackTrace();
- }
- }
- }
- <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
- <property name="customEditors">
- <map>
- <entry key="java.util.Date" value="com.demo.model.CustomerProperty"/>
- </map>
- </property>
- </bean>
配置之后就可以注入 Date 类型的属性了.
- <property name="date" value="2018-8-20"/>
- 这里新建了 XmlCollectionsDemo 类, 配置了上面的几个类型的属性来演示.
- package com.demo.model;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class XmlCollectionsDemo {
- private List<Object> list;
- private Properties pros;
- private Set<Object> sets;
- private Map<String,Object> maps;
- private Date date;
- public Date getDate() {
- return date;
- }
- public void setDate(Date date) {
- this.date = date;
- }
- public List<Object> list() {
- return list;
- }
- public void setLists(List<Object> list) {
- this.list = list;
- }
- public Properties getPros() {
- return pros;
- }
- public void setPros(Properties pros) {
- this.pros = pros;
- }
- public Set<Object> getSets() {
- return sets;
- }
- public void setSets(Set<Object> sets) {
- this.sets = sets;
- }
- public Map<String, Object> getMaps() {
- return maps;
- }
- public void setMaps(Map<String, Object> maps) {
- this.maps = maps;
- }
- public static void main( String[] args ) throws Exception
- {
- ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"});
- BeanFactory factory=context;
- XmlCollectionsDemo annontationInstance=(XmlCollectionsDemo)factory.getBean("xmlCollectionsDemo");
- System.out.println(annontationInstance.list);
- System.out.println(annontationInstance.pros);
- System.out.println(annontationInstance.sets);
- System.out.println(annontationInstance.maps);
- System.out.println(annontationInstance.date);
- }
- }
- View Code
- <bean id="xmlCollectionsDemo" class="com.demo.model.XmlCollectionsDemo">
- <property name="lists">
- <list>
- <value>1</value>
- <ref bean="CleanAir" />
- <bean class="com.demo.model.CleanAir"/>
- </list>
- </property>
- <property name="sets">
- <set>
- <value>1</value>
- <ref bean="CleanAir" />
- <bean class="com.demo.model.CleanAir"/>
- </set>
- </property>
- <property name="maps">
- <map>
- <entry key="key1" value="1"></entry>
- <entry key="key2" value-ref="CleanAir"></entry>
- <entry key="key3">
- <bean class="com.demo.model.CleanAir"/>
- </entry>
- </map>
- </property>
- <property name="pros">
- <props>
- <prop key="prokey1">prokeyA</prop>
- <prop key="prokey2">prokeyB</prop>
- </props>
- </property>
- <property name="date" value="2018-8-20"/>
- </bean>
- View Code
通过运行 main 方法, 打印出属性值.
- [1, CleanAir, CleanAir]
- {prokey2=prokeyB, prokey1=prokeyA}
- [1, CleanAir, CleanAir]
- {key1=1, key2=CleanAir, key3=CleanAir}
- Mon Aug 20 00:00:00 CST 2018
五, 初始化函数, 销毁函数
通过上面 3 种注入方式的学习也对通过 xml 对 bean 实例化有的了解, 有的对象在实例化之后还需要执行某些初始化代码, 但这些初始化代码还不能写在构造函数中, 此时可以将初始化代码写到某个方法中, 将 init-method 属性值设置为该方法, Spring 会强制执行该方法进行初始化. 而又的对象在使用完毕之后需要释放, 可以使用 destroy-method 来进行销毁.
- public void DestoryMethod()
- {
- System.out.println("DestoryMethod");
- }
- public void InitMethod()
- {
- System.out.println("InitMethod");
- }
这里先在 XMLInstance 类中增加了上面两个方法来模拟销毁和初始化方法. 然后在 xml 配置 bean 时就可以设置 destroy-method,init-method 属性的值对应两个方法的方法名. 注解中 @PostConstruct 对应 init-method,@PreDestory 对应 destroy-method.
<bean id="xmlfactoryinstance" factory-bean="xmlfactory" factory-method="CreateInstance" destroy-method="DestoryMethod" init-method="InitMethod"></bean>
二, 注解注入方式
一, 常用注解介绍
学习完 XML 注入之后再学习注解方式注入就容易的多, 注解方式注入主要涉及到 @Autowired,@Resource,@Required,@Qualifier,@Value 这几个注解. 在第 2 章节的 2.2.4IOC 实例中定义 Person 时就使用过 @Autowired,@Qualifier. 下面来了解下它们具体用法.
@Autowired: 默认是按类型匹配注入 bean, 它可以对类成员变量, 方法及构造函数进行标注, 完成自动装配的工作. 在使用 @Autowired 时, 首先在容器中查询对应类型的 bean, 如果查询结果刚好为一个, 就将该 bean 装配给 @Autowired 指定的数据, 如果查询的结果不止一个, 那么 @Autowired 会根据名称来查找. 如果查询的结果为空, 那么会抛出异常. 解决方法时, 使用 required=false.
@Required: 适用于 bean 属性 setter 方法, 并表示受影响的 bean 属性必须在 XML 配置文件在配置时进行填充. 否则, 容器会抛出一个 BeanInitializationException 异常.
@Qualifier:@Autowired 默认是单实例的, 但是在面向接口编程中, 如果把一个属性设置为接口类型, 一个接口可能有多个实现, 那到底是注入哪一个呢? 为了解决这个问题, 就有了 @Qualifier.
@Value: 在 xml 配置属性时可以通过 property 的 value 设置默认值,@Value 也可以为属性设置默认值.
@Resource: 默认按名称匹配注入 bean. 要求提供一个 bean 名称的属性, 如果属性为空, 则自动采用标注处的变量名或方法名作为 bean 的名称. 如果我们没有在使用 @Resource 时指定 bean 的名字, 同时 Spring 容器中又没有该名字的 bean, 这时候 @Resource 就会退化为 @Autowired 即按照类型注入.
- package com.demo.model;
- import javax.annotation.Resource;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.beans.factory.annotation.Required;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
- @Component
- public class AnnontationInstance {
- @Value("abc")
- private String name;
- public void setName(String name) {
- this.name = name;
- }
- //@Resource 与 @Autowired 两者选其一
- // @Autowired
- // @Qualifier(value="cleanair")
- private IAir air;
- @Resource(name="CleanAir")
- public void setAir(IAir air) {
- this.air = air;
- }
- public void Breath()
- {
- System.out.println("Name:"+this.name+";Air:"+this.air.toString());
- }
- }
上面代码使用 @Value 注解为 name 设置了默认值, 使用 @Resources 设置 bean 的名称为 IAir 属性注入 bean, 也可以使用 @Autowired+@Qualifier 为 IAir 注入 bean.
二, 开启注解
上面配置完注解之后, 还要告诉 Spring 开启注解, 这样 @Autowired,@Resources 这些注解才起作用. 开启有两种比较简单的方式.
1. 在 xml 配置文件中使用 context:annotation-config
<context:annotation-config />
2. 在 xml 配置文件中使用 context:component-scan
<context:component-scan base-package="com.demo.model"/>
来源: https://www.cnblogs.com/5ishare/p/9508805.html