How to fix the Autowired in an External jar import - java

I'm having a well working library and I would like to utilize the library in one of my sample Spring boot console application.
I built the library using the command mvn clean install and the newly generated .jar file I imported in my sample Spring boot console application.
I created a bean in my new sample application and tried to create an object of UserManagementService, which is in the external .jar and the said jar internally has two properties
External Jar's - Service file
public class UserManagementService {
#Autowired
UserService userService;
#Autowired
ManagementService managementService;
public String getUserFirstName(String userName) {
return userService.getUserFirstName(userName);
}
.. Rest of the implementation
}
These two autowired is not working and it has the value null moreover it throws an exception org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class...
My Sample Application:
public class ApplicationBean {
private UserManagementService userService = new UserManagementService();
public void run() {
if(userService == null) {
System.out.println("Oops");
}
String userFirstName = userService.getUserFirstName("Emma");
... Rest of the implementation
}
}
Kindly assist me how to fix this.

You need to declare your user management service as a bean in order to be able to inject it.
First Approach
Annotate the UserManagementService class with #Service if you have control over the library, UserService and ManagementService needs to be annotated with #Service too so they can be injected into UserManagementService. You may need to use #ComponentScan(basePackages = { "libray.package" }) in your spring app (over main class) to make it scan your library and load the services.
Second Approach
You make your library framework independent and make UserManagementService a simple POJO where you pass UserService and ManagementService as constructor arguments, then declare those beans in your spring app
public class ApplicationBean {
#Bean
public UserService userServiceProvider(){
return new UserService();
}
#Bean
public ManagementService managementServiceProvider(){
return new ManagementService();
}
#Bean
public UserManagementService userManagementServiceProvider(UserService userService, ManagementService managementService){
return new UserManagementService(userService, managementService);
}
}
These beans declaration go to application main class or to a class annotated with #Configuration

Related

Is there any way to create a #ConditionalOnMissingBean but only after spring is fully configured?

I'm building a Spring Boot Starter for a college project on Java Reflection and Bytecode alteration.
The Reflection/Bytecode is done now, but it will scan for Spring #Controllers/#RestControllers so it can detect certain annotations to run the process.
My question here is what's the best approach? Seems to me that an annotation processor doesn't quite work nicely, and my idea is to create a #Configuration class. Now I need to ensure that all #Controller beans have been booted before I actually process them and I also need to put the result of this processing in a bean that could already exist.
So for example:
#Configuration
public class TestConfig {
#Autowired //I want to autowire but it may not exist, if the user doesn't define I need to create it
private ExternalAnnotatedRequestsModel model;
#Autowired // needed for the framework to acess spring controllers
private ConfigurableApplicationContext ctx;
#Bean // this can also be overriden since the definitions can be done via yaml
public ExternalRequestsProvider() {
return new AnnotationExternalRequestsProvider(ctx);
}
}
Now I also want that when the ExternalRequestsProvider bean is started, it runs the process method and saves the result in the object in the "model" variable.
Using #EventListener for ApplicationReadyEvent to run your process after Spring is fully configured.
#Configuration
public class ExternalRequestsConfig {
#Autowired
private ExternalAnnotatedRequestsModel model;
#Autowired
private ExternalRequestsProvider provider;
#EventListener(ApplicationReadyEvent.class)
public void onApplicationReady(ApplicationReadyEvent event) {
// do your process
}
}

Unable to create a bean for a service class present inside a jar

I have a Spring Boot project where I have an external dependency jar. In that jar there is an interface as below :
public interface Feature {
List<FeatureResponse> getFeatures(FeatureRequest req);
}
Its implementation is:
#Service
public class FeatureImpl implements Feature {
public List<FeatureResponse> getFeatures(FeatureRequest featureReq) {
// do something
return featureList;
}
}
Now in my Spring Boot project inside one my class I have used the request and response to object the feature list. However, though it compiles, it fails to run. The error is simple bean creation error at the injection point of Autowired that we get. Below is my class:
#Component
public class FeatureServiceImpl implements featureService {
#Autowired
Feature feature;
-----
}
The injection with #Autowired is not happening here.
In your configuration class or your SpringBootApplication class add the following
#ComponentScan({"base.package.of.your.currentJar","base.package.of.your.ImportedJar"})

Better way to register beans in Spring

