要使应用程序中的Spring容器成功启动,需要同时具备以下三方面的条件:
Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
Bean配置信息是Bean的元数据信息,它由以下四个方面组成:
Bean元数据信息在Spring容器中的内部对应物是由一个个BeanDefinition形成的Bean注册表,Spring实现了Bean元数据信息内部表示和外部定义的解耦。Spring支持多种形式的Bean配置方式。基于Xml、注解、JAva类的配置。
装配一个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配置:
<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);
}
}
要求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>200value>property>
<property name="brand"><value>CA72value>property>
<property name="price"><value>20000.00value>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]
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>CA72value>
constructor-arg>
<constructor-arg index="1" type="double">
<value>20000value>
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]
使用注解定义Bean
//通过Repository定义一个DAO的Bean
@Component("userDao")
public class UserDao{
...
}
它和以下Xml配置等效
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:
<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
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配置:
<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
@Autowired
private UserDao userDao;
如果容器中没有一个标注变量类型匹配Bean,那么容器将会报NoSuchBeanDefinitionException异常,如果希望Spring即使找不到匹配的Bean完成注入也不要抛出异常,可以使用@Autowired(required=false)
@Autowired(required=false)
private UserDao userDao;
...
@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进行注入。
对list进行注解
@Component("myComponent")
public class MyComponent {
@Autowired
private List 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
//将一个POJO标注为定义Bean的配置类
@Configuration
public class AppConf {
//定义Bean
@Bean
public UserDao userDao() {
return new UserDao();
}
}
@Configuration注解类本身标注了@Component注解,所以任何标注了@Configuration的类,本身也相当于标注了@Component,即它可以像普通Bean一样被注入其他Bean中。