SLK的个人博客

  • 首页

  • 搜索
LRU Stack ThreadLocal Nacos RejectedExecutionHandler Executor RocketMQ ConcurrentHashMap CyclicBarrier Semaphore CountDownLatch canal Unsafe Atomic BlockingQueue AQS ReentrantLock Synchronized MESI Volatile JMM BufferPool Explain MySQL 常量池 Arthas JVM调优 三色标记 CMS ParNew 垃圾收集器 G1 Java Redis Android HTTPDNS DNS ioc 爬虫 seleniumhq 推荐引擎 Mahout IM Netty Vert.x HashMap 梯子 翻墙 V2ray Docker 搜索引擎 SpringBoot elasticsearch

SpringBoot IOC 容器使用

发表于 2020-04-18 | 分类于 Java | 0 | 阅读次数 247

IOC 使用 (XML 引用)

定义一个接口和它的实现类

public interface FactoryInterface {
    String getName();
}

@Setter
public class FactoryInterfaceImpl implements FactoryInterface {

    private String name;

    @Override
    public String getName() {
        return name;
    }
}

在resource下新建beans.xml 文件 并且设置成员变量name值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="cn.pencilso.study.studyioc.xmlbean.FactoryInterfaceImpl">
        <property name="name" value="大白"/>
    </bean>
</beans>

获取bean对象 最后输出 “大白”

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
FactoryInterface factoryInterface = classPathXmlApplicationContext.getBean(FactoryInterface.class);
String name = factoryInterface.getName();
System.out.println("name:" + name);

使用注解导入xml的bean方式 @ImportResource

@SpringBootApplication
@ImportResource(locations = {"classpath:beans.xml"})
public class StudyIocApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(StudyIocApplication.class, args);
        FactoryInterface factoryInterface = run.getBean(FactoryInterface.class);
        String name = factoryInterface.getName();
        System.out.println("name:" + name);
    }
}

IOC使用 (注解+过滤器)

创建配置类, 添加注解,扫描base包为 "cn.pencilso.study" 并且导入有注解Repository 和 Service注解的Bean。

useDefaultFilters 默认过滤器关闭。设置为false 。

@ComponentScan(basePackages = {"cn.pencilso.study"}
        , includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class, Service.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {


}

添加测试需要的bean

@Repository
public class UserDao {
}

@Service
public class UserService {
}

@Component
public class HttpPlugin {
}

尝试打印加载到容器里的bean对象

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}


-----最后输出   userDao  userService已经在容器里了 但是 httpPlugin 并没有在容器里。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userDao
userService

自定义过滤器 只要是 Component Service Repository 注解的都引入到bean容器

public class CompoentFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("className:" + className);

        Set<String> annotationTypes = metadataReader.getAnnotationMetadata().getAnnotationTypes();
        if (annotationTypes.contains(Component.class.getName()) ||
                annotationTypes.contains(Repository.class.getName()) ||
                annotationTypes.contains(Service.class.getName())
        ) {
            return true;
        }
        return false;
    }
}
@ComponentScan(basePackages = {"cn.pencilso.study"}
        , includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {
}

最后输出 这次httpPlugin也出来了 。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
httpPlugin
userDao
userService

排除bean引入,如果需要排除某些bean加载到容器里,可以用excludeFilters 这个过滤器,使用方法跟includeFilters 一致。

@ComponentScan(basePackages = {"cn.pencilso.study"}
        , excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {

}

IOC 容器单例、多例

Bean 默认是单例的,且是饿汉模式。容器启动的时候,就会加载Bean对象。

public class UserModel {
    public UserModel() {
        System.out.println("user model Initialization");
    }
}

@Configuration
public class StudyIocConfig {

    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
    }
}

-- 输出内容为  可以看到 并没有去调用Bean,但是Bean也创建了。这是因为默认为饿汉单例。
user model Initialization
  

-- 也可以设置为懒汉模式  通过@Lazy 注解 如下 
-- 通过添加Lazy注解后,该Bean将会在第一次使用的的时候才会创建对象,容器启动的时候则不会创建对象。
@Configuration
public class StudyIocConfig {
    @Lazy
    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

接下来多次获取Bean 并且比对hashcode

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
        UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println(user1 == user2);
    }
}
--比对结果输出为true  表明hashcode 一致
user model Initialization
true

