zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    ubuntu安装jdk的两种方法
    LeetCode 606. Construct String from Binary Tree (建立一个二叉树的string)
    LeetCode 617. Merge Two Binary Tree (合并两个二叉树)
    LeetCode 476. Number Complement (数的补数)
    LeetCode 575. Distribute Candies (发糖果)
    LeetCode 461. Hamming Distance (汉明距离)
    LeetCode 405. Convert a Number to Hexadecimal (把一个数转化为16进制)
    LeetCode 594. Longest Harmonious Subsequence (最长的协调子序列)
    LeetCode 371. Sum of Two Integers (两数之和)
    LeetCode 342. Power of Four (4的次方)
  • 原文地址:https://www.cnblogs.com/FondWang/p/12070817.html
Copyright © 2011-2022 走看看