@ComponentScan(basePackages={"xxx","xxx"})这种两种方式都是传入字符串,无法方便的重构。因为我们的编辑器一般不会解析你的字符串值,可以使用下面方式:
@ComponentScan(basePackageClasses={xxx.class, xxxx.class}) 这些类所在的包会作为组件扫描的基础包,可以为需要扫描的包中添加一个空接口,标记该包需要扫描,这种方式避免了传入字符串,方便重构修改需要在 xxx.class 中加载配置
向容器中注册组件
用在构造器上 当 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("xxx")@Configuration
@ComponentScan
public class AutowiredDemoConfig {
/**
* User 是引入的第三方工具,无法修改 User 类,在类名上加 @Component 注解
*/
@Bean
public User user(){
return new User(10,"dc1");
}
}
spring.profiles.default=dev 或者 spring.profiles.active=dev 激活 dev 环境,如果没有激活的环境,只会创建那些没有定义在 profile 中的 bean处理自动装配的歧义
如果一个 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;
}
}
默认所有的 bean 都是单例的。Spring 定义了多种作用域,包括:
使用 @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;
}
}
把配置文件中的字段注入到对象中
通过注解@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;
}
}