当前位置: 首页 > 后端技术 > Java

你真的了解Spring注解驱动的前世今生吗?这篇文章将为您解惑!

时间:2023-04-01 20:09:29 Java

这篇文章,在Spring1.x到Spring5.x的迭代中,从现在的角度思考Spring注解驱动的发展历程,有助于我们更好的理解Spring注解设计。SpringFramework1.x处于SpringFramework1.x时代,1.2.0是这个时代的分水岭。当时Java5刚刚发布,业界都在使用Annotation技术。自然,SpringFramework也提供了支持。比如当时已经支持了。@Transactional等注解,但此时XML配置方式仍然是唯一的选择。在xml中添加Bean声明应用程序上下文.xml");TestServicetestService=(TestService)context.getBean("testService");System.out.println(测试服务);}}SpringFramework2.xSpringFramework2.x时代,2.0版本在注解中增加了@Required、@Repository、AOP相关的@Aspect等注解,同时也提升了XML配置能力,即可扩展的XML。比如Dubbo等开源框架,都是基于SpringXML扩展,完美的集成了Spring,从而降低了Dubbo的使用门槛。在2.x时代,2.5版本也是这个时代的分水岭。引入了一些核心的AnnotationAutowired依赖注入@Qualifier依赖查找@Component、@Service组件声明@Controller、@RequestMapring等springmvc注解。Spring2.x时代虽然提供了很多注解,但是仍然不是XML配置驱动的,比如,前者负责注册Annotation处理器,而后者负责扫描classpath下的指定包,将路径下Spring方式注解的类注册为SpringBeans。在applicationContext.xml中定义并添加注解声明@ServicepublicclassTestService{}testclasspublicclassXmlMain{publicstaticvoidmain(String[]args){ApplicationContextcontext=newFileSystemXmlApplicationContext("classpath:applicationContext.xml");TestServicetestService=(TestService)context.getBean("testService");System.out.println(测试服务);}}SpringFramework3.xSpringFramework3.0是一个里程碑时代,它的功能特性开始有了非常大的扩展,比如全面拥抱Java5和SpringAnnotation。更重要的是,它提供了配置类注解@Configuration,其首要任务是替代XML配置方式,但遗憾的是,SpringFramework3.0还没有引入替代XML元素的注解,而是选择了一个转换方法@ImportResource。@ImportResource允许导入遗留的XML配置文件,例如@ImportResource("classpath:/META-INF/spring/other.xml")@ConfigurationpublicclassSpringConfiguration{}并在SpringFrameworkd中提供AnnotationConfigApplicationContext注册来注册@Configuration类,通过组装解析配置类。在3.1版本中,引入了@ComponentScan来替代XML元素。这个注解虽然是一个小小的升级,但是对于spring在注解驱动领域来说是一个很大的进步。体现了Spring对免配置的支持。Configuration配置演示Configuration的注解应该对大家有用。是基于SpringIOC容器的配置类以JavaConfig形式使用的注解。因为SpringBoot本质上是一个spring应用,通过这个注解加载IOC容器的配置是正常的。所以在启动类中标注了@Configuration,也就是说它其实是一个IoC容器的配置类。举个很简单的例子测试代码ConfigurationDemo@ConfigurationpublicclassConfigurationDemo{@BeanpublicDemoClassdemoClass(){returnnewDemoClass();}}DemoClasspublicclassDemoClass{publicvoidsay(){System.out.println("say:HelloMic");}}ConfigurationMainpublicclassConfigurationMain{publicstaticvoidmain(String[]args){ApplicationContextapplicationContext=newAnnotationConfigApplicationContext(ConfigurationDemo.class);DemoClassdemoClass=applicationContext.getBean(DemoClass.class);demoClass.say();}}Component-scanComponentScan是最常见的注解,相当于xml配置文件中的。它的主要功能是扫描指定路径,识别出需要组装的类,自动组装到springIoc容器中。标识需要组装的类的形式主要有:@Component、@Repository、@Service、@Controller等注解标识的类。在spring-mvc项目中,创建一个单独的包路径,并创建一个OtherServcie。@ServicepublicclassOtherService{}在Controller中,注入一个OtherService的实例。这时候访问这个接口会报错,提示没有otherService的实例。@RestControllerpublic类HelloController{@AutowiredOtherServiceotherService;@GetMapping("/hello")publicStringhello(){System.out.println(otherService);return"你好古炮教育";}}添加conpoment-scan注解,再次访问,错误解决。@ComponentScan("com.gupaoedu")ComponentScan默认会将当前包下所有标有相关注解的类扫描到IoC容器中;导入注释是什么意思?联想到xml形式的形式的注解,可以理解其作用。导入是将多个容器配置组合成一个配置。JavaConfig中表达的意思是一样的。创建一个包并添加一个单独的配置}}此时运行测试方法,publicclassMainDemo{publicstaticvoidmain(String[]args){ApplicationContextac=newAnnotationConfigApplicationContext(SpringConfig.class);String[]defNames=ac.getBeanDefinitionNames();for(Stringname:defNames){System.out.println(name);}}}在另一个包路径下创建一个配置类。这个时候再运行之前的测试方法,打印OtherBean实例的时候,此时会报错,提示没有这个实例publicclassOtherBean{}@ConfigurationpublicclassOtherConfig{@BeanpublicOtherBeanotherBean(){返回新的OtherBean();}}修改springConfig,引入另一个配置@Import(OtherConfig.class)@ConfigurationpublicclassSpringConfig{@BeanpublicDefaultBeandefaultBean(){returnnewDefaultBean();}}再次运行测试方法以查看对象实例的输出。至此,我们了解了SpringFramework在注解驱动时代完全替代XML的解决方案。至此,Spring团队就此止步了吗?你太简单了。非配置虽然可以减少配置维护带来的麻烦,但是还是会有很多第三方构建的基础配置语句。也很麻烦,所以Spring退出了@Enable模块驱动。该特性的作用是将具有相同职责的功能组件以模块化的方式组装起来,进一步简化了SpringBean的配置。Enable模块通过spring提供的定时任务机制驱动我们实现定时任务的功能,并演示了使用Enable注解和不使用Enable的区别。让大家感受一下一些Enable注解的作用。使用EnableScheduing前在applicationContext.xml中添加定时调度配置编写任务处理类@ServicepublicclassTaskService{@Scheduled(fixedRate=5000)//通过@Scheduled声明该方法为定时任务,使用fixedRate属性执行publicvoid每隔一定时间reportCurrentTime(){System.out.println("每5秒执行一次"+newDate());}}写一个测试类}}使用EnableScheding后创建一个配置类@Configuration@ComponentScan("com.gupaoedu.controller")@EnableSchedulingpublicclassSpringConfig{}创建一个service@ServicepublicclassTaskService{@Scheduled(fixedRate=5000)//声明这个方法是通过@Scheduled定时任务,使用fixedRate属性定时执行publicvoidreportCurrentTime(){System.out.println("每5秒执行一次"+newDate());}}创建一个主要方法publicclassTaskMain{publicstaticvoidmain(String[]args){ApplicationContextcontext=newAnnotationConfigApplicationContext(SpringConfig.class);}}启动服务可以实现定时调度的功能。想想用Enable省略了哪一步?首先,让我们看一下不使用Enable的代码。里面会有一个。这个调度器是一个注解驱动,会被解析器AnnotationDrivenBeanDefinitionParser解析。在parse方法中,会有如下代码的定义builder.getRawBeanDefinition().setSource(源);该类用于解析的@Scheduled注解。好了,我们再看一下EnableScheduling注解,可以看到它会自动注册一个ScheduledAnnotationBeanPostProcessorbean。所以,通过这个例子,我想表达一下Enable注解的作用,可以帮助我们省去一些第三方模块的bean声明的配置。公共类SchedulingConfiguration{publicSc??hedulingConfiguration(){}@Bean(name={"org.springframework.context.annotation.internalScheduledAnnotationProcessor"})@Role(2)publicSc??heduledAnnotationBeanPostProcessorscheduledAnnotationProcessor(){returnNewScheduledProstme}4.xSpring4.x版本是完美时代的注解。主要改进了条件装配的能力,引入@Conditional注解,通过自定义Condition实现协同,弥补了上一版条件配置的不足。简单来说,Conditional提供了一个Bean加载条件判断,也就是说,如果不满足这个条件,那么@Bean声明的对象就不会被自动加载。它是如何使用的?,让我们简单了解一下它的基本用法。Conditional概述@Conditional是一个注解。让我们观察这个注解的声明,它可以接收一个条件数组。@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interface条件{类[]value();}@FunctionalInterfacepublicinterfaceCondition{booleanmatches(ConditionContextvar1,AnnotatedTypeMetadatavar2);}这个Condition是一个提供匹配器方法的功能接口。简单地说,它提供了一个匹配的判断规则。返回true表示该bean可以注入,返回false表示不能注入。Conditional的实战自定义一个Condition,逻辑比较简单,如果当前操作系统是Windows,则返回true,否则返回false判断,如果返回true,说明需要加载配置类或Bean//否则说明不加载Stringos=conditionContext.getEnvironment().getProperty("os.name");如果(os.contains(“Windows”)){返回真;}返回假;}}创建一个配置类并加载一个BeanClass@ConfigurationpublicclassConditionConfig{@Bean@Conditional(GpCondition.class)publicBeanClassbeanClass(){returnnewBeanClass();}}在BeanClass的bean声明方法中添加@Conditional(GpCondition.class),这里具体的条件就是我们自定义的GpCondition类。上面代码的意思是,如果GpCondition类中的matches返回true,则BeanClass会被加载到SpringIoC容器中运行测试方法publicclassConditionMain{publicstaticvoidmain(String[]args){(条件配置类.class);BeanClassbeanClass=context.getBean(BeanClass.class);System.out.println(beanClass);}}总结全面分析了Spring注解驱动后,不难发现,之所以我们现在可以很方便的在Spring中大量的功能都是基于注解来完成的,这要归功于注解驱动的各种努力Spring团队不断解决用户痛点。SpringBoot的自动装配机制也是在Spring注解驱动的基础上演化而来的。在接下来的内容中,我将具体分析一下SpringBoot的自动组装机制。版权声明:除特别声明外,本博客所有文章均采用CCBY-NC-SA4.0许可协议。转载请注明来自Mic带你学建筑!如果本文对您有帮助,请给个关注和点赞。您的坚持是我不断创作的动力。欢迎关注同名微信公众号获取更多技术干货!