@ComponentScan
- 启用组件扫描
- 默认扫描与配置类相同的包以及子包
如何扫描指定的包
- @ComponentScan(“包名”)
- 扫描一组包:
@ComponentScan(basePackages={"xxx","xxx"})
这种两种方式都是传入字符串,无法方便的重构。因为我们的编辑器一般不会解析你的字符串值,可以使用下面方式:
- 扫描类和接口:
@ComponentScan(basePackageClasses={xxx.class, xxxx.class})
这些类所在的包会作为组件扫描的基础包,可以为需要扫描的包中添加一个空接口,标记该包需要扫描,这种方式避免了传入字符串,方便重构修改
@ContextConfiguration(classes=xxx.class)
需要在 xxx.class 中加载配置
@Component
向容器中注册组件
- bean 的名字默认是类名首字母小写
- 也可以指定: @Component(“xxx”)
- @Named 可以替代 @Component
@Autowired
-
用在构造器上 当 Spring 去创建该类的 bean 时,会调用这个构造方法,同时传入一个 满足参数的 bean
Class User { private Car car; // 会使用这个方法去构造 bean, 而不是无 参构造器 @Autowired public User(Car car){ this.car = car; } }
-
用在 Setter 方法上
-
其实可以用在类的任何方法上,Spring 尝试满足方法参数上声明的依赖,如果找不到对应的 bean 就会抛出异常
-
@Autowired(required=false) 即使没找到也不会抛出异常,会传入 Null. 可能会导致空指针异常
-
如果有多个 bean 都能满足,会抛出异常
-
可以使用 Inject 替换,Inject 是 java 中依赖注入规范
@Bean
- 显示配置 Bean, 可以把第三方对象设置成 Bean,因为我们无法直接在第三方类上添加@Component 注解
- bean Id 默认是方法名,可以指定:
@Bean("xxx")
@Configuration @ComponentScan public class AutowiredDemoConfig { /** * User 是引入的第三方工具,无法修改 User 类,在类名上加 @Component 注解 */ @Bean public User user(){ return new User(10,"dc1"); } }
@Profile(“dev”)
- 和@Bean 一起使用,当 dev 环境上才会创建 bean
- 配置
spring.profiles.default=dev
或者spring.profiles.active=dev
激活 dev 环境,如果没有激活的环境,只会创建那些没有定义在 profile 中的 bean - 从 Spring 4 开始,依赖 @Conditional 注解
@Conditional
- Since 4.0
- 条件化的 bean
- 用到带有@Bean 的注解的方法上
- 给定条件的结果为 true 时,创建 bean 否则不创建
- 需要实现 Conditional 接口中的 matches 方法
@Primary
-
处理自动装配的歧义
-
如果一个 Bean 是接口,系统中有多个实现类。注入时就会有歧义,抛出异常
-
@Primary 用在 Bean 上,标记这个 bean 是首选的,发生歧义时首选它, 不会报错
-
缺点: 只能使用其中一个 bean,无法指定注入哪个,@Qualifier 解决这个问题
示例:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = AutowiredDemoConfig.class) public class QualifierDemo { @Autowired private Dessert dessert; @Test public void eat(){ dessert.getName(); } } /** * Cake IceCream 都实现了 Dessert */ interface Dessert{ String getName(); } @Component @Primary // 会优先注入 Cake class Cake implements Dessert { public String getName() { String name = "Cake"; System.out.println(name); return name; } } @Component class IceCream implements Dessert{ public String getName() { String name = "IceCream"; System.out.println(name); return name; } }
@Qualifier(“xxx bean name”)
- 与 @Autowired 一起使用时直接指定 bean 的名字进行装配:
- 与 @Component 一起使用,可以设置 bean 的名字
- 与 @Bean 一起使用,可以设置 bean 的名字
@Scope bean 的作用域
默认所有的 bean 都是单例的。Spring 定义了多种作用域,包括:
- 单例 (Singleton): 整个应用中只创建一个 bean 实例
- 原型 (Prototype): 每次注入或者通过 Spring 应用上下文获取的时候,都会创建一个新的 bean。
- 会话 (Session): 在 Web 应用中,为每个会话创建一个实例
- 请求 (Request): 在 Web 应用中,为每个请求创建一个实例
示例
使用 @Scope 注解,改变bean的作用域
@RunWith(SpringJUnit4ClassRunner.class)
@ComponentScan
@ContextConfiguration(classes = ScopeDemo.class)
public class ScopeDemo {
@Autowired
@Qualifier("singleSTU")
private Student s1;
@Autowired
@Qualifier("singleSTU")
private Student s2;
@Autowired
@Qualifier("prototypeSTU")
private Student s3;
@Autowired
@Qualifier("prototypeSTU")
private Student s4;
@Test
public void scopeDemo(){
Assert.assertEquals("s1 s2 是单例作用域,两者应该相等:", s1.hashCode(), s2.hashCode());
Assert.assertNotEquals("s3 s4 是原型作用域,两者应该不相等:", s3.hashCode(), s4.hashCode());
}
}
@Configuration
class Student {
private int age;
/**
* 默认是单例作用域
* @return
*/
@Bean("singleSTU")
public Student stu(){
Random random = new Random();
Student s1 = new Student();
s1.setAge(random.nextInt(100));
return s1;
}
/**
* 设置原型作用域
* @return
*/
@Bean("prototypeSTU")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Student stu1(){
Random random = new Random();
Student s1 = new Student();
s1.setAge(random.nextInt(100));
return s1;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行时注入外部值
把配置文件中的字段注入到对象中
Enviroment
通过注解@PropertySource(“xxx”)讲配置文件中的信息注入到Enviorment 对象中:
@RunWith(SpringJUnit4ClassRunner.class)
@ComponentScan
@ContextConfiguration(classes = EnviromentDemo.class)
public class EnviromentDemo {
@Autowired
private Mysql mysql;
@Test
public void testDataSource(){
System.out.println(mysql.getPort());
}
}
@Configuration
@PropertySource("classpath:/app.properties")// 将配置文件中的字段加载到 Environment 中,稍后可以通过 Environment 对象获取
class DataSource {
@Autowired
Environment env;
@Bean
public Mysql mysql(){
// 获取对应的字段值,getProperty 更多用法参考源码
return new Mysql(env.getProperty("mysql.port", Integer.class), env.getProperty("mysql.host"));
}
}
class Mysql {
private int port;
private String host;
public Mysql(int port, String host) {
this.port = port;
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}
通过属性占位符装配
还可以通过 ${} 占位符读取配置文件中的字段,注入到字段中; 如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ComponentScan
@ContextConfiguration(classes = ValueDemo.class)
public class ValueDemo {
@Autowired
private Mysql1 mysql;
@Test
public void testDataSource(){
System.out.println(mysql.getPort());
}
}
@Configuration
class DataSource1 {
@Autowired
Environment env;
@Bean // 这个bean 是必须的。可以解析占位符,自动查找
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
// ${} 占位符方式注入
public Mysql1 mysql(@Value("${mysql.port}") int port,@Value("${mysql.host}") String host){
// 获取对应的字段值,getProperty 更多用法参考源码
return new Mysql1(port, host);
}
}
class Mysql1 {
private int port;
private String host;
public Mysql1(int port, String host) {
this.port = port;
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}