I am new in Spring. I understand process of Dependency Injection and Inversion Of Control as well. But in few days ago I found one source code which compel me thinking about it.
If I am not wrong, Beans can be registered by Stereotype annotations - #Component, #Service, etc.
In code which I found will be defined class with some logic, but without annotation. Next that same class will be initialized in some #Configuration class as been like that:
#Bean
public Foo fooBean() {
return new Foo();
}
Can you tell me what is different between these options and when they use? Thanks in advice.
The greatest benefit of #Configuration and #Bean is that allows you to create spring beans that are not decorated with #Component or any of its children (#Service, #Repository and those). This is really helpful when you want/need to define spring beans that are defined in an external library that has no direct interaction with Spring (maybe written by you or somebody else).
E.g.
You have a jar created by an external provider that contains this class:
public class EmailSender {
private String config1;
private String config2;
//and on...
public void sendEmail(String from, String to, String title, String body, File[] attachments) {
/* implementation */
}
}
Since the class is in an external jar, you cannot modify it. Still, Spring allows you to create spring beans based on this class (remember, the bean is the object, not the class).
In your project, you'll have something like this:
import thepackage.from.externaljar.EmailSender;
#Configuration
public class EmailSenderConfiguration {
#Bean
public EmailSender emailSender() {
EmailSender emailSender = new EmailSender();
emailSender.setConfig1(...);
emailSender.setConfig2(...);
//and on...
return emailSender;
}
}
And then you can inject the bean as needed:
#Service
public class MyService {
#Autowired
private EmailSender emailSender;
}
#Configuration is used to define your configuration of your application. In the end #Bean, #Service, #Component will all register a bean, but using #Configuration with all beans (services, components) defined in a single place makes your app more organized and easier to troubleshoot.

Inject service in another service

I have UserService and MissionService.
Is it ok to inject MissionService in UserSerivce or vice versa?
If yes, what about unit testing?
Of course you can and it is perfectly fine. But I recommend that you use method-injection in order to allow you to set the instance at runtime without using using reflection (you can create an instance manually).
For example:
#Service
public class MissionService { }
#Service
public class UserService {
private MissionService missionService;
#Autowired
public void setMissionService(MissionService missionService) {
this.missionService = missionService;
}
}
This allows you to create both services using regular Java without Spring:
MissionService missionService = new MissioNService();
UserService userService = new UserService();
userService.setMissionService(missionService);
Caution: You have to take care of not building dependency cycles. It is not set that Spring resolves them, I think

How to Autowire a Spring-annotated service class in a #Configuration class?

I'm trying to inject a service-annotated class into a configuration class in a Spring Boot application, but it doesn't get injected (is set to null), which I assume is due to the Spring lifeycle.
Also, this service has an overloaded constructor that uses constructor injection, and I guess this is also a problem, as autowiring acts upon a default constructor. However, the service needs to be Spring-configured, so I don't think one can create a new instance in a Bean annotated method.
How can one solve this?
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SessionService sessionService;
#Bean
public SessionService sessionService() {
return sessionService;
}
}
public interface SessionService extends BaseCacheService<Session> {
void extendExpiration(String key);
String getSessionId(String key);
}
#Service
public class SessionServiceImpl implements SessionService {
private Environment environment;
private UserService userService;
#Autowired
public SessionServiceImpl(Environment environment, UserService userService) {
this.environment = environment;
this.userService = userService;
}
}
If I exclude the #Bean method, then I get a compilation error:
Your error is the following (you are returning a null value):
#Bean
public SessionService sessionService() {
return sessionService;
}
Solution
Since your SessionServiceImpl is annotated with #Service, you can just remove the #Bean method and let spring create it. Spring already makes it available for you.
Or, If your SessionServiceImpl wasn't annotated with #Service, you would need the following :
#Bean
public SessionService sessionService() {
return new SessionService();
}
If this doesn't work, it may just be that your SessionServiceImpl is in a package not being scanned by spring (as suggested by #Miloš Milivojević)
You may add #ComponentScan to your Configuration class
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#ComponentScan("com.package.to.sessionServiceImpl-or-higher")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Expanding on #Alex's answer, when you annotate a method with #Bean, it tells Spring that this method will produce that type. So, you essentially told Spring to give you the null reference you already had for all Beans of type SessionService.
If you are using Annotation-based context configuration, you can Autowire any #Component Bean (not just #Service) that can be constructed without runtime parameters (e.g. has a default constructor or an Autowired Constructor). If you need to do something to create the bean (e.g. runtime configuration required), you would either create a method as #Alex suggested, or you can use getBean and pass in the Type and Constructor arguments. The former is generally preferred.
I was facing similar issue while writing an integration test class for a spring boot application. RestTemplate class and CounterService of metrics API are autowired in my service class. I could use #ContextConfiguration(Classes={RestTemplate.class}) for injecting RestTemplate to my service, but adding CounterService.class to above annotation does not help, maybe because CounterService is an interface not a concrete class, Hence I was getting "No bean of type CounterService found" issue.
Thanks to answer by Milos, I included #EnableAutoConfiguration to my integration test class, issue was resolved!
If Alex's answer does not work (removing the #Bean method), you're probably not using #EnableAutoConfiguration or your Application is not in the root-hierarchy package so it's not scanning the whole classpath. Try adding #ComponentScan("service.class.package") to your configuration (in addition to removing the sessionService method) and see if it helps.

Categories