栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot 原理 -自动配置

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot 原理 -自动配置

Condition条件判断功能,通过这个功能可以实现选择性的创建Bean操作。

问题1:
SpringBoot是如何知道创建RedisTemplate的?

新建模块
查看对应springboot的启动方法返回值,返回的就是IOC容器。


ctrl+alt+v获取返回值,改写为

package com.yy.springboot_condition;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        //获取Bean,redisTemplate
        Object redisTemplate = context.getBean("redisTemplate");
        System.out.println(redisTemplate);
    }

}

此时运行会报错,原因就是没有导入redis坐标

No bean named 'redisTemplate' available

pom.xml文件添加坐标后,再启动就可正常打印内存地址(如果还是不行就将maven reload一次)。

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

问题1:对于有导入的坐标,就会创建Bean。

问题2:
SpringBoot是如何知道导没导入坐标?

创建User.java

package com.yy.springboot_condition.domain;

public class User {
}

修改启动类
此时运行可正常打印User内存地址。

package com.yy.springboot_condition;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        //获取Bean,redisTemplate
//        Object redisTemplate = context.getBean("redisTemplate");
//        System.out.println(redisTemplate);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

先查看Conditional注解;
这个注解里面需要一个数组,而数组Class又必须是Condition或子类;

查看Condition有一个matches方法,返回一个boolean值,也就是说如果返回True,Spring才会创建对象。

现在尝试Jedis坐标导入后,再加载User的bean类,没导入,则不加载。

编写测试
创建ClassCondition.java实现Condition接口,matches方法中通过Class.forName 是否能够加载对应的字节码判定有没有坐标,然后返回true或false

package com.yy.springboot_condition.condition;


import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypemetadata;
import redis.clients.jedis.Jedis;

public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypemetadata metadata) {
        //1.需求:导入jedis坐标后创建Bean
        //根据Class.forName 是否能够加载对应的字节码判定有没有坐标
        Boolean flag=true;
        try {
            Class cls = Class.forName("redis.clients.jedis.Jedis");

        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
            flag=false;
        }
        return flag;
    }
}

创建UserConfig.java
用@Conditional指定,此Bean的判定方法。

package com.yy.springboot_condition.config;

import com.yy.springboot_condition.condition.ClassCondition;
import com.yy.springboot_condition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Userconfig {

    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
        return new User();
    }
}

最后通过修改pom.xml文件,来测试jedis有没有导入坐标,对于User Bean的创建。
问题2:Bean类通过@Conditional注解,指定的ClassCondition类中matches方法返回的结果来创建Bean。

目录结构

将Jedis改为动态字节码判定
自己定义一个注解ConditionOnClass

package com.yy.springboot_condition.condition;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
//将Conditional注解内容的三个注解也添加到此处,其中Target代表本注解可以写到类上或方法;Retention注解生效时机;documented生成javadoc文档。
@Conditional(ClassCondition.class)
//将原UserConfig中的此注解移到此将
public @interface ConditionOnClass {
    String[] value();
    //定义一个value属性,用于接收注解时设置的值
}

UserConfig.java修改为

package com.yy.springboot_condition.config;

import com.yy.springboot_condition.condition.ClassCondition;
import com.yy.springboot_condition.condition.ConditionOnClass;
import com.yy.springboot_condition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {

    @Bean
//    @Conditional(ClassCondition.class)
    @ConditionOnClass("redis.clients.jedis.Jedis")
    public User user(){
        return new User();
    }
}

再运行也可正常打印User内存,说明自己定义的注解运行正常。

修改ClassCondition 通过metadata动态获取参数

package com.yy.springboot_condition.condition;


import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypemetadata;
import redis.clients.jedis.Jedis;

import java.util.Map;

public class ClassCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypemetadata metadata) {
//        context可用于获取配置文件中属性值
//        Environment environment = context.getEnvironment();
//        environment.getProperty();


        //1.需求:导入jedis坐标后创建Bean
        //根据Class.forName 是否能够加载对应的字节码判定有没有坐标
//        Boolean flag=true;
//        try {
//            Class cls = Class.forName("redis.clients.jedis.Jedis");
//
//        } catch (ClassNotFoundException e) {
            e.printStackTrace();
//            flag=false;
//        }
//        return flag;
        //需求:导入通过注解属性值value指定坐标创建Bean
        //获取注解属性值
        Map map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        String[] value = (String[]) map.get("value");
        Boolean flag = true;
        try {
            for (String className : value) {
                Class cls = Class.forName(className);
            }

        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
}

添加pom.xml fastjson用于测试


            com.alibaba
            fastjson
            1.2.4
        

修改UserConfig 传com.alibaba.fastjson.JSON用于测试

package com.yy.springboot_condition.config;


import com.yy.springboot_condition.condition.ClassCondition;
import com.yy.springboot_condition.condition.ConditionOnClass;
import com.yy.springboot_condition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {


    @Bean
//    @Conditional(ClassCondition.class)
    @ConditionOnClass("com.alibaba.fastjson.JSON")
    public User user(){
        return new User();
    }
}


springboot自带,ConditionOnClass:有才对于字节码才创建Bean;ConditionOnClass:IOC中有这个Bean才创建指定的Bean;ConditionOnProperty:当你的配置文件中配置了指定属性才创建Bean;ConditionOnMissingBean:当IOC中没有这个Bean时才创建指定的Bean

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration部分代码
其中类上的ConditionalOnClass:有对应 字节码才会被创建Bean;
ConditionalOnMissingBean:没有对应redisTemplate Bean才会被创建;

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )

@ConditionOnProperty演示
重写UserConfig.java

package com.yy.springboot_condition.config;


import com.yy.springboot_condition.condition.ClassCondition;
import com.yy.springboot_condition.condition.ConditionOnClass;
import com.yy.springboot_condition.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {


//    @Bean
//    @Conditional(ClassCondition.class)
//    @ConditionOnClass("com.alibaba.fastjson.JSON")
//    public User user(){
//        return new User();
//    }

    @Bean
    @ConditionalOnProperty(name="zhang",havingValue = "yy")
    //当配置文件中有属性zhang且值为yy时被创建些Bean
    public User user(){
        return new User();
    }
}

直接测试会报错
在application.yml添加属性值后运行正常

zhang:
  yy
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/439821.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号