如果说有多例的需求应该怎么做呢,可以采用 @Scope 注解

singleton: 单例的(默认)

prototype: 多例的

request: 同一次请求

session: 同一个会话级别

@Configuration
public class StudyIocConfig {

    @Scope(value = "prototype")
    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
        UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println(user1 == user2);
    }
}

--最后输出结果,构造方法执行了两次,比对结果也是false,这时候已经是多例了。 每次获取bean都是新的对象。
user model Initialization
user model Initialization
false  

Conditional 通过条件控制bean是否被加载到容器

public class StudyIocConfig {

    @Bean
    public UserModel userModel() {
        return new UserModel();
    }

    @Conditional(value = {CustomCondition.class})
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
}

//条件为 容器里已经加载了userModel  
public class CustomCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getBeanFactory().containsBean("userModel");
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}
--最后打印输出  有三个自定义bean,studyIocConfig、userModel、userDao
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userModel
userDao
  
  
 --尝试去掉 userModel的bean 后打印如下  这个时候userDao已经不会加载到容器了,因为条件是先加载userModel到容器。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig

Import 导入bean对象

/**
 * 可以直接指定需要导入的bean class 比如说UserModel.class
 * 其次可以通过ImportSelector 接口来进行批量装载bean
 * 也可以通过ImportBeanDefinitionRegistrar 对象进行bean定义,并且注册。
 */
@Import(value = {UserModel.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class StudyIocConfig {

}

/**
 * 这些类型根据给定的选择条件(通常是一个或多个批注属性)确定应导入哪个类
 */
public class CustomImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{UserDao.class.getName()};
    }
}

/**
 * 根据需要注册bean定义
 */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //构造bean定义
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(HttpPlugin.class);
        //注册到bean容器
        registry.registerBeanDefinition("httpPlugin", rootBeanDefinition);
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

--输出结果 UserModel、UserDao、httpPlugin  这几个bean都已经被加载到容器当中。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
cn.pencilso.study.studyioc.model.UserModel
cn.pencilso.study.studyioc.dao.UserDao
httpPlugin

自定义FactoryBean

public class StudyIocConfig {

    @Bean
    public UserFacotryBean userFacotryBean() {
        return new UserFacotryBean();
    }
}

/**
 * 自定义Bean工厂
 * 应用场景可以在初始化这个bean的时候需要初始化其他的组件或者依赖之类。
 * 而且用了工厂后,默认则不是饿汉单例了,需要用到的时候才会创建。
 */
public class UserFacotryBean implements FactoryBean<UserModel> {
    @Override
    public UserModel getObject() throws Exception {
        return new UserModel();
    }

