Bean的配置

继续引用spring中的java基础(注解)的例子代码person类和air接口。

1、基于XML配置Bean

Spring通过<bean>配置来实例化、设置Bean的属性以及设置bean的相互依赖,有id和class的属性,class相当于我们new来创建新实例时所需要导入的类,而id相当于创建new时的对象名。class是必须的,id是非必须的,但在配置文件中配置其他bean时引用该bean时,该bean也必须要有定义id。

<bean id="CleanAir" class="test.model.CleanAir">

2、使用注解定义Bean

使用xml配置的时候,定义bean和实现bean是分离的,而用注解定义bean时,只要在bean实现类上标注注解就能实现配置bean。

@Component
public class CleanAir implements IAir {
    @Override
    public String toString(){
        return "CleanAir";
    }
}

使用@Component注解在CleanAir类声明处对类进行标注,它可以被Spring容器识别,自动将POJO类转换为容器管理的bean。他和1、中的xml配置是等效的。定义Bean的注解还提供了其他3个功能与@Component等效的注解

  • @Repository:用于对DAO实现类进行标注。
  • @Service:用于对Service实现类进行标注。
  • @Controller:用于对Controller实现类进行标注。

3、基于Java类提供Bean定义

在普通的pojo类中标注@Configuration注解,相当于一个xml文件配置,每个@Bean注解,相当于一个<bean>。

@Configuration
public class AppConf {
    @Bean
    public CleanAir cleanAir(){
        return new CleanAir();
    }
    @Bean
    public DirtyAir dirtyAir(){
        return  new DirtyAir();
    }
    @Bean
    public Person person(){
        return new Person(dirtyAir());
    }
}
 public static void main(String [] args){
        ApplicationContext ac = 
		new AnnotationConfigApplicationContext(AppConf.class);
        Person person1 = ac.getBean(Person.class);
        person1.Breath();
    }

Bean的注入

Bean的注入方式有两种:一种是在XML中配置,另一种是使用注解的方法注入

1、XML方式注入

1)属性注入

对类中的字段进行注入,在传统的对象实例化可以使用new,再进行set方法来设置字段的值,在Spring中,可以使用property来进行属性注入,需要有字段对应的set方法。

新建XMLInstance类

public class XMLInstance {
    private String name;
    public void setName(String name ){
        this.name = name;
    }
    private IAir iAir;
    public void setAir(IAir iAir){
        this.iAir = iAir;
    }
    public void Breath(){
        System.out.println("Name:"+this.name+"\nAir:"+this.iAir.toString());
    }
}

在XMLInstance中已设置了set方法,可以使用XMLproperty来配置属性,name表示属性名,value用来设置基本类型的属性值,ref标签配置非基本类型的属性值。XML配置如下。

 <bean id="CleanAir" class="test.model.CleanAir">
        <qualifier value="cleanair"/>
    </bean>
    <bean id="xmlinstance" class="test.model.XMLInstance">
        <property name="name" value="abc"/>
        <property name="air" ref="CleanAir"/>
    </bean>

运行输出

Name:abc
Air:CleanAir

2)构造函数注入

在属性注入时先使用默认的无参构造函数,再使用set,在使用有参构造函数时进行的属性注入,就是构造函数注入,在1)中增加一个构造函数,在XMLInstance类中增加如下代码

public XMLInstance(String name,IAir iAir){
        super();
        this.name = name;
        this.iAir = iAir;
    }

在XML中使用construect-arg来设置构造函数的参数,index代表参数的顺序位置,value设置构造参数的基本类型,ref引用非基本类型的bean的id。XML配置如下。

<bean id="xmlcontructinstance" class="test.model.XMLInstance">
        <constructor-arg index="0" value="def"/>
        <constructor-arg index="1" ref="CleanAir"/>
    </bean>

运行输出

Name:def
Air:CleanAir

3)工厂方法注入

工厂方法注入参考的工厂设计模式,通过工厂类来实现对象的实例化,工厂方法有静态与非静态方法。新建一个工厂类。

public class XMLFactory {
    public XMLInstance CreateInstance(){
        return new XMLInstance("instance",new CleanAir());
    }
    public static XMLInstance CreateStaticInstance(){
        return new XMLInstance("static instance",new CleanAir());
    }
}

可见工厂类中有静态方法和非静态方法,静态时不需要实例化工厂类直接调用静态工厂方法,非静态需要实例化工厂类再调用工厂方法。XML配置如下。

<!--静态工厂配置-->
 <bean id="xmlstaticfactoryinstance" class="test.model.XMLFactory" 
	factory-method="CreateStaticInstance"/>
<!--非静态工厂配置-->
    <bean id="xmlfactory" class="test.model.XMLFactory" />
    <bean id="xmlfactoryinstance" factory-bean="xmlfactory" 
	factory-method="CreateInstance"/>

运行结果

Name:static instance
Air:CleanAir
Name:instance
Air:CleanAir

4)常见数据类型注入

已有类型注入

常见数据类型有List、Set、Map、Properties。 新建XmlCollectionsDemo类,为这四种类型进行数据注入。

