I have created a custom Flink RichSinkFunction and attempted to autowire a JpaRepository within this custom class but I am constantly getting a NullPointerException.
If I autowire it in the constructor, I can see that the JpaRepo has been found - but when the invoke method is called, I receive a NullPointerException.
public interface MessageRepo extends JpaRepository<Message, Long> {
}
#Component
public class MessageSink extends RichSinkFunction<Message> {
private final transient MessageRepo messageRepo; //if i don't make this transient, i get the error message "The implementation of the RichSinkFunction is not serializable"
#Autowired
public MessageSink(MessageRepo messageRepo){
this.messageRepo = messageRepo;
messageRepo.save(new Message()); //no issues when i do this
}
#Override
public void invoke(Message message, Context context) {
// the message is not null
messageRepo.save(message); // NPE
}
Has anyone experienced this issue before? It looks like the MessageSink invoke method is being called in a separate thread which is why the messageRepo is always null?
Other parts of my code is able to use the MessageRepo apart from when I have my own custom sink.
I think the issue here is that flink needs to serialize the custom sink function before it distribute to its workers.
By marking the MessageRepo transit, meaning the field will be null when the worker node deserlize this function. Normally, you would initialise the transit dependency in the open function, which will be called after the object is deserialised.
I am not clearly sure about the reason, but I think spring boot gives priority to your service classes when it comes to injecting beans. I have faced a similar issue when I was trying to write a listener for my Entity class. This is how I solved it.
Create a component class which implements ApplicationContextAware interface and override setApplicationContext method. Have a static method in your class named getBean which will autowire on your first request. Sample code ---
#Component
public class SpringBeansUtil implements ApplicationContextAware {
private static ApplicationContext context;
#SuppressWarnings("static-access")
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
And then just get bean in your code ------->>
ClassName referenceName = (ClassName)SpringBeansUtil.getBean(ClassName.class);
Related
After an upgrade of one of our app in Spring Boot v2.7.6, I have an issue about a circular reference.
I have an abstract class like this:
public abstract class AbstractTransactionalService<Request, Response, Exception> {
#Autowired
private ApplicationContext applicationContext;
private AbstractTransactionalService<Request, Response, Exception> me;
#SuppressWarnings("unchecked")
#PostConstruct
public void init() {
me = applicationContext.getBean(this.getClass());
}
public Response performService(final Request request, final Class<Exception> exceptionClass)
throws Exception {
try {
final Response response = this.me.perform(request);
return response;
} catch (final Exception e) {
...
}
}
public abstract Response perform(Request request) throws Exception;
}
A service 2 extends AbstractTransactionalService and implements the perform() method. This service 2 is used in an other service 1 (injected with #Autowired and a call to performService() method is done).
I have a circular reference issue with the service that calls the service 2 extending the abstract class at server startup. When the service 1 does #Autowired for the service 2 extending the abstract class, the service 2 extending the abstract class is in creation. It seems that when the call to ApplicationContext.getBean() is done in the abstract class, it causes the circular reference error because he current bean is in creation.
I do not know how to solve it. Do you have an idea please?
I faced a similar issue and fixed it as follows. Created ServiceManager as a bean, autowired all the services involved in cycle(s) within it. And then retrieved the services via getters from the serviceManager. The serviceManager itself is to be retrieved via #Autowire where it is needed.
I developed a kind of wrapper to make it work as a custom logger. I'm instantiating this class using #CustomLog Lombok annotation just to make it easier and cleaner. The tricky thing comes next: the idea behind this wrapper is to use a common logger (as org.slf4j.Logger) along with a custom monitor class that each time I call log.error(), the proper message gets logged in the terminal and the event is sent to my monitoring tool (Prometheus in this case).
To achieve this I did the following classes:
CustomLoggerFactory the factory called by Lombok to instantiate my custom logger.
public final class CustomLoggerFactory {
public static CustomLogger getLogger(String className) {
return new CustomLogger(className);
}
}
CustomLogger will receive the class name just to then call org.slf4j.LoggerFactory.
public class CustomLogger {
private org.slf4j.Logger logger;
private PrometheusMonitor prometheusMonitor;
private String className;
public CustomLogger(String className) {
this.logger = org.slf4j.LoggerFactory.getLogger(className);
this.className = className;
this.monitor = SpringContext.getBean(PrometheusMonitor.class);
}
}
PrometheusMonitor class is the one in charge of creating the metrics and that kind of things. The most important thing here is that it's being managed by Spring Boot.
#Component
public class PrometheusMonitor {
private MeterRegistry meterRegistry;
public PrometheusMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
}
As you may noticed, to access PrometheusMonitor from CustomLogger I need an additional class in order to get the Bean / access the context from a non Spring managed class. This is the SpringContext class which has an static method to get the bean by the class supplied.
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
}
So all this works just fine when running the application. I ensure to load SpringContext class before anything else, so once each CustomLogger gets instantiated it just works.
But the BIG issue comes here: this is not working while unit testing my app. I tried many things and I saw some solutions that may help me but that I'm trying to avoid (e.g. using PowerMockito). Lombok is processing #CustomLog annotation before any #Before method I add to my test class. Once getBean() method is called I get an exception cause context is null.
My guesses are that I could solve it if I can force the SpringContext to be loaded before Lombok does its magic, but I'm not sure that's even possible. Many thanks for taking your time to read this. Any more info I can provide just let me know.
NOTE: It sounds like your custom logging needs are better served by logging to slf4j as normal, and registering an additional handler with the slf4j framework so that slf4j will forward any logs to you (in addition to the other handlers, such as the one making the log files).
Lombok is processing #CustomLog
The generated log field is static. If an annotation is going to help at all, you'd need #BeforeClass, but that probably also isn't in time. Lombok's magic doesn't seem relevant here. Check out what delombok tells you lombok is doing: It's just.. a static field, being initialized on declaration.
Well I managed to solve this issue changing a little how the CustomLogger works. Meaning that instead of instantiating monitor field along with the logger, you can do it the first time you'll use it. E.g.:
public class CustomLogger {
private org.slf4j.Logger logger;
private Monitor monitor;
public CustomLogger(String className) {
this.logger = org.slf4j.LoggerFactory.getLogger(className);
}
public void info(String message) {
this.logger.info(message);
}
public void error(String message) {
this.logger.error(message);
if (this.monitor == null) {
this.monitor = SpringContext.getBean(PrometheusMonitor.class);
}
this.monitor.send(message);
}
}
But after all I decided to not follow this approach because I don't think it's the best one possible and worth it.
I have Service class and Repository interface (Spring Data). I have also one abstract class:
public abstract class TestingMethod {
public TestingMethod() {
timeSum = 0;
}
protected long timeSum;
}
And class that extends it:
#Component
public class LimitTestingMethod extends TestingMethod {
#Autowired
private GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize) {
super();
this.limitSize = limitSize;
}
}
In my Service I want to create instance of LimitTestingMethod and set its argument limitSize.
Something like:
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
But I got error:
Description: Parameter 0 of constructor in
com.exence.postgiscma.testingMethod.LimitTestingMethod required a bean
of type 'int' that could not be found. Action: Consider defining a
bean of type 'int' in your configuration.
Is that possible to achieve something like I want?
All best!
//EDIT
As I can see in comments it's bad approach. So maybe someone will give me advise how to project this better?
Is this good solution to pass repo as argument in constructor (I guess that not, but I can't get the idea how to do this better)?
LimitTestingMethod ltm3 = new LimitTestingMethod(3, geoTestDataRepository);
Is there a good and elegant solution?
As you are creating instances outside the scope of Spring your current solution won't work. The error comes from the fact that you have annotated it with #Component, it will detect it at startup and tries to create a bean, and fails.
To solve this you can do 1 of 2 things.
Let Spring handle the creation of the beans by using the ApplicationContext as a factory, providing additional arguments and make the bean prototype scoped.
Let Spring handle the injection after you manually created the instance using the ApplicationContext.
Use ApplicationContext as a factory
First make your bean a prototype so that it will be constructed when needed.
#Component
#Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LimitTestingMethod extends TestingMethod { ... }
Now an instance won't be created during startup. In your service inject the ApplicationContext and use the getBean method to get your desired instance.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = ctx.getBean(LimitTestingMethod.class, 3);
LimitTestingMethod ltm10 = ctx.getBean(LimitTestingMethod.class, 10);
}
}
This will let Spring create the instance using the value passed in for the constructor and do the autowiring.
Injection after creation
Another solution is to manually create the instances and after that let Spring handle the auto wiring. You will lose the AOP abilities with this and will get only auto wiring.
First remove the #Component annotation from your LimitTestingMethod so it won't get detected during startup.
public class LimitTestingMethod extends TestingMethod { ... }
Now in your service autowire the ApplicationContext and after creating your bean use that to inject the dependencies.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt3);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt10);
}
}
Both will achieve what you want, however, now your code directly depends on the Spring API. So instead of doing this, you are probably better of with another option and that is to inject everything for the LimitTestingMethod through the constructor and pass the repository yourself.
Use constructor to create an instance
public class LimitTestingMethod extends TestingMethod {
private final GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize, GeoTestDataRepository geoTestDataRepository) {
this.limitSize=limitSize;
this.geoTestDataRepository=geoTestDataRepository;
}
}
Then you can simply autowire the repository in your service class and create the instances as needed (or create a factory which contains the complexity of creating this object).
public class Service {
#Autowired
private GeoTestDataRepository repo;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3, repo);
LimitTestingMethod ltm10 = new LimitTestingMethod(10, repo);
}
}
I'm having trouble getting #Autowired to work in a class annotated by #Service, the autowired variable always is null. Let me explain:
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider; <-- always null
...
public Searcher() {
sessionProvider.doSomeStuff();
}
sessionProvider here is always null.
The strange thing is that the same autowire in a #Controller does work:
#Controller
#RequestMapping("/search")
#Secured({ "ROLE_USER" })
public class SearchController extends BaseController {
#Autowired
protected ISessionProvider sessionProvider; <-- does work
#Autowired
protected ISearcher searcher;
The last line throws exception because the constructor of Searcher (implementing ISearcher) tries to access sessionProvider, which is null.
I am not sure what i might be doing wrong, it looks like spring doesn't autowire the ISessionProvider in Searcher.
It might be that spring first autowires the Searcher in SearchController, but it should first autowire SessionProvider in Searcher and next autowire Searcher in SearchController. Cause searcher cannot be autowired without a valid SessionProvider. Puzzles my brain ;-)
Can somebody offer a helping brain?
[edit]
component-scan includes my services, controllers and everything, just checked.
the I before interfaces is indeed not very nice (old habits)
not sure if this is a "duplicate" question, mainly because i'm not doing anything with "new", i let do spring do all the hard work, but i'll take a better peek.
Spring will first create the bean instance, then inject the beans. You're trying to access to the injected bean when the current bean is created, thus the bean will be null. That's default behavior.
If you want/need to execute any logic after creating the bean, use #PostConstruct decorated method, which is invoked after the bean has been created and all the dependencies injected. Here's a sample:
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider;
public Searcher() {
//nothing should be here...
}
#PostConstruct
public void init() {
sessionProvider.doSomeStuff();
}
}
Spring can only do dependeny injection after the bean has been constructed. You are calling the method in the constructor and at that point the ISessionProvider hasn't been injected yet and hence it is null which in turn leads to a nice NullPointerException.
You have 2 solutions
Move the code from the constructor to a method annotated with #PostConstruct
Change the default no-arg constructor to take an argument and use that to do dependeny injection instead of the #Autowired field.
Solution 1: move that code to a method annotated with #PostConstruct.
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider;
...
public Searcher() {}
#PostConstruct
public void init() {
sessionProvider.doSomeStuff();
}
Solution 2: Use constructor based dependency injection.
#Service
public class Searcher extends Thread implements ISearcher {
protected final ISessionProvider sessionProvider;
#Autowired
public Searcher(ISessionProvider sessionProvider) {
this.sessionProvider=sessionProvider;
sessionProvider.doSomeStuff();
}
}
I didn't do test, but I think the problem is, in class Searcher, you created a no-argument constructor, and there you used the "autowired" bean. I guess there you would get NPE. Since spring would instantiate your Searcher using default constructor (by reflection) if you didn't specify one, that is, it will use the no-argument constructor you created, but at this moment, the "autowired" bean was not yet injected.
If you want to do something immediately after the bean was instantiated, you can wrap the logic code in a method, and annotate it with #PostConstruct.
I am also getting the same issue but i cannot use #PostConstruct because i am not using doinit. I have a simple method which is getting the language from lang service.
My lang service is null here but in another controller it is not null, it is getting autowired.
#Service
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
public class FormSelectLanguageHandler extends SimpleTagSupport {
#Autowired
private LangService langService;
public List<Lang> getLanguages(){
Sessions session = (Sessions) getJspContext().findAttribute("SHLSESSION");
List<Lang> languages=new ArrayList<Lang>();
//List<Lang> allLanguages = langService.findAllLangs();
List<Lang> all =new ArrayList<Lang>();
if (isRenderLangLabel()) {
all = langService.findByDisplayOrderAsc();
} else {
all = langService.findByNameOrderByNameAsc();
}
Long companyId = (null == session || null == session.getCompany())? null:session.getCompany().getId();
}
}
I am using Spring 3 and Hibernate 4
How can I use the following in a non ManagedBean
#Inject
EmployeeService employeeService
Or if I would want to access DAO method I have to make that a ManagedBean as
#Named("mymanagedbean")
#ViewAccessScoped
I have a few Converter class and in order to access DAO service methods I had to use that as ManagedBean even though they are not ManagedBeans.
What is the best approach to call DAO service methods?
Thanks
You will need to implement the Spring interface ApplicationContextAware and then set the ApplicationContext. Then you need provide static methods to get the bean instance.
public class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context)
throws BeansException {
CONTEXT = context;
}
public static Object getBean(String beanName) { ...}
public static <T> T getBean(Class<T> arg0) {...}
Then in your non-managed bean you can call SpringApplicationContext.getBean method by passing in EmployeeService.class as the argument or the bean name as the argument.
If you want to keep your Converter class clean and use dependency injection (which is highly recommended in order to be able the test the class easily) instead of the class pulling in its dependencies manually, you can use Spring's ability to configure a pre-existing object created outside of the application context. See the related section in Spring's reference documentation here.
Here is a working example (pertinent to zagyi's answer). Application uses Spring Roo and therefore aspectj.
#FacesConverter("example.entity.converter")
#Configurable
public class EntityConverter implements Converter {
#Resource
MyDAO dao;
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
Entity obj;
try {
obj = dao.getEntity(Long.valueOf(value));
} catch( NumberFormatException e ) {
throw new ConverterException( message );
}
return obj;
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
Entity obj = (Entity) value;
return (obj != null) ? obj.getId().toString() : "";
}
}
The dao class
#Repository("myDAO")
public class MyDAOImpl implements MyDAO {
...
}
I have managed to get the DAO method in Converter without #Inject using the following and in EmployeeService class which implements Interface I have defined as #Service(value="employeeService")
EmployeeService employeeService =
(EmployeeService)facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null,
"employeeService");