    @Override
    public Class<UserModel> getObjectType() {
        return UserModel.class;
    }
    /**
     * 是否为单例
     *
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}


public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        Object userBean = annotationConfigApplicationContext.getBean("userFacotryBean");
        //userModel
        System.out.println("userBean:"+userBean);
        //获取工厂本身的bean
        Object userFacotryBean = annotationConfigApplicationContext.getBean("&userFacotryBean");
        System.out.println("userFacotryBean:"+userFacotryBean);
    }
}

--最后输出结果
user model Initialization
userBean:cn.pencilso.study.studyioc.model.UserModel@25359ed8
userFacotryBean:cn.pencilso.study.studyioc.facotry.UserFacotryBean@21a947fe

Bean 的生命周期

在bean的注解中,有两个属性,一个是initMethod,还有一个是destroyMethod。

如果指定了的话,那么在bean初始化的时候会执行initMethod,bean销毁时会执行destroyMethod。

应用场景在于用作一些数据初始化和数据的释放。但是在bean为多例的情况下,ioc则不会管理bean的销毁方法。

注解方式管理生命周期

@Service
public class UserService {
    public UserService(){
        System.out.println("user service constructor");
    }

    @PostConstruct
    public void init(){
        System.out.println("user service init");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("user service destroy");
    }
}


public class StudyIocConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}


public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService user1 = annotationConfigApplicationContext.getBean(UserService.class);
        UserService user2 = annotationConfigApplicationContext.getBean(UserService.class);
        annotationConfigApplicationContext.close();
    }
}

--输出结果如下,先执行构造方法,其次是PostConstruct注解所标记的初始化方法,最后销毁的时候执行destroy 方法。
user service constructor
user service init
user service destroy
 

尝试把bean修改为多例,看是否还能再执行destroy 方法。

public class StudyIocConfig {
    @Scope("prototype")
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

再次运行代码后结果输出如下。执行了两遍构造器和初始化方法,因为是多例模式,而我调用了两次,所以产生了两个对象。并且并没有调用PreDestroy所标记的销毁方法。

user service constructor
user service init
user service constructor
user service init

通过bean注解管理生命周期

public class StudyIocConfig {

    /**
     * 指定初始化方法为init,指定销毁方法为destroy
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public UserService userService() {
        return new UserService();
    }
}

@Service
public class UserService {
    public UserService(){
        System.out.println("user service constructor");
    }

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

    public void destroy(){
        System.out.println("user service destroy");
    }
}

--最后执行结果如下,先执行的构造器,其次是初始化方法,销毁则执行destroy。效果与注解方式一致。
user service constructor
user service init
user service destroy

Bean的后置处理器 BeanPostProcessor

public class CustomBeanPostProcessor implements BeanPostProcessor {

    /**
     * Bean 的初始化方法之前
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "  postProcessBeforeInitialization");
        return bean;
    }

    /**
     * Bean 的初始化方法之后
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "  postProcessAfterInitialization");
        return bean;
    }
}


public class StudyIocConfig {

    /**
     * 指定初始化方法为init,指定销毁方法为destroy
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public CustomBeanPostProcessor beanPostProcessor(){
        return new CustomBeanPostProcessor();
    }
}


    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        annotationConfigApplicationContext.close();
    }

--输出结果如下,先执行构造方法,其次是后置处理器的postProcessBeforeInitialization方法,再然后是bean的初始化方法,初始化方法执行后是,后置处理器的postProcessAfterInitialization方法,销毁bean时执行destroy方法。
--应用场景可以在于拦截bean修改某些成员变量,不过我认为这种场景业务中还是很少遇到。
  
user service constructor
userService  postProcessBeforeInitialization
user service init
userService  postProcessAfterInitialization
user service destroy

贴一个初始化bean的源码截图,该代码块在AbstractAutowireCapableBeanFactory这个类中。

QQ20200419-193506@2x

Bean工厂后置处理器 BeanFactoryPostProcessor

Bean工厂后置处理器,在bean解析,但是未初始化之前调用。代码示例如下

/**
 * 自定义Bean工厂后置处理器
 */
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //获取userService的bean定义
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
      	//设置为多例模式
        beanDefinition.setScope("prototype");
    }
}

@Configuration
public class StudyIocConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public CustomBeanFactoryPostProcessor customBeanFactoryPostProcessor(){
        return new CustomBeanFactoryPostProcessor();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService1 = annotationConfigApplicationContext.getBean(UserService.class);
        UserService userService2 = annotationConfigApplicationContext.getBean(UserService.class);
        System.out.println(userService1==userService2);
    }
}

--输出结果如下,表明该bean已经变为多例了。即使在@Bean注解声明为单例后,也可以通过后置处理器修改为多例,或者懒加载等等。
false

InitializingBean接口使用

由bean实现的接口,这些bean需要在{@link BeanFactory}设置完所有属性*后作出反应:例如,执行自定义初始化,*或仅仅检查是否设置了所有必需属性。(翻译自源码注释)

也就是说 该接口是用来校验属性或者执行某些自定义初始化的,直接上代码。

/**
 * 这里定义了一个name属性,并且在afterPropertiesSet方法中进行校验,如果name为空的话,则抛出异常。
 */
@Data
public class TestInitializingBean implements InitializingBean {
    private String name;
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
    }
}


public class StudyIocConfig {

    @Bean
    public TestInitializingBean initializingBean() {
        return new TestInitializingBean();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
    }
}


