Spring-boot|如何自定义@Enable模块装配

背景

在学习SpringBoot的时候,我们会使用到@Enable***注解的地方,使用上也都是加在@Configuration 类注解的类上面,比如:
(1)@EnableAutoConfiguration 开启自动扫描装配Bean

(2)@EnableScheduling 开启计划任务的支持

(3)@EnableTransactionManagement 开启注解式事务的支持。

(4)@EnableCaching开启注解式的缓存支持。

(5)@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持,

(6) @EnableAsync 开启异步方法的支持

(7) @EnableWebMvc 开启Web MVC的配置支持。

(8) @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持。

(9)@EnableJpaRepositories 开启对Spring Data JPA Repository的支持。

通过上述的一些注解装配,可以省去了以往xml的很多配置。那么@Enable是如何实现的呢?

何为@Enable模块装配

Spring Framework 3.1 开始支持”@Enable 模块驱动“。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立
的单元。比如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管 理扩展)模块、Async(异步处
理)模块等。

模块装配是spring-boot 另外一种装配方式

在spring框架中@EnableWebMvc就是自动组装webMVc相关的组件

在spring-boot框架中@EnableAutoConfiguration 开启自动扫描装配Bean

  • 实现方式
    • 注解方式
    • 编程方式

查看@Enable的源码

查看这些注解的实现,我们发现每一个注解都有一个 @Import 注解。@Import注解在4.2之前只支持导入配置类,在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean。这也说明了,自动开启的实现,其实是导入了一些配置类。

@EnableWebMvc采用注解驱动方式

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

@EnableCaching采用接口编程方式

@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;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cache.annotation;

import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
    private static final String PROXY_JCACHE_CONFIGURATION_CLASS = "org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
    private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJCachingConfiguration";
    private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJJCacheConfiguration";
    private static final boolean jsr107Present;
    private static final boolean jcacheImplPresent;

    public CachingConfigurationSelector() {
    }

    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return this.getProxyImports();
        case ASPECTJ:
            return this.getAspectJImports();
        default:
            return null;
        }
    }

    private String[] getProxyImports() {
        List<String> result = new ArrayList(3);
        result.add(AutoProxyRegistrar.class.getName());
        result.add(ProxyCachingConfiguration.class.getName());
        if (jsr107Present && jcacheImplPresent) {
            result.add("org.springframework.cache.jcache.config.ProxyJCacheConfiguration");
        }

        return StringUtils.toStringArray(result);
    }

    private String[] getAspectJImports() {
        List<String> result = new ArrayList(2);
        result.add("org.springframework.cache.aspectj.AspectJCachingConfiguration");
        if (jsr107Present && jcacheImplPresent) {
            result.add("org.springframework.cache.aspectj.AspectJJCacheConfiguration");
        }

        return StringUtils.toStringArray(result);
    }

    static {
        ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
        jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
        jcacheImplPresent = ClassUtils.isPresent("org.springframework.cache.jcache.config.ProxyJCacheConfiguration", classLoader);
    }
}
package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

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

通过源码可以看到:

  • 首先@EnableCaching需要@Import一个CachingConfigurationSelector类
  • CachingConfigurationSelector又继承AdviceModeImportSelector
  • AdviceModeImportSelector又实现了ImportSelector
  • 所有,当我们自定以一个@Enable模块时需要ImportSelector的实现类

总结

从上述的两个@Enbale注解看,有两种实现方式。

下面我们分别进行自定义@enable模块

自定义@Enable模块

基于注解驱动实现

定义给一个配置类

package com.dsdj.springbootdemo1.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName MyConfiguration
 * @Description 我的自定义配置类
 * @Author dsdj
 * @Date 2018/12/16 下午10:01
 * @Version 1.0
 **/
@Configuration
public class MyConfiguration {
    /**
     * 方法名为 Bean 名称
     * @return
     */
    @Bean
    public String test() {
        return "自定义配置类";
    }
}

使用注解的方式,@Import需要导入一个配置类。

package com.dsdj.springbootdemo1.annotation;

import com.dsdj.springbootdemo1.configuration.MyConfiguration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @ClassName EnableMyConfig
 * @Description 自定义enable模块装载
 * @Author dsdj
 * @Date 2018/12/16 下午10:32
 * @Version 1.0
 **/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MyConfiguration.class})
public @interface EnableMyConfig {
}
package com.dsdj.springbootdemo1.bootstrap;

import com.dsdj.springbootdemo1.annotation.EnableMyConfig;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @ClassName EnableTestBootstrap
 * @Description 测试自定义@Enable模块装载
 * @Author dsdj
 * @Date 2018/12/16 下午10:34
 * @Version 1.0
 **/
// 使用自定义的Enable加载相关的bean模块
@EnableMyConfig
public class EnableTestBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableTestBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // 验证test bean是否存在
        String test = context.getBean("test",String.class);
        System.out.println("bean是否存在--->"+test);
    }
}

测试结果