public class XmlCollectionsDemo {
    private List<Object> list;
    private Properties pros;
    private Set<Object> sets;
    private Map<String,Object> maps;
    public List<Object> getList() {
        return list;
    }
    public void setList(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){
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/model.xml");
        BeanFactory factory = ac;
        XmlCollectionsDemo xmlCollectionsDemo
                = (XmlCollectionsDemo)factory.getBean("xmlCollectionsDemo");
        System.out.println(xmlCollectionsDemo.getList());
        System.out.println(xmlCollectionsDemo.getPros());
        System.out.println(xmlCollectionsDemo.getSets());
        System.out.println(xmlCollectionsDemo.getMaps());
        System.out.println(xmlCollectionsDemo.getDate());
    }
}

XML配置

<bean id="xmlCollectionsDemo" class="test.model.XmlCollectionsDemo">
<!--        list类型注入-->
        <property name="list">
            <list>
                <value>1</value>
                <ref bean="CleanAir"/>
                <bean class="test.model.CleanAir"/>
            </list>
        </property>
        <!-- set类型注入-->
        <property name="sets">
            <set>
                <value>1</value>
                <ref bean="CleanAir"/>
                <bean class="test.model.CleanAir"/>
            </set>
        </property>
        <!-- pros类型注入-->
        <property name="pros">
            <props>
                <prop key="prokey1">prokeyA</prop>
                <prop key="prokey2">prokeyB</prop>
            </props>
        </property>
        <!-- maps类型注入-->
        <property name="maps">
            <map>
                <entry key="key1" value="1"></entry>
                <entry key="key2" value-ref="CleanAir"></entry>
                <entry key="key3" >
                    <bean class="test.model.CleanAir"/>
                </entry>
            </map>
        </property>

运行结果

[1, CleanAir, CleanAir]
{prokey2=prokeyB, prokey1=prokeyA}
[1, CleanAir, CleanAir]
{key1=1, key2=CleanAir, key3=CleanAir}
自定义属性编辑器

有一些属性是没法注入的,比如说日子。但可以通过PropertyEditorySupport,重写setAsText方法来实现注入。新建CustomerProperty类

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);
        try{
            this.setValue(sdf.parse(text));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
<bean id="customEditorConfigurer" 
 class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.util.Date" value="test.model.CustomerProperty"/>
            </map>
        </property>
    </bean>

该类中将日期以String的形式传入,再重写了setAsText方法,setAsText方法会自动执行转换成日期类重新赋值给传入的value,在XML配置中,相当于import,将Date类引用到customEditors中。将4)中XmlCollectionsDemo添加字段Date,并在xml中增加

<property name="date" value="2019-8-12"/>

运行结果

[1, CleanAir, CleanAir]
{prokey2=prokeyB, prokey1=prokeyA}
[1, CleanAir, CleanAir]
{key1=1, key2=CleanAir, key3=CleanAir}
Mon Aug 12 00:00:00 CST 2019

5)初始化函数、销毁函数

在某些对象实例化完毕后还需要执行某些初始化代码,但由于某些原因并不能写在构造函数中,这个时候可以使将初始化代码写到另一个方法中,将init-method属性设置为该方法,有的对象使用完毕之后需要释放操作,可以用destroy-method设置销毁方法

在XMLInstance类中增加初始化方法和销毁方法

    public void DestroyMethod(){
        System.out.println("DestroyMethod");
    }
    public void InitMethod(){
        System.out.println("InitMethod");
    }

并在XML中注入初始化方法和销毁方法,修改xml

 <bean id="xmlinstance" class="test.model.XMLInstance" 
          destroy-method="DestroyMethod" init-method="InitMethod">
        <property name="name" value="abc"/>
        <property name="air" ref="CleanAir"/>
    </bean>

这样在创建实例后会输出InitMethod,在执行销毁之后会输出DestroyMethod。

2、注解方式注入

1)常用注解

  • @Autowired:默认按类型匹配注入bean,可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。在使用该注解时,会在容器中查询对应类型的bean。如果查询结果刚好为一个,就将该bean装配给他指定的数据:如果查询结果不止一个,会按照名称来查找,如果查询结果为空,会抛出异常。
  • @Required:适用于bean属性的setter方法,并表示受影响的bean属性必须在XML配置文件在配置时进行填充:否则会抛出一个异常。
  • @Qualifier:@Autowird为单实例的,但通常在面向接口编程中,一个接口会有可能有多个实现,这个时候@Qualifier可选择用哪个实现进行注入。
  • @Value:等效于property中的value设置。
  • @Resource:默认按名称匹配注入bean。要求提供一个bean名称,如果没有提供bean名称,会自动采用标注处的变量名或方法名作为bean的名称,如果没有指定bean的名字,同时Spring容器中又没有该名字的bean.这时@Resource会退回为@Autowired按类型匹配注入。

新建一个AnnontationInstance类

@Component
public class AnnontationInstance {
    @Value("789")
    private String name;

    public void setName(String name){
        this.name = name;
    }
    @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+"\nAir:"+this.air.toString());
    }
}

注:@Autowired和@Qualifier是可以与@Resource相互代替使用的,但是在@Resource中使用的是name是bean的id,而@Qualifier中的valu使用的是bean的qualifier。

回顾xml

<bean id="CleanAir" class="test.model.CleanAir">
        <qualifier value="dirtyair"></qualifier>
    </bean>
    <bean id="DirtyAir" class="test.model.DirtyAir">
        <qualifier value="cleanair"></qualifier>
    </bean>

在配置完注解之后,还要告诉Spring开启注解,这样自动装配的注解才会生效,可以使用component-scan或者annotation-config来启用注解

 <context:component-scan base-package="test.model"/>
<context:annotation-config/>