Spring5源码分析------基础知识了解(二)

代码地址
spring01
觉得博主还可以给个Star

1.@Import的使用
在com.entity下创建UserEntity.java

package com.entity;
public class UserEntity {
    
    

}

com.config下创建MyConfig.java

package com.config;

import com.entity.UserEntity;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(UserEntity.class)
public class MyConfig {
    
    

}

com下创建Application.java

package com;

import com.config.MyConfig;
import com.entity.UserEntity;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        UserEntity userEntity = applicationContext.getBean("userEntity", UserEntity.class);
        System.out.println(userEntity);
    }
}

测试代码如上,运行结果:
在这里插入图片描述
我们会发现,我们的操作步骤和昨天差不多,但是却会报错出“没有找到‘UserEntity’对象”,接下来,我们把注入的bean名称全部打印出来

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++) {
    
    
    System.out.println(beanDefinitionNames[i]);
}

运行结果:
在这里插入图片描述
我们会发现它的bean名称为“com.entity.UserEntity”,这时候我们才能发现,与我们寻常使用注解注入bean的名字不同,它注入的名称是全路径,所以我们的代码应该改为

UserEntity userEntity = applicationContext.getBean("com.entity.UserEntity", UserEntity.class);

这样运行的结果就没有任何问题了

2.@EnableXXX
我们见过很多的Enable开头的注解,比如@EnableScheduling,@EnableAsync等等,我们可以进去看看,它们的内部是怎样的
在这里插入图片描述
在这里插入图片描述
我们都可以看到一个很熟悉的身影@Import,是的,他就是核心,主要就是为了导入外部的类

3.FactoryBean
FactoryBean表示往IOC容器存储(注入对象),BeanFactory表示从IOC容器中获取对象

com.entity创建FactoryBeanEntity.java

package com.entity;
public class FactoryBeanEntity {
    
    
}

com.config创建FactoryBeanConfig.java并且实现接口FactoryBean

package com.config;

import com.entity.FactoryBeanEntity;
import org.springframework.beans.factory.FactoryBean;

public class FactoryBeanConfig implements FactoryBean<FactoryBeanEntity> {
    
    
    @Override
    public FactoryBeanEntity getObject() throws Exception {
    
    
        return new FactoryBeanEntity();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return FactoryBeanEntity.class;
    }

    @Override
    public boolean isSingleton() {
    
    
        // 是否为单例 true为单例 false 为多例
        return true;
    }
}

在MyConfig.java中的@Import加入“FactoryBeanConfig.class”
下面我们测试一下是否注入成功
Application.java加入代码

FactoryBeanEntity factoryBeanEntity = applicationContext.getBean("factoryBeanEntity", FactoryBeanEntity.class);
System.out.println(factoryBeanEntity);

运行结果:
在这里插入图片描述
与第一个知识点一样,也报错没有找到bean,那么我们把所有的beanName打印出来
在这里插入图片描述
发现我们里面并没有“factoryBeanEntity”,而是只出现了“com.config.FactoryBeanConfig”
那我们从“com.config.FactoryBeanConfig”去一探究竟,更改一下代码

// 此处打断点
Object factoryBeanConfig = applicationContext.getBean("com.config.FactoryBeanConfig");
System.out.println(factoryBeanConfig);

我们会发现
在这里插入图片描述
这样就明了了,原来注入的名字是“com.config.FactoryBeanConfig”,而对应的对象是“FactoryBeanEntity”,那么我们就可以直接强转类型试试

FactoryBeanEntity factoryBeanConfig = (FactoryBeanEntity)applicationContext.getBean("com.config.FactoryBeanConfig");
System.out.println(factoryBeanConfig);

结果是可以强转为FactoryBeanEntity,说明FactoryBeanEntity注入成功

4.@Conditional的使用
@Conditional我们主要用于条件注册,比如我们在不同的操作系统中,要使用不同的bean
com.config下创建ConditionConfig.java实现Condition接口

package com.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class ConditionConfig implements Condition {
    
    
    /**
     *
     * @param conditionContext 获取当前上下文
     * @param annotatedTypeMetadata 获取当前注解的信息
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    
    
        // 获取当前系统环境
        Environment environment = conditionContext.getEnvironment();
        String osName = environment.getProperty("os.name");
        if(osName.equals("Windows 10")){
    
    
            // 注册
            return true;
        }
        // 不进行注册
        return false;
    }
}

com.entity创建Win10Entity.java

package com.entity;

public class Win10Entity {
    
    
}

MyConfig.java

package com.config;

import com.entity.UserEntity;
import com.entity.Win10Entity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
//@EnableAsync
//@EnableScheduling
@Import({
    
    UserEntity.class,FactoryBeanConfig.class})
public class MyConfig {
    
    

    @Bean
    @Conditional(ConditionConfig.class)
    public Win10Entity win10Entity(){
    
    
        return new Win10Entity();
    }
}

运行并打印所有beanName
在这里插入图片描述
就这样,我们可以实现通过@Conditional控制对象在 不同环境下的注册

5.ImportSelector接口使用
ImportSelector选择性注册bean
com.entity创建Select01Entity.java

package com.entity;

public class Select01Entity {
    
    
}

com.config创建ImportSelectorConfig并实现接口ImportSelector

package com.config;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class ImportSelectorConfig implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
        return new String[]{
    
    "com.entity.Select01Entity"};
    }
}

打印除所有的beanName,可以发现“com.entity.Select01Entity”注入成功
在这里插入图片描述
5.ImportBeanDefinitionRegistrar使用
com.config包下创建ImportBeanDefinitionRegistrarConfig.java并且实现接口ImportBeanDefinitionRegistrar

package com.config;

import com.entity.SmsEntity;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;

public class ImportBeanDefinitionRegistrarConfig implements ImportBeanDefinitionRegistrar {
    
    
    /**
     * @param annotationMetadata 注解信息
     * @param beanDefinitionRegistry bean的基本信息
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
    
    
        // 手动注册信息到ioc
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(SmsEntity.class);
        beanDefinitionRegistry.registerBeanDefinition("smsEntity",rootBeanDefinition);
    }
}

在MyConfig.java的@Import加入ImportBeanDefinitionRegistrarConfig.class即可

问题思考

  1. Spring IOC线程是否安全
  2. @Service和@Component区别
  3. @Primary和@Qualifier区别
    1.SpringIOC线程安全,底层通过beanDefinitionMap存放
    2.两个注解底层相同,只为写的时候更好区分
    3.两者使用在有个实现类均用一个接口的情况,@Qualifier指定使用实现类,@Primary指定两个实现类优先使用某个实现类

猜你喜欢

转载自blog.csdn.net/weixin_43911969/article/details/113846033