Springboot的手动装配----@Enable模块装配原理

Springboot的手动装配原理

Springboot加载bean的过程分为了

  • 手动装配
  • 自动装配
    两种方式,而手动装配又分为了
  • 模式注解装配
  • @Enable模块装配
  • 条件装配
    三种方式,这篇博客主要探讨@Enable模块装配。
    既然总结原理,就写个人理解,在分析源码的基础上讨论个人案例,以@EnableWebMvc和@EnableCaching为例。

1.@EnableWebMvc(基于注解驱动实现)

源码分析:

package org.springframework.web.servlet.config.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

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

可以很直观看到,其实@EnableWebMvc其实就是为了导入DelegatingWebMvcConfiguration配置类,某种程度上,可以认为@EnableWebMvc其实和@Import({DelegatingWebMvcConfiguration.class})是对等的,只是起了一个有意义的名字而已。
根据原理实现案例:

假设有一个将要被装配的类

public class World {

    public String say() {
        System.out.println("hello world!");
        return "hello world";
    }
}

那么我们首先要把这个类在一个配置类中进行注册登记

@Configuration
public class HelloConfig {

    @Bean
    World world() {
        System.out.println("加载HelloConfig");
        return new World();
    }
}

此时就需要一个用于导入配置类的类,这里不妨起名为EnableHello,也就是jdk源码中的EnableWebMvc

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({HelloConfig.class})//这句最为重要,导入了配置类
public @interface EnableHello {
}

现在我们的注解驱动已经有了,就可以直接使用了

@EnableHello // 引入注解驱动
@RestController
@SpringBootApplication
public class Demo01Application {

	// 注入World对象
    @Autowired
    World world;
    
    public static void main(String[] args) {
        SpringApplication.run(Demo01Application.class, args);
    }

    @GetMapping("/test")
    public Object test() {
        return "Hello World";
    }

}

2.@EnableCaching(基于接口驱动实现)

当我们需要开启springboot项目的缓存功能时候,我们直接打开@EnableCaching注解就可以注入Caching 模块,这时候我们就可以开心使用@Cacheable、@CacheEvict等注解。
源码分析:

package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;

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

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

上面最重要的一句代码就是@Import({CachingConfigurationSelector.class}),你会发现,其实使用@EnableCaching,就是为了导入CachingConfigurationSelector.class这配置类。

而这个CachingConfigurationSelector,其实实现了ImportSelector接口,ImportSelector接口是spring中导入外部配置的核心接口,有一个方法selectImports,其实就是根据EnableCaching的元数据属性(proxyTargetClass、mode、order),选择出需要转配的Configuration。

package org.springframework.context.annotation;

import java.util.function.Predicate;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

根据原理实现案例:
假设有两个用于注册登记bean的配置类HelloConfig1和HelloConfig2,我们就需要一个选择配置类的类HelloConfigSelector,相当于jdk源码中的CachingConfigurationSelector类,我们这里起名叫HelloConfigSelector,让其实现ImportSelector,并重写selectImports方法

public class HelloConfigSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        // 获取元数据
        Map<String, Object> metaData = annotationMetadata.getAnnotationAttributes(EnableHelloSelector.class.getName());

        Boolean isLinux = (Boolean) metaData.get("isLinux");
		
		// 由isLinux的值选择配置类
        return new String[]{isLinux ? HelloConfig1.class.getName() : HelloConfig2.class.getName()};
    }
}

接下来需要一个用于导入选择配置类的类,我们这里叫EnableHelloSelector,相当于jdk源码中的EnableCaching

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

    boolean isLinux();
}

最后我们就可以根据元数据属性选择性条件判断注入需要的配置了

@EnableHelloSelector(isLinux = false)// 由isLinux的值在配置类中的selectImports方法选择注入哪个配置
@RestController
@SpringBootApplication
public class Demo01Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo01Application.class, args);
    }

    @GetMapping("/test")
    public Object test() {
        return "Hello World";
    }

}

总结:@EnableCaching比@EnableWebMvc多了一步选择的过程,根据元数据属性选择注入哪个配置,比较灵活。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值