shangxiaofei

View Post

【spring-boot】spring向IOC容器中注入bean相关知识

一、springboot向IOC容器中注入bean的几种方式

案例类

public class ClockService {

    public void showTime() {
        System.out.println("today is "+ new Date());
    }
}
View Code

第一种方式:待注入bean的类添加@Service或者@Component等注解

springboot会扫描启动类所在的包下面所有带有这些注解的类,实例化bean加到ioc容器。

@Service
public class ClockService {

    public void showTime() {
        System.out.println("today is "+ new Date());
    }
}
View Code

第二种方式:使用@Configuration@Bean注解来配置bean到ioc容器

@Configuration
public class BeanConfig {

    @Bean
    public ClockService clockService() {
        return new ClockService();
    }
}
View Code

第三种方式:使用@Import注解

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@Import(ClockService.class)
public class AutoConfDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AutoConfDemoApplication.class, args);
    }
}
View Code

第四种方式:springboot自动装配机制

1、待自动注入的bean对象

package com.spring.sxf.study.springtradeservice.testbean;

/**
 * 
 */
public class AutoConfigBean {
    private String name="888888";
    private String aget="999999";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAget() {
        return aget;
    }

    public void setAget(String aget) {
        this.aget = aget;
    }
}
View Code

2、在该类所在jar包的resources目录下新建META-INF/spring.factories文件,文件内容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.spring.sxf.study.springtradeservice.testbean.AutoConfigBean
View Code

3、启动项目,验证容器中是否存在指定的bean

@Slf4j
@Controller
@RequestMapping("/blog")
public class BlogController implements ApplicationContextAware {

    @Value("${env.envDesc}")
    private String envDesc;


    private ApplicationContext applicationContext;

    @Autowired
    private BlogService blogService;



    @RequestMapping("/env")
    @ResponseBody
    public String testEnv() {
        log.info("current envDesc:{}", envDesc);
        
        boolean autoFlag=applicationContext.containsBean("com.spring.sxf.study.springtradeservice.testbean.AutoConfigBean");
        if(!autoFlag){
            autoFlag= applicationContext.containsBean("AutoConfigBean");
        }else {
            AutoConfigBean autoConfigBean=applicationContext.getBean(AutoConfigBean.class);
            log.info("spring context hasAutoConfigBean name:{},age:{}",autoConfigBean.getName(),autoConfigBean.getAget());
        }
        return envDesc;
    }

 @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}


日志打印结果:[19:44:39:245] [INFO] [qtp892237946-33] - com.spring.sxf.study.springtradeweb.controller.BlogController.testEnv(BlogController.java:70) - spring context hasAutoConfigBean name:888888,age:999999
View Code

 

 

二、springboot的spring-boot-starter-xxx的实现原理

1、原理概述:

该类starter的jar包(jar包中无任何代码实现)存在在项目中,其pom.xml文件会向项目中自动导入其他需要自动装配的jar包,从而完成springboot的自动装配需求。(依赖springboot的自动装配机制)

  • 第一种情况:导入的jar包中的pom.xml文件存在传递依赖,会引入其他jar包,该jar包中存在一些@Configuration注解的配置。
  • 第二种情况:导入的jar包的class目录下存在/META-INF/spring.factories文件,该文件下存在以下自动装配的类。

2、注解在自动装配的类上的注解

@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)

@ConditionalOnClass(当注解在方法上,某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnClass (当注解于类上, 某个class位于类路径上,否则不解析该注解修饰的配置类)
View Code

3、案例类

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.jdbc;

import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource}.
 *
 * @author Dave Syer
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @author Kazuki Shimizu
 * @since 1.0.0
 */
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

    @Configuration
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {

    }

    @Configuration
    @Conditional(PooledDataSourceCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
            DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
            DataSourceJmxConfiguration.class })
    protected static class PooledDataSourceConfiguration {

    }

    /**
     * {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}
     * is set or {@link PooledDataSourceAvailableCondition} applies.
     */
    static class PooledDataSourceCondition extends AnyNestedCondition {

        PooledDataSourceCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty(prefix = "spring.datasource", name = "type")
        static class ExplicitType {

        }

        @Conditional(PooledDataSourceAvailableCondition.class)
        static class PooledDataSourceAvailable {

        }

    }

    /**
     * {@link Condition} to test if a supported connection pool is available.
     */
    static class PooledDataSourceAvailableCondition extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource");
            if (getDataSourceClassLoader(context) != null) {
                return ConditionOutcome.match(message.foundExactly("supported DataSource"));
            }
            return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());
        }

        /**
         * Returns the class loader for the {@link DataSource} class. Used to ensure that
         * the driver class can actually be loaded by the data source.
         * @param context the condition context
         * @return the class loader
         */
        private ClassLoader getDataSourceClassLoader(ConditionContext context) {
            Class<?> dataSourceClass = DataSourceBuilder.findType(context.getClassLoader());
            return (dataSourceClass != null) ? dataSourceClass.getClassLoader() : null;
        }

    }

    /**
     * {@link Condition} to detect when an embedded {@link DataSource} type can be used.
     * If a pooled {@link DataSource} is available, it will always be preferred to an
     * {@code EmbeddedDatabase}.
     */
    static class EmbeddedDatabaseCondition extends SpringBootCondition {

        private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
            if (anyMatches(context, metadata, this.pooledCondition)) {
                return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
            }
            EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
            if (type == null) {
                return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());
            }
            return ConditionOutcome.match(message.found("embedded database").items(type));
        }

    }

}
View Code

 

分类:

技术点:

相关文章: