最小化Spring XML配置

一,自动装配

使用自动装配,容器可以自动装配Bean,省去手工装配Bean的麻烦。当涉及自动装配Bean的依赖关系时,Spring提供了多种处理方式,如下:(参考Spring实战 - P64)

  • byName
  • byType
  • constructor
  • autodetect

1.1,byName自动装配

把与Bean的属性有相同名字的其他Bean自动装配到Bean对应的属性中,如果没有跟属性名字相匹配的Bean,则该属性不进行自动装配。

//Student.java

package com.sequence;

public class Student {
    private String name;
    private int age;
    private Address addr;
    ...
}
//Address.java

package com.sequence;

public class Address {
    private String city;
    ...
}
//Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="lisi" class="com.sequence.Student" autowire="byName">
        <property name="name" value="lisi" />
        <property name="age" value="30" />
    </bean>

    <bean id="addr" class="com.sequence.Address">
        <property name="city" value="xuchang" />
    </bean>

</beans>

程序分析:

通过设置autowire属性为byName,Spring将特殊对待lisi Bean的所有属性,为这些属性寻找与其名字相同的Bean。在这里,Spring发现有一个与addr属性名相同的Bean,直接通过setter注入来进行自动装配。

1.2,使用注解自动装配

从Spring2.5开始,可以使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配没有太大差别。但是,使用注解可以更细粒度的自动装配,在Spring配置文件中设置autowire属性进行的自动装配将装配一个Bean的所有属性,而且,只能通过名称与类型来装配Bean。可以通过使用@Autowired或@Resource注解一个设置方法、构造函数、属性甚至任意方法自动装配特定的属性。

1.2.1,工作原理

为了使Spring自动装配具有@Autowired或@Resource注解的属性,必须在IoC容器中注册一个AutowiredAnnotationBeanPostProcessor实例。可以在Bean配置文件中包含<context:annotation-config>元素,这将自动注册一个AutowiredAnnotationBeanPostProcessor实例。(参考Spring攻略 - P41)

1.2.2,使用@Autowired

package com.sequence;

import org.springframework.beans.factory.annotation.Autowired;

public class Student {
    private String name;
    private int age;

    @Autowired
    private Address addr;
    ...
}

程序分析:

使用@Autowired注解可以直接标注属性,也可以标注setter方法,还可以标注需要自动装配Bean引用的任意方法,甚至可以标注构造函数。

1.2.3,可选的自动装配

默认情况下,@Autowired具有强契约特征,其所标注的属性必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性,自动装配就会失败,抛出NoSuchBeanDefinitionException异常。如果属性不一定非要装配,null值也是可以接受的,可以通过设置@Autowired的required属性为false来配置自动装配是可选的。

二,自动检测

<context:component-scan>元素允许Spring自动检测与定义Bean,可以减少使用<bean>元素。为了配置Spring自动检测,需要使用<context:component-scan>元素。(参考Spring实战 - P77)

2.1,为自动检测标注Bean

<context:component-scan>元素会扫描指定的包及其所有子包,并查找出能自动注册为Spring Bean的类,base-package属性标示了所扫描的包。默认情况下, <context:component-scan>查找使用构造型注解所标注的类,把这些类注册为Spring Bean。构造型注解如下:@Component、@Controller、@Repository、@Service

//MachineGun.java

@Component("gun")
public class MachineGun implements Gun{
    public void shoot() {
        System.out.println("MachineGun Shoot!");
    }
}
//Character.java

package com.game;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("badMen")
public class Character {
    @Autowired
    private Gun gun;

    public void setGun(Gun gun) {
        this.gun = gun;
    }

    public void kill() {
        gun.shoot();
    }
}
//Beans.xml

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.game">
    </context:component-scan>

</beans>
//Main.java

package com.game;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Character badMen = (Character) context.getBean("badMen");
        badMen.kill();
    }
}

输出结果:

五月 15, 2018 8:35:57 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Tue May 15 08:35:57 CST 2018]; root of context hierarchy
五月 15, 2018 8:35:57 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
MachineGun Shoot!

程序分析:

使用了自动装配与自动检测技术,配置文件中没有出现<bean>、<property>等标签,就可以实现相关的功能,配置文件非常的简洁。

2.2,过滤组件扫描

在如何通过扫描获取候选Bean方面,<context:component-scan>非常灵活。通过为<context:component-scan>配置<context:include-filter><context:exclude-filter>子元素,可以随意调整扫描行为。

三,使用Spring基于java的配置

3.1,定义一个配置类

基于java的配置,使用@Configuration注解的java类,就等价于XML配置中的<beans>元素。

package com.game;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MachineGun implements Gun{
    public void shoot() {
        System.out.println("MachineGun Shoot!");
    }
}

程序分析:

@Configuration注解会通知Spring,这个类将包含一个或多个Spring Bean定义。这些Bean定义是使用@Bean注解所标注的方法。

3.2,声明一个简单的Bean

使用@Bean注解定义一个方法来定义gun Bean

package com.game;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MachineGun implements Gun{
    public void shoot() {
        System.out.println("MachineGun Shoot!");
    }

    @Bean 
    public MachineGun gun() {
        return new MachineGun();
    }
}

@Bean通知Spring,这个被标注的方法将返回一个对象,该对象被注册为Spring应用上下文中的一个Bean,方法名作为该Bean的ID,在该方法中所实现的所有逻辑本质上都是为了创建Bean。

使用java配置的优点:

在XML配置中,Bean的类型与ID都是由String属性来标示的,String标识符的缺点就是无法进行编译期检查。如果重命名了MachineGun类,可能会忘记修改相应的XML配置。在基于java的配置中,Bean的ID与类型都被视为方法签名的一部分,Bean的创建都是在方法中定义的。因为它们全都是java代码,所以可以进行编译期检查来确保Bean的类型是否合法,并且Bean的ID是唯一的。

3.3,使用Spring的基于java的配置进行注入

package com.sequence;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Student {
    private String name;
    private int age;
    private Address addr;

    @Bean
    public Address address() {
        Address obj = new Address();
        obj.setCity("xuchang");
        return obj;
    }

    @Bean
    public Student zhangsan() {
        String name = "zhangsan";
        int age = 30;
        Student obj = new Student();
        obj.setName(name);
        obj.setAge(age);
        obj.setAddr(address());
        return obj;
    }

    public Student() {

    }
    ...
}

程序分析:

Spring基于java的配置感觉很自然,定义Bean就像使用java编写类的实例化代码一样,setter注入也是自然的java代码。

注意:

在Spring的java配置中,通过声明方法引用一个Bean并不等同于调用该方法,例如:

    @Bean
    public Student zhangsan() {
        String name = "zhangsan";
        int age = 30;
        Student obj = new Student();
        obj.setName(name);
        obj.setAge(age);
        obj.setAddr(address());
        return obj;
    }

通过调用@Bean注解标注的address()方法,会通知Spring该方法定义的Bean要被注册进Spring的上下文中。因此,在其他Bean声明方法中引用这个方法时,Spring都会拦截该方法调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。

猜你喜欢

转载自blog.csdn.net/cloud323/article/details/80317879
今日推荐