2022 10 24
2022-10-24¶
Spring vs SpringBoot¶
- 설명
- 스프링 부트는 단지 실행만 하면 되는 스프링 기반의 어플리케이션을 쉽게 만들 수 있다
- 원래 스프링은... xml로 복잡하게 내용들을 다 작성해줘야해
- 모든 dependency 버전까지 하나하나 정확하게!
- AutoConfiguration을 활용한 스프링 부트
- 어플리케이션 개발에 필요한 모든 Dependency를 프레임워크에서 관리
- jar 파일이 클래스 패스에 있는 경우 스프링 부트는 Dispatcher Servlet으로 자동 구성
- 스프링 부트는 미리 설정되어있는 starter 프로젝트를 제공
- SpringBoot-Starter를 제공하여 자동으로 호환되는 버전을 알아서 관리 => 패키지 디펜던시
- spring-boot-starter-web
- spring-boot-starter-test
- spring-boot-starter-data-jpa
- spring-boot-starter-jdbc
- spring-boot-starter-security
- xml 설정 없이 자바 코드를 통해 설정할 수 있음
@SpringBootApplication@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
SpringApplication.run() => ApplicationContext 생성 및 구동¶
-
일단 psvm에서 본인의 클래스 class를 변수로 두고 정적 메서드로 실행을 시킨다
- SpringApplication : 자바 메인 메서드에서 부트스트랩 시켜 실행시킬 수 있는 클래스
- 알맞은 ApplicationContext 객체를 생성
- CommandLinePropertySource를 통해 CommandLine에 있는 친구들 실행
- ApplicationContext를 리프레시 하고 모든 Singleton 빈들을 로딩하기
- 모든 CommandLineRunner 빈들 실행
- SpringApplication : 자바 메인 메서드에서 부트스트랩 시켜 실행시킬 수 있는 클래스
-
SpringApplication 클래스 안에서 새로운 SpringApplication 객체 만들고 run!
-
run() 메서드는 다음과 같음
- ConfigurableApplicationContext 타입의 ApplicationContext 생성하는 과정!
- AnnotationConfigApplicationContext 타입이 대입되게 됨
- refreshContext(context)를 통해 각각 구동에 필요한 빈들의 설정들이 적용되고 등록됨
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); // 4번 context.setApplicationStartup(this.applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); // 5번 afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
-
createApplicationContext()은 기본으로 AnnotationConfigApplicationContext 방식으로 생성함
public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } -
refreshContext(context)에서 다음 설정들이 다들 적용됨
- Repository 레이어 초기화 진행
- Redis, JPA 스캔하면서 설정한 모드에 따라 초기화
- 톰캣(서블릿 엔진)도 초기화 : 8080 포트로 띄우기
- 쓰레드 풀도 열어두고!
- 히카리풀도 초기화 : 데이터베이스 연결하기
- JPAEntityManagerFactory도 초기화!
- OSIV도 설정 열어두고...
- SecurityFilterChain도 등록해주고!
14:04:44.891 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Multiple Spring Data modules found, entering strict repository configuration mode 14:04:44.896 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Bootstrapping Spring Data Redis repositories in DEFAULT mode. 14:04:45.157 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Finished Spring Data repository scanning in 98 ms. Found 0 Redis repository interfaces. 14:04:50.067 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Multiple Spring Data modules found, entering strict repository configuration mode 14:04:50.068 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Bootstrapping Spring Data JPA repositories in DEFAULT mode. 14:04:52.107 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationExtensionSupport] - Spring Data JPA - Could not safely identify store assignment for repository candidate interface com.backend.connectable.user.redis.UserTicketEntranceRedisRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: javax.persistence.Entity, javax.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository 14:04:52.764 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Finished Spring Data repository scanning in 2667 ms. Found 7 JPA repository interfaces. 14:04:52.951 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Multiple Spring Data modules found, entering strict repository configuration mode 14:04:52.952 [INFO ] [restartedMain] [o.s.d.r.c.RepositoryConfigurationDelegate] - Bootstrapping Spring Data Redis repositories in DEFAULT mode. 14:05:00.290 [INFO ] [restartedMain] [o.s.cloud.context.scope.GenericScope] - BeanFactory id=2fa6ef28-6c1c-3e6f-a9d4-84a4731e7c62 14:05:15.633 [INFO ] [restartedMain] [o.s.b.w.e.tomcat.TomcatWebServer] - Tomcat initialized with port(s): 8080 (http) 14:05:15.811 [INFO ] [restartedMain] [o.a.coyote.http11.Http11NioProtocol] - Initializing ProtocolHandler ["http-nio-8080"] 14:05:15.813 [INFO ] [restartedMain] [o.a.catalina.core.StandardService] - Starting service [Tomcat] 14:05:15.814 [INFO ] [restartedMain] [o.a.catalina.core.StandardEngine] - Starting Servlet engine: [Apache Tomcat/9.0.64] 14:05:18.247 [INFO ] [restartedMain] [o.a.c.c.C.[Tomcat].[localhost].[/]] - Initializing Spring embedded WebApplicationContext 14:05:18.248 [INFO ] [restartedMain] [o.s.b.w.s.c.ServletWebServerApplicationContext] - Root WebApplicationContext: initialization completed in 54183 ms 14:05:19.493 [INFO ] [restartedMain] [com.zaxxer.hikari.HikariDataSource] - HikariPool-1 - Starting... 14:05:21.290 [INFO ] [restartedMain] [com.zaxxer.hikari.HikariDataSource] - HikariPool-1 - Start completed. 14:05:21.426 [INFO ] [restartedMain] [o.s.b.a.h.H2ConsoleAutoConfiguration] - H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:connectable' 14:05:25.298 [INFO ] [restartedMain] [o.h.jpa.internal.util.LogHelper] - HHH000204: Processing PersistenceUnitInfo [name: default] 14:05:25.942 [INFO ] [restartedMain] [org.hibernate.Version] - HHH000412: Hibernate ORM core version 5.6.9.Final 14:05:27.876 [INFO ] [restartedMain] [o.h.annotations.common.Version] - HCANN000001: Hibernate Commons Annotations {5.1.2.Final} 14:05:29.209 [INFO ] [restartedMain] [org.hibernate.dialect.Dialect] - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect 14:05:38.954 [INFO ] [restartedMain] [o.h.e.t.j.p.i.JtaPlatformInitiator] - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 14:05:39.037 [INFO ] [restartedMain] [o.s.o.j.LocalContainerEntityManagerFactoryBean] - Initialized JPA EntityManagerFactory for persistence unit 'default' 14:06:17.568 [WARN ] [restartedMain] [o.s.b.a.o.j.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 14:06:21.534 [INFO ] [restartedMain] [o.s.b.d.a.OptionalLiveReloadServer] - LiveReload server is running on port 35729 14:06:37.304 [INFO ] [restartedMain] [o.s.s.web.DefaultSecurityFilterChain] - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@4f3eb020, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4eecf606, org.springframework.security.web.context.SecurityContextPersistenceFilter@3a6f3601, org.springframework.security.web.header.HeaderWriterFilter@62216031, org.springframework.security.web.authentication.logout.LogoutFilter@39257525, com.backend.connectable.security.custom.JwtAuthenticationFilter@7235c6ae, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1493436d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@1afbaa27, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@7d5440a4, org.springframework.security.web.session.SessionManagementFilter@9e89d5a, org.springframework.security.web.access.ExceptionTranslationFilter@742c305f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@7214c6c8] 14:06:46.967 [INFO ] [restartedMain] [o.a.coyote.http11.Http11NioProtocol] - Starting ProtocolHandler ["http-nio-8080"] 14:06:47.144 [INFO ] [restartedMain] [o.s.b.w.e.tomcat.TomcatWebServer] - Tomcat started on port(s): 8080 (http) with context path ''
- Repository 레이어 초기화 진행
-
다음과 같이 생성된 Context 반환
context = {AnnotationConfigServletWebServerApplicationContext@6428} "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@227f5c14, started on Mon Oct 24 14:15:01 KST 2022" - reader = {AnnotatedBeanDefinitionReader@16464} - scanner = {ClassPathBeanDefinitionScanner@16465} - annotatedClasses = {LinkedHashSet@16466} size = 0 - basePackages = null - webServer = {TomcatWebServer@16467} - servletConfig = null - serverNamespace = null - servletContext = {ApplicationContextFacade@16468} - themeSource = {ResourceBundleThemeSource@16469} - beanFactory = {DefaultListableBeanFactory@16470} "org.springframework.beans.factory.support.DefaultListableBeanFactory@d333d97: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,connectableApplication,org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory,adminIssueService,adminOrderService,adminService,adminController,artistService,artistController,authService,authController,eventRepositoryImpl,ticketRepositoryImpl,eventService,eventController,globalExceptionHandler,timeCheckAspect,resourceHandlerConfig,swaggerConfig,workaround,restTemplateClient,redisDao,embeddedRedisConfig,redisConfig,kasWebClient,kasWebClientConfigur" - resourceLoader = {ClassLoaderFilesResourcePatternResolver@16471} - customClassLoader = false - refreshed = {AtomicBoolean@16472} "true" - logger = {LogAdapter$Slf4jLocationAwareLog@16473} - id = "application" - displayName = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@227f5c14" - parent = null - environment = {ApplicationServletEnvironment@6299} "ApplicationServletEnvironment {activeProfiles=[local, console-logging], defaultProfiles=[default], propertySources=[MapPropertySource {name='server.ports'}, ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, CachedRandomPropertySource {name='cachedrandom'}, AwsParamStorePropertySource {name='/config/connectable_console-logging/'}, AwsParamStorePropertySource {name='/config/connectable_local/'}, AwsParamStorePropertySource {name='/config/connectable/'}, AwsParamStorePropertySource {name='/config/application_console-logging/'}, AwsParamStorePropertySource {name='/config/application_local/'}, AwsParamStorePropertySource {name='/config/application/'}, OriginTrackedMapPropertySource {nam" - beanFactoryPostProcessors = {ArrayList@16476} size = 3 - startupDate = 1666588501271 - active = {AtomicBoolean@16477} "true" - closed = {AtomicBoolean@16478} "false" - startupShutdownMonitor = {Object@16479} - shutdownHook = null - resourcePatternResolver = {ServletContextResourcePatternResolver@16480} - lifecycleProcessor = {DefaultLifecycleProcessor@16481} - messageSource = {DelegatingMessageSource@16482} "Empty MessageSource" - applicationEventMulticaster = {SimpleApplicationEventMulticaster@16483} - applicationStartup = {DefaultApplicationStartup@2416} - applicationListeners = {LinkedHashSet@16484} size = 53 - earlyApplicationListeners = {LinkedHashSet@16485} size = 17 - earlyApplicationEvents = null - classLoader = null - protocolResolvers = {LinkedHashSet@16486} size = 0 - resourceCaches = {ConcurrentHashMap@16487} size = 0 -
Bean Factory에 등록된 Bean들 (엄청많음...)
org.springframework.beans.factory.support.DefaultListableBeanFactory@d333d97: defining beans [ org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.annotation.internalPersistenceAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, // 어플리케이션 connectableApplication, org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory, // 내가 정의한 빈들 adminIssueService, adminOrderService, adminService, adminController, artistService, artistController, authService, authController, eventRepositoryImpl, ticketRepositoryImpl, eventService, eventController, globalExceptionHandler, timeCheckAspect, resourceHandlerConfig, swaggerConfig, workaround, restTemplateClient, redisDao, embeddedRedisConfig, redisConfig, kasWebClient, kasWebClientConfiguration, kasService, kasEndPointGenerator, kasContractService, transactionOptionManager, kasTokenService, klipService, orderRepositoryImpl, orderService, orderController, s3Service, scheduledTasks, securityConfiguration, webConfiguration, customUserDetailsService, jwtAuthenticationFilter, jwtProvider, smsConfig, smsService, userRepositoryImpl, userService, userTicketService, userController, openApiControllerWebMvc, oasVendorExtensionsMapperImpl, styleEnumMapperImpl, securitySchemeMapperImpl, oasSecurityMapperImpl, // 수많은 Swagger 관련 빈들 schemaMapperImpl, examplesMapperImpl, serviceModelToOpenApiMapperImpl, oasLicenceMapper, apiListingReferenceScanner, apiDocumentationScanner, apiDescriptionReader, apiListingReader, apiModelSpecificationReader, cachingOperationReader, mediaTypeReader, apiListingScanner, apiModelReader, apiDescriptionLookup, operationModelsProvider, operationDeprecatedReader, responseMessagesReader, operationParameterReader, operationTagsReader, apiOperationReader, defaultOperationReader, operationParameterRequestConditionReader, operationParameterHeadersConditionReader, // ViewResolver, ObjectMapper, Jackson, Parameter 변환기 등 여러가지 요청/응답에 대한 처리를 위한 빈들 contentParameterAggregator, operationResponseClassReader, cachingOperationNameGenerator, parameterMultiplesReader, modelAttributeParameterExpander, parameterTypeReader, parameterRequiredReader, parameterDataTypeReader, parameterDefaultReader, parameterNameReader, expandedParameterBuilder, webMvcRequestHandlerProvider, defaultResponseTypeReader, documentationPluginsBootstrapper, documentationPluginsManager, queryStringUriTemplateDecorator, pathMappingDecorator, pathSanitizer, operationPathDecorator, jacksonJsonViewProvider, cachingModelDependencyProvider, typeNameExtractor, propertyDiscriminatorBasedInheritancePlugin, xmlModelPlugin, schemaPluginsManager, jsonIgnorePropertiesModelPlugin, cachingModelPropertiesProvider, objectMapperBeanPropertyNamingStrategy, accessorsProvider, fieldProvider, xmlPropertyPlugin, optimized, factoryMethodProvider, modelSpecificationFactory, cachingModelProvider, defaultModelDependencyProvider, jacksonEnumTypeDeterminer, defaultModelProvider, defaultModelSpecificationProvider, openApiSchemaPropertyBuilder, apiModelPropertyPropertyBuilder, apiModelTypeNameProvider, apiModelBuilder, operationImplicitParameterReader, vendorExtensionsReader, // Swagger 설정 swaggerOperationResponseClassReader, swaggerOperationModelsProvider, openApiOperationAuthReader, swaggerMediaTypeReader, operationHttpMethodReader, operationImplicitParametersReader, operationAuthReader, operationHiddenReader, operationSummaryReader, openApiOperationTagsReader, swaggerResponseMessageReader, operationNicknameIntoUniqueIdReader, operationPositionReader, openApiResponseReader, operationNotesReader, swaggerOperationTagsReader, swaggerParameterDescriptionReader, swaggerExpandedParameterBuilder, openApiParameterBuilder, swaggerApiListingReader, inMemorySwaggerResourcesProvider, apiResourceController, serviceModelToSwagger2MapperImpl, vendorExtensionsMapperImpl, propertyMapperImpl, compatibilityModelMapperImpl, parameterMapperImpl, modelMapperImpl, requestParameterMapperImpl, modelSpecificationMapperImpl, licenseMapperImpl, securityMapperImpl, swagger2ControllerWebMvc, // 핸들러 매핑, ViewResolver 등의 Spring MVC flow에 필요한 빈들 org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration, requestMappingHandlerMapping, mvcPatternParser, mvcUrlPathHelper, mvcPathMatcher, mvcContentNegotiationManager, viewControllerHandlerMapping, beanNameHandlerMapping, routerFunctionMapping, resourceHandlerMapping, mvcResourceUrlProvider, defaultServletHandlerMapping, requestMappingHandlerAdapter, handlerFunctionAdapter, mvcConversionService, mvcValidator, mvcUriComponentsContributor, httpRequestHandlerAdapter, simpleControllerHandlerAdapter, handlerExceptionResolver, mvcViewResolver, mvcHandlerMappingIntrospector, localeResolver, themeResolver, flashMapManager, viewNameTranslator, parseApi, redisConnectionFactory, // Redis 관련 설정들 org.springframework.data.redis.repository.configuration.RedisRepositoryConfigurationExtension#0, keyValueMappingContext, redisCustomConversions, redisReferenceResolver, redisConverter, redisKeyValueAdapter, redisKeyValueTemplate, webClientForKas, redisTemplate, // 수많은 Configuration들... Jackson, Servlet, WebMvc, Security, Codec, Validation, AOP 등등... org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration, objectPostProcessor, org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration, authenticationManagerBuilder, enableGlobalAuthenticationAutowiredConfigurer, initializeUserDetailsBeanManagerConfigurer, initializeAuthenticationProviderBeanManagerConfigurer, org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration, delegatingApplicationListener, webSecurityExpressionHandler, springSecurityFilterChain, privilegeEvaluator, conversionServicePostProcessor, org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration, requestDataValueProcessor, org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration, org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity, initMessageService, org.springframework.scheduling.annotation.SchedulingConfiguration, org.springframework.context.annotation.internalScheduledAnnotationProcessor, org.springframework.context.config.internalBeanConfigurerAspect, jpaAuditingHandler, jpaMappingContext, org.springframework.boot.autoconfigure.AutoConfigurationPackages, org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration, propertySourcesPlaceholderConfigurer, org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration, websocketServletWebServerCustomizer, org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat, tomcatServletWebServerFactory, org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration, servletWebServerFactoryCustomizer, tomcatServletWebServerFactoryCustomizer, org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor, org.springframework.boot.context.internalConfigurationPropertiesBinderFactory, org.springframework.boot.context.internalConfigurationPropertiesBinder, org.springframework.boot.context.properties.BoundConfigurationProperties, org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter, server-org.springframework.boot.autoconfigure.web.ServerProperties, webServerFactoryCustomizerBeanPostProcessor, errorPageRegistrarBeanPostProcessor, org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration, dispatcherServlet, spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties, org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration, dispatcherServletRegistration, org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration, standardJacksonObjectMapperBuilderCustomizer, spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration, jacksonObjectMapperBuilder, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration, parameterNamesModule, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration, jacksonObjectMapper, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, jsonComponentModule, jsonMixinModule, org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$DefaultCodecsConfiguration, defaultCodecCustomizer, org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$JacksonCodecConfiguration, jacksonCodecCustomizer, org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration, spring.codec-org.springframework.boot.autoconfigure.codec.CodecProperties, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, defaultValidator, methodValidationPostProcessor, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, taskExecutorBuilder, applicationTaskExecutor, spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration, error, beanNameViewResolver, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration, conventionErrorViewResolver, spring.web-org.springframework.boot.autoconfigure.web.WebProperties, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, errorAttributes, basicErrorController, errorPageCustomizer, preserveErrorControllerTargetClassPostProcessor, org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration, mbeanExporter, objectNamingStrategy, mbeanServer, spring.jmx-org.springframework.boot.autoconfigure.jmx.JmxProperties, org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, springApplicationAdminRegistrar, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration, org.springframework.aop.config.internalAutoProxyCreator, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration, applicationAvailability, org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari, dataSource, org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari, org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration, org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration, hikariPoolDataSourceMetadataProvider, org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties, org.springframework.cloud.autoconfigure.RefreshAutoConfiguration$RefreshScopeBeanDefinitionEnhancer, org.springframework.cloud.autoconfigure.RefreshAutoConfiguration$JpaInvokerConfiguration, org.springframework.cloud.autoconfigure.RefreshAutoConfiguration, refreshScope, loggingRebinder, configDataContextRefresher, refreshEventListener, spring.cloud.refresh-org.springframework.cloud.autoconfigure.RefreshAutoConfiguration$RefreshProperties, // JPA 관련 빈들 org.springframework.data.jpa.domain.support.AuditingEntityListener, org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor, org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration, openEntityManagerInViewInterceptor, openEntityManagerInViewInterceptorConfigurer, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration, transactionManager, jpaVendorAdapter, entityManagerFactoryBuilder, entityManagerFactory, spring.jpa.hibernate-org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties, spring.jpa-org.springframework.boot.autoconfigure.orm.jpa.JpaProperties, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration, lettuceClientResources, org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, stringRedisTemplate, spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties, org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration, org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration, lifecycleProcessor, spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties, org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration, persistenceExceptionTranslationPostProcessor, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, emBeanDefinitionRegistrarPostProcessor, jpaContext, org.springframework.data.jpa.util.JpaMetamodelCacheCleanup, org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension, commentRepositoryImpl, commentRepository, artistRepository, orderRepository, ticketRepository, userRepository, eventRepository, orderDetailRepository, // Redis 설정 빈들 org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration, userTicketEntranceRedisRepository, // Gson, Jackson, HttpMessageConverter, ArgumentResolver org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, gsonBuilder, gson, standardGsonBuilderCustomizer, spring.gson-org.springframework.boot.autoconfigure.gson.GsonProperties, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration, stringHttpMessageConverter, org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration, mappingJackson2HttpMessageConverter, org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration, org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, messageConverters, org.springframework.data.web.config.ProjectingArgumentResolverRegistrar, projectingArgumentResolverBeanPostProcessor, // DB 접근 기술 org.springframework.data.web.config.SpringDataWebConfiguration, pageableResolver, sortResolver, org.springframework.data.web.config.SpringDataJacksonConfiguration, jacksonGeoModule, org.springframework.data.web.config.QuerydslWebConfiguration, querydslPredicateArgumentResolver, querydslBindingsFactory, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, pageableCustomizer, sortCustomizer, spring.data.web-org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateConfiguration, jdbcTemplate, org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcTemplateConfiguration, namedParameterJdbcTemplate, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, spring.jdbc-org.springframework.boot.autoconfigure.jdbc.JdbcProperties, org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor, org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration, h2Console, spring.h2.console-org.springframework.boot.autoconfigure.h2.H2ConsoleProperties, org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration, spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, // Netty org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration, spring.netty-org.springframework.boot.autoconfigure.netty.NettyProperties, org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration, spring.security-org.springframework.boot.autoconfigure.security.SecurityProperties, org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration$ErrorPageSecurityFilterConfiguration, errorPageSecurityFilter, org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, authenticationEventPublisher, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, securityFilterChainRegistration, org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration, dataSourceScriptDatabaseInitializer, // SQL 관련 설정 org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties, org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration, taskScheduler, scheduledBeanLazyInitializationExcludeFilter, taskSchedulerBuilder, spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration, // Transaction 담당 빈들 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration, org.springframework.transaction.config.internalTransactionAdvisor, transactionAttributeSource, transactionInterceptor, org.springframework.transaction.config.internalTransactionalEventListenerFactory, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration, transactionTemplate, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, platformTransactionManagerCustomizers, spring.transaction-org.springframework.boot.autoconfigure.transaction.TransactionProperties, org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration, restTemplateBuilderConfigurer, restTemplateBuilder, // WebServer 관련 빈들 org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$NettyWebServerFactoryCustomizerConfiguration, nettyWebServerFactoryCustomizer, org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration, tomcatWebServerFactoryCustomizer, org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration, // Reactor 관련 빈들 org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorConfiguration$ReactorNetty, reactorClientResourceFactory, reactorClientHttpConnector, org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration, clientConnectorCustomizer, org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration$WebClientCodecsConfiguration, exchangeStrategiesCustomizer, org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration, webClientBuilder, org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration, characterEncodingFilter, localeCharsetMappingsCustomizer, org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration, multipartConfigElement, multipartResolver, spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties, org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$DatabaseShutdownExecutorEntityManagerFactoryDependsOnPostProcessor, org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration, inMemoryDatabaseShutdownExecutor, org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration, restartingClassPathChangedEventListener, classPathFileSystemWatcher, classPathRestartStrategy, fileSystemWatcherFactory, conditionEvaluationDeltaLoggingListener, org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration, liveReloadServer, optionalLiveReloadServer, liveReloadServerEventListener, org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration, spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties, org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration, configurationPropertiesBeans, configurationPropertiesRebinder, org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration, environmentManager, springfox.documentation.schema.configuration.ModelsConfiguration, typeResolver, modelBuilderPluginRegistry, modelPropertyBuilderPluginRegistry, typeNameProviderPluginRegistry, syntheticModelProviderPluginRegistry, viewProviderPluginRegistry, // Swagger 관련 빈들 springfox.documentation.spring.web.SpringfoxWebConfiguration, defaults, resourceGroupCache, jsonSerializer, descriptionResolver, methodResolver, pathProvider, documentationPluginRegistry, apiListingBuilderPluginRegistry, operationBuilderPluginRegistry, parameterBuilderPluginRegistry, responseBuilderPluginRegistry, expandedParameterBuilderPluginRegistry, operationModelsProviderPluginRegistry, defaultsProviderPluginRegistry, pathDecoratorRegistry, apiListingScannerPluginRegistry, modelNamesRegistryFactoryPluginRegistry, springfox.documentation.spring.web.SpringfoxWebMvcConfiguration, webMvcObjectMapperConfigurer, springfox.documentation.swagger.configuration.SwaggerCommonConfiguration, springfox.documentation.oas.configuration.OpenApiMappingConfiguration, openApiModule, springfox.documentation.oas.configuration.OpenApiWebMvcConfiguration, webMvcOpenApiTransformer, webMvcOpenApiTransformationFilterRegistry, springfox.documentation.oas.configuration.OpenApiDocumentationConfiguration, springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration, expanderMinMax, expanderNotNull, expanderNotBlank, expanderPattern, expanderSize, parameterMinMax, parameterNotNull, parameterNotBlank, parameterPattern, parameterSize, minMaxPlugin, decimalMinMaxPlugin, sizePlugin, isNullPlugin, notNullPlugin, notBlankPlugin, patternPlugin, springfox.documentation.swagger2.configuration.Swagger2WebMvcConfiguration, webMvcSwaggerTransformer, webMvcSwaggerTransformationFilterRegistry, springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration, swagger2Module, springfox.boot.starter.autoconfigure.SwaggerUiWebMvcConfiguration, swaggerUiConfigurer, springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration, springfox.documentation-springfox.boot.starter.autoconfigure.SpringfoxConfigurationProperties, org.springframework.orm.jpa.SharedEntityManagerCreator#0 ]; root of factory hierarchy -
Thread에서 (아마 Main이겠죠?) run() 시켜버림 - Reflection Method invoke로 실행시켜버림! -> 드디어 구동!
@Override public void run() { try { Class<?> mainClass = Class.forName(this.mainClassName, false, getContextClassLoader()); Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); mainMethod.invoke(null, new Object[] { this.args }); } catch (Throwable ex) { this.error = ex; getUncaughtExceptionHandler().uncaughtException(this, ex); } }
시큐리티 인가 과정 디버거와 함께¶
엔티티 준영속? 어플리케이션 컨텍스트 새로 생긴다? => 하나의 요청 톺아보기¶
- Filter단 준영속 + OSIV
// HTTP 요청이 들어온다 2022-10-24 19:45:29.024[DEBUG] : Received [PUT /users HTTP/1.1 Host: localhost:8080 Connection: keep-alive Content-Length: 55 sec-ch-ua: "Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99" sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Authorization: Bearer secret.secret.w-secret Content-Type: application/json Cache-Control: no-cache Postman-Token: 4404766e-5d4c-ab73-d4cb-98faf425b943 sec-ch-ua-platform: "macOS" Accept: */* Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop Sec-Fetch-Site: none Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Accept-Encoding: gzip, deflate, br Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5 { "nickname":"hello", "phoneNumber":"010-0000-0000" }] 2022-10-24 19:45:29.039[DEBUG] : Set query string encoding to UTF-8 2022-10-24 19:45:29.042[DEBUG] : Security checking request PUT /users 2022-10-24 19:45:29.043[DEBUG] : No applicable constraints defined 2022-10-24 19:45:29.047[DEBUG] : Loading persistent provider registrations from [/private/var/folders/q7/z5mst0411js4_ng0wr6qw9v00000gn/T/tomcat.8080.8416881270018184595/conf/jaspic-providers.xml] 2022-10-24 19:45:29.047[DEBUG] : Not subject to any constraint // 최초 요청에 대해서는 DispatcherServlet을 생성한다. 2022-10-24 19:45:29.049[INFO ] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-10-24 19:45:29.049[INFO ] : Initializing Servlet 'dispatcherServlet' // 요청을 처리하는데 필요한 Bean들은 캐싱을 해두게 된다. 2022-10-24 19:45:29.049[TRACE] : Returning cached instance of singleton bean 'multipartResolver' 2022-10-24 19:45:29.049[TRACE] : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4b7d7c57 2022-10-24 19:45:29.049[TRACE] : Returning cached instance of singleton bean 'localeResolver' 2022-10-24 19:45:29.049[TRACE] : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@2783508d 2022-10-24 19:45:29.049[TRACE] : Returning cached instance of singleton bean 'themeResolver' 2022-10-24 19:45:29.049[TRACE] : Detected org.springframework.web.servlet.theme.FixedThemeResolver@64a43a3c 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'requestMappingHandlerMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'viewControllerHandlerMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'beanNameHandlerMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'routerFunctionMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'resourceHandlerMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'defaultServletHandlerMapping' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'requestMappingHandlerAdapter' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'handlerFunctionAdapter' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'httpRequestHandlerAdapter' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'simpleControllerHandlerAdapter' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'handlerExceptionResolver' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'errorAttributes' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'viewNameTranslator' 2022-10-24 19:45:29.050[TRACE] : Detected DefaultRequestToViewNameTranslator 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'mvcViewResolver' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'beanNameViewResolver' 2022-10-24 19:45:29.050[TRACE] : Returning cached instance of singleton bean 'flashMapManager' 2022-10-24 19:45:29.050[TRACE] : Detected SessionFlashMapManager 2022-10-24 19:45:29.050[DEBUG] : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 2022-10-24 19:45:29.051[INFO ] : Completed initialization in 1 ms 2022-10-24 19:45:29.051[DEBUG] : Returning non-STM instance // SpringSecurity를 사용하기 때문에 DelegatingFilterProxy로 부터 SpringSecurityFilterChain에게 위임해 요청을 타게한다 2022-10-24 19:45:29.053[TRACE] : Returning cached instance of singleton bean 'springSecurityFilterChain' 2022-10-24 19:45:29.057[TRACE] : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@3936f8e0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@39354298, org.springframework.security.web.context.SecurityContextPersistenceFilter@28e542ce, org.springframework.security.web.header.HeaderWriterFilter@71fcf111, org.springframework.security.web.authentication.logout.LogoutFilter@7e1ffd05, com.backend.connectable.security.JwtAuthenticationFilter@7ed73b15, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@cc0d699, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7bf7677c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6727efeb, org.springframework.security.web.session.SessionManagementFilter@c3bac62, org.springframework.security.web.access.ExceptionTranslationFilter@326c132d, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@10c697d0]] (1/1) 2022-10-24 19:45:29.057[DEBUG] : Securing PUT /users 2022-10-24 19:45:29.057[TRACE] : Invoking DisableEncodeUrlFilter (1/12) 2022-10-24 19:45:29.058[TRACE] : Invoking WebAsyncManagerIntegrationFilter (2/12) 2022-10-24 19:45:29.059[TRACE] : Invoking SecurityContextPersistenceFilter (3/12) 2022-10-24 19:45:29.061[DEBUG] : Set SecurityContextHolder to empty SecurityContext 2022-10-24 19:45:29.061[TRACE] : Invoking HeaderWriterFilter (4/12) 2022-10-24 19:45:29.061[TRACE] : Invoking LogoutFilter (5/12) 2022-10-24 19:45:29.062[TRACE] : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]] // 내가 정의한 JwtAuthenticationFilter를 탄다 2022-10-24 19:45:29.062[TRACE] : Invoking JwtAuthenticationFilter (6/12) 2022-10-24 19:45:29.064[INFO ] : [[Token]] : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIweDNhYjMxZDIxOWQ0NWNlNDBkNjg2MmQ2OGQzN2RlNmJiNzNlMjFhOGQiLCJleHBpcmUiOjE2ODA5MDgwODZ9.w-atmmKZjGxkn8Zbl850YC4KlyJQ6AyiOpHSyjQzhBE 2022-10-24 19:45:29.064[INFO ] : [[Path]] : /users // UserDetailsService에 @Transactional을 붙였기 때문에 트랜잭션이 생성된다 - Hibernate Session이 생성되고 HikariCP의 커넥션이 매칭된다 2022-10-24 19:45:29.102[DEBUG] : Creating new transaction with name [com.backend.connectable.security.CustomUserDetailsService.loadUserByUsername]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 2022-10-24 19:45:29.102[TRACE] : Opening Hibernate Session. tenant=null 2022-10-24 19:45:29.102[TRACE] : Opened Session [50317ba0-a0b4-49ff-b135-4000cce23439] at timestamp: 1666608329102 2022-10-24 19:45:29.102[DEBUG] : Opened new EntityManager [SessionImpl(1543251544PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction 2022-10-24 19:45:29.102[DEBUG] : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false 2022-10-24 19:45:29.102[DEBUG] : begin 2022-10-24 19:45:29.102[TRACE] : Preparing to begin transaction via JDBC Connection.setAutoCommit(false) 2022-10-24 19:45:29.102[TRACE] : Transaction begun via JDBC Connection.setAutoCommit(false) 2022-10-24 19:45:29.102[TRACE] : ResourceLocalTransactionCoordinatorImpl#afterBeginCallback 2022-10-24 19:45:29.102[DEBUG] : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4380507c] 2022-10-24 19:45:29.102[TRACE] : Getting transaction for [com.backend.connectable.security.CustomUserDetailsService.loadUserByUsername] 2022-10-24 19:45:29.110[TRACE] : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByKlaytnAddress]: This method is not transactional. 2022-10-24 19:45:29.116[DEBUG] : Rendered criteria query -> select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 2022-10-24 19:45:29.117[TRACE] : Unable to locate HQL query plan in cache; generating (select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0) 2022-10-24 19:45:29.117[DEBUG] : parse() - HQL: select generatedAlias0 from com.backend.connectable.user.domain.User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 // Statement를 만들어 처리한다. (이하 생략) 2022-10-24 19:45:29.117[TRACE] : -> statement 2022-10-24 19:45:29.118[TRACE] : ---> selectStatement 2022-10-24 19:45:29.118[TRACE] : -----> queryRule 2022-10-24 19:45:29.118[TRACE] : -------> selectFrom 2022-10-24 19:45:29.119[TRACE] : <--- selectStatement 2022-10-24 19:45:29.119[TRACE] : <- statement 2022-10-24 19:45:29.119[DEBUG] : throwQueryException() : no errors 2022-10-24 19:45:29.119[DEBUG] : --- HQL AST --- \-[QUERY] Node: 'query' +-[SELECT_FROM] Node: 'SELECT_FROM' | +-[FROM] Node: 'from' | | \-[RANGE] Node: 'RANGE' | | +-[DOT] Node: '.' | | | +-[DOT] Node: '.' | | | | +-[DOT] Node: '.' | | | | | +-[DOT] Node: '.' | | | | | | +-[DOT] Node: '.' | | | | | | | +-[IDENT] Node: 'com' | | | | | | | \-[IDENT] Node: 'backend' | | | | | | \-[IDENT] Node: 'connectable' | | | | | \-[IDENT] Node: 'user' | | | | \-[IDENT] Node: 'domain' | | | \-[IDENT] Node: 'User' | | \-[ALIAS] Node: 'generatedAlias0' | \-[SELECT] Node: 'select' | \-[IDENT] Node: 'generatedAlias0' \-[WHERE] Node: 'where' \-[EQ] Node: '=' +-[DOT] Node: '.' | +-[IDENT] Node: 'generatedAlias0' | \-[IDENT] Node: 'klaytnAddress' \-[COLON] Node: ':' \-[IDENT] Node: 'param0' 2022-10-24 19:45:29.119[DEBUG] : select << begin [level=1, statement=select] 2022-10-24 19:45:29.121[DEBUG] : FromClause{level=1} : com.backend.connectable.user.domain.User (generatedAlias0) -> user0_ 2022-10-24 19:45:29.122[DEBUG] : Resolved : generatedAlias0 -> user0_.id 2022-10-24 19:45:29.122[DEBUG] : Resolved : generatedAlias0 -> user0_.id 2022-10-24 19:45:29.122[TRACE] : Handling property dereference [com.backend.connectable.user.domain.User (generatedAlias0) -> klaytnAddress (class)] 2022-10-24 19:45:29.122[DEBUG] : getDataType() : klaytnAddress -> org.hibernate.type.StringType@6a59154d 2022-10-24 19:45:29.122[DEBUG] : Resolved : generatedAlias0.klaytnAddress -> user0_.klaytn_address 2022-10-24 19:45:29.122[DEBUG] : select : finishing up [level=1, statement=select] 2022-10-24 19:45:29.122[DEBUG] : processQuery() : ( SELECT ( {select clause} user0_.id ) ( FromClause{level=1} user user0_ ) ( where ( = ( user0_.klaytn_address user0_.id klaytnAddress ) ? ) ) ) 2022-10-24 19:45:29.122[DEBUG] : Tables referenced from query nodes: \-QueryNode +-SelectClause | referencedTables(entity User): [user] | +-IdentNode | | persister: SingleTableEntityPersister(com.backend.connectable.user.domain.User) | | originalText: generatedAlias0 | \-SqlFragment +-FromClause | \-FromElement \-SqlNode \-BinaryLogicOperatorNode +-DotNode | persister: SingleTableEntityPersister(com.backend.connectable.user.domain.User) | path: generatedAlias0.klaytnAddress | +-IdentNode | | persister: SingleTableEntityPersister(com.backend.connectable.user.domain.User) | | originalText: generatedAlias0 | \-IdentNode | persister: null | originalText: klaytnAddress \-ParameterNode 2022-10-24 19:45:29.122[DEBUG] : Using FROM fragment [user user0_] 2022-10-24 19:45:29.122[DEBUG] : select >> end [level=1, statement=select] 2022-10-24 19:45:29.122[DEBUG] : --- SQL AST --- \-[SELECT] QueryNode: 'SELECT' querySpaces (user) +-[SELECT_CLAUSE] SelectClause: '{select clause}' | +-[ALIAS_REF] IdentNode: 'user0_.id as id1_5_' {alias=generatedAlias0, className=com.backend.connectable.user.domain.User, tableAlias=user0_} | \-[SQL_TOKEN] SqlFragment: 'user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_' +-[FROM] FromClause: 'from' FromClause{level=1, fromElementCounter=1, fromElements=1, fromElementByClassAlias=[generatedAlias0], fromElementByTableAlias=[user0_], fromElementsByPath=[], collectionJoinFromElementsByPath=[], impliedElements=[]} | \-[FROM_FRAGMENT] FromElement: 'user user0_' FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=generatedAlias0,role=null,tableName=user,tableAlias=user0_,origin=null,columns={,className=com.backend.connectable.user.domain.User}} \-[WHERE] SqlNode: 'where' \-[EQ] BinaryLogicOperatorNode: '=' +-[DOT] DotNode: 'user0_.klaytn_address' {propertyName=klaytnAddress,dereferenceType=PRIMITIVE,getPropertyPath=klaytnAddress,path=generatedAlias0.klaytnAddress,tableAlias=user0_,className=com.backend.connectable.user.domain.User,classAlias=generatedAlias0} | +-[ALIAS_REF] IdentNode: 'user0_.id' {alias=generatedAlias0, className=com.backend.connectable.user.domain.User, tableAlias=user0_} | \-[IDENT] IdentNode: 'klaytnAddress' {originalText=klaytnAddress} \-[NAMED_PARAM] ParameterNode: '?' {name=param0, expectedType=org.hibernate.type.StringType@6a59154d} 2022-10-24 19:45:29.122[DEBUG] : throwQueryException() : no errors 2022-10-24 19:45:29.122[DEBUG] : HQL: select generatedAlias0 from com.backend.connectable.user.domain.User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 2022-10-24 19:45:29.122[DEBUG] : SQL: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? 2022-10-24 19:45:29.122[DEBUG] : throwQueryException() : no errors 2022-10-24 19:45:29.128[TRACE] : Find: select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 2022-10-24 19:45:29.128[TRACE] : Named parameters: {param0=0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d} // JPA 내부적으로는 JDBC를 활용하여 pstm, rs, connection을 통한 쿼리 수행을 진행한다 2022-10-24 19:45:29.129[DEBUG] : select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? 2022-10-24 19:45:29.130[TRACE] : Registering statement [HikariProxyPreparedStatement@526283268 wrapping prep2: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=?] 2022-10-24 19:45:29.130[TRACE] : Registering last query statement [HikariProxyPreparedStatement@526283268 wrapping prep2: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=?] 2022-10-24 19:45:29.130[TRACE] : binding parameter [1] as [VARCHAR] - [0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d] 2022-10-24 19:45:29.132[TRACE] : Bound [2] parameters total 2022-10-24 19:45:29.133[TRACE] : Registering result set [HikariProxyResultSet@1140706281 wrapping rs20: org.h2.result.LocalResultImpl@2fdf4fc0 columns: 6 rows: 1 pos: -1] 2022-10-24 19:45:29.133[TRACE] : Processing result set 2022-10-24 19:45:29.133[DEBUG] : Result set row: 0 2022-10-24 19:45:29.133[TRACE] : extracted value ([id1_5_] : [BIGINT]) - [1] 2022-10-24 19:45:29.133[DEBUG] : Result row: EntityKey[com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.134[TRACE] : Initializing object from ResultSet: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.135[TRACE] : Hydrating entity: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.136[TRACE] : extracted value ([is_activ2_5_] : [BOOLEAN]) - [true] 2022-10-24 19:45:29.136[TRACE] : extracted value ([klaytn_a3_5_] : [VARCHAR]) - [0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d] 2022-10-24 19:45:29.136[TRACE] : extracted value ([nickname4_5_] : [VARCHAR]) - [joel] 2022-10-24 19:45:29.136[TRACE] : extracted value ([phone_nu5_5_] : [VARCHAR]) - [010-1111-1111] 2022-10-24 19:45:29.136[TRACE] : extracted value ([privacy_6_5_] : [BOOLEAN]) - [true] 2022-10-24 19:45:29.136[TRACE] : Done processing result set (1 rows) 2022-10-24 19:45:29.136[TRACE] : Total objects hydrated: 1 2022-10-24 19:45:29.137[DEBUG] : Resolving attributes for [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.137[DEBUG] : Processing attribute `isActive` : value = true 2022-10-24 19:45:29.137[DEBUG] : Attribute (`isActive`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.137[DEBUG] : Processing attribute `klaytnAddress` : value = 0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d 2022-10-24 19:45:29.137[DEBUG] : Attribute (`klaytnAddress`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.137[DEBUG] : Processing attribute `nickname` : value = joel 2022-10-24 19:45:29.137[DEBUG] : Attribute (`nickname`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.137[DEBUG] : Processing attribute `phoneNumber` : value = 010-1111-1111 2022-10-24 19:45:29.137[DEBUG] : Attribute (`phoneNumber`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.137[DEBUG] : Processing attribute `privacyAgreement` : value = true 2022-10-24 19:45:29.137[DEBUG] : Attribute (`privacyAgreement`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.137[DEBUG] : Done materializing entity [com.backend.connectable.user.domain.User#1] // 해당 쿼리를 수행할 때 만들어둔 pstm, rs, connection을 닫는다 2022-10-24 19:45:29.137[TRACE] : Releasing statement [HikariProxyPreparedStatement@526283268 wrapping prep2: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? {1: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d'}] 2022-10-24 19:45:29.137[TRACE] : Closing result set [HikariProxyResultSet@1140706281 wrapping rs20: org.h2.result.LocalResultImpl@2fdf4fc0 columns: 6 rows: 1 pos: 1] 2022-10-24 19:45:29.137[TRACE] : Closing prepared statement [HikariProxyPreparedStatement@526283268 wrapping prep2: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? {1: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d'}] 2022-10-24 19:45:29.137[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.138[TRACE] : Initializing non-lazy collections 2022-10-24 19:45:29.140[TRACE] : Completing transaction for [com.backend.connectable.security.CustomUserDetailsService.loadUserByUsername] 2022-10-24 19:45:29.140[DEBUG] : Initiating transaction commit // 트랜잭션이 종료되는 시점에 DB에 커밋한다 2022-10-24 19:45:29.140[DEBUG] : Committing JPA transaction on EntityManager [SessionImpl(1543251544PersistenceContext[entityKeys=[EntityKey[com.backend.connectable.user.domain.User#1]], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] 2022-10-24 19:45:29.140[DEBUG] : committing 2022-10-24 19:45:29.140[TRACE] : ResourceLocalTransactionCoordinatorImpl#beforeCompletionCallback 2022-10-24 19:45:29.140[TRACE] : SessionImpl#beforeTransactionCompletion() 2022-10-24 19:45:29.140[TRACE] : Automatically flushing session 2022-10-24 19:45:29.140[TRACE] : Flushing session 2022-10-24 19:45:29.140[DEBUG] : Processing flush-time cascades 2022-10-24 19:45:29.140[TRACE] : Processing cascade ACTION_PERSIST_ON_FLUSH for: com.backend.connectable.user.domain.User 2022-10-24 19:45:29.140[TRACE] : Done processing cascade ACTION_PERSIST_ON_FLUSH for: com.backend.connectable.user.domain.User // Dirty Checking을 하고 Flush를 진행한다 2022-10-24 19:45:29.140[DEBUG] : Dirty checking collections 2022-10-24 19:45:29.140[TRACE] : Flushing entities and processing referenced collections 2022-10-24 19:45:29.140[TRACE] : Processing unreferenced collections 2022-10-24 19:45:29.140[TRACE] : Scheduling collection removes/(re)creates/updates 2022-10-24 19:45:29.140[DEBUG] : Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects 2022-10-24 19:45:29.140[DEBUG] : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections 2022-10-24 19:45:29.140[DEBUG] : Listing entities: 2022-10-24 19:45:29.140[DEBUG] : com.backend.connectable.user.domain.User{phoneNumber=010-1111-1111, klaytnAddress=0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d, privacyAgreement=true, nickname=joel, id=1, isActive=true} 2022-10-24 19:45:29.140[TRACE] : Executing flush 2022-10-24 19:45:29.140[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.140[TRACE] : Post flush 2022-10-24 19:45:29.140[TRACE] : LogicalConnection#beforeTransactionCompletion 2022-10-24 19:45:29.140[TRACE] : SynchronizationRegistryStandardImpl.notifySynchronizationsBeforeTransactionCompletion // Connection Commit을 진행한다 2022-10-24 19:45:29.140[TRACE] : Preparing to commit transaction via JDBC Connection.commit() 2022-10-24 19:45:29.140[TRACE] : Transaction committed via JDBC Connection.commit() 2022-10-24 19:45:29.140[TRACE] : re-enabling auto-commit on JDBC Connection after completion of JDBC-based transaction 2022-10-24 19:45:29.140[TRACE] : LogicalConnection#afterTransaction // JDBC 리소스 릴리즈 2022-10-24 19:45:29.140[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.140[TRACE] : ResourceLocalTransactionCoordinatorImpl#afterCompletionCallback(true) 2022-10-24 19:45:29.140[TRACE] : SynchronizationRegistryStandardImpl.notifySynchronizationsAfterTransactionCompletion(3) 2022-10-24 19:45:29.140[TRACE] : SessionImpl#afterTransactionCompletion(successful=true, delayed=false) // JPA 엔티티 매니저 닫기 -> 영속성 컨텍스트도 닫히면서 엔티티는 준영속 상태로 넘어감 2022-10-24 19:45:29.140[DEBUG] : Closing JPA EntityManager [SessionImpl(1543251544PersistenceContext[entityKeys=[EntityKey[com.backend.connectable.user.domain.User#1]], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction 2022-10-24 19:45:29.140[TRACE] : Closing session [50317ba0-a0b4-49ff-b135-4000cce23439] 2022-10-24 19:45:29.140[TRACE] : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@47a52022] 2022-10-24 19:45:29.140[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.140[TRACE] : Closing logical connection 2022-10-24 19:45:29.140[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.140[TRACE] : Logical connection closed // 그 다음 필터들이 수행된다 2022-10-24 19:45:29.141[TRACE] : Invoking RequestCacheAwareFilter (7/12) 2022-10-24 19:45:29.141[TRACE] : Invoking SecurityContextHolderAwareRequestFilter (8/12) 2022-10-24 19:45:29.141[TRACE] : Invoking AnonymousAuthenticationFilter (9/12) // SpringSecurity 측에서 Authentication 객체가 있는 경우, 따로 또 만들어 저장하진 않음 2022-10-24 19:45:29.141[TRACE] : Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=com.backend.connectable.security.ConnectableUserDetails@67531e47, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]] 2022-10-24 19:45:29.141[TRACE] : Invoking SessionManagementFilter (10/12) 2022-10-24 19:45:29.141[TRACE] : Preparing session with ChangeSessionIdAuthenticationStrategy (1/1) 2022-10-24 19:45:29.141[TRACE] : Invoking ExceptionTranslationFilter (11/12) 2022-10-24 19:45:29.141[TRACE] : Invoking FilterSecurityInterceptor (12/12) 2022-10-24 19:45:29.142[TRACE] : Did not match request to Ant [pattern='/**/*', OPTIONS] - [permitAll] (1/9) 2022-10-24 19:45:29.142[TRACE] : Did not match request to Ant [pattern='/users/login'] - [permitAll] (2/9) 2022-10-24 19:45:29.142[TRACE] : Did not match request to Ant [pattern='/events'] - [permitAll] (3/9) 2022-10-24 19:45:29.142[TRACE] : Did not match request to Ant [pattern='/events/**'] - [permitAll] (4/9) 2022-10-24 19:45:29.142[TRACE] : Did not re-authenticate UsernamePasswordAuthenticationToken [Principal=com.backend.connectable.security.ConnectableUserDetails@67531e47, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]] before authorizing 2022-10-24 19:45:29.142[TRACE] : Authorizing filter invocation [PUT /users] with attributes [authenticated] 2022-10-24 19:45:29.147[DEBUG] : Authorized filter invocation [PUT /users] with attributes [authenticated] 2022-10-24 19:45:29.147[TRACE] : Did not switch RunAs authentication since RunAsManager returned null 2022-10-24 19:45:29.147[DEBUG] : Secured PUT /users 2022-10-24 19:45:29.150[DEBUG] : Set encoding to UTF-8 // DispatcherServlet의 핸들러 매핑을 통해 요청을 처리할 핸들러(컨트롤러) 를 가져온다 // 찾아낸 핸들러를 처리할 핸들러 어댑터를 찾아, 핸들러 어댑터로 핸들러를 처리한다 2022-10-24 19:45:29.150[TRACE] : PUT "/users", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet' 2022-10-24 19:45:29.153[TRACE] : Returning cached instance of singleton bean 'userController' 2022-10-24 19:45:29.153[TRACE] : Mapped to com.backend.connectable.user.ui.UserController#modifyUser(ConnectableUserDetails, UserModifyRequest) // OSIV를 활짝 연다. 이제 인터셉터단 까지 영속성 컨텍스트는 살아있게 된다. 2022-10-24 19:45:29.158[DEBUG] : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor 2022-10-24 19:45:29.158[TRACE] : Opening Hibernate Session. tenant=null 2022-10-24 19:45:29.158[TRACE] : Opened Session [b88524e4-3fd7-4db1-b935-7b521c4b58fd] at timestamp: 1666608329158 2022-10-24 19:45:29.194[TRACE] : Read "application/json;charset=UTF-8" to [com.backend.connectable.user.ui.dto.UserModifyRequest@40401b01] 2022-10-24 19:45:29.232[TRACE] : Calling isReachable on object com.backend.connectable.user.ui.dto.UserModifyRequest@40401b01 with node name nickname. 2022-10-24 19:45:29.240[TRACE] : Validating value hello against constraint defined by ConstraintDescriptorImpl{annotation=j.v.c.NotBlank, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, constraintLocationKind=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface com.backend.connectable.exception.sequence.ValidationGroups$NotEmptyGroup], attributes={groups=[Ljava.lang.Class;@68274b46, message=닉네임은 필수 입력값 입니다., payload=[Ljava.lang.Class;@5cec7483}, constraintType=GENERIC, valueUnwrapping=DEFAULT}. 2022-10-24 19:45:29.240[TRACE] : Creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.NotBlankValidator' 2022-10-24 19:45:29.240[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' 2022-10-24 19:45:29.244[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' 2022-10-24 19:45:29.244[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' // Bean Validation이 진행된다. 2022-10-24 19:45:29.245[TRACE] : No @Scheduled annotations found on bean class: class org.hibernate.validator.internal.constraintvalidators.bv.NotBlankValidator 2022-10-24 19:45:29.245[TRACE] : Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.NotBlankValidator' 2022-10-24 19:45:29.247[TRACE] : Calling isReachable on object com.backend.connectable.user.ui.dto.UserModifyRequest@40401b01 with node name phoneNumber. 2022-10-24 19:45:29.247[TRACE] : Validating value 010-0000-0000 against constraint defined by ConstraintDescriptorImpl{annotation=j.v.c.NotBlank, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, constraintLocationKind=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface com.backend.connectable.exception.sequence.ValidationGroups$NotEmptyGroup], attributes={groups=[Ljava.lang.Class;@5dd1427f, message=연락처는 필수 입력값 입니다., payload=[Ljava.lang.Class;@5cec7483}, constraintType=GENERIC, valueUnwrapping=DEFAULT}. 2022-10-24 19:45:29.248[TRACE] : Creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.NotBlankValidator' 2022-10-24 19:45:29.249[TRACE] : Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.NotBlankValidator' 2022-10-24 19:45:29.249[TRACE] : Validating value 010-0000-0000 against constraint defined by ConstraintDescriptorImpl{annotation=j.v.c.Pattern, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, constraintLocationKind=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface com.backend.connectable.exception.sequence.ValidationGroups$PatternCheckGroup], attributes={flags=[Ljavax.validation.constraints.Pattern$Flag;@7fa06575, regexp=^\d{2,3}-\d{3,4}-\d{4}$, groups=[Ljava.lang.Class;@10c95502, message=연락처는 xxx-xxxx-xxxx 양식으로 구성되어야 합니다., payload=[Ljava.lang.Class;@424e80cd}, constraintType=GENERIC, valueUnwrapping=DEFAULT}. 2022-10-24 19:45:29.249[TRACE] : Creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator' 2022-10-24 19:45:29.250[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' 2022-10-24 19:45:29.252[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' 2022-10-24 19:45:29.252[TRACE] : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor' 2022-10-24 19:45:29.252[TRACE] : No @Scheduled annotations found on bean class: class org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator 2022-10-24 19:45:29.252[TRACE] : Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator' 2022-10-24 19:45:29.254[TRACE] : Validating value hello against constraint defined by ConstraintDescriptorImpl{annotation=j.v.c.Pattern, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, constraintLocationKind=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface com.backend.connectable.exception.sequence.ValidationGroups$PatternCheckGroup], attributes={flags=[Ljavax.validation.constraints.Pattern$Flag;@7fa06575, regexp=^(?=.*[a-zA-Z0-9가-힣])[a-zA-Z0-9가-힣]{2,10}$, groups=[Ljava.lang.Class;@4f7a0a73, message=닉네임은 2자 이상 10자 이하, 영어 또는 숫자 또는 한글로 구성된 닉네임이여야 합니다., payload=[Ljava.lang.Class;@424e80cd}, constraintType=GENERIC, valueUnwrapping=DEFAULT}. 2022-10-24 19:45:29.254[TRACE] : Creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator' 2022-10-24 19:45:29.255[TRACE] : Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator' 2022-10-24 19:45:29.255[TRACE] : Arguments: [com.backend.connectable.security.ConnectableUserDetails@67531e47, com.backend.connectable.user.ui.dto.UserModifyRequest@40401b01] // 컨트롤러 메서드가 실행된다. 2022-10-24 19:45:29.255[INFO ] : $$ Controller METHOD START $$ // 서비스에 CGLIB 프록시가 적용되어, 해당 Service 메서드 실행전에 트랜잭션이 먼저 생성이 된다 // EntityManager는 Thread에 하나 넣는다 2022-10-24 19:45:29.255[DEBUG] : Found thread-bound EntityManager [SessionImpl(1131948410PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction 2022-10-24 19:45:29.255[DEBUG] : Creating new transaction with name [com.backend.connectable.user.service.UserService.modifyUserByUserDetails]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 2022-10-24 19:45:29.255[DEBUG] : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false 2022-10-24 19:45:29.255[DEBUG] : begin 2022-10-24 19:45:29.256[TRACE] : Preparing to begin transaction via JDBC Connection.setAutoCommit(false) 2022-10-24 19:45:29.256[TRACE] : Transaction begun via JDBC Connection.setAutoCommit(false) 2022-10-24 19:45:29.256[TRACE] : ResourceLocalTransactionCoordinatorImpl#afterBeginCallback 2022-10-24 19:45:29.256[DEBUG] : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4c09c2b8] 2022-10-24 19:45:29.256[TRACE] : Getting transaction for [com.backend.connectable.user.service.UserService.modifyUserByUserDetails] // CGLIB 프록시가 target 서비스를 직접 호출한다 2022-10-24 19:45:29.260[INFO ] : $$ SERVICE METHOD START $$ // 어플리케이션 단에서 트랜잭션이 필요없다면, 생성하지 않는다. // 하지만 조회된 엔티티의 경우 영속성 컨텍스트의 혜택을 받을 수 있다. 우리에겐 엔티티 매니저 있으니까 // 열심히 쿼리 날린다 2022-10-24 19:45:29.260[TRACE] : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByKlaytnAddress]: This method is not transactional. 2022-10-24 19:45:29.261[DEBUG] : Rendered criteria query -> select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 2022-10-24 19:45:29.261[TRACE] : Located HQL query plan in cache (select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0) 2022-10-24 19:45:29.261[TRACE] : Find: select generatedAlias0 from User as generatedAlias0 where generatedAlias0.klaytnAddress=:param0 2022-10-24 19:45:29.261[TRACE] : Named parameters: {param0=0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d} 2022-10-24 19:45:29.261[DEBUG] : select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? 2022-10-24 19:45:29.261[TRACE] : Registering statement [HikariProxyPreparedStatement@502302921 wrapping prep3: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=?] 2022-10-24 19:45:29.261[TRACE] : Registering last query statement [HikariProxyPreparedStatement@502302921 wrapping prep3: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=?] 2022-10-24 19:45:29.261[TRACE] : binding parameter [1] as [VARCHAR] - [0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d] 2022-10-24 19:45:29.261[TRACE] : Bound [2] parameters total 2022-10-24 19:45:29.261[TRACE] : Registering result set [HikariProxyResultSet@527341005 wrapping rs21: org.h2.result.LocalResultImpl@2074a17f columns: 6 rows: 1 pos: -1] 2022-10-24 19:45:29.261[TRACE] : Processing result set 2022-10-24 19:45:29.261[DEBUG] : Result set row: 0 2022-10-24 19:45:29.261[TRACE] : extracted value ([id1_5_] : [BIGINT]) - [1] 2022-10-24 19:45:29.261[DEBUG] : Result row: EntityKey[com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.262[TRACE] : Initializing object from ResultSet: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.262[TRACE] : Hydrating entity: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.262[TRACE] : extracted value ([is_activ2_5_] : [BOOLEAN]) - [true] 2022-10-24 19:45:29.262[TRACE] : extracted value ([klaytn_a3_5_] : [VARCHAR]) - [0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d] 2022-10-24 19:45:29.262[TRACE] : extracted value ([nickname4_5_] : [VARCHAR]) - [joel] 2022-10-24 19:45:29.262[TRACE] : extracted value ([phone_nu5_5_] : [VARCHAR]) - [010-1111-1111] 2022-10-24 19:45:29.262[TRACE] : extracted value ([privacy_6_5_] : [BOOLEAN]) - [true] 2022-10-24 19:45:29.262[TRACE] : Done processing result set (1 rows) 2022-10-24 19:45:29.262[TRACE] : Total objects hydrated: 1 2022-10-24 19:45:29.262[DEBUG] : Resolving attributes for [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.262[DEBUG] : Processing attribute `isActive` : value = true 2022-10-24 19:45:29.262[DEBUG] : Attribute (`isActive`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.262[DEBUG] : Processing attribute `klaytnAddress` : value = 0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d 2022-10-24 19:45:29.262[DEBUG] : Attribute (`klaytnAddress`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.262[DEBUG] : Processing attribute `nickname` : value = joel 2022-10-24 19:45:29.262[DEBUG] : Attribute (`nickname`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.262[DEBUG] : Processing attribute `phoneNumber` : value = 010-1111-1111 2022-10-24 19:45:29.262[DEBUG] : Attribute (`phoneNumber`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.262[DEBUG] : Processing attribute `privacyAgreement` : value = true 2022-10-24 19:45:29.262[DEBUG] : Attribute (`privacyAgreement`) - enhanced for lazy-loading? - false 2022-10-24 19:45:29.262[DEBUG] : Done materializing entity [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.262[TRACE] : Releasing statement [HikariProxyPreparedStatement@502302921 wrapping prep3: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? {1: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d'}] 2022-10-24 19:45:29.262[TRACE] : Closing result set [HikariProxyResultSet@527341005 wrapping rs21: org.h2.result.LocalResultImpl@2074a17f columns: 6 rows: 1 pos: 1] 2022-10-24 19:45:29.262[TRACE] : Closing prepared statement [HikariProxyPreparedStatement@502302921 wrapping prep3: select user0_.id as id1_5_, user0_.is_active as is_activ2_5_, user0_.klaytn_address as klaytn_a3_5_, user0_.nickname as nickname4_5_, user0_.phone_number as phone_nu5_5_, user0_.privacy_agreement as privacy_6_5_ from user user0_ where user0_.klaytn_address=? {1: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d'}] 2022-10-24 19:45:29.262[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.262[TRACE] : Initializing non-lazy collections 2022-10-24 19:45:29.262[INFO ] : @@USER_DETAILS_USER_OBJECT::com.backend.connectable.user.domain.User@20 // 서비스 메서드가 끝났다. 트랜잭션을 종료하고, commit을 한다 2022-10-24 19:45:29.262[INFO ] : $$ SERVICE METHOD DONE $$ 2022-10-24 19:45:29.262[TRACE] : Completing transaction for [com.backend.connectable.user.service.UserService.modifyUserByUserDetails] 2022-10-24 19:45:29.262[DEBUG] : Initiating transaction commit 2022-10-24 19:45:29.262[DEBUG] : Committing JPA transaction on EntityManager [SessionImpl(1131948410PersistenceContext[entityKeys=[EntityKey[com.backend.connectable.user.domain.User#1]], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] 2022-10-24 19:45:29.262[DEBUG] : committing 2022-10-24 19:45:29.262[TRACE] : ResourceLocalTransactionCoordinatorImpl#beforeCompletionCallback 2022-10-24 19:45:29.262[TRACE] : SessionImpl#beforeTransactionCompletion() 2022-10-24 19:45:29.262[TRACE] : Automatically flushing session 2022-10-24 19:45:29.262[TRACE] : Flushing session 2022-10-24 19:45:29.262[DEBUG] : Processing flush-time cascades 2022-10-24 19:45:29.262[TRACE] : Processing cascade ACTION_PERSIST_ON_FLUSH for: com.backend.connectable.user.domain.User 2022-10-24 19:45:29.262[TRACE] : Done processing cascade ACTION_PERSIST_ON_FLUSH for: com.backend.connectable.user.domain.User // Dirty Checking을 진행한다. 엔티티의 스냅샷과 다르니, update 쿼리 날려보도록 하자 2022-10-24 19:45:29.262[DEBUG] : Dirty checking collections 2022-10-24 19:45:29.262[TRACE] : Flushing entities and processing referenced collections 2022-10-24 19:45:29.262[TRACE] : com.backend.connectable.user.domain.User.nickname is dirty 2022-10-24 19:45:29.262[TRACE] : com.backend.connectable.user.domain.User.phoneNumber is dirty 2022-10-24 19:45:29.262[TRACE] : Found dirty properties [[com.backend.connectable.user.domain.User#1]] : [nickname, phoneNumber] 2022-10-24 19:45:29.262[TRACE] : Updating entity: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.263[TRACE] : Processing unreferenced collections 2022-10-24 19:45:29.263[TRACE] : Scheduling collection removes/(re)creates/updates 2022-10-24 19:45:29.263[DEBUG] : Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects 2022-10-24 19:45:29.263[DEBUG] : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections 2022-10-24 19:45:29.263[DEBUG] : Listing entities: 2022-10-24 19:45:29.263[DEBUG] : com.backend.connectable.user.domain.User{phoneNumber=010-0000-0000, klaytnAddress=0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d, privacyAgreement=true, nickname=hello, id=1, isActive=true} 2022-10-24 19:45:29.263[TRACE] : Executing flush 2022-10-24 19:45:29.265[TRACE] : Updating entity: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.265[TRACE] : Initializing service [role=org.hibernate.engine.jdbc.batch.spi.BatchBuilder] 2022-10-24 19:45:29.267[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.267[DEBUG] : update user set is_active=?, klaytn_address=?, nickname=?, phone_number=?, privacy_agreement=? where id=? 2022-10-24 19:45:29.268[TRACE] : Registering statement [HikariProxyPreparedStatement@227393620 wrapping prep4: update user set is_active=?, klaytn_address=?, nickname=?, phone_number=?, privacy_agreement=? where id=?] 2022-10-24 19:45:29.268[TRACE] : Dehydrating entity: [com.backend.connectable.user.domain.User#1] 2022-10-24 19:45:29.268[TRACE] : binding parameter [1] as [BOOLEAN] - [true] 2022-10-24 19:45:29.268[TRACE] : binding parameter [2] as [VARCHAR] - [0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d] 2022-10-24 19:45:29.268[TRACE] : binding parameter [3] as [VARCHAR] - [hello] 2022-10-24 19:45:29.268[TRACE] : binding parameter [4] as [VARCHAR] - [010-0000-0000] 2022-10-24 19:45:29.268[TRACE] : binding parameter [5] as [BOOLEAN] - [true] 2022-10-24 19:45:29.268[TRACE] : binding parameter [6] as [BIGINT] - [1] // 쿼리 끝났으면 또 pstm, conn, rs 릴리즈 2022-10-24 19:45:29.270[TRACE] : Releasing statement [HikariProxyPreparedStatement@227393620 wrapping prep4: update user set is_active=?, klaytn_address=?, nickname=?, phone_number=?, privacy_agreement=? where id=? {1: TRUE, 2: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d', 3: 'hello', 4: '010-0000-0000', 5: TRUE, 6: 1}] 2022-10-24 19:45:29.270[TRACE] : Closing prepared statement [HikariProxyPreparedStatement@227393620 wrapping prep4: update user set is_active=?, klaytn_address=?, nickname=?, phone_number=?, privacy_agreement=? where id=? {1: TRUE, 2: '0x3ab31d219d45ce40d6862d68d37de6bb73e21a8d', 3: 'hello', 4: '010-0000-0000', 5: TRUE, 6: 1}] 2022-10-24 19:45:29.270[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.270[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.271[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.271[TRACE] : Post flush 2022-10-24 19:45:29.271[TRACE] : LogicalConnection#beforeTransactionCompletion 2022-10-24 19:45:29.271[TRACE] : SynchronizationRegistryStandardImpl.notifySynchronizationsBeforeTransactionCompletion 2022-10-24 19:45:29.271[TRACE] : Preparing to commit transaction via JDBC Connection.commit() 2022-10-24 19:45:29.271[TRACE] : Transaction committed via JDBC Connection.commit() 2022-10-24 19:45:29.271[TRACE] : re-enabling auto-commit on JDBC Connection after completion of JDBC-based transaction 2022-10-24 19:45:29.271[TRACE] : LogicalConnection#afterTransaction 2022-10-24 19:45:29.271[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.271[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.271[TRACE] : ResourceLocalTransactionCoordinatorImpl#afterCompletionCallback(true) 2022-10-24 19:45:29.271[TRACE] : SynchronizationRegistryStandardImpl.notifySynchronizationsAfterTransactionCompletion(3) 2022-10-24 19:45:29.271[TRACE] : SessionImpl#afterTransactionCompletion(successful=true, delayed=false) // OSIV가 인터셉터 단까지 걸려있기 때문에, 엔티티 매니저는 트랜잭션이 끝나도 살아있다. 2022-10-24 19:45:29.271[DEBUG] : Not closing pre-bound JPA EntityManager after transaction 2022-10-24 19:45:29.271[INFO ] : $$ Controller METHOD DONE $$ // 뷰리졸버에서 이제 DTO를 JSON으로 바꿔주자 2022-10-24 19:45:29.280[DEBUG] : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/cbor] 2022-10-24 19:45:29.280[TRACE] : Writing [com.backend.connectable.user.ui.dto.UserModifyResponse@6398b197] 2022-10-24 19:45:29.284[TRACE] : Not injecting HSTS header since it did not match request to [Is Secure] 2022-10-24 19:45:29.287[TRACE] : No view rendering, null ModelAndView returned. // 인터셉터 끝났나보다. 이제 OSIV 닫자 // 닫는거 보니까 필터에서 닫는거랑 똑같이 생겼다! 영속성 컨텍스트 이런거 싹다 파기시켜버려! 2022-10-24 19:45:29.287[DEBUG] : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor 2022-10-24 19:45:29.287[TRACE] : Closing session [b88524e4-3fd7-4db1-b935-7b521c4b58fd] 2022-10-24 19:45:29.287[TRACE] : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@7e415931] 2022-10-24 19:45:29.287[DEBUG] : HHH000420: Closing un-released batch 2022-10-24 19:45:29.287[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.287[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.287[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.287[TRACE] : Closing logical connection 2022-10-24 19:45:29.287[TRACE] : Releasing JDBC resources 2022-10-24 19:45:29.287[TRACE] : Starting after statement execution processing [ON_CLOSE] 2022-10-24 19:45:29.287[TRACE] : Logical connection closed // 200 OK를 반환한다. 2022-10-24 19:45:29.287[DEBUG] : Completed 200 OK, headers={masked}
Transactional Readonly True?¶
- 참고: https://willseungh0.tistory.com/75
- 성능 향상의 이유
- 강제로 플러시 호출하지 않으면 플러시 호출 안됨
- 등록/수정/삭제 동작 안함
- 영속성 컨텍스트 변경 감지 스냅샷 보관하지 않아 성능 향상
트랜잭션 수행 디버거와 함께¶
Spring은 항상 CGLIB을 쓸까?¶
- 참고: https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html
- 참고: https://stackoverflow.com/questions/68084650/why-does-spring-inject-some-of-my-dependencies-as-cglib-proxies-and-why-are-the
- Spring AOP는 JDK dynamic proxy 혹은 CGLIB 프록시를 통해 프록시를 생성한다.
- JDK dynamic proxy를 통해 프록시를 만들 수 있다면 이걸로 우선적으로 만드려 한다.
- 타겟 객체가 하나의 인터페이스라도 구현하고 있다면, JDK dynamic proxy가 생성될 것
- 만약 인터페이스로 구현을 해둔게 없다면 CGLIB으로 프록시화 할 것
- 물론 CGLIB으로 싹다 프록시화 시킬 수 있음. 하지만 다음을 유념할 것
- final은 재정의 할 수 없기에 AOP 도입 못함
- CGLIB 라이브러리가 꼭 필요합니다
- 프록시 객체의 생성자는 2번 호출됨. 프록시화된 객체에 대한 서브클래스가 생성되는 과정에서 어쩔 수 없음
- 프록시화 된 객체는 2개가 각각 생성됨.
- JDK는 이게 2번 호출 안됨.
- 생성자에 로직 없으면 큰 문제 안됨
JDK Dynamic Proxy¶
- 공통으로 처리할 전/후처리 로직을 InvocationHandler로 구현하여 인터페이스를 구현할 때 사용하도록 하자
AInterface target = new AImpl(); // JDK 동적 프록시의 InvocationHandler를 구현한 구현체로, // target 객체의 메서드 수행 전/후처리에 대한 로직을 담아둔다 TimeInvocationHandler handler = new TimeInvocationHandler(target); // AInterface 타입으로 프록시를 만든다 // 따라서 실 구현체와는 아무런 연관이 없는 프록시가 생성이 된다 // 그저 각 메서드 호출시 필요한 전/후처리를 담은 핸들러만 전달 AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler); proxy.call();
- 스프링에서 사용하는 jdk - getProxy() 메서드
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces); } @Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); }
CGLIB¶
- 공통으로 처리할 전/후처리 로직을 MethodInterceptor로 구현하여 프록시 메서드 수행시 사용하도록 하자
ConcreteService target = new ConcreteService(); // CGLIB는 enhancer를 통한 프록시를 생성한다 Enhancer enhancer = new Enhancer(); // 상속받을 구체 클래스를 지정한다. enhancer.setSuperclass(ConcreteService.class); // CGLIB은 MethodInterceptor를 통해 프록시에 적용할 실행 로직을 할당 enhancer.setCallback(new TimeMethodInterceptor()); // 지정한 콘크리트 클래스를 상속받아 프록시를 생성한다. ConcreteService proxy = (ConcreteService) enhancer.create(); proxy.call();
- 스프링에서 사용하는 cglib - getProxy() 메서드
@Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); // Callback 구현을 통한 인터셉터 등록 Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
DefaultAopProxyFactory¶
- DefaultAopProxyFactory로 기본적으로 프록시화를 시키네!
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { private static final long serialVersionUID = 7930414337282325166L; @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!NativeDetector.inNativeImage() && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 타겟 클래스가 인터페이스 있으면 => Jdk if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 없으면 CGLIB return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } }