• Spring Boot之从Spring Framework装配掌握SpringBoot自动装配


    Spring Framework模式注解

      模式注解是一种用于声明在应用中扮演“组件”角色的注解。如 Spring Framework 中的 @Repository 标注在任何类上 ,用于扮演仓储角色的模式注解。

    模式注解(角色注解)

    Spring Framework 注解场景说明
    @Component 通用组件模式注解
    @Controller Web 控制器模式注解
    @Service 服务模式注解
    @Repository 数据仓储模式注解
    @Configuration 配置类模式注解

    在Spring中进行装配 方式

    <?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-
    context.xsd">
    <!-- 激活注解驱动特性 -->
    <context:annotation-config />
    <!-- 找寻被 @Component 或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean -->
    <context:component-scan base-package="com.imooc.dive.in.spring.boot" />
    </beans>

    在Spring中基于Java注解配置方式

    @ComponentScan(basePackages = "com.imooc.dive.in.spring.boot")
    public class SpringConfiguration {
    ...
    }

    自定义模式注解

    上面这些都是spring自带的注解装配。那么如何自定义注解装配呢?

    利用@Component模式注解具有“派生性”和“层次性”,我们能够自定义创建Bean注解

    第一步:自定义SpringBean注解

    //@Component 派生性
    @Target
    ({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Repository
    public @interface FirstLevelRepository { String value() default ""; }
    //@Component 层次性
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @FirstLevelRepository
    public @interface SecondLevelRepository {
    String value() default "";
    }
     

    第二步:将注解作用在自定义Bean上。

    
    
    // @SecondLevelRepository(value = "myFirstLevelRepository") 这个注解和下面的注解作用相同,都是将类交给spring容器管理,这个注解体现@Component层次性
    @FirstLevelRepository (value = "myFirstLevelRepository")
    public class MyFirstLevelRepository {
    }

    第三步:测试是否可以spring容器中获取到自定义Bean

    import com.example.springboot01.repository.MyFirstLevelRepository;
    import org.springframework.boot.WebApplicationType;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan(basePackages = "com.example.springboot01.repository") //basePackages的值就是注解@FirstLevelRepository所注解类的包名
    public class RepositoryBootstrap {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext  context = new SpringApplicationBuilder(RepositoryBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
    
            MyFirstLevelRepository myFirstLevelRepository = context.getBean("myFirstLevelRepository",MyFirstLevelRepository.class);
            System.out.println("======"+myFirstLevelRepository);
            //关闭上下文
            context.close();
        }
    }

    //或者
    @SpringBootApplication
    public class SpringBoot01Application {
        public static void main(String[] args) {
            ConfigurableApplicationContext run = SpringApplication.run(SpringBoot01Application.class, args);
            MyFirstLevelRepository myFirstLevelRepository = run.getBean("myFirstLevelRepository", MyFirstLevelRepository.class);
            System.out.println("myFirstLevelRepository" + myFirstLevelRepository.toString());
            run.close();
        }
    }

    Spring @Enable 模块注解

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

    @Enable 注解模块举例

    框架实现@Enable 注解模块激活模块
    Spring Framework @EnableWebMvc Web MVC 模块
      @EnableTransactionManagement 事务管理模块
      @EnableCaching Caching 模块
      @EnableMBeanExport JMX 模块
      @EnableAsync 异步处理模块
      @EnableWebFlux Web Flux 模块
      @EnableAspectJAutoProxy AspectJ 代理模块
    Spring Boot @EnableAutoConfiguration 自动装配模块
      @EnableManagementContext Actuator 管理模块
      @EnableConfigurationProperties 配置属性绑定模块
      @EnableOAuth2Sso OAuth2 单点登录模块
    Spring Cloud 
    @EnableEurekaServer
    Eureka服务器模块
     
    @EnableConfigServer
    配置服务器模块
     
    @EnableFeignClients 
    Feign客户端模块
     
    @EnableZuulProxy 
    服务网关 Zuul 模块 
     
    @EnableCircuitBreaker 
    服务熔断模块 

    @Enable实现方式

    • 注解驱动方式
    • 接口编程方式

    自定义注解驱动方式

    第一步:实现自定义注解@EnableHelloWorld

    /**
     *  激活 HelloWorld 模块
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(HelloWorldConfiguration.class) //指定激活的类
    //@Import(HelloWorldImportSelector.class)
    public @interface EnableHelloWorld {
    }

     第二步:创建MyBeanConfig配置类

    /**
     * HelloWorld 配置
     * 要激活的类
     */
    public class HelloWorldConfiguration {
    
        //激活的Bean
        @Bean
        public String helloWorld() { // 方法名即 Bean 名称
            return "Hello,World 2020";
        }
    
    }

    第三步:在应用中测试使用@EnableMyBean

    /**
     * {@link EnableHelloWorld} 引导类
     */
    @EnableHelloWorld  //自定义的注解中,会自动激活标志的类
    public class EnableHelloWorldBootstrap {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloWorldBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
    
            // helloWorld Bean 是否存在
            String helloWorld =
                    context.getBean("helloWorld", String.class);
    
            System.out.println("helloWorld Bean : " + helloWorld);
    
            // 关闭上下文
            context.close();
        }
    }

    //或者
    @SpringBootApplication
    @EnableHelloWorld
    public class SpringBoot01Application {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context =
                    new SpringApplicationBuilder(SpringBoot01Application.class)
                            .web(WebApplicationType.NONE)
                            .run(args);
            String bean = context.getBean("helloWorld", String.class);
            System.out.println("bean: " + bean);
            context.close();
        }
    }

    自定义@Enable接口编程方式

    第一步:实现自定义注解@EnableMyBean

    /**
     *  激活 HelloWorld 模块
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(HelloWorldImportSelector.class) 
    public @interface EnableHelloWorld {
    }

    PS:注意@Import(HelloWorldConfigSelector.class)导入的类和@Enable注解驱动导入的不一样,这里导入的是一个实现了ImportSelector接口的类

    /**
     * HelloWorld {@link ImportSelector} 实现
     * ImportSelector接口是至spring中导入外部配置的核心接口,
     * 在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在
     * 主要作用是收集需要导入的配置类
     */
    public class HelloWorldImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            importingClassMetadata.getAnnotationTypes().forEach(System.out::println);
            return new String[]{MyBeanConfig.class.getName()};
        }
    }
    PS:在HelloWorldConfigSelector类中我们可以自定义复杂的逻辑,这里我们仅仅简单返回MyBeanConfig配置类。

    第二步:创建MyBeanConfig配置类

    /**
     * HelloWorld 配置
     * 要激活的类
     */
    public class HelloWorldConfiguration {
        //激活的Bean
        @Bean
        public String helloWorld() { // 方法名即 Bean 名称
            return "Hello,World 2020";
        }
    }

    第三步:测试使用@EnableMyBean

    /**
     * {@link EnableHelloWorld} 引导类
     */
    @EnableHelloWorld  //自定义的注解中,会自动激活标志的类
    public class EnableHelloWorldBootstrap {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloWorldBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
    
            // helloWorld Bean 是否存在
            String helloWorld =
                    context.getBean("helloWorld", String.class);
    
            System.out.println("helloWorld Bean : " + helloWorld);
    
            // 关闭上下文
            context.close();
        }
    }

    PS:其实@Enable接口的实现方式和@Enable注解实现方式是基本一样的,只不过多了一个步骤,方便我们更灵活地进行编写逻辑。

    Spring Framework条件装配

    从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断

    Spring 注解场景说明起始版本
    @Profile 配置化条件装配 3.1
    @Conditional 编程条件装配 4.0

    自定义@Profile配置化条件装配

    第一步:自定义创建某服务不同的@Profile实现类

    /**
     * 计算服务
     */
    public interface CalculateService {
    
        /**
         * 从多个整数 sum 求和
         * @param values 多个整数
         * @return sum 累加值
         */
        Integer sum(Integer... values);
    }
    /**
     * Java 7 for 循环实现 {@link CalculateService}
     */
    @Profile("Java7")
    @Service
    public class Java7CalculateService implements CalculateService {
    
        @Override
        public Integer sum(Integer... values) {
            System.out.println("Java 7 for 循环实现 ");
            int sum = 0;
            for (int i = 0; i < values.length; i++) {
                sum += values[i];
            }
            return sum;
        }
    
        public static void main(String[] args) {
            CalculateService calculateService = new Java7CalculateService();
            System.out.println(calculateService.sum(1,2,3,4,5,6,7,8,9,10));
        }
    
    }
    /**
     * Java 8 Lambda 实现 {@link CalculateService}
     */
    @Profile("Java8")
    @Service
    public class Java8CalculateService implements CalculateService {
    
        @Override
        public Integer sum(Integer... values) {
            System.out.println("Java 8 Lambda 实现");
            int sum = Stream.of(values).reduce(0, Integer::sum);
            return sum;
        }
    
        public static void main(String[] args) {
            CalculateService calculateService = new Java8CalculateService();
            System.out.println(calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        }
    }

    第二步:在构建Spring容器指定配置

    /**
     * {@link CalculateService} 引导类*/
    @SpringBootApplication(scanBasePackages = "com.example.springboot01.service") //将类放入容器中
    public class CalculateServiceBootstrap {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .profiles("Java8") //指定那个实现
                    .run(args);
    
            // CalculateService Bean 是否存在
            CalculateService calculateService = context.getBean(CalculateService.class);
    
            System.out.println("calculateService.sum(1...10) : " +
                    calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    
            // 关闭上下文
            context.close();
        }
    }

    自定义@Conditional 编程条件装配

    第一步:创建一个自定义注解

    /**
     * Java 系统属性 条件判断
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Documented
    @Conditional(OnSystemPropertyCondition.class)
    public @interface ConditionalOnSystemProperty {
    
        /**
         * Java 系统属性名称
         * @return
         */
        String name();
    
        /**
         * Java 系统属性值
         * @return
         */
        String value();
    }

     PS:注意@Conditional注解,将会找到MyOnConditionProperty类的matches方法进行条件验证

     第二步:创建该注解的条件验证类,该类实现Condition接口

    /**
     * 系统属性条件判断
     */
    public class OnSystemPropertyCondition implements Condition {
    
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //注解传过来的所有值
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
            //获取name的值
            String propertyName = String.valueOf(attributes.get("name"));
            //获取value的值
            String propertyValue = String.valueOf(attributes.get("value"));
            //根据name值获取对应name的系统值
            String javaPropertyValue = System.getProperty(propertyName);
            //判断value值是否与name属性对应的系统值相同
            return propertyValue.equals(javaPropertyValue);
        }
    }

    第三步:在Spring应用中应用条件装配

    /**
     * 系统属性条件引导类
     */
    public class ConditionalOnSystemPropertyBootstrap {
    
        @Bean
        @ConditionalOnSystemProperty(name = "java.runtime.name", value = "Java(TM) SE Runtime Environment aa")  //对应系统java.runtime.name属性的值不是value中的,多了aa,所以装配失败
        public String helloWorld() {
            return "Hello,World";
        }
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionalOnSystemPropertyBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
            // 通过名称和类型获取 helloWorld Bean
            String helloWorld = context.getBean("helloWorld", String.class);
    
            System.out.println("helloWorld Bean : " + helloWorld);
    
            // 关闭上下文
            context.close();
        }
    }

    PS:本例自定义的MyConditionOnPropertyAnnotion在应用中装配的时候可以指定name和value值,该值将会在实现了Condition借口的matches进行条件验证,如果验证通过,则在Spring容器中装配该Bean,反之则不装配。

    SpringBoot 自动装配

    在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的。其中底层使用了一系列的Spring Framework手动装配的方法来构成Spring Boot自动装配。

    自定义SpringBoot自动装配

    1. 激活自动装配 - @EnableAutoConfiguration
    2. 实现自动装配 - XXXAutoConfiguration
    3. 配置自动装配实现 - META-INF/spring.factories

    第一步:实现自动装配 - XXXAutoConfiguration

    /**
     * HelloWorld 自动装配
     */
    @Configuration // Spring 模式注解装配
    @EnableHelloWorld // Spring @Enable 注解装配
    @ConditionalOnSystemProperty(name = "java.runtime.name", value = "Java(TM) SE Runtime Environment") // 条件装配
    public class HelloWorldAutoConfiguration {
    }

    第二步:配置自动装配实现 - META-INF/spring.factories

    放到resources文件夹中

    # 自动装配
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    com.example.springboot01.configuration.HelloWorldAutoConfiguration

    第三步:激活自动装配- @EnableAutoConfiguration

    /**
     * {@link EnableAutoConfiguration} 引导类
     */
    @EnableAutoConfiguration
    public class EnableAutoConfigurationBootstrap {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
    
            // helloWorld Bean 是否存在
            String helloWorld =
                    context.getBean("helloWorld", String.class);
    
            System.out.println("helloWorld Bean : " + helloWorld);
    
            // 关闭上下文
            context.close();
    
        }
    }

    本章总结

    本章我们主要了解了Spring Framework的模式注解装配,@Enable装配和条件装配。对于SpringBoot的自动装配我们仅仅做了一下演示,遵循SpringBoot装配的三个步骤,我们就可以运行SpringBoot的自动装配。但是对于SpringBoot为什么要遵循这三个步骤?自动装配的原理?我们不知所以然,所以下一章节我们仍然以SpringBoot的自动装配为主题,对SpringBoot的底层源码做剖析。

    感谢:https://www.cnblogs.com/jimisun/p/10070123.html

  • 相关阅读:
    Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物
    Redis(五)--- Redis的持久化RDB与AOF
    Redis(四)--- Redis的命令参考
    Redis(三)--- Redis的五大数据类型的底层实现
    Redis(二)--- Redis的底层数据结构
    Redis(一)--- 概述
    jquery dataTable 获取某行数据
    对这学期软件工程课的评价
    求二维数组的最大子数组
    求整数数组中和最大的子数组的和
  • 原文地址:https://www.cnblogs.com/FondWang/p/12070817.html
Copyright © 2020-2023  润新知