Skip to content

Documentation the limitation of injecting components in @Configuration classes [SPR-12773] #17370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Mar 2, 2015 · 10 comments
Assignees
Labels
type: documentation A documentation task
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Mar 2, 2015

Amit Gadkari opened SPR-12773 and commented

I have @Configuration class with @EnableAspectJAutoProxy.
In this class I have injected
@Autowired
@Qualifier(value="customerService")
private CustomerService customerService;

Code for CustomerService is as follows

@Service(value="customerService")
public class CustomerServiceImpl implements CustomerService

In this customerService class I have @Transactional method which is also present in Service Interface class

In @Configuration class instead of proxy instance of CustomerService I get instance of customerServiceImpl. Hence transaction management is not working in application.
If I use applicationContext.getBean("customerService", CustomerService.class) and print classname name then also I am getting CustomerServiceImpl and not proxy.

I ran application 10 times
a) 7 times I got instance of customerServiceImpl
b) 3 times I got instance of proxy

My application works fine when I get instance of proxy
I think this is bug in Spring JavaConfig


Affects: 4.1.5

Attachments:

Issue Links:

Referenced from: commits c7fcf7c

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This sounds like a strange initialization order issue. Could you please try to extract a minimal test app which reproduces the problem, and submit it here?

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Amit Gadkari commented

Tomcat web application for recreating problem of proxy not been created for autowired beans in @Configuration files

As per Maten Denium, proxy will not created for autowired beans injected in @Configuration java code.
http://stackoverflow.com/questions/28806970/javaconfig-configuration-classes-with-autowired-service-class-with-transaction
Also proxies are also not created in following situation
Transactional CustomerService is injected in CustomerTask through autowired
and CustomerTask is injected in @Configuration class through autowired, then proxy is not created for CustomerService class.

In that case this should be documented in reference documentation and injection of beans in @Configuration java code should be through method parameter to @Bean methods

@spring-projects-issues
Copy link
Collaborator Author

Amit Gadkari commented

I did clean compile and ran application on tomcat for 10 times and all 10 times I did not got proxy of CustmerService.
Earlier I was commenting and uncommenting autowired code and running application on tomcat without clean build.
Hence sometimes I was getting proxy (when it was not supposed to get, because it was executing previous code as clean build was not done )

I have attached sample application sample-tomcat as an attachment with this email. Import project as maven project and run it on TC or tomcat server
a) CustomerTask.java
b) SpringScheduledTaskConfig.java
c) CustomerService.java
d) CreateCustomerController.java

@spring-projects-issues
Copy link
Collaborator Author

Janning Vygen commented

Don't know if it relates to this bug, but I have similar problems. When I upgrade from 4.1.4 to 4.1.5 the application startup fails.
I did a clean build. I can switch back and forward. With 4.1.4 everything runs fine. With 4.1.5 it fails every time.

I have a configuration like this:

@Configuration
public class JawrAssetsConfig
{
	@Bean(name = "jawrProperties")
	@Profile(Profiles.PRODUCTION)
	public Properties productionProperties ( )
	{
		Properties properties = getCommonProperties();
		// ...
		return properties;
	}

	@Bean(name = "jawrProperties")
	@Profile({ Profiles.DEVELOPMENT, Profiles.INTEGRATION })
	public Properties getDevelopmentProperties ( )
	{
		Properties properties = getCommonProperties();
		// ... more irrelevant code 
		return properties;
	}

	public Properties getCommonProperties ( )
	{
		Properties properties = new Properties();
		// ... more irrelevant code 
		return properties;
	}
}

and a controller like this:


@Controller
public class ImgController extends JawrSpringController
{
	@Inject
	public ImgController ( @Qualifier("jawrProperties") Properties jawrProperties )
	{
		// ... more irrelevant code 
	}

Exception goes like this:

Mrz 04, 2015 8:33:29 AM org.apache.catalina.core.StandardContext loadOnStartup
SCHWERWIEGEND: Servlet  threw load() exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.util.Properties] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=jawrProperties)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1131)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1034)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
	at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:629)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:677)
	at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:548)
	at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489)
	at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
	at javax.servlet.GenericServlet.init(GenericServlet.java:160)
	at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1266)
	at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1185)
	at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1080)
	at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5026)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5313)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1595)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1585)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)

Strange. Never had regressions like this with spring. I can try to isolate a test case if it helps. It seems to me that the order of initialization is wrong. Spring tries to create ImgController before creating the other beans.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 4, 2015

Juergen Hoeller commented

Janning Vygen, I'm afraid you're running into #17341, an unfortunate regression in 4.1.5 (due to the lack of a unit test for overlapping bean names in configuration classes). This has been fixed in recent 4.1.6 snapshots already.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

I can see that you are accessing your service in the @PostConstruct phase of another bean.

There is no guarantee that all proxies will be created at this point which is a reason why @Transactional methods should not be used in initialization callbacks

Let me know If I miss anything.

@spring-projects-issues
Copy link
Collaborator Author

Amit Gadkari commented

In CustomerTask in Postconstruct, I was checking if proxy of customerService is injected or not. This postconstruct was only for debugging purpose
@PostConstruct
public void test(){
System.out.println(customerService.getClass());
System.out.println(customerService.getClass().getName());
}

Main problem was in SpringScheduledTaskConfig class which is annotated by @Configuration. Inside this I have injected CustomerTask.
I was thinking that proxy of CustomerService will be injected in CustomerTask. ( which is required for transaction to work). But that is not happening
Proxy of CustomerService is not getting injected in CustomerTask
@Autowired
@Qualifier(value="customerTask")
private CustomerTask customerTask;

For verification I put System.out in
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
CustomerService service = applicationContext.getBean("customerService", CustomerService.class);
System.out.println("CustomerService class name" + service.getClass().getName());

If I comment following code from SpringScheduledTaskConfig (which is annotated with @Configuration) then proxy of CustomerService is injected in CustomerTask ( which is correct behavior)

@Autowired
@Qualifier(value="customerTask")
private CustomerTask customerTask;

I think that its documentation problem.
In reference documentation, @Autowired of bean in @Configuration classes have side effects
Aspects will not be attached to beans which are autowired in configuration classes. ( and also for beans which are contained in autowired bean)

@spring-projects-issues
Copy link
Collaborator Author

Amit Gadkari commented

Documentation problem which needs to be specified in Reference documentation

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

sorry, it would haven been nice to provide a project that exhibits the actual problem rather than asking us to look into 10 different classes to find out what the problem might be.

dependencies in configuration classes should be of the simplest kind only so wiring your service there forces an early initialization that may lead to such situation. Looking whether the proxy has been created or not during the startup phase is pointless anyway as I mentioned in my previous comment.

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

The reference guide has been updated to put an explicit note regarding the use of "hard-wiring" in configuration classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

2 participants