惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
J
Java Code Geeks
I
InfoQ
V
Visual Studio Blog
M
MIT News - Artificial intelligence
H
Help Net Security
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
A
About on SuperTechFans
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
云风的 BLOG
云风的 BLOG
S
SegmentFault 最新的问题
WordPress大学
WordPress大学

博客园 - jianyi

w3wp CPU 100%问题解决 ShrePoint 迁移域控 SharePoint online 获取文件版本记录 SharePonit online 列表表单定制 VS2015 ionic 开发环境配置纪要 TFS online build change web.config SharePoint 2016 - 安装QuickFlow2013 EF Migrations error: No connection string named 'MpDb' could be found in the application config file. TFS online 自动部署配置 SharePoint Permission Extension SharePoint暂时禁用事件触发 视图xsl定制之嵌入服务器控件 自定义View字段表头 自定义母版页之列表过滤菜单位置issue fix ListDefinition Tips QuickFlow UI 控件之 NamedFormAttachment SharePoint 2010 GridView/SPGridView完全应用系统样式 QuickFlow-如何通过QFD and ExecuteCode获取其他列表数据 DLL嵌入exe中
SpringBoot - open-in-view 机制分析
jianyi · 2020-10-22 · via 博客园 - jianyi

open-in-view功能是指SpringBoot在请求开始前自动初始化一个数据库会话,一般是EntityManager。

当这个功能启用时,会输出一条日志:

2020-10-22 20:46:46.720  WARN 6484 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

-----open-in-view功能的启动条件是: 
1)  引用spring-boot-starter-jpa

2)或者引用spring-boot-starter-jdbc + hibernate 

3)或者,直接引用hibernate + spring-orm

----这个功能的启用过程:

1)在spring-boot-autoconfigure包中,META-INF/Spring.factories 文件配置了启动类:

org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
HibernateJpaAutoConfiguration:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {

}

 从这个类的Condition条件可以看出其启用条件:‘

当前classpath下要有LocalContainerEntityManagerFactoryBean类,这个类在org.springframework.orm.jpa包下,所以要引用spring-orm

EntityManager在javax.persistence下,SessionImplementor 是Hibernate的类

2)HibernateJpaAutoConfiguration引入了HibernateJpaConfiguration,HibernateJpaConfiguration的启用条件是当前仅有一个DataSource bean

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HibernateProperties.class)
@ConditionalOnSingleCandidate(DataSource.class)
class HibernateJpaConfiguration extends JpaBaseConfiguration {

HibernateJpaConfiguration的基类JpaBaseConfiguration定义了一个内部类

@Configuration(proxyBeanMethods = false)
	@ConditionalOnWebApplication(type = Type.SERVLET)
	@ConditionalOnClass(WebMvcConfigurer.class)
	@ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class, OpenEntityManagerInViewFilter.class })
	@ConditionalOnMissingFilterBean(OpenEntityManagerInViewFilter.class)
	@ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", havingValue = "true", matchIfMissing = true)
	protected static class JpaWebConfiguration {

		private static final Log logger = LogFactory.getLog(JpaWebConfiguration.class);

		private final JpaProperties jpaProperties;

		protected JpaWebConfiguration(JpaProperties jpaProperties) {
			this.jpaProperties = jpaProperties;
		}

		@Bean
		public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
			if (this.jpaProperties.getOpenInView() == null) {
				logger.warn("spring.jpa.open-in-view is enabled by default. "
						+ "Therefore, database queries may be performed during view "
						+ "rendering. Explicitly configure spring.jpa.open-in-view to disable this warning");
			}
			return new OpenEntityManagerInViewInterceptor();
		}

		@Bean
		public WebMvcConfigurer openEntityManagerInViewInterceptorConfigurer(
				OpenEntityManagerInViewInterceptor interceptor) {
			return new WebMvcConfigurer() {

				@Override
				public void addInterceptors(InterceptorRegistry registry) {
					registry.addWebRequestInterceptor(interceptor);
				}

			};
		}

	}

这个类注册了OpenEntityManagerInViewInterceptor 拦截器。OpenEntityManagerInViewInterceptor 用来在请求启动前初始化EntityManager,在请求完成后关闭EntityManager

JpaBaseConfiguration 会自动创建一个事务管理器和EntityManagerFactory. OpenEntityManagerInViewInterceptor默认会采用这个EntityManagerFactory类创建EntityManager

@Bean
	@ConditionalOnMissingBean(TransactionManager.class)
	public PlatformTransactionManager transactionManager(
			ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
		return transactionManager;
	}
@Bean
	@Primary
	@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder) {
		Map<String, Object> vendorProperties = getVendorProperties();
		customizeVendorProperties(vendorProperties);
		return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()).properties(vendorProperties)
				.mappingResources(getMappingResources()).jta(isJta()).build();
	}

------------------------

LocalContainerEntityManagerFactoryBean 依赖DataSource,DataSourde的创建过程:

META-INF/Spring.factories 文件配置了启动类:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {

}

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

}

 DataSourceAutoConfiguration 的启用条件是,当前类路径下存在DataSource(框架类)和org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType,EmbeddedDatabaseType是spring-jdbc下的类

所以,只要引用spring-jdbc包,DataSource自动初始话过程就会启用。注意:spring-orm包引用了spring-jdbc。

DataSourceAutoConfiguration按照顺序,如果当前存在签入数据库引用,则创建签入数据源, 否侧尝试创建

Hikari/Tomcat/Dbcp2/OracleUcp 等连接池类型的数据源

-----------

只要项目引用了hibernate,spring-orm, 并且当前容器中只有一个DataSource bean, 这个open-in-view默认会启用,如果需要禁用,则要显示设置 spring.jpa.open-in-view=false

-----------

可以在代码中设置spring.jpa.open-in-view默认值:

public static void main(String[] args) {

		new SpringApplicationBuilder(DemoApplication.class)
				.properties(props())
				.build()
				.run(args);

		//SpringApplication.run(DemoApplication.class, args);
	}

	private static Properties props() {
		Properties properties = new Properties();
		properties.setProperty("spring.jpa.open-in-view", "false");
		return properties;
	}