SpringBoot(一)

SpringBoot(一)

最开始使用spring时,配置bean使用的都是xml格式, 在xml中写标签,属性也要用ref去指定.
后来接触到使用注解进行配置, 只要使用@Bean注解修饰这个方法,在扫包的时候扫到,该方法的返回值就会放到ioc容器中交给spring管理.


BeanDefinition

BeanDefinition, 即Bean定义.
可以通过@ComponentScan扫描的@Repository,@Service,@Component,@Controller和@Bean形式注入.会被spring识别出来后形成一个个的BeanDefinition, 这些BeanDefinition就是用来描述这个bean的一些属性.
BeanDefinition就类似于图纸, ioc会凭借图纸将对象new出来.

BeanFactoryPostProcessor

BeanFactoryPostProcessor,是用来扩展的.在bean还没有实例化之前,可以用来修改beanDefinition的属性.
比如可以修改BeanDefinition的lazyInit属性

public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext("com.example");
        System.out.println("---------");
        User user = annotationConfigApplicationContext.getBean("user", User.class);
        System.out.println(user.getName());
    }
}

@Component
public class User {

    private String name;

    public User (){
        System.out.println("user init");
    }

    public User (String name){
        this.name = name;
        System.out.println("user init");
    }
}

运行结果是

user init
---------
null

添加BeanFactoryPostProcessor实现类将该bean改成懒加载


@Component
public class UserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        BeanDefinition userDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        userDefinition.setLazyInit(true);
    }
}

运行输出:

---------
16:11:13.688 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
user init
null

可以看到,分割线打在创建ioc容器之后,获取bean之前.在没有使用BeanFactoryPostProcessor扩展之前,user类的实例在创建ioc容器的时候就已经创建出来了,在使用BeanFactoryPostProcessor扩展之后,当使用到了user这个bean才会去创建.

同样, BeanFactoryPostProcessor也可以用来修改bean的实现类.

// 添加两个user的实现类,并将ZhangSan设置为自动扫描
public class LiSi extends User {
    public LiSi(){
        setName("lisi");
        System.out.println("lisi init");
    }
}

// 在BeanFactoryPostProcessor中将user的改为实现类李四
@Component
public class UserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        BeanDefinition userDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        userDefinition.setBeanClassName(LiSi.class.getName());
    }
}

// 在main方法中获取zhangSan对应的bean并输出类名
public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext("com.example");
        System.out.println("---------");
        User user = annotationConfigApplicationContext.getBean("user", User.class);
        System.out.println(user.getClass().getName());
    }
}

输出结果:

user init
lisi init
---------
com.example.repository.LiSi

我们知道,在@ComponentScan扫描的bean,会默认调用该类的无参构造器.那么也可以通过BeanFactoryPostProcessor来改变调用的构造器.


@Component
public class UserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        GenericBeanDefinition userDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition("user");

        // 修改User类实例化时调用的构造器, 将"zhangsan"作为构造器参数传入
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, "zhangsan");
        userDefinition.setConstructorArgumentValues(constructorArgumentValues);
    }
}


// 添加User类含参构造器
@Data
@Component
public class User {

    private String name;

    public User (){
        System.out.println("user init");
    }

    public User(String name){
        this.name = name;
        System.out.println("user init with args");
    }
}

输出结果:

user init with args
---------
com.example.repository.User
zhangsan

@Import

  • 直接导入一个类
public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    }
}

@ComponentScan(basePackages = {"com.example"})
@Import(value = {User.class})
public class MainConfig {
}

输出结果 :

user init

@Import将User的BeanDefinition导入到BeanDefinitionMap中.

  • 导入BeanDefinitionRegister
@ComponentScan(basePackages = {"com.example"})
//@Import(value = {User.class})
@Import(value = {MainImportBeanDefinitionRegister.class})
public class MainConfig {
}

public class MainImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class);
        registry.registerBeanDefinition("user", rootBeanDefinition);

    }
}

输出结果 :

user init
  • 通过ImportSelector导入

*这种方法可以批量加载.(自动装配原理的核心)

public class MainImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 会返回一个数组, 数组的内容是类的全类名
        return new String[]{"com.example.repository.User"};
    }
}

输出结果 :

user init

搜索ImportSelector,在org.springframework.boot.autoconfigure.cache包下有一个CacheAutoConfiguration类中, 静态内部类实现了这个接口CacheConfigurationImportSelector.

static class CacheConfigurationImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        CacheType[] types = CacheType.values();
        String[] imports = new String[types.length];
        for (int i = 0; i < types.length; i++) {
            imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
        }
        return imports;
    }
}

缓存了CacheType这个枚举,会根据CacheConfigurations.getConfigurationClass(types[i]);依次去取缓存类的全类名.
CacheConfigurationImportSelector.class

CacheConfigurations中缓存的mapping


   转载规则


《SpringBoot(一)》 echi1995 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
String.contains()的实现 String.contains()的实现
前段时间有一个需求是, 有一个二进制文件, 在二进制文件中有一段是一张png图片. 现在已经有png文件二进制文件头和文件尾, 需要做的是在读取的byte[]数组中查找到这个文件头和文件尾的位置,并截出这段数组. 思前想后也想不到什么好的方
下一篇 
jvm的运行机制(二) 栈详解 jvm的运行机制(二) 栈详解
栈实际叫线程栈当main线程开始时,jvm就在jvm的内存区中,分配一块内存.存放主线程运行时的局部变量. 看一段代码 : public class Main { public static void main(String[]
  目录