要使应用程序中的Spring容器成功启动,需要同时具备以下三方面的条件:

  • Spring框架的类包都已经放到应用程序的类路径下。
  • 应用程序为Spring提供了完备的Bean配置信息。
  • Bean的类都已经放到应用程序的类路径下。

Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
Bean配置信息是Bean的元数据信息,它由以下四个方面组成:

  • bean的实现类
  • Bean的属性信息,如数据源的连接数、用户名、密码等。
  • Bean的依赖关系,Spring根据依赖关系配置完成Bean之间的装配。
  • Bean的行为配置,如生命周期范围及生命周期各过程的回调函数等。

Bean元数据信息在Spring容器中的内部对应物是由一个个BeanDefinition形成的Bean注册表,Spring实现了Bean元数据信息内部表示和外部定义的解耦。Spring支持多种形式的Bean配置方式。基于Xml、注解、JAva类的配置。

1.基于XML的配置

装配一个Bean

Car实体:

package com.yf;

public class Car {
    private int maxSpeed;
    public String brand;
    private double price;

    @Override
    public String toString() {
        return "Car [maxSpeed=" + maxSpeed + ", brand=" + brand + ", price=" + price + "]";
    }

}

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="car" class="com.yf.Car">  
    </bean>
</beans>

main:

package com.yf;

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

/**
 * 第一个Spring实例main
 * 
 * @author yf
 *
 */
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Car car = (Car) context.getBean("car");
        System.out.println(car);

    }
}

1.1依赖注入

1.1.1属性注入:

要求Bean提供一个默认构造函数,并为需要注入属性提供相应的Setter方法,Spring先通过默认的构造函数实例化Bean对象,然后通过反射的方式调用setter方法注入属性值。

Car:默认构造函数和setter方法

package com.yf;

public class Car {
    private int maxSpeed;
    public String brand;
    private double price;

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car [maxSpeed=" + maxSpeed + ", brand=" + brand + ", price=" + price + "]";
    }

}

属性配置片段:

<bean id="car" class="com.yf.Car">
        <property name="maxSpeed"><value>200</value></property>
        <property name="brand"><value>CA72</value></property>
        <property name="price"><value>20000.00</value></property>

    </bean>

测试main:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Car car = (Car) context.getBean("car");
        System.out.println(car);

    }
}

运行结果:
Car [maxSpeed=200, brand=CA72, price=20000.0]

1.1.2构造函数注入

Bean必须提供带参数的构造函数:
Car:

public class Car {
    public String brand;
    private double price;

    public Car(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car [ brand=" + brand + ", price=" + price + "]";
    }

}

配置:

<bean id="car1" class="com.yf.Car">
        <constructor-arg index="0" type="java.lang.String">
            <value>CA72</value>
        </constructor-arg>
        <constructor-arg index="1" type="double">
            <value>20000</value>
        </constructor-arg>

    </bean>

main:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Car car = (Car) context.getBean("car1");
        System.out.println(car);

    }
}

结果:
Car [ brand=CA72, price=20000.0]

2.基于注解的配置

使用注解定义Bean

//通过Repository定义一个DAO的Bean
@Component("userDao")
public class UserDao{
    ...
}

它和以下Xml配置等效

<bean id="userDao" class="com.yf.UserDao"/>

除了@Component外,Spring还提供了3个功能基本和@Component等效的注解,分别用于对DAO,Service及Web层的Controller进行注解。
@Repository:用于对DAO实现类进行标注。
@Service:用于对Service实现类进行标注。
@Controller:用于对Controller实现类进行标注。
之所以要在@Component之外提供这3个特殊注解,是为了让标注类本身的用途清晰化,完全可以用@Component替代这3个特殊注解,但是,推荐使用特定的注解标注特定的Bean,这样一眼可以看出Bean的真实身份。

扫描注解定义的Bean:

<?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:component-scan base-package="com.yf"></context:component-scan>

</beans>

通过context命名空间的component-scan的bask-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。
UserDao类:

@Component("userDao")
public class UserDao {

    public void addUser(String user) {
        System.out.println("添加User");
    }
}

main:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserDao dao = (UserDao) context.getBean("userDao");
        dao.addUser("333");

    }
}

结果:
添加User

3.自动装配Bean

3.1使用@Autowired进行自动注入

UserService类:

@Service("userService")
public class UserService {
    @Autowired
    private UserDao userDao;

    public void addUser(String user) {
        userDao.addUser(user);
    }

}

UserDao类

@Repository("userDao")
public class UserDao {

    public void addUser(String user) {
        System.out.println("添加User" + user);
    }
}

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:component-scan base-package="com.yf"></context:component-scan>

</beans>

main:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService service = (UserService) context.getBean("userService");
        service.addUser("333");

    }
}

结果:
添加User333

3.2使用@Autowired的required属性

@Autowired
private UserDao userDao;

如果容器中没有一个标注变量类型匹配Bean,那么容器将会报NoSuchBeanDefinitionException异常,如果希望Spring即使找不到匹配的Bean完成注入也不要抛出异常,可以使用@Autowired(required=false)

@Autowired(required=false)
private UserDao userDao;
...

3.3@Autowired对类方法进行标注

@Service("userService")
public class UserService {
    private UserDao userDao;

    // 自动将UserDao传给方法入参
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(String user) {
        userDao.addUser(user);
    }

}

如果一个方法拥有多个入参,默认情况下,Spring将自动选择匹配入参类型的Bean进行注入。

3.4@Autowired对集合进行标注

对list进行注解

@Component("myComponent")
public class MyComponent {

    @Autowired
    private List<BeanInterface> list;

    public void say() {
        for (BeanInterface bean : list) {
            bean.say();
        }
    }
}

BeanInterface 类:

public interface BeanInterface {
    public void say();

}

BeanOneImpl 类:

@Component
public class BeanOneImpl implements BeanInterface {

    @Override
    public void say() {
        System.out.println("BeanOneImpl");

    }

}

BeanTowImpl 类:

@Component
public class BeanTowImpl implements BeanInterface {

    @Override
    public void say() {
        System.out.println("BeanTowImpl");

    }

}

main:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        MyComponent component = (MyComponent) context.getBean("myComponent");
        component.say();

    }
}

结果:
BeanOneImpl
BeanTowImpl

4.基于Java类的配置

//将一个POJO标注为定义Bean的配置类
@Configuration
public class AppConf {
    //定义Bean
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }

}

@Configuration注解类本身标注了@Component注解,所以任何标注了@Configuration的类,本身也相当于标注了@Component,即它可以像普通Bean一样被注入其他Bean中。