View Post
【spring-boot】spring向IOC容器中注入bean相关知识
一、springboot向IOC容器中注入bean的几种方式
案例类
public class ClockService { public void showTime() { System.out.println("today is "+ new Date()); } }
第一种方式:待注入bean的类添加@Service或者@Component等注解
springboot会扫描启动类所在的包下面所有带有这些注解的类,实例化bean加到ioc容器。
@Service public class ClockService { public void showTime() { System.out.println("today is "+ new Date()); } }
第二种方式:使用@Configuration和@Bean注解来配置bean到ioc容器
@Configuration public class BeanConfig { @Bean public ClockService clockService() { return new ClockService(); } }
第三种方式:使用@Import注解
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @Import(ClockService.class) public class AutoConfDemoApplication { public static void main(String[] args) { SpringApplication.run(AutoConfDemoApplication.class, args); } }
第四种方式: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; } }
2、在该类所在jar包的resources目录下新建META-INF/spring.factories文件,文件内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.spring.sxf.study.springtradeservice.testbean.AutoConfigBean
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
二、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位于类路径上,否则不解析该注解修饰的配置类)
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)); } } }