「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」
本篇文章,从Spring1.x到Spring 5.x的迭代中,站在现在的角度去思考Spring注解驱动的发展过程,这将有助于我们更好的理解Spring中的注解设计。
Spring Framework 1.x
在SpringFramework1.x时代,其中在1.2.0是这个时代的分水岭,当时Java5刚刚发布,业界正兴起了使用Annotation的技术风,Spring Framework自然也提供了支持,比如当时已经支持了@Transactional等注解,但是这个时候,XML配置方式还是唯一选择。
- 在xml中添加Bean的声明
1 | xml复制代码<bean name="testService" class="com.gupaoedu.controller.TestService"/> |
- 测试
1 | java复制代码public class XmlMain { |
Spring Framework 2.x
Spring Framework2.x时代,2.0版本在Annotation中添加了@Required、@Repository以及AOP相关的@Aspect等注解,同时也提升了XML配置能力,也就是可扩展的XML,比如Dubbo这样的开源框架就是基于Spring XML的扩展来完美的集成Spring,从而降低了Dubbo使用的门槛。
在2.x时代,2.5版本也是这个时代的分水岭, 它引入了一些很核心的Annotation
- Autowired 依赖注入
- @Qualifier 依赖查找
- @Component、@Service 组件声明
- @Controller、@RequestMappring等spring mvc的注解
尽管Spring 2.x时代提供了不少的注解,但是仍然没有脱离XML配置驱动,比如context:annotation-config context:componet-scan , 前者的职责是注册Annotation处理器,后者是负责扫描classpath下指定包路径下被Spring模式注解标注的类,将他们注册成为Spring Bean
- 在applicationContext.xml中定义context:componet-scan
1 | xml复制代码<context:component-scan base-package="com.gupaoedu.controller"/> |
- 添加注解声明
1 | java复制代码@Service |
- 测试类
1 | java复制代码public class XmlMain { |
Spring Framework 3.x
Spring Framework3.0是一个里程碑式的时代,他的功能特性开始出现了非常大的扩展,比如全面拥抱Java5、以及Spring Annotation。更重要的是,它提供了配置类注解@Configuration, 他出现的首要任务就是取代XML配置方式,不过比较遗憾的是,Spring Framework3.0还没有引入替换XML元素context:componet-scan的注解,而是选择了一个过渡方式@ImportResource。
@ImportResource允许导入遗留的XML配置文件,比如
1 | java复制代码@ImportResource("classpath:/META-INF/spring/other.xml") |
并且在Spring Frameworkd提供了AnnotationConfigApplicationContext注册,用来注册@Configuration Class,通过解析Configuration类来进行装配。
在3.1版本中,引入了@ComponentScan,替换了XML元素Context:component-scan , 这个注解虽然是一个小的升级,但是对于spring 来说在注解驱动领域却是一个很大的进步,至此也体现了Spring 的无配置化支持。
Configuration配置演示
- Configuration这个注解大家应该有用过,它是JavaConfig形式的基于Spring IOC容器的配置类使用的一种注解。因为SpringBoot本质上就是一个spring应用,所以通过这个注解来加载IOC容器的配置是很正常的。所以在启动类里面标注了@Configuration,意味着它其实也是一个IoC容器的配置类。
举个非常简单的例子
- 测试代码
1 | java复制代码ConfigurationDemo |
Component-scan
ComponentScan这个注解是大家接触得最多的了,相当于xml配置文件中的context:component-scan。 它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到spring的Ioc容器中。
标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。
- 在spring-mvc这个工程中,创建一个单独的包路径,并创建一个OtherServcie。
1 | java复制代码@Service |
- 在Controller中,注入OtherService的实例,这个时候访问这个接口,会报错,提示没有otherService这个实例。
1 | java复制代码@RestController |
- 添加conpoment-scan注解,再次访问,错误解决。
1 | java复制代码@ComponentScan("com.gupaoedu") |
ComponentScan默认会扫描当前package下的的所有加了相关注解标识的类到IoC容器中;
Import注解
import注解是什么意思呢? 联想到xml形式下有一个<import resource/>
形式的注解,就明白它的作用了。import就是把多个分来的容器配置合并在一个配置中。在JavaConfig中所表达的意义是一样的。
- 创建一个包,并在里面添加一个单独的configuration
1 | java复制代码public class DefaultBean { |
- 此时运行测试方法,
1 | java复制代码public class MainDemo { |
- 在另外一个包路径下在创建一个配置类。此时再次运行前面的测试方法,打印OtherBean实例时,这个时候会报错,提示没有该实例
1 | java复制代码public class OtherBean { |
- 修改springConfig,把另外一个配置导入过来
1 | java复制代码@Import(OtherConfig.class) |
- 再次运行测试方法,即可看到对象实例的输出。
至此,我们已经了解了Spring Framework在注解驱动时代,完全替代XML的解决方案。至此,Spring团队就此止步了吗?你们太单纯了。虽然无配置化能够减少配置的维护带来的困扰,但是,还是会存在很对第三方组建的基础配置声明。同样很繁琐,所以Spring 退出了@Enable模块驱动。这个特性的作用是把相同职责的功能组件以模块化的方式来装配,更进一步简化了Spring Bean的配置。
Enable模块驱动
我们通过spring提供的定时任务机制来实现一个定时任务的功能,分别拿演示在使用Enable注解和没使用Enable的区别。让大家感受一些Enable注解的作用。
使用EnableScheduing之前
- 在applicationContext.xml中添加定时调度的配置
1 | xml复制代码<?xml version="1.0" encoding="UTF-8"?> |
- 编写任务处理类
1 | java复制代码@Service |
- 编写测试类
1 | java复制代码public class TestTask { |
使用EnableScheding之后
- 创建一个配置类
1 | java复制代码@Configuration |
- 创建一个service
1 | java复制代码@Service |
- 创建一个main方法
1 | java复制代码public class TaskMain { |
- 启动服务即可实现定时调度的功能。
思考使用Enable省略了哪个步骤呢?
首先我们看没使用Enable的代码,它里面会有一个
1 | xml复制代码<task:annotation-driven scheduler="scheduler"/> |
这个scheduler是一个注解驱动,会被AnnotationDrivenBeanDefinitionParser 这个解析器进行解析。
在parse方法中,会有如下代码的定义
1 | java复制代码 builder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"); |
这个类是用来解析@Scheduled注解的。
ok,我们再看一下EnableScheduling注解,我们可以看到,它会自动注册一个ScheduledAnnotationBeanPostProcessor的bean。所以,通过这个例子,就是想表达Enable注解的作用,它可以帮我们省略一些第三方模块的bean的声明的配置。
1 | java复制代码public class SchedulingConfiguration { |
Spring Framework 4.x
Spring 4.x版本,是注解的完善时代,它主要是提升条件装配能力,引入了@Conditional注解,通过自定义Condition实现配合,弥补了之前版本条件化配置的短板。
简单来说,Conditional提供了一个Bean的装载条件判断,也就是说如果这个条件不满足,那么通过@Bean声明的对象,不会被自动装载进来,具体是怎么用的呢?,先来简单带大家了解一下它的基本使用。
Conditional的概述
@Conditional是一个注解,我们观察一下这个注解的声明, 它可以接收一个Condition的数组。
1 | java复制代码@Target({ElementType.TYPE, ElementType.METHOD}) |
1 | java复制代码@FunctionalInterface |
这个Condition是一个函数式接口,提供了一个matchers的方法,简单来说,它就是提供了一个匹配的判断规则,返回true表示可以注入bean,返回false表示不能注入。
Conditional的实战
- 自定义个一个Condition,逻辑比较简单,如果当前操作系统是Windows,则返回true,否则返回false
1 | java复制代码public class GpCondition implements Condition{ |
- 创建一个配置类,装载一个 BeanClass
1 | java复制代码@Configuration |
- 在 BeanClass 的 bean 声明方法中增加@Conditional(GpCondition.class),其中具体的条件是我们自定义的 GpCondition 类。上述代码所表达的意思是,如果 GpCondition 类中的 matchs 返回 true,则将 BeanClass 装载到 Spring IoC 容器中
- 运行测试方法
1 | java复制代码public class ConditionMain { |
总结
经过对Spring注解驱动的整体分析,不难发现,我们如今之所以能够非常方便的基于注解来完成Spring中大量的功能,得益于Spring团队不断解决用户痛点而做的各种努力。
而Spring Boot的自动装配机制,也是在Spring 注解驱动的基础上演化而来,在后续的内容中,我会专门分析Spring Boot的自动装配机制。
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
Mic带你学架构
!
如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注同名微信公众号获取更多技术干货!
本文转载自: 掘金