I have a method with #PostConstruct annotation which uses autowired service
#Service
public ServiceWithPostConstruct{
private AutowiredService autowiredService;
#Autowired
public ServiceWithPostConstruct(AutowiredService autowiredService) {
this.autowiredService= autowiredService;
}
#PostConstruct
public void doSomething() {
autowiredService.doSomethingElse();
}
}
AutowiredService declaration
#Component
public class AutowiredService {
private static final Logger LOGGER = LoggerFactory.getLogger(AutowiredService .class);
private static final String SUCCESS_CODE = "0";
private RestOperations restOperations;
#Autowired
public AutowiredService (RestOperations restOperations) {
this.restOperations = restOperations;
}
///
}
somewhere else in the code, I have a completely unrelated component, which has a scope = request
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopedService{
}
and listener in web.xml
org.springframework.web.context.request.RequestContextListener
this service works correctly, where it is used.
However while starting the application, I get the following error:
12-Dec-2017 13:24:15.221 SEVERE [localhost-startStop-1]
org.apache.catalina.core.StandardContext.listenerStart Exception
sending context initialized event to listener instance of class
org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'controller' defined in file
[controller.class]:
Unsatisfied dependency expressed through constructor parameter 1;
nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'serviceWithPostConstruct':
Invocation of init method failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'scopedTarget.scopedService': Scope
'request' is not active for the current thread; consider defining a
scoped proxy for this bean if you intend to refer to it from a
singleton; nested exception is java.lang.IllegalStateException: No
thread-bound request found: Are you referring to request attributes
outside of an actual web request, or processing a request outside of
the originally receiving thread? If you are actually operating within
a web request and still receive this message, your code is probably
running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the
current request.
This error might have been understood if I would be autowiring the mentioned ScopedService. But clearly I am not. I don't understand, why this #PostConstruct method has anything to do with ScopedService.
If I remove the #PostConstruct annotation problem disappears. And the ScopedService works everywhere as intended, in Singletons as well. However, only if I get rid of the #PostConstruct annotation. Unfortunately, I need to keep it.
I am certain that the use of autowired service in doSomething() method is the core of the problem, if I remove the autowired service, the application starts correctly.
It seems, that because of this #PostConstruct method, Spring checks ALL services, and when it comes to the ScopedService (the only component with scope=request) it recognizes that in that moment there is no HttpRequest and throws an error. Why? It is not connected in any way with that ScopedService.
What can be done about that?
I will appreciate any ideas.
Related
The instantiating class is:
#Service("aaa")
public class CustomAaaApiService {
#Inject
protected AaaApiKmxImpl aaaApiKmxImpl;
....
The instantiated class is:
#Service
public class AaaApiKmxImpl{
...
#PostConstruct
protected void init() {
File userInfoCsv = new File(sprRootDir, USER_INFO_FILE_NAME);
userInfoCsvEngine = (CSVEngine) context.getBean("CSVEngine", userInfoCsv);
File userRolesCsv = new File(sprRootDir, USER_ROLES_FILE_NAME);
userRolesCsvEngine = (CSVEngine) context.getBean("CSVEngine", userRolesCsv);
}
....
Both of them are mentioned in the configuration xml file UhradyForIns.xml as:
<bean id="aaaApiService" class="amcssz.spr.srv.sec.CustomAaaApiService"/>
<bean id="AaaApiKmxImpl" class="amcssz.spr.srv.sec.AaaApiKmxImpl"/>
That configuration is called from the test by
#ContextConfiguration({ "classpath:UhradyForIns.xml"})
I am getting the output:
Error creating bean with name 'AaaApiKmxImpl': Invocation of init method failed; nested exception is
org.springframework.beans.factory.BeanDefinitionStoreException: Can only specify arguments for the
getBean method when referring to a prototype bean definition
Why does Spring say it cannot specify arguments when init() has no arguments? The normal launch of the application runs without these problems. Of course, I would get the beans configuration from it, but both these files have no description in any configurations, except that test one.
I don't think the reason could be these creatBean's in the init(), for then the error message will name CSVEngine bean as the bean that could not be created. And the content of init() due to #PostConstruct won't be called at context creation.
Oh, sorry, it was my misunderstanding. It falls at
context.getBean("CSVEngine", userInfoCsv);
I checked that by a breakpoint. So, the problem happens in init() without arguments, but due to the call with arguments. I found that due to #tgdavies' question.
Why does Spring say it cannot specify arguments when init() has no
arguments?
The problem is not related to the injection of arguments in the #Postconstruct annotated method, as the post-construct method does not require arguments like you stated.
The problem occurs during the invocation of the method; on either one of these lines:
userInfoCsvEngine = (CSVEngine) context.getBean("CSVEngine", userInfoCsv);
userRolesCsvEngine = (CSVEngine) context.getBean("CSVEngine", userRolesCsv);
From the XML and Java snippets you provided the beans seem to be defined as singleton beans (as no bean scope is provided and Singleton is the default). As context.getBean("beanId", secondArg); requires the bean to have a scope of type prototype, the exception is thrown. From the BeanFactory JavaDoc:
/**
* ...
* #return an instance of the bean
* #throws BeanDefinitionStoreException if arguments have been given but
* the affected bean isn't a prototype
*/
Object getBean(String name, Object... args) throws BeansException;
As for the reason why it works during a normal launch but not during tests, I am not sure. The issue could be related to duplicate bean declarations. As both classes are annotated with a #Service annotation they might be picked up during component scanning (as #Service annotations themselves have the component meta annotation), while at the same time being declared in the XML. This depends on component scanning being enabled in your project.
I'm receiving a null pointer exception when operating on my service because my service is not being Autowired into the class. I've implemented this class's repository and service exactly the same as others in this application and I haven't had this problem before. The class does in fact warn about issues with the Autowire but I'm not sure how to fix them:
Autowired members must be defined in valid spring bean
Again, this is set up the same as other classes and I do not have this issue. Within the service class, it complains that the repository cannot be autowired into the constructor because there are multiple beans of the same type. My other service class shows this warning as well but does not have problems being Autowired into classes and operated upon. Definitions below, please ask for any other context that would be helpful.
//TransactionCategoryRepository.java
#Repository("transactionCategoryRepository")
public interface TransactionCategoryRepository extends
CrudRepository<TransactionCategory, Integer> {
}
--
//TransactionCategoryService.java
#Service("transactionCategoryService")
public class TransactionCategoryService {
private TransactionCategoryRepository transactionCategoryRepository;
#Autowired
public TransactionCategoryService(TransactionCategoryRepository repository) {
this.transactionCategoryRepository = repository;
}
public void saveTransactionCategory(TransactionCategory transactionCategory) {
transactionCategoryRepository.save(transactionCategory);
}
}
--
//Utilities.java
public class PlaidUtilities {
private Logger logger =
LoggerFactory.getLogger(PlaidUtilities.class.getSimpleName());
private PlaidClient mPlaidClient;
#Autowired
TransactionCategoryService mTransactionCategoryService;
...
The multiple bean warning is thrown on respository in TransactionCategoryService.java and the Autowired definition warning is thrown in Utilities.java. The breaking null pointer exception error occurs later in Utilities.java when operating on mTransactionCategoryService.
Unless you need them, take the names out of the #Service and #Repository annotations. I've found it just makes things awkward.
The other thing that might be wrong is that you're not scanning those packages. You can change that in your main class by altering the boot application attribute to #SpringBootApplication(scanBasePackages={"your.package.here"})
Have a look here at this question where they detail it
I have a bean declared in by config file as:
#Bean
#Lazy
#Scope("prototype")
public SomeClass someClass()
{
return new SomeClass();
}
Then inside my test class I have the following code:
#Autowired
private SomeClass someClass;
#Before
public void setUp()
{
xyzObject.someMethod(someClass);
}
My question here is inside my setUp() method I am just referencing the autowired SomeClass bean but not getting it from the spring context using getBean(), but still it is creating a new bean everytime the setUp() is running before a test. Why is this happening? Does prototype scope means a new bean gets created whenever the bean's reference is used anywhere in the code? I haven't seen anything specified like that in the documentation.
That is the essence of Inversion of Control. Because you injected it into another bean, every time you instantiate another bean your injected bean would be created and provided by spring as part of instantiation of your containing bean. You don't need to call getBean() here. However if in some place you just need an instance of your SomeClass bean you may call getBean() to tell your spring environment to give you that bean to you. However, It is better to avoid such practice and rely on injection. Or create a Factory that can provide you such Bean (Still relaying on spring though) That would make your code not aware of Spring which is IMHO is more ellegant
I have the spring mvc app which works great.
I have a bean X which has the following declaration
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
I need to create a background cron job.
I have added a threadPool and #Scheduled annotation to my new bean which is using dependency bean X.
When my cron task is started by Spring I get this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.X': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I understand that my bean X will be injected only by DispatcherServlet.
My question is how to fix this behaviour?
in my service layer
public class MyServiceLayerImpl{
public void method1(){
MyServicelayer.method(); //is this correct?
}
public void method2(){
}
#Autowired
MyServiceInterface MyServiceLayer;
}
if i have method inside service layer that need to call another service inside service layer. i cannot use this._method ,because, i'm using AOP for caching. In order for the caching to work, i have to use #Autowired to get the service. Therefore, is the above style ok?
i get below error
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.company.iss.services.MyServiceLayerImpl#85aedd': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.iss.services.MyServicelayer com.company.iss.services.MyServiceLayerImpl.MyServiceLayer; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.company.iss.services.MyServiceLayer] is defined: Unsatisfied dependency of type [interface com.company.iss.services.MyServiceLayer]: expected at least 1 matching bean
It's hard to tell from the weird formatting and naming, but if you want to call one service from another:
public interface MasterService {
void someMethod();
}
public class MasterServiceImpl implements MasterService {
private OtherService otherService;
public void someMethod() {
this.otherService.someCallOnOtherService();
}
#Autowired
public void setOtherService(OtherService otherService) {
this.otherService = otherService;
}
}
Now, you must have configured both MasterServiceImpl and whatever implements OtherService. There are many ways to do this, the most popular being explicitly in your XML configuration with annotation-based configured a close second.
Also note that AOP tends to be very flaky if you aren't using interfaces. In your code, your Impl doesn't actually implement anything. I would recommend against that.
Apart from having an uppercase variable and no colon - it's fine.
You would, of course, need to define your class as a bean. Either by using the #Service (or another stereotype) annotation on it, or using <bean> in applicationContext.xml (see here for the annotation-based config introduced in spring 2)
Another thing: your member variables should be lowercase, not uppercase.