本篇文章介绍如何使用Annotation方式来配置元数据,当然在整个demo讲解过程中,你会看到xml与Annotation共存。但是Annotation注入会在XML注入之前解析。 因此,XML配置将覆盖通过两种方法连接的属性的Annotation。
人在一起叫聚会,心在一起叫团队
目录
1 开启IoC Annotation
1.1 创建自定义标签模拟实现<context:annotation-config/>
1.1.1 定义xsd
1.1.2 向Spring告知xsd所在的位置 - 配置spring-schemas
1.1.3 创建NamespaceHandler
1.1.4 创建BeanDefinitionParser
2 常用注解
2.1 @Autowired
2.1.1 Autowired 在构造函数上的使用
2.1.2 Autowired 在属性和方法使用
2.1.3 Autowired 配合使用 Optional @Nullable
2.2 @Configuration 、 @Import 和 @Bean
2.2.1 <context:component-scan > 相关配置说明
2.2.1.1 过滤器配置
2.2.1.2 annotation-config
2.2.1.3 scope-resolver 和 scoped-proxy
2.2.1.4 name-generator
2.3 @Component
2.4 @PostConstruct 和 @PreDestroy
2.5 @Profile
3 AnnotationConfigApplicationContext
1 开启IoC Annotation
我们在使用Annotation 之前必须先把这个功能开启。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
添加上面的配置后,Spring执行过程中,会默认帮我们注入
ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,EventListenerMethodProcessor,DefaultEventListenerFactory。
那么问题来了,Spring是如何通过这么一个简单的配置,帮我们做了这么多事呢?别急,别慌!
具体流程与细节在这里就不多说了,我先带着大家也实现一个类似功能。
1.1 创建自定义标签模拟实现<context:annotation-config/>
就像<context:annotation-config>一样,首先我们要先创建一个自定义的元素<lykos:auto-config>。
1.1.1 定义xsd
在使用自定义元素之前,我们需要先创建一个XSD文件(存放在resources资源文件夹下),用来定义我们自定义的元素结构,因为XSD定义xml文件结构不是这里的重点,所以我们只是简单的定义auto-config元素,对于xsd这里不做过多的解释。注意lykos单词出现的地方,我们称为命名空间,这里可以随变定义自己的唯一的命名空间,命名空间将会在<beans>中引入并使用
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.lykosliu.org/spring/lykos"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.lykosliu.org/spring/lykos"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="https://www.springframework.org/schema/beans/spring-beans.xsd"/>
<xsd:element name="auto-config">
</xsd:element>
</xsd:schema>
创建好xsd文件之后,我们在beans.xml文件引入并使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:lykos="http://www.lykosliu.org/spring/lykos"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.lykosliu.org/spring/lykos
https://www.lykosliu.org/spring/lykos/spring-lykos.xsd">
<lykos:auto-config></lykos:auto-config>
</beans>
同样注意引入lykos命名空间。此时引入可能会报错,报红。如果老铁是用的idea,你需要做下一个操作
idea 引入xsd
1.1.2 向Spring告知xsd所在的位置 - 配置spring-schemas
操作完上面的几步后,我们只是能在xml文件中可以添加自定义标签元素,此时Spring解析器还无法识别我们的标签,我们还需要告诉Spring 我们定义的命名空间应该如何解析,也就是告诉Spring我们的xsd在哪个位置。
在resources/META-INF下添加文件spring-schemas.xml,内容如下:
http\://www.lykosliu.org/spring/lykos/spring-lykos.xsd=xsd/spring-lykos.xsd
注意 :需要使用\转义
1.1.3 创建NamespaceHandler
Spring 对 xml解析都是封装成NamespaceHandler解析器,上面我们自己定义了新的命名空间,当然我们也要为其创建一个解析器。
public class LykosNamespaceHandlerSupport extends NamespaceHandlerSupport {
//注册解析器,元素解析器
@Override
public void init() {
registerBeanDefinitionParser("auto-config", new CustomBeanDefinitionParser());
}
}
这里使用了registerBeanDefinitionParser向NamespaceHandler注册了一个auto-config元素解析器(BeanDefinitionParser)。创建好NamespaceHandler后还需要在META-INF目录下添加spring.handlers文件,内容如下:
http\://www.lykosliu.org/spring/lykos=com.lykos.handle.LykosNamespaceHandlerSupport
1.1.4 创建BeanDefinitionParser
BeanDefinitionParser主要是将元素标签解析成BeanDefinition对象,并将其注册到Spring IoC中去。这里我们就使用它来注册我们需要的扩展对象。
public class CustomBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
RootBeanDefinition def = new RootBeanDefinition(MyBeanPostProcessor.class);
def.setSource(source);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionRegistry registry = parserContext.getRegistry();
registry.registerBeanDefinition("myBeanPostProcessor", def);
return null;
}
}
到这里相信老铁们应该知道<context:annotation-config/>实现的大致流程了,其最终也是使用AnnotationConfigBeanDefinitionParser帮我自动注册的几个扩展对象。
当然Spring 不只annotation-config还有很多,如:component-scan,spring-configured,property-placeholder等,还有其它元素解析器,建义老铁查看ContextNamespaceHandler对象。
上面的讲解建议老铁们,可以从
DefaultBeanDefinitionDocumentReader.parseBeanDefinitions为突破口查看源码。
2 常用注解
2.1 @Autowired
autowired 自动装配注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface Autowired {
boolean required() default true;
}
由上面的定义可以知道,autowired可以用于构造函数,方法,属性等上面。
required用于标注依赖项是否必须的,默认是必须的。比如下面的例子中,如果容器中没有Person实例,容器不会报错也不会执行setPerson方法,一旦将reuired设为true(或者不写),容器就会报NoSuchBeanDefinitionException: No qualifying bean异常。
@Autowired(required = false)
private void setPerson(Person person){
this.person = person;
}
2.1.1 Autowired 在构造函数上的使用
有且仅有一个@Autowired(required=true)添加在构造函数上,可以使用多个@Autowired(required=false)用在其它构造函数上
默认情况下,构造函数上不加@Autowired 容器选择的规则是
- 只有一个构造函数:如果有参数,那参数对象在容器中必须注册
- 多个构造函数,必须要有无参构造函数
全部使用@Autowired或者混合使用时,顺序如下
- 选择@Autowired(required=true) 注解
- 选择@Autowired(required=false):存在多个优先选择参数多的
- @Autowired(required=false)参数都不满足的,选无参构造函数,如果没有则报错。
2.1.2 Autowired 在属性和方法使用
autowired在属性和方法上其它没有什么需要说明的,这里只是稍微说明下,@Autowired还可以用于集合、数组、Map
2.1.3 Autowired 配合使用 Optional @Nullable
Autowired可以配合使用Optional,Nullable,目的和@Autowired(required=false)是一样的,但是区别是如果没有匹配对象@Autowired(required=false)处理逻辑是不执行,而Optional,Nullable是可以继续执行方法的。
@Autowired
private void setPerson(Optional<Person> personOptional){
if(personOptional.isPresent()){
this.person = personOptional.get();
}
}
@Autowired
private void setTelevision(@Nullable Television television){
this.television = television;
}
2.2 @Configuration 、 @Import 和 @Bean
此三个注解类似我们配置文件中<beans>,<import>,<bean>标签的功能。此功能是ConfigurationClassPostProcessor为我们引入的,原理是使用BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry实现的。还包含Component,ComponentScan,ImportResource注解。下面是普通用法。
@Import(TargetImportConfigration.class)
@Configuration
public class CustomConfigration {
@Bean
public Television television(){
return new Television();
}
}
当然如果要能正常使用@Configuration我还必须要告诉容器应该自动注册哪些包下面的Configuration类,这里我们需要添加<context:component-scan base-package="包地址,这里可以写多个用,;等分隔" /> 配置,此配置默认是引入了我们前面重点讲的<context:annotation-config/>配置。其实现原理可以查看ComponentScanBeanDefinitionParser,之所以容器可以识别我们的配置类,是因为ComponentScanBeanDefinitionParser在创建ClassPathBeanDefinitionScanner扫描器时默认会添加AnnotationTypeFilter(Component.class)扫描过滤器,而@Configuration又是包含了@Component的组合注解。
2.2.1 <context:component-scan > 相关配置说明
2.2.1.1 过滤器配置
include-filter扫描器的过滤器配置,目的是筛选出配置类。
exclude-filter刚与include-filter功能相反。
type配置过滤器类型,支持:annotation,assignable,aspectj,regex,custom。
expression是配合type使用的实际值。
<context:component-scan base-package="com.lykos.ioc.chapter4" >
<context:include-filter type="" expression=""/>
<context:exclude-filter type="annotation" expression=""/>
</context:component-scan>
其它细节可以查看原码ComponentScanBeanDefinitionParser.parseTypeFilters
2.2.1.2 annotation-config
是否开启annotation-config配置,默认开启。
2.2.1.3 scope-resolver 和 scoped-proxy
scope-resolver自定义ScopeMetadata解析配置类需要实现ScopeMetadataResolver接口,与scope-proxy不能同步使用,scope-proxy设置代理模式,有targetClass,interfaces,no选项值。
2.2.1.4 name-generator
设置bean命名器,自定义命名器需要实现BeanNameGenerator接口。
2.3 @Component
上面有提到过,@Component注解,默认容器扫描会扫描包下面所有@Component类。我们有很多常用注解都以@Component做为元注解,@Service,@Repository,@Configuration,@Controller,@ControllerAdvice,@RestController等。
2.4 @PostConstruct 和 @PreDestroy
这两个注解功能是分别是在Bean初始化完成后和销毁时执行的方法,此功能是CommonAnnotationBeanPostProcessor引入的,其原理也分别是BeanPostProcessor.postProcessBeforeInitialization和DestructionAwareBeanPostProcessor.postProcessBeforeDestruction实现的。
2.5 @Profile
此注解可以用于方法或者类上面,主要是功能是依照active profiles来选择性实例化Bean对象。
我们可以通过以下方式来设置active profiles
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
-Dspring.profiles.active="profile1,profile2"
当我们使用@Profile(@Conditional作为元注解)时,容器实际会使用ProfileCondition.matches筛选出符合active profiles配置的标识对象。
3 AnnotationConfigApplicationContext
之前的讲解都是基于ClassPathXmlApplicationContext xml的启动配置,还是依赖于xml配置文件,而AnnotationConfigApplicationContext可以让我们达到零配置文件启动IoC容器。
下面列举常用三种启动模式
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(App.class);
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(App1.class,App2.class);
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(App.class);
ac.refresh();
AnnotationConfigApplicationContext有两个重要属性
- AnnotatedBeanDefinitionReader 主要是向容器中注入组件类,比如常用的
AnnotationConfigProcessors这个与我们之前讲解的annotation-config一样 - ClassPathBeanDefinitionScanner 组件扫描器。
感谢
感谢各位老铁花时间观看!
欢迎留言指正!
内容持续更新!















网友评论