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
属性。
示例代码
通过 @Bean
的name
属性,创建两个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,后者覆盖前者。