【#yyds干货盘点#你真的知道Spring注解驱动的前世今生吗(这篇文章让你豁然开朗!)】时人不识凌云木,直待凌云始道高。这篇文章主要讲述#yyds干货盘点#你真的知道Spring注解驱动的前世今生吗?这篇文章让你豁然开朗!相关的知识,希望能为你提供帮助。
文章图片
本篇文章,从Spring1.x到Spring 5.x的迭代中,站在现在的角度去思考Spring注解驱动的发展过程,这将有助于我们更好的理解Spring中的注解设计。
Spring Framework 1.x在SpringFramework1.x时代,其中在1.2.0是这个时代的分水岭,当时java5刚刚发布,业界正兴起了使用Annotation的技术风,Spring Framework自然也提供了支持,比如当时已经支持了@Transactional等注解,但是这个时候,XML配置方式还是唯一选择。
- 在xml中添加Bean的声明
< bean name="testService" class="com.gupaoedu.controller.TestService"/>
- 测试
public class XmlMain { public static void main(String[] args) { ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); TestService testService=(TestService)context.getBean("testService"); System.out.println(testService); } }
在2.x时代,2.5版本也是这个时代的分水岭, 它引入了一些很核心的Annotation
- Autowired 依赖注入
- @Qualifier 依赖查找
- @Component、@Service 组件声明
- @Controller、@RequestMappring等spring mvc的注解
- 在applicationContext.xml中定义<
context:componet-scan>
< context:component-scan base-package="com.gupaoedu.controller"/>
- 添加注解声明
@Service public class TestService { }
- 测试类
public class XmlMain { public static void main(String[] args) { ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); TestService testService=(TestService)context.getBean("testService"); System.out.println(testService); } }
@ImportResource允许导入遗留的XML配置文件,比如
@ImportResource("classpath:/META-INF/spring/other.xml")
@Configuration
public class SpringConfiguration{}
并且在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容器的配置类。
举个非常简单的例子
- 测试代码
ConfigurationDemo @Configuration public class ConfigurationDemo { @Bean public DemoClass demoClass(){ return new DemoClass(); } } DemoClass public class DemoClass {public void say(){ System.out.println("say: Hello Mic"); } } ConfigurationMain public class ConfigurationMain {public static void main(String[] args) { ApplicationContext applicationContext= new AnnotationConfigApplicationContext (ConfigurationDemo.class); DemoClass demoClass=applicationContext.getBean(DemoClass.class); demoClass.say(); } }
标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。
- 在spring-mvc这个工程中,创建一个单独的包路径,并创建一个OtherServcie。
@Service public class OtherService { }
- 在Controller中,注入OtherService的实例,这个时候访问这个接口,会报错,提示没有otherService这个实例。
@RestController public class HelloController {@Autowired OtherService otherService; @GetMapping("/hello") public String hello(){ System.out.println(otherService); return "Hello Gupaoedu"; } }
- 添加conpoment-scan注解,再次访问,错误解决。
@ComponentScan("com.gupaoedu")
Import注解
import注解是什么意思呢? 联想到xml形式下有一个
&
lt;
import resource/&
gt;
形式的注解,就明白它的作用了。import就是把多个分来的容器配置合并在一个配置中。在JavaConfig中所表达的意义是一样的。- 创建一个包,并在里面添加一个单独的configuration
public class DefaultBean { } @Configuration public class SpringConfig {@Bean public DefaultBean defaultBean(){ return new DefaultBean(); } }
- 此时运行测试方法,
public class MainDemo {public static void main(String[] args) { ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class); String[] defNames=ac.getBeanDefinitionNames(); for(String name:defNames){ System.out.println(name); } } }
- 在另外一个包路径下在创建一个配置类。此时再次运行前面的测试方法,打印OtherBean实例时,这个时候会报错,提示没有该实例
public class OtherBean { } @Configuration public class OtherConfig {@Bean public OtherBean otherBean(){ return new OtherBean(); } }
- 修改springConfig,把另外一个配置导入过来
@Import(OtherConfig.class) @Configuration public class SpringConfig {@Bean public DefaultBean defaultBean(){ return new DefaultBean(); } }
- 再次运行测试方法,即可看到对象实例的输出。
Enable模块驱动我们通过spring提供的定时任务机制来实现一个定时任务的功能,分别拿演示在使用Enable注解和没使用Enable的区别。让大家感受一些Enable注解的作用。
使用EnableScheduing之前
- 在applicationContext.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:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> < context:component-scan base-package="com.gupaoedu.controller"/> < !--AnnotationDrivenBeanDefinitionParser--> < task:annotation-driven scheduler="scheduler"/> < !-- 定时器开关--> < task:scheduler id="scheduler" pool-size="5"/> < /beans>
- 编写任务处理类
@Service public class TaskService {@Scheduled(fixedRate = 5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行 public void reportCurrentTime(){ System.out.println("每隔5秒执行一次 "+new Date()); } }
- 编写测试类
public class TestTask {public static void main(String[] args) { ApplicationContext applicationContext=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); } }
- 创建一个配置类
@Configuration @ComponentScan("com.gupaoedu.controller") @EnableScheduling public class SpringConfig { }
- 创建一个service
@Service public class TaskService { @Scheduled(fixedRate = 5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行 public void reportCurrentTime(){ System.out.println("每隔5秒执行一次 "+new Date()); } }
- 创建一个main方法
public class TaskMain { public static void main(String[] args) { ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class); } }
- 启动服务即可实现定时调度的功能。
首先我们看没使用Enable的代码,它里面会有一个
<
task:annotation-driven scheduler="scheduler"/>
这个scheduler是一个注解驱动,会被AnnotationDrivenBeanDefinitionParser 这个解析器进行解析。
在parse方法中,会有如下代码的定义
builder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor");
builder.getRawBeanDefinition().setSource(source);
这个类是用来解析@Scheduled注解的。
ok,我们再看一下EnableScheduling注解,我们可以看到,它会自动注册一个ScheduledAnnotationBeanPostProcessor的bean。所以,通过这个例子,就是想表达Enable注解的作用,它可以帮我们省略一些第三方模块的bean的声明的配置。
public class SchedulingConfiguration {
public SchedulingConfiguration() {
}@Bean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@Role(2)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
Spring Framework 4.xSpring 4.x版本,是注解的完善时代,它主要是提升条件装配能力,引入了@Conditional注解,通过自定义Condition实现配合,弥补了之前版本条件化配置的短板。
简单来说,Conditional提供了一个Bean的装载条件判断,也就是说如果这个条件不满足,那么通过@Bean声明的对象,不会被自动装载进来,具体是怎么用的呢?,先来简单带大家了解一下它的基本使用。
Conditional的概述@Conditional是一个注解,我们观察一下这个注解的声明, 它可以接收一个Condition的数组。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<
? extends Condition>
[] value();
}
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
这个Condition是一个函数式接口,提供了一个matchers的方法,简单来说,它就是提供了一个匹配的判断规则,返回true表示可以注入bean,返回false表示不能注入。
Conditional的实战
- 自定义个一个Condition,逻辑比较简单,如果当前操作系统是Windows,则返回true,否则返回false
public class GpCondition implements Condition{ @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //此处进行条件判断,如果返回 true,表示需要加载该配置类或者 Bean //否则,表示不加载 String os=conditionContext.getEnvironment().getProperty("os.name"); if(os.contains("Windows")){ return true; } return false; } }
- 创建一个配置类,装载一个 BeanClass
@Configuration public class ConditionConfig { @Bean @Conditional(GpCondition.class) public BeanClass beanClass(){ return new BeanClass(); } }
- 在 BeanClass 的 bean 声明方法中增加@Conditional(GpCondition.class),其中具体的条件是我们自定义的 GpCondition 类。上述代码所表达的意思是,如果 GpCondition 类中的 matchs 返回 true,则将 BeanClass 装载到 Spring IoC 容器中
- 运行测试方法
public class ConditionMain { public static void main(String[] args) { AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(ConditionConfig.class); BeanClass beanClass=context.getBean(BeanClass.class); System.out.println(beanClass); } }
而Spring Boot的自动装配机制,也是在Spring 注解驱动的基础上演化而来,在后续的内容中,我会专门分析Spring Boot的自动装配机制。
推荐阅读
- #yyds干货盘点# kubeadm添加新master或node
- Azure Virtual Desktop-3-配置Azure文件共享及配置用户配置文件漫游
- 部署OpenGauss DB一主二备集群
- Java 项目中使用 Resilience4j 框架实现异步超时处理
- Linux进程和任务管理
- #yyds干货盘点# RPCRESTGraphQL接口类型概述
- #yyds干货盘点# 6. Python 元组,不可变的列表,滚雪球学 Python
- 用户增长分析模型,该如何搭建
- #yyds干货盘点#数据分析从零开始实战,Pandas读写CSV数据