除了@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普通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没有打印出来。
如果根据配置文件是否配置来实例化:
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment evn = conditionContext.getEnvironment();
return evn.containsProperty("a.b.c");
}
如果需要某个bean是否存在来实例化:
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
boolean contains = conditionContext.getBeanFactory().containsBean("abcBean");
return contains;
}