2018-12-16 22:54:09.653  INFO 27590 --- [           main] c.d.s.bootstrap.EnableTestBootstrap      : Started EnableTestBootstrap in 0.593 seconds (JVM running for 1.045)
bean是否存在--->自定义配置类

总结

使用注解方式需要在@EnableXXX中直接添加配置类既即可以。

基于接口驱动实现

配置类

package com.dsdj.springbootdemo1.configuration;

import org.springframework.context.annotation.Bean;

/**
 * @ClassName MyConfiguration
 * @Description 配置类
 * @Author dsdj
 * @Date 2018/12/16 下午10:01
 * @Version 1.0
 **/
public class MyConfiguration {
    /**
     * 方法名为 Bean 名称
     * @return
     */
    @Bean
    public String test() {
        return "自定义配置类";
    }
}

自定义ImportSelector的实现类。

package com.dsdj.springbootdemo1.annotation;

import com.dsdj.springbootdemo1.configuration.MyConfiguration;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @ClassName MyImportSelector
 * @Description 自定义ImportSelector的实现类
 * @Author dsdj
 * @Date 2018/12/16 下午9:57
 * @Version 1.0
 **/
public class MyImportSelector implements ImportSelector {
    /**
     *
     * @param annotationMetadata
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 返回一个自定义类
        // 使用MyImportSelector导入MyConfiguration,而不是直接导入MyConfiguration
        return new String[]{MyConfiguration.class.getName()};
    }
}

自定义@Enable

package com.dsdj.springbootdemo1.annotation;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @ClassName EnableMyConfig
 * @Description 自定义enable模块装载
 * @Author dsdj
 * @Date 2018/12/16 下午10:32
 * @Version 1.0
 **/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MyImportSelector.class})
public @interface EnableMyConfig {
}

spring-boot启动类

package com.dsdj.springbootdemo1.bootstrap;

import com.dsdj.springbootdemo1.annotation.EnableMyConfig;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @ClassName EnableTestBootstrap
 * @Description 测试自定义@Enable模块装载
 * @Author dsdj
 * @Date 2018/12/16 下午10:34
 * @Version 1.0
 **/
// 使用自定义的Enable加载相关的bean模块
@EnableMyConfig
public class EnableTestBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableTestBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // 验证test bean是否存在
        String test = context.getBean("test",String.class);
        System.out.println("bean是否存在--->"+test);
    }
}

测试结果

package com.dsdj.springbootdemo1.bootstrap;

import com.dsdj.springbootdemo1.annotation.EnableMyConfig;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @ClassName EnableTestBootstrap
 * @Description 测试自定义@Enable模块装载
 * @Author dsdj
 * @Date 2018/12/16 下午10:34
 * @Version 1.0
 **/
// 使用自定义的Enable加载相关的bean模块
@EnableMyConfig
public class EnableTestBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableTestBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // 验证test bean是否存在
        String test = context.getBean("test",String.class);
        System.out.println("bean是否存在--->"+test);
    }
}

总结

  • 运行顺序

首先,EnableTestBootstrap启动,之后根据@EnableMyConfig注解,找到MyImportSelector类,找到selectImports方法,进行加载“test”bean操作。

  • 对比

这种实现方式,我们可以在装载bean中间进行一些操作,相对注解实现方式比较自由,编程实现方式可以有一些弹性操作。

总结

此时我们在回顾看开头的两种enbale装配是不是清晰了。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要获取spring-boot-admin-starter-client自定义的属性,可以按照以下步骤进行操作: 1. 首先,确保你的项目中已经添加了spring-boot-admin-starter-client包,并成功进行了打包。 2. 在项目的配置文件(比如application.properties或application.yml)中,可以使用以下格式设置spring-boot-admin-starter-client的自定义属性: ``` spring.boot.admin.client.custom-property=value ``` 其中,custom-property是你自定义的属性名,value是对应的属性值。 3. 可以通过在spring-boot-admin-starter-server项目的代码中使用@ConfigurationProperties注解来获取spring-boot-admin-starter-client的自定义属性。例如: ```java @ConfigurationProperties(prefix = "spring.boot.admin.client") public class MyConfiguration { private String customProperty; // getter and setter // other configurations } ``` 在这个配置类中,使用prefix属性指定了属性的前缀为"spring.boot.admin.client",这样就能获取到spring-boot-admin-starter-client的自定义属性。 4. 运行spring-boot-admin-starter-server项目时,就可以通过MyConfiguration类获取spring-boot-admin-starter-client的自定义属性了。 综上所述,你可以通过在项目的配置文件中设置属性,并使用@ConfigurationProperties注解获取这些自定义属性来获取spring-boot-admin-starter-client的自定义属性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [spring-boot-admin-client-2.6.2-API文档-中文版.zip](https://download.csdn.net/download/qq_36462452/85294581)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [spring-boot-admin-starter-client与spring-boot版本不匹配的坑](https://blog.csdn.net/mahui_1980/article/details/117528352)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值