spring config - downgoon/hello-world GitHub Wiki

spring-config 零配置

Spring两大特点:IoC和AOP。IoC的核心理念是:要让开发软件跟开发硬件一样,硬件上有一个“主板”,“主板”上有各种“标准化定义”的插槽,比如声卡,我们可以买各个厂商的声卡,便可以即插即用。这个“主板”在Spring中就叫ApplicationContext,表示应用程序运行的上下文环境。传统方式下ApplicationContext是基于XML配置的,实现类是ClassPathXmlApplicationContext,零配置下,要么是“约定俗称高于配置”,要么是“基于Java Annotation”,实现类是AnnotationConfigApplicationContext

实验-1: Hello Annotation

进入代码

$ git checkout spring-config
$ git checkout spring-config-c1-annotation

实验内容

依靠Spring容器创建一个Employee对象,并从容器中获取。

获取Employee对象 (代码是AnnotationConfigExample.java

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Employee employee = ctx.getBean(Employee.class);
System.out.println(employee);

很简单,就是用AnnotationConfigApplicationContext,加载了一个Java对象AppConfig.java,可想而知AppConfig.java上必然用Annotation描述了对象见的依赖关系。

依赖关系描述(代码是AppConfig.java

public class AppConfig {

	@Bean
	public Employee employee() {
		Employee e = new Employee();
		e.setName("wangyi");
		e.setAge(28);
		return e;
	}
}

仅仅需要使用到@Bean,这样AnnotationConfigApplicationContext在解析AppConfig.java就会扫描@Bean

实验-2: One Type, Two Beans

进入代码

$ git checkout spring-config-c2-twobeans

实验内容

刚才定义的Employee,如果想定义两个Bean呢? 很简单,只要在@Bean 这个Annotation上增加name属性。

示例代码

通过 @Beanname属性,创建两个Bean:

public class AppConfig {

	@Bean(name = "wangyi")
	public Employee employee() {
		Employee e = new Employee();
		e.setName("wangyi");
		e.setAge(28);
		return e;
	}
	
	
	@Bean(name = "zhangsan")
	public Employee employeeZhangsan() {
		Employee e = new Employee();
		e.setName("zhangsan");
		e.setAge(37);
		return e;
	}

}

获取方式:只需要在原来的基础上,增加name

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Employee wangyi = ctx.getBean("wangyi", Employee.class);
System.out.println(wangyi);
        
Employee zhangsan = ctx.getBean("zhangsan", Employee.class);
System.out.println(zhangsan);

实验-3:ConfigFile + Annotation 结合

进入代码

$ git checkout spring-config-c3-fileconf

实验内容

使用Annotation的好处是维护依赖关系,彻底摆脱XML,实际上依赖关系并不会经常变化的。但是类似JDBC的连接地址,在生产和测试环境下,往往不同,这个如果写到Annotation里面,就得重新编译,能不能写到配置文件呢?

参考资料

示例代码

AppConfig.java 如下:

@PropertySource("classpath:/application.properties")
public class AppConfig {
	
	@Value("${employee.name}")
	private String employeeName;

	@Value("${employee.age}")
	private Integer employeeAge;

	@Bean(name = "chenliu")
	public Employee employeeChenliu() {

		Employee e = new Employee();
		e.setName(employeeName);
		e.setAge(employeeAge);
		return e;
	}

通过@PropertySource("classpath:/application.properties") 指定配置文件,接着通过@Value("${employee.age}")引用配置文件中的变量,并赋值给成员变量private Integer employeeAge;

application.properties 配置如下:

$ cat src/main/resources/application.properties
employee.name=chenliu
employee.age=43

主函数:

public static void main(String[] args) throws Exception {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        
        Employee chenliu = ctx.getBean("chenliu", Employee.class);
        System.out.println(chenliu);
	}

直接运行的时候会报一个异常:

Caused by: java.lang.NumberFormatException: For input string: "${employee.age}"

很奇怪,怎么${employee.age}会无法转换成Int呢?仔细看会发现,从配置文件中获取的变量的取值就是字符串"${employee.age}",并没有从PropertySource替换。我们可以把成员变量private Integer employeeAge;注释掉,结果没有异常,但输出内容是:

Employee [name=${employee.name}, age=null]

简单说,问题的根本在于``@Value("${employee.name}") 如果变量找不到替换,就会当做一个普通字符赋值。

重要提示: 解决办法是在AppConfig.java中,增加静态方法(务必是静态方法) placeHolder (名字可以起别的),返回PropertySourcesPlaceholderConfigurer

@Bean public static PropertySourcesPlaceholderConfigurer placeHolder() { return new PropertySourcesPlaceholderConfigurer(); }


>Note  @ [spring-propertysources-example/](http://www.mkyong.com/spring/spring-propertysources-example/)
To resolve ``${}`` in ``@Values``, you must register a static ``PropertySourcesPlaceholderConfigurer`` in either XML or annotation configuration file.

## 实验-4:ConfigFile 的 Environment 读取方式

### 进入代码

$ git checkout spring-config-c4-confbyenc


### 实验内容

前面通过``@Value("${var}")``的方式,可以直接赋值给成员变量,运行在Bean组装的时候。但是有的时候,我们希望写代码的那种方式,类似``prop.getProperty("employee.name")``的方式呢?

的确有,Spring官方也推荐这种方式,他们把这种方式叫``Environment``方式。

### 示例代码

在 ``AppConfig.java``中:

@PropertySource("classpath:/application.properties") public class AppConfig {

@Autowired
private Environment env;

@Bean(name = "env")
public Employee employeeFromEnv() {
	Employee e = new Employee();
	e.setName(env.getProperty("employee.name"));
	e.setAge(env.getProperty("employee.age", Integer.class));
	return e;
}

}


重要的是:
>```
@Autowired
private Environment env;

它会自动把@PropertySource的东西,全部装载进来。而且它并不需要PropertySourcesPlaceholderConfigurer静态对象的支持(静态对象只是为了支持@Value的)。

实验-5:多个 ConfigFile

进入代码

$ git checkout spring-config-c5-confmulti

实验内容

两个配置文件 ?如果Key相同,谁覆盖谁?还是冲突?

示例代码

@PropertySource({ "classpath:/application.properties", 
"classpath:/another.properties" })
public class AppConfig {

	@Autowired
	private Environment env;

	@Bean
	public Employee employeeFromEnv() {
		Employee e = new Employee();
		e.setName(env.getProperty("employee.name"));
		e.setAge(env.getProperty("employee.age", Integer.class));
		return e;
	}

}

遇到相同的Key,后者覆盖前者。