Using Spring Boot 1.5.12, we create scoped proxies for #ConfigurationProperties beans. We do this so that we can effectively have property values that are scoped to the user/session/etc. We use a BeanFactoryPostProcessor to register a scoped proxy created by ScopedProxyUtils.createScopedProxy and change the scope of the target bean definition to our custom scope.
This worked great in Spring Boot 1.5.12. However, in Spring Boot 2, the introduction of the Binder API has made this stop working. This is because when Spring Boot binds #ConfigurationProperties in its ConfigurationPropertiesBindingPostProcessor, it uses Bindable.of(type).withExistingValue(bean) passing in a type (e.g. org.example.Foo) and the bean which is an instance of ScopedProxyFactoryBean. Bindable checks that bean is an instance of type which it's not and things blow up.
Does anyone know of a general way to accomplish this? Specifically, to replace the #ConfigurationProperties bean definition with a proxy while still allowing the properties to bind to the instance? I've considered delaying the creation of the proxy until after the target has already been bound by ConfigurationPropertiesBindingPostProcessor (i.e. in my own BeanPostProcessor). However, at this point the bean is instantiated and my proxy can only replace the bean. It can't really leave the original bean in the context as a target. So I'm struggling with how to do this using a BeanPostProcessor.
EDIT: I've created a simple example project that demonstrates the issue (with the ability to check out code that works on Spring Boot 1 and fails on Spring Boot 2).
Related
I know that Quarkus does dependency injection at build-time. The problem is the following:
Let's say I have a bean which has some of its fields annotated with certain annotation. I want to do something with this bean before it gets injected in other beans. Is there a workaround for this or not? Where should I start?
In Spring (Boot) I would probably use BeanPostProcessor, but I coudln't find anything like this in Quarkus
I am attempting to convert an existing spring weblogic application to a spring boot embedded tomcat application.
There are lots of moving parts so it's hard to show any code, I'm hoping there is some general answer that might clue me in to the issue.
Under weblogic, using the spring-framework 4.3.6.RELEASE libraries, the application deploys fine. It has no problems creating the different service, repository and component beans.
However, when I migrate it to Spring Boot 1.5.1.RELEASE, I get the following error:
2017-06-21 17:08:16,402 [ERROR] SpringApplication reportFailure (815) - Application startup failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'alertEventServiceImpl': Unsatisfied dependency expressed through field 'alertEventDao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'alertEventDaoImpl' defined in URL [jar:file:/Users/username/Development/source/carma-war/target/carma-war-2.0.0-SNAPSHOT.war!/WEB-INF/lib/protocol-manager-1.8.0-SNAPSHOT.jar!/org/ihc/hwcir/protocol/dao/AlertEventDaoImpl.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class org.ihc.hwcir.protocol.dao.AlertEventDaoImpl]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class org.ihc.hwcir.protocol.dao.AlertEventDaoImpl
Many of our service classes are final as they shouldn't be extended. Since there are so many that are final, I wanted to minimize the amount of code in our different libraries that we modify to make this work.
I thought because the bean creation process works under weblogic, it should work under spring boot.
Things I have tried to force not using the cglib proxies:
All implementations implement interfaces already
In beans created via xml, added <aop:scoped-proxy proxy-target-class="false"/>
In beans created through annotations, added (example for service bean)
#Service
#Scope(proxyMode = ScopedProxyMode.INTERFACE)
However, in the end, I'm perplexed as to why spring can create beans (the classes marked as final) under the weblogic container but unable to do so under the embedded tomcat spring-boot container.
Spring Boot by default uses class based proxies, which will not work with final classes/methods.
To disable this add spring.aop.proxy-target-class=false to the application.properties to enable JDK Dynamic Proxies instead of class based proxies. (And revert your modifications).
NOTE: To have everything take into account the spring.aop.proxy-target-class you might need to upgrade to Spring Boot 1.5.3 as some final patches where made to include this property in parts that were missed in previous versions.
See the following issues for more information 8434, 8869 and 8887.
I was unable to make this work using M. Deinums' answer using spring.aop.proxy-target-class=false.
What worked for me was to add in the application.properties file
spring.dao.exceptiontranslation.enabled=false
Please note that this option disables proxy creation for repositories.
And in my spring boot application configurer the annotation to handle transactions without using a proxy class.
#EnableTransactionManagement(proxyTargetClass = false)
This is using Spring Boot version 1.5.1.RELEASE.
Is there a way to update a Spring bean dynamically if the spring beans configuration changes?
E.g. assume that I have a spring bean with boolean property x and the spring beans has the value true when the application starts.
So spring creates the bean with the property x set to true.
Is there a way so that if I changes the property to x (while the application is running) that the property will be updated e.g. to false?
Calling the setter for x setX() method will do that.
But it should not be a prototype bean.
it is possible with the jrebel-spring integration. it monitors your configuration and TRIES to re-wire your beans at runtime.
Though i would not use it in production...only for playing around, testing etc
Spring reads configuration files at startup. If you really need to update configs while application running, you should manually implement all the chain: detecting config changes, validating config, detecting changed beans, updating beans in context.
Spring beans can be initialized using applicationContext.xml or even programatically. In your case; you will need to remove configurations from xml and add into java program. You can get some idea from How to programmatically create bean definition with injected properties? . Other good links were also available on google.
I need to inject a Spring bean into a Seam context. Unless I declare the spring bean as a EJB, I cannot get it injected into other seam-managed components. But when I do this, all the spring injected fields are usless cause Seam creates new instances at run-time.
I also tried to add the <seam:component/> element to the spring bean definition and tried to inject it in the container with the #In("beanId") annotation in the target class but I always end up with a NullpointerException...
EDIT:
I read the online articles and did as they say. My spring component is also added to the seam context (I can tell, cause when I define one with the same ID in seam, it complains). Looks like #In is not picking up....
Have you read this chapter? It should tell you exactly what to do.
Raoul,
Although i do not use Seam along with Spring, chapter 15 of Seam In Action books talks about Spring integration. It is free and is updated.
You have said
I also tried to add the element to the spring bean definition and tried to inject it in the container with the #In("beanId")
Seam in Action book says
The EL expression used in the #In annotation, #{tournamentManager}, resolves to an equivalently named bean in the Spring container, courtesy of the delegating variable resolver
Do you have to use #In("#{beanId}") instead of #In("beanId"), do not ?
I have seen
By default, <seam:component/> will create a STATELESS Seam component with class and name provided in the bean definition.
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
<seam:component/>
</bean>
And
The scope attribute of <seam:component/> may be used if you wish the Spring bean to be
managed in a particular Seam scope. The Spring bean must be scoped to prototype if the
Seam scope specified is anything other than STATELESS.
Have you done as above ?
I have a project using Seam + Spring and I have to set #In(create=true) when I want to inject a Spring bean into my Seam component otherwise I get a NullPointerException, you should try it.
I got the same problem as yours. I followed strictly the "Chapter 27. Spring Framework integration" in seam ref. document. But my spring bean was never got injected to seam component. And finally, I found out I had the #BypassInterceptors in my seam component. By removing that annotation, my spring bean was successfully injected. Then I realized that, dependency injection is handled by seam BijectionInterceptor. Thus, the #BypassInterceptors will effectively bypass this filter :)
I've got a project using Jetspeed portal and Springframework 2.5.6 where I need a Jetspeed level service to be unique for each user logged in. This would be best done using Spring AOP and scope="session". The problem is, that these are behind the scenes beans that need to be running as soon as the session in initiated. It appears that Spring's AOP chooses a lazy load design and does not create or init the actual implementation until a method on the bean is called.
Is there a way to force Spring AOP to create the new bean as soon as the session object is created?
An excellent question. The easiest option that springs to mind (if you'll pardon the expression) is to wire your session-scoped bean into the controller that is invoked when the first request of the session comes in (and to do this, the controller would have to either be a session-scoped bean itself, or your bean would need to use aop:scoped-proxy).
If different controllers could be invoked at the start of the session, then you could wire the bean into a session-scoped interceptor instead, and configure your url mapping to pass the requests through the interceptor, ensuring that the bean is initialized right at the start of the session.
I can't really think of a more elegant way to do this. You could potentially create a custom HttpSessionListener which calls getBean("my-session-scoped-bean") on the app context when sessionCreated() is called, but that's a bit clunky.