--执行出错,输出如下。
Caused by: java.lang.IllegalArgumentException: name can not be null
	at org.springframework.util.Assert.isTrue(Assert.java:118)
	at cn.pencilso.study.studyioc.bean.TestInitializingBean.afterPropertiesSet(TestInitializingBean.java:16)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
	... 11 more

对name属性进行赋值后则正常装载bean

@Data
public class TestInitializingBean implements InitializingBean {
    @Value("大白")
    private String name;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
    }
}

PropertySource 加载配置文件

user.properties 文件

user.nikename= 大白

Java 代码

@PropertySource(value = {"classpath:user.properties"},encoding = "UTF-8")
@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        return new UserModel();
    }
}

@Data
public class UserModel {
    @Value("#{30-10}")
    private int age;

    @Value("${user.nikename}")
    public String name;
}

@SpringBootApplication
public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel bean = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println("bean:"+bean);
    }
}

--最后输出结果
 bean:UserModel(age=20, name=大白)

但是其实PropertySource 默认是不支持加载yaml文件的,那么如果要加载yaml文件的话,需要多一些处理。

yaml文件

user:
  nikename: 大白

Java 代码实现

/**
 * 自定义yaml Factory
 */
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        return sources.get(0);
    }
}

@PropertySource(value = {"classpath:user.yaml"},encoding = "UTF-8",factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        return new UserModel();
    }
}

--最后结果输出如下
bean:UserModel(age=20, name=大白)

@Autowired

@Data
public class UserModel {
    public String name;
}

@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    @Bean
     public UserService userService(){
        return new UserService();
    }
}

public class UserService {

    @Autowired
    private UserModel user;

    public void printlnUser() {
        System.out.println("userModel:" + user);
    }

}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        userService.printlnUser();
    }
}

--同一种类型,单个bean的正常获取,输出结果如下。
userModel:UserModel(name=小白)

尝试同一种类型的多个bean对象,这里在config类中声明了两个bean对象。

如下,name属性对应的数据不同,userModel对应的name是小白,userModel2对应的name是大白。

@Configuration
public class StudyIocConfig {

    /**
     * bean 名字为userModel
     * @return
     */
    @Bean
    public UserModel userModel(){
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    /**
     * bean 名字为userModel2
     * @return
     */
    @Bean
    public UserModel userModel2(){
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    @Bean
     public UserService userService(){
        return new UserService();
    }
}

这时候再运行的话会报错,Spring会告诉你,你要加载的bean对象有两个。而Spring不知道应该帮你加载哪个。

这是因为Autowired这个注解默认是按照类的class来进行匹配的,而它匹配到两个,并且你没有指定加载某一个bean名字,则会异常。

那么解决方案也比较简单,有两种,一种是直接通过变量名与bean名字一致的情况下,如下。

public class UserService {
    /**
     * 对应bean名字  userModel
     */
    @Autowired
    private UserModel userModel;
    /**
     * 对应bean名字  userModel2
     */
    @Autowired
    private UserModel userModel2;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + userModel2);
    }
}

输出结果如下,符合预期。

userModel:UserModel(name=小白)
userModel2:UserModel(name=大白)

那么再讲第二种方案,第二种方案的话需要配合Qualifier 注解,如下。

public class UserService {
    /**
     * 对应bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel;
    /**
     * 对应bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel2;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + userModel2);
    }
}

最后输出结果如下,可以看到两次输出的name都是小白,这是因为已经固定了按照bean的名字也就是userModel来获取bean对象。

userModel:UserModel(name=小白)
userModel2:UserModel(name=小白)

另外如果需要允许null值的话,在使用注解的时候将required设置为false 例如: @Autowired(required=false)

@Resource

Resource 这个注解是jdk所提供的,那么这个注解所带来装载顺序。

1、优先匹配bean名字,如果没有指定名字的话,会获取变量名作为bean名字去匹配 ;

2、倘若bean名字匹配不到,则根据类型也就是type进行唯一性匹配 ;

3、如果同时指定了type,和bean名字,则匹配这两个条件的bean;

示范如下

public class XiaoMingModel extends UserModel{
}

public class UserService {
    /**
     * 按照固定的bean名字获取对象
     */
    @Resource(name = "userModel")
    private UserModel userModel;
    /**
     * 根据指定的class类型 进行匹配装载
     */
    @Resource(type = XiaoMingModel.class)
    private UserModel xiaoMingModel;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + xiaoMingModel);
    }
}

@Configuration
public class StudyIocConfig {

    /**
     * bean 名字为userModel
     *
     * @return
     */
    @Bean
    public UserModel userModel() {
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    /**
     * bean 名字为userModel2
     *
     * @return
     */
    @Bean
    public UserModel userModel2() {
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    @Bean
    public XiaoMingModel xiaoming() {
        XiaoMingModel xiaoMingModel = new XiaoMingModel();
        xiaoMingModel.setName("小明同学");
        return xiaoMingModel;
    }

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

最后输出结果如下,变量userModel指定加载的bean是“userModel” ,所以这里打印小白是符合预期的。

其次,变量userModel2指定加载的bean类型是XiaoMingModel.class,所以这里打印小明同学也是符合预期的。

userModel:UserModel(name=小白)
userModel2:UserModel(name=小明同学)

@Profile

根据环境来决定是否加载ben对象,常见于开发环境、测试环境、生产环境,切换bean实现。

在配置文件中声明当前的环境

spring:
  profiles:
    active: test

其次在代码中配置bean,并且配置Profile注解环境。

@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
    /**
     * 环境为prod时,则装载
     * @return
     */
    @Profile("prod")
    @Bean
    public UserModel userModelProd() {
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    /**
     * 环境为dev 或者  test时 则装载
     * @return
     */
    @Profile(value = {"dev", "test"})
    @Bean
    public UserModel userModelDev() {
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

public class UserService {

    @Autowired
    private UserModel userModel;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
    }
}

--最后运行结果输出如下,在配置文件里设置环境为 active:test ,所以它最后装载的bean是userModelDev 。

--输出name==小白,是符合预期的。

userModel:UserModel(name=小白)

EmbeddedValueResolverAware

可以通过实现该接口,获取配置文件值。

yaml配置文件如下

mysql:
  jdbcUrl: jdbc:mysql://127.0.0.1:3306/xxxxxx

Java代码如下

public class UserService implements EmbeddedValueResolverAware {

    private String jdbcUrl;

    /**
     * 将StringValueResolver设置为用于解析嵌入的定义值
     *
     * @param resolver
     */
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        jdbcUrl = resolver.resolveStringValue("${mysql.jdbcUrl}");
    }

    public void printlnJdbc() {
        System.out.println("jdbcUrl:" + jdbcUrl);
    }
}

@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        userService.printlnJdbc();
    }
}

最后结果输出如下,结果是符合预期的。

jdbcUrl:jdbc:mysql://127.0.0.1:3306/xxxxxx
# LRU # Stack # ThreadLocal # Nacos # RejectedExecutionHandler # Executor # RocketMQ # ConcurrentHashMap # CyclicBarrier # Semaphore # CountDownLatch # canal # Unsafe # Atomic # BlockingQueue # AQS # ReentrantLock # Synchronized # MESI # Volatile # JMM # BufferPool # Explain # MySQL # 常量池 # Arthas # JVM调优 # 三色标记 # CMS # ParNew # 垃圾收集器 # G1 # Java # Redis # Android # HTTPDNS # DNS # ioc # 爬虫 # seleniumhq # 推荐引擎 # Mahout # IM # Netty # Vert.x # HashMap # 梯子 # 翻墙 # V2ray # Docker # 搜索引擎 # SpringBoot # elasticsearch
SpringBoot 集成 elasticsearch 搜索引擎
移动端通过IP地址访问服务器接口
  • 文章目录
  • 站点概览
宋龙宽

宋龙宽

87 日志
13 分类
53 标签
RSS
Github E-mail
Creative Commons
Links
  • 黑客派
  • Relyn
  • 张小妞的博客
  • ElasticSearch教程
© 2021 宋龙宽
由 Halo 强力驱动
|
主题 - NexT.Gemini v5.1.4