美文网首页
Spring 系列篇之彻底了解Annotation的使用

Spring 系列篇之彻底了解Annotation的使用

作者: 雪狼_lykos | 来源:发表于2020-04-20 01:41 被阅读0次

    本篇文章介绍如何使用Annotation方式来配置元数据,当然在整个demo讲解过程中,你会看到xmlAnnotation共存。但是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或者混合使用时,顺序如下

      1. 选择@Autowired(required=true) 注解
      1. 选择@Autowired(required=false):存在多个优先选择参数多的
      1. @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.postProcessBeforeInitializationDestructionAwareBeanPostProcessor.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 组件扫描器。

    感谢

    感谢各位老铁花时间观看!
    欢迎留言指正!
    内容持续更新!

    相关文章

      网友评论

          本文标题:Spring 系列篇之彻底了解Annotation的使用

          本文链接:https://www.haomeiwen.com/subject/rbddphtx.html