一,注解上下文
Spring3.0之后,提供了使用AnnotationConfigApplicationContext来实现基于Java配置类加载Spring应用上下文的功能。这种方式可以避免使用application.xml进行配置,相比XML配置这种方式也更加便捷。
这种ApplicationContext实现不仅能够接受@Configuration类作为输入,而且还可以接受@Component注解类和带有JSR-330元数据注解的类。
public class TestAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
try {
System.out.println(dataSource.getConnection() == null);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
AnnotationConfigApplicationContext不仅限于与@Configuration注解一起使用。任何包含@Component的注解类或带JSR-330注解的类都可以作为输入提供给构造函数。
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class, MyServiceImpl.class, MyDaoImpl.class);
MyService myService = ctx.getBean(MyService.class);
myService.todo();
}
AnnotationBeanDefinition
该接口继承自BeanDefinition接口
public interface AnnotatedBeanDefinition extends BeanDefinition {
// 获取注解元数据
AnnotationMetadata getMetadata();
// 获取此Bean定义的工厂方法的元数据
// 如果在配置类中使用了@Bean注解,被@Bean标记的方法就会被解析为FactoryMethodMetadata
MethodMetadata getFactoryMethodMetadata();
}
AnnotatedBeanDefinition有三个实现类:
-
AnnotatedGenericBeanDefinition
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(JavaConfig.class); }
如上代码块中JavaConfig,和使用@Import注解导入的类,都会被解析为这种类型。
-
ScannedGenericBeanDefinition
通过注解扫描类,如@Service@Compnent等配置的方式,都会被解析为ScannedGenericBeanDefinition。
-
ConfigurationClassBeanDefinition
通过使用@Bean注解配置的方式,都会被解析为ConfigurationClassBeanDefinition。
注解上下文源码解析
org.springframework.context.annotation.AnnotationConfigApplicationContext
// 注册带注解的Bean(编程方式注册,仅适用于显式注册的类) private final AnnotatedBeanDefinitionReader reader; // 扫描方式注册带注解的Bean(Bean定义扫描器,检测指定路径中的Bean候选者) private final ClassPathBeanDefinitionScanner scanner; // AnnotationConfigApplicationContext默认构造函数 public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } // 使用BeanFactory创建一个新的AnnotationConfigApplicationContext public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } // 使用给定的带注解的类创建AnnotationConfigApplicationContext,并刷新上下文 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); // 刷新上下文 refresh(); } // 创建AnnotationConfigApplicationContext,扫描指定包路径中的Bean定义 public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); // 刷新上下文 refresh(); }
org.springframework.context.annotation.AnnotatedBeanDefinitionReader
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) { // BeanDefinition定义,增加了注解元数据的支持 AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); // 检测是否需要跳过@Conditional注解 if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // 解析对应的Scope ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); // 设置abd的Scope abd.setScope(scopeMetadata.getScopeName()); // 使用BeanNameGenerator生成对应的beanName String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 对常用注解进行处理 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // 除了Bean类级别的限定符外,还需要处理特定限定符注解 if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } // 定义BeanDefinition和beanName的映射关系类 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); // 为组件设置Scope代理(如果有的话) definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 注册BeanDefinition到容器并且注册别名 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
二,常用注解
容器配置
Spring配置中的两个主要注解是@Configuration和@Bean。
@Bean
该注解主要用于表示由Spring IoC容器管理对象的实例化,配置和初始化。对于熟悉Spring XML配置的人来说,@ Bean注解与Spring XML配置中的<bean/>元素具有相同作用。
配套注解
@Lazy
表明一个Bean 是否延迟加载。
@Primary
多个候选者都有资格自动装配依赖项时,应当优先考虑的Bean。
@DependsOn
指当前Bean所依赖的Bean,可以控制Bean的加载顺序。
@Role
Spring使用Role来标识Bean的分类。目前有三种Role:ROLE_APPLICATION、ROLE_INFRASTRUCTURE、ROLE_SUPPORT。
@Description
BeanDefinition的文本说明
@Configuration
该注解表示该类的主要目的是作为Bean定义的来源(即:表明这就是一个配置类)。此外,@Configuration类允许通过简单地调用同一类中的其他@Bean注解来定义Bean间的依赖关系。
@Configuration public class JavaConfig { @Bean public static TestBean testBean(){ System.out.println("注解......."); return new A(); } }
上面的JavaConfig类等同于在Spring XML配置文件中如下定义:
<beans> <bean id="testBean" class="xxx.xxx.TestBean"/> </beans>
@ComponentScan
根据指定的配置自动扫描组件,通常与@Configuration注解一起使用。该注解与Spring XML配置文件中的<context:component-scan>元素作用相同。
注解参数
默认值
说明
basePackageClasses
{}
basePackages()的安全替代方案,用于扫描带注解组件的包名。包名中每一个类都会被扫描
basePackages
{}
扫描带注解的组件的包名
excludeFilters
Filter[]
扫描时不需要包含哪些组件
includeFilters
Filter[]
扫描时需要包含哪些组件
lazyInit
false
被扫描到的Bean是否应注册为延迟初始化
nameGenerator
BeanNameGenerator
BeanNameGenerator接口用于对Spring容器扫描到的组件进行命名
resourcePattern
**/*.class
组件扫描的匹配模式
scopedProxy
ScopedProxyMode.DEFAULT
是否为扫描到的组件生成代理
scopeResolver
AnnotationScopeMetadataResolver
ScopeMetadataResolver用于处理扫描到的组件的Scope
useDefaultFilters
true
是否需要使用Spring默认的扫描规则,即自动检测带@Component @ Repository@ Service或@Controller的注解类
value
{}
basePackages()的别名
过滤类型
过滤类型
使用示例
说明
annotation
org.example.SomeAnnotation
默认方式。在目标组件的类型级别上存在的注释。
assignable
org.example.SomeClass
组件实现/继承(extends/implements)的类
aspectj
org.example..*Service+
组件要匹配的AspectJ类型表达式
regex
org\.example\.Default.*
与正则表达式匹配的目标组件
custom
org.example.MyTypeFilter
自定义实现了org.springframework.core.type .TypeFilter 的过滤类。
参数使用示例
@ComponentScan(basePackageClasses = Person.class) // 扫描xxx.xxx包下的组件 @ComponentScan(value = "xxx.xxx") // 扫描包含有@Component注解的组件 @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Component.class)) // 扫描ai.yunxi包,包含与“.*Stub.*Repository”表达式匹配的组件,排除包含@Repository注解的组件 @ComponentScan(basePackages = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @ComponentScan.Filter(Repository.class)) // 扫描包含extends或implements Test的组件 @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Test.class), useDefaultFilters = false) // 扫描时排除有@Component注解的组件 @ComponentScan(value = "xxx.xxx", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Component.class)) // 自定义过滤接口 @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, value = MyComponentFilter.class), useDefaultFilters = true)
@Conditional
该注解用来表示仅当指定条件匹配时,Bean才能够注册。如果@Configuration注解用@Conditional进行标记,则与该类关联的所有@Bean,@ Import和@ComponentScan注解将受条件限制。总的来说,就是根据特定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动的配置。
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition Conditions} that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
使用示例
Bean类
public interface Vehicle { public void run(); } public class Car implements Vehicle { @Override public void run() { System.out.println("car run"); } } public class Motor implements Vehicle { @Override public void run() { System.out.println("motor run"); } }
Condition接口实现类
public class CarCondition implements Condition { @Override public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata metadata) { return ctx.getEnvironment().containsProperty("car"); } } public class MotorCondition implements Condition { @Override public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata metadata) { return ctx.getEnvironment().containsProperty("motor"); } }
测试类
// Spring配置类 @Configuration public class JavaConfig { @Bean @Conditional(MotorCondition.class) public Vehicle motor() { return new Motor(); } @Bean @Conditional(CarCondition.class) public Vehicle car() { return new Car(); } } // 主要测试类 public class TestCondition { public static void main(String[] args) { System.setProperty("motor", ""); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class); Vehicle vehicle = ctx.getBean(Vehicle.class); vehicle.run(); } }
扩展注解
注解
说明
@ConditionalOnBean
当容器里有指定Bean的条件下实例化
@ConditionalOnClass
当类路径下有指定的类的条件下实例化
@ConditionalOnExpression
基于SpEL表达式作为判断条件
@ConditionalOnJava
基于JVM版本作为判断条件
@ConditionalOnJndi
在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean
当容器里没有指定Bean的情况下实例化
@ConditionalOnMissingClass
当类路径下没有指定的类的情况下实例化
@ConditionalOnNotWebApplication
当前项目不是web项目的条件下实例化
@ConditionalOnResource
类路径有指定资源的条件下实例化
@ConditionalOnSingleCandidate
当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication
当前项目是Web项目的情况下的条件下
@ConditionalOnProperty
当属性有指定的值时进行实例化
@Import
与Spring XML文件中使用<import/>元素来进行模块化配置一样,@Import注解允许从另一个配置类加载@Bean定义。
@Configuration public class ConfigA { @Bean public A a() { return new A(); } } @Configuration @Import(ConfigA.class) public class ConfigB { @Bean public B b() { return new B(); } }
@ImportResource
在Spring应用程序中@Configuration注解是配置容器的主要机制,但仍然可能需要使用一些XML,该注解就是用来加载xml配置的。
@Configuration @ImportResource("classpath:/xxx/xxx/jdbc-config.xml") public class AppConfig { @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource() { return new DriverManagerDataSource(url, username, password); } }
@ImportResource配置等同于Spring XML配置中
<context:property-placeholder location="classpath:/xxx/xxx/jdbc.properties"/>
@Autowired
自动装配注解。
环境注解
@Profile
该注解可以根据当前环境,动态的激活和切换一系列组件的功能。通过设置Profile,只有被激活的Profile才会将其中所对应的Bean注册到Spring容器中。
@PropertySource
组件管理
@Component
@Service
@Repository
@Controller