spring-注解驱动模式
spring web装配原理:
/** * WebApplicationInitializer Spring MVC 提供接口。 * * Spring中的web自动配置,也是可以, */ /** * AbstractContextLoaderInitializer 装配原理: * ContextLoaderListener是标准ServletContextListener的实现,监听ServletContext生命周期 * 启动-Servlet调用ServletContextListener实现类的默认方法。contextInitialized * 关闭-contextDestroyed */ /** * 运行到3.0中,web.xml配置文件中的ContextLoaderListener的方式可替换为AbstractContextLoaderInitializer的实现类 * 只需要实现createRootApplicationContext()方法 * * 注意:官方不推荐,使用AbstractContextLoaderInitializer进行,ContextLoaderListener允许重复注册到SevletContext。 * 如果Web应用的ClassPath下同时存在多个AbstractContextLoaderInitializer的实现类,抛异常IllegalStateException */
public class SpringWeb2 { /** * AbstractDispatcherServletInitializer装配原理 * * AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer的子类 * 补上没有注册DispatcherServlet,且没有实现父类的createRootApplicationContext() * 提供注册Filter模板方法 */ }
public class SpringWeb3 { /** * AbstractAnnotationConfigDispatcherServletInitializer装配原理 * 由于其父类AbstractDispatcherServletInitializer没有实现createRootApplicationContext() * 且需要子类实现:protected abstract WebApplicationContext createServletApplicationContext(); * * 而AbstractAnnotationConfigDispatcherServletInitializer直接实现了这两个方法 * * Root WebApplicationContext 和DispatcherServlet WebApplicationContext,都 * 采用注解驱动Web应用上下文实现:AnnotationConfigWebApplicationContext * */ /** * @Nullable:字段可以为空 */ }
import原理:
/** * @author beter.quan * @date 2020/10/20 2:20 下午 */ public class Theory { /** * 装载@Configuration Class,之前没有@ComponentScan,所以只能使用导入注解@Import * <context:annotation-config>对应的BeanDefinitionParser实现为:AnnotationConfigBeanDefinitionParser * 里面的方法:public BeanDefinition parse(Element element, ParserContext parserContext) * 没有直接解析BeanDefinition实例,调用了 * AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source); * * @Configuration 处理类ConfigurationClassPostProcessor 被封装为BeanDefinition并注册 * * ConfigurationClassPostProcessor xml配置驱动,注解驱动,都通过registerAnnotationConfigProcessors方法进行装载 * 且为最高优先级的BeanFactoryPostProcessor实现 * 注释解析 * p>This post processor is priority-ordered as it is important that any * * {@link Bean} methods declared in {@code @Configuration} classes have * * their corresponding bean definitions registered before any other * * {@link BeanFactoryPostProcessor} executes. * */ /** * AbstractApplicationContext#refresh()调用时,spring容器(BeanFactory)将ConfigurationClassPostProcessor 初始化为Bean * 随后invokeBeanFactoryPostProcessors实际上是使用: * PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors。这个方法里面; * private static void invokeBeanFactoryPostProcessors( * Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { * * for (BeanFactoryPostProcessor postProcessor : postProcessors) { * postProcessor.postProcessBeanFactory(beanFactory); * } * 调用接口的实现类的postProcessBeanFactory 方法 * 例如:ConfigurationClassPostProcessor#public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { * * 执行postProcessBeanFactory期间,ConfigurationClassParser是将spring BeanDefinition 进行注解原信息解析 * 解析的时候有两个parse重载方法, * @Configuration Class被ConfigurationClass 类所抽象,且处理方法为processConfigurationClass,但真正执行的是doProcessConfigurationClass * 在doProcessConfigurationClass方法中,@Import @ImportResource @Bean都被处理 * * * 解析后,ConfigurationClass集合将被ConfigurationClassBeanDefinitionReader 再次注册Spring Bean */ /** * ConfigurationClassBeanDefinitionReader 将@Import @ImportResource @Bean 一并注册 */ }
@Conditional
public class OwnerTest { /** * @Profile 配置条件装配---静态激活和配置 */ /** * @Conditional 指定多个或一个Condiction,所有condiction匹配----》条件成立 * * 这里的匹配只的是执行condiction返回true * boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); * ConditionContext这个参数包含Spring应用上下文相关的:BeanDefinitionRegistry,ConfigurableBeanFactory */ /** * 还有包括: * @ConditionalOnClass * @ConditionalOnBean * @ConditionalOnProperty * */ }
@conditional原理:
/** * @author beter.quan * @date 2020/10/20 8:34 下午 * @Conditional条件装配原理 */ public class TheoryConditional { /** * @Conditional(ProfileCondition.class) * public @interface Profile */ // ProfileCondition 代码内容一下 class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) {// if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) { return true; } } return false; } return true; } /** * @Conditional的统一处理实现ConditionEvaluator *ConditionEvaluator里面的方法 * public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) * 将AnnotatedTypeMetadata标注的所有COndition实例一一匹配,不匹配放回true 跳过。 */ } }
自定义conditional注解
先定义Condition,实现该接口
/** * @author beter.quan * @date 2020/10/20 8:10 下午 */ public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 获取ConditionalOnSystemProperty所有属性方法的值,这里就是属性名name对应的值 MultiValueMap<String, Object> attibutes = metadata.getAllAnnotationAttributes(ConditionalOnSystemProperty.class.getName()); String propertyName = (String) attibutes.getFirst("name"); String propertyValue = (String) attibutes.getFirst("value"); String systemPropertyValue = System.getProperty(propertyName); if (Objects.equals(systemPropertyValue, propertyValue)) { System.out.printf("属性名称 %s 找到匹配 %s", propertyName, propertyValue); return true; } return false; } }
编写自定义条件注解
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnSystemPropertyCondition.class) public @interface ConditionalOnSystemProperty { String name(); String value(); }
编写配置类
@Configuration public class CondictionalMessageConfiguration { @ConditionalOnSystemProperty(name = "language", value = "Chinese") @Bean("message") public String cMessage() { return "中文"; } @ConditionalOnSystemProperty(name = "language", value = "English") @Bean("message") public String eMessage() { return "English"; } }
测试
/** * @author beter.quan * @date 2020/10/20 8:22 下午 */ public class ConditionalTest { public static void main(String[] args) { System.setProperty("language", "English"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(CondictionalMessageConfiguration.class); context.refresh(); String message = context.getBean("message", String.class); System.out.println(message); } }