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

推荐订阅源

月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
T
Tailwind CSS Blog
博客园_首页
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
V2EX
J
Java Code Geeks
量子位
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
C
Check Point Blog
V
Visual Studio Blog
H
Help Net Security
Recent Announcements
Recent Announcements
Engineering at Meta
Engineering at Meta

博客园 - 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;
	}