Spring 高级话题

1. Spring Aware

1.1 基础知识

Spring 的依赖注入,即所有的 Bean 对 Spring 容器的存在是没有意识的。就说明 Bean 之间的耦合度很低。
但在实际项目中,要想用到 Spring 容器中所提供的资源就需要使 Bean 意识到容器 Spring 的存在,才能调用 Spring 所提供的资源,这就是所谓的 Spring Aware。若使用了 Spring Aware ,你的 Bean 将会和 Spring 框架耦合。
Spring 提供的 Aware 接口如下表所示:
|函数名|函数功能|
|--|:--😐--|
|BeanNameAware|获得到容器中 Bean 的名称|
|BeanFactoryAware|获得当前 bean factory,这样可以调用容器的服务|
|ApplicationContextAware*|当前application context,这样可以调用容器的服务|
|MessageSourceAware|获得message source,这样可以获得文本信息|
|ApplicationEventPublisherAware|应用事件发布器,可以发布事件|
|ResourceLoaderAware|获得资源加载器,可以获得外部资源文件|
Spring Aware 的目的是为了让 Bean 获得 Spring 容器的服务。因为 ApplicationContext 接口集成了 MessageSource 接口、ApplicationEventPublisher 接口和 ResourceLoader 接口,所以 Bean 继承 ApplicationContextAware 可以获得 Spring 容器的所有服务。

2. 多线程

2.1 基础知识

Spring 通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用 ThreadPoolTaskExecutor 可实现一个基于线程池的 TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过 @EnbaleAsync 开启对异步任务的支持,并通过在实际执行的 Bean 的方法中使用 @Async 注解来声明其是一个异步任务。

3. 计划任务

3.1 基础知识

首先通过在配置类中注解 @EnableScheduling 来开启对计划任务的支持,然后在要执行计划任务的方法上注解 @Scheduled ,声明这是一个计划任务。

4. 条件注解 @Conditional

4.1 基础知识

@Conditional 根据满足某一个特定条件创建一个特定的 Bean。比方说,当某一个 jar 包在一个类路径下的时候,自动配置一个或多个 Bean;或者只有某个 Bean 被创建才会创建另外一个 Bean。总的来说,就是根据特定条件来控制 Bean 的创建行为,这样我们可以利用这个特性来进行一些自动配置。

5. 组合注解与元注解

5.1 基础知识

元注解 其实就是可以注解到别的注解上的注解,被注解的注解称之为 组合注解,组合注解具备注解其上的元注解的功能。Spring 的很多注解都可以作为元注解,而且 Spring 本身已经有很多组合注解,如 @Configuration 就是一个组合 @Component 注解,表明这个类其实也是个 Bean。

6. @Enable* 注解的工作原理

6.1 基础知识

@Enable* 的例子有:@EnableAspectJAutoProxy 开启对 AspectJ 自动代理的支持;@EnableAsync 开启异步方法的支持;@EnableScheduling 开启计划任务的支持。

通过观察这些 @Enable* 注解的源码,我们发向所有的注解都有一个 @Import 注解,@Import 是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的 Bean。这些导入的配置方式主要分为以下三种类型。
(1)直接导入配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}

直接导入配置类 SchedulingConfiguraiton,这个类注解了 @Configuration,且注册了一个 scheduledAnnotationProcessor 的 Bean,源码如下:

@Configuration
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }

    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

(2)依据条件选择配置类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

AsyncConfigurationSelector 通过条件来选择需要导入的配置类,AsyncConfigurationSelector 的根接口为 ImportSelector,这个接口需重写 selectImports 方法,在此方法内进行事先条件判断。此例中,若 adviceMode 为 PROXY,则返回 ProxyAsyncConfiguration 这个配置类;若 activeMode 为 ASPECTJ,则返回 AspectJAsyncConfiguration 配置类,源码如下:

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    public AsyncConfigurationSelector() {
    }

    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}

(3)动态注册 Bean

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

AspectJAutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,它的作用是在运行时自动添加 Bean 到已有的配置类,通过重写方法:

registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

其中,AnnotationMetadata 参数用来获得当前配置类上的注解;BeanDefinitionRegistry 参数来注册 Bean。源码如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }
}
posted @ 2020-10-21 15:11  John_yan15  阅读(122)  评论(0编辑  收藏  举报