除了 @Bean 定义 bean 以外,spring 还提供了其他的定义形式。
@Component、@Repository、@Service、@Controller 和 @Configuration
@Component 是 spring 定义 bean 的通用注解,@Repository、@Service 和 @Controller 等注解,都是继承 @Component。
@Repository 用于持久层,@Service 用于服务层,@Controller 用于控制层。
如果仅仅只是为了定义 bean,随便用哪个都可以,但是为了的区分用于哪个场景,以及未来 spring 的扩展,还是用对应场景的注解。
@Configuration 主要场景是当作配置文件,引入 bean,比如之前的测试代码,都是用 @Configuration 来做配置文件。
默认是第一个小写的 class 名,如果想自定义,可以在括号后面定义自己想要的名称,比如@Service("abc")
。
MyConfig,ComponentScan 注解后面讲,就是扫描包的路径。
@Configuration
@ComponentScan(value="com.learn.annotation")
public class MyConfig {}
MyComponent
@Component
public class MyComponent {}
其他注解雷同,这边不做重复。
测试代码:
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println(app.getBean("myConfig"));
System.out.println(app.getBean("myDao"));
System.out.println(app.getBean("myComponent"));
System.out.println(app.getBean("myService"));
System.out.println(app.getBean("myController"));
}
运行结果:
@import
在 XML 配置文件中,用的是 <import>
标签,在注解中,用 @import 注解。可以 import 普通 bean,也可以引入配置 bean。
这种方法简化了容器实例化,因为只需要处理一个类,而不需要在构造期间记住大量的 @Configuration 类。
MyConfig
@Configuration
@Import({MyComponent.class,MyService.class,MyDao.class,MyController.class})
public class MyConfig {}
测试方法:
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
String[] names = app.getBeanDefinitionNames();
for (String name : names) {System.out.println(name);
}
}
运行结果:
虽然 AnnotationConfigApplicationContext 只引入了 MyConfig.class,但是 MyConfig 中 import 了多个类,所以都打印了出来。与 @Bean 不同的是,bean 的 id 是全类名。
ImportSelector
除了 @import 注解,也可以通过实现 ImportSelector 接口来引入类。
MyConfig
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
MyImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.learn.annotation.MyComponent","com.learn.annotation.MyService"};
}
}
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
String[] names = app.getBeanDefinitionNames();
for (String name : names) {System.out.println(name);
}
}
运行结果如下:
注意的是,MyImportSelector 并没有在容器中。
@Conditional
当我们根据不同的场景,比如是否实例化一个 bean、根据不同环境变量实例化某些不同参数的 bean 等,我们可以用 @Conditional 这个注解。
下面模拟一个简单的例子,如果 bean 的名称是 one 就实例化,除非不实例化。
MyConditional
public class MyConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {StandardMethodMetadata standardMethodMetadata = ((StandardMethodMetadata) annotatedTypeMetadata);
return standardMethodMetadata.getMethodName().equals("one");
}
}
MyConfig
@Configuration
public class MyConfig {@Bean()
@Conditional(MyConditional.class)
public One one(){return new One();
}
@Bean()
@Conditional(MyConditional.class)
public Two two(){return new Two();
}
}
测试代码
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
String[] names = app.getBeanDefinitionNames();
for (String name : names) {System.out.println(name);
}
}
运行结果如下:
可以看出,two 这个 bean 没有打印出来。