Bean life cycle - afterPropertiesSet() - java

I am trying to create CustomWebServiceMessageReceiverHandlerAdapter which extends org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.
WebServiceMessageReceiverHandlerAdapter extends abstract WebServiceMessageReceiverObjectSupportwhich implements InitializingBean.
I have a problem, because I don’t understand why I have to call
afterPropertiesSet() in custom handler. I get an error without calling this method: “factory message is required”. But, this method is calling in abstract class, so my custom handler should run afterPropertiesSet() from abstract class. If you know the solution, let me know. Thanks a lot.
edit: This is my CustomWebServiceMessageReceiverHandlerAdapter :
public class CustomWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter {
#Override
protected void handleInvalidXmlException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler,
InvalidXmlException ex) throws Exception {
//code
}
#Override
public void afterPropertiesSet() {
}
}
WebServiceMessageReceiverHandlerAdapter and WebServiceMessageReceiverObjectSupport are from Spring Framework
public class WebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverObjectSupport{}
There is no afterPropertiesSet()
public abstract class WebServiceMessageReceiverObjectSupport implements InitializingBean {
private WebServiceMessageFactory messageFactory;
/** Returns the {#code WebServiceMessageFactory}. */
public WebServiceMessageFactory getMessageFactory() {
return messageFactory;
}
/** Sets the {#code WebServiceMessageFactory}. */
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
this.messageFactory = messageFactory;
}
#Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(messageFactory, "messageFactory is required");
}
And now, when I am removing afterPropertiesSet() from my custom handler an exception is thrown. In my opinion, I don't understand something about life cycle of bean.

I'm unsure about your specific case. In general if a bean implements InitializingBean and thus the afterPropertiesSet this method is called after instantiation of the bean instance and after Spring injected all #Autowired properties/values.
In your specific case you need to ensure that messageFactory property of your (via inheritance) class is set. Typically this is done by Spring, if you provide a suitable setter for autowiring:
#Autowired
#Override
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
super.setMessageFactory(messageFactory);
}
If you overrride afterPropertiesSet without calling super.afterPropertiesSet() creation of the bean will work as the assertion of the super implementation is skipped. But you will likely encounter problems further down the line as the messageFactory property is not properly initialized.

Related

Circular reference issue with abstract class

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.

JpaRepository not autowiring in custom RichSinkFunction

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);

Spring can't Autowired abstract class with final method

public abstract class BaseLoaneeRepayment implements Repayment {
#Autowired
protected LoanRepository loanRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public final void repay(RepaymentInfo repaymentInfo) {
Loan loan = loanRepository.lockAndLoad(repaymentInfo.getLoan().id());
}
protected abstract void preCheck(final RepaymentInfo repaymentInfo);
protected abstract void updateLoanee(final RepaymentInfo repaymentInfo);
protected abstract void repayment(final RepaymentInfo repaymentInfo);
protected abstract void calcDifference(final RepaymentInfo repaymentInfo);
}
#Service("loaneeNormalRepayment")
public class NormalRepayment extends BaseLoaneeRepayment implements Repayment {
private static final CatLogger logger = CatLoggerFactory.getLogger(NormalRepayment.class);
#Override
public final void preCheck(RepaymentInfo repaymentInfo) {}
#Override
public final void updateLoanee(RepaymentInfo repaymentInfo) {}
#Override
public final void repayment(RepaymentInfo repaymentInfo) {}
#Override
public final void calcDifference(RepaymentInfo repaymentInfo) {}
}
in Junit,
#TransactionConfiguration(defaultRollback = true)
public class NormalRepaymentTest extends ServiceTest {
#Autowired
#Qualifier("normalRepayment2")
private NormalRepayment normalRepayment;
#Autowired
private LoanService loanService;
#Test
public void test() {
normalRepayment.repay(repaymentInfo);
}
}
This normalRepayment.repay(repaymentInfo); in NormalRepayment loanRepository is null. The autowire not work.
The behavior you see is related to proxies. In certain cases, for Spring managed beans, Spring will create a proxy for the target class. In this case, methods are marked as #Transactional, and Spring will create a proxy which implements transaction handling for your service. Depending on the proxy strategy, Spring may proxy by subclass, which means a subclass will override methods of the target class and implement various logic such as transaction handling in those methods. In this case, the dependencies will be injected in the proxy, not the target class.
Java, by design, does now allow overriding final methods, and because of this, the proxy subclass will not be able to override the final methods in your concrete class.
The calling code will, in this case, in fact call the non-proxied target methods, where dependencies are not injected and no transactional handling is present.
Solution: Remove the final modifier from the methods in your concrete class, which will allow Spring to properly proxy your service class.

Why does this Apache Camel intercept not work?

I have a main route builder:
public class MainRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:a.out").to("activemq:b.in");
from("activemq:b.in").bean(MainMessageConsumer.class);
}
}
I have a second "intercept" route builder:
public class InterceptRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("activemq:a.out").to("activemq:c.in").skipSendToOriginalEndpoint();
from("activemq:c.in").bean(InterceptMessageConsumer.class);
}
}
Both of which are registered to the CamelContext (MainRouteBuilder is registered first, and InterceptRouteBuilder second). However, when I send a message to "activemq:a.out" via:
public class App {
#Produce(uri="activemq:a.out")
private Producer producer;
public void run() {
producer.request("hello");
}
}
The message still arrives on MainMessageConsumer instead of being intercepted. What am I doing wrong?
The interceptor only applies for all routes in the same route builder class. If you want it to work on both, then create a base class, and put the interceptor there, and let the other routes extend your base class, and call its super in the configure method (eg OO inheritance)
Seems to be that if you create your producer using the #Produce annotation, then it won't be intercepted. Whereas if I put:
#Bean
public ProducerTemplate producerTemplate() {
return camelContext().createProducerTemplate();
}
In my application config, and use that instead then it does get intercepted. Not sure if this is the expected behaviour?

How to write tag in my spring project?

I want to write my tag (extends TagSupport) in my spring framework. In my tag class, will use some service which should auto inject by spring. But I always get null, seems spring can't inject service instance in my tag class.
The code is like the following:
public class FetchTagNameTag extends TagSupport {
#Autowired
private TaskService taskService;
...
taskService is always null.
How can I resolve this?
Thanks.
Have a try by utilizing RequestContextAwareTag. It will offer you methods to obtain RequestContext and then WebApplicaitonContext. Have a look at here.
JSP tag objects are not managed by Spring, they are managed by the servlet container. As a result, you cannot autowire stuff into your tags.
If you need to get hold of beans from the spring appcontext, then your Spring MVC controller needs to set the bean as a request attribute (using request.setAttribute()), so that the tag object can get hold of it.
Annotate your Tag-Implementation with #Configurable and add <context:component-scan base-package="your.webapp"> to your Spring-Configuration.
Check out these spring packages in the spring reference docs and in the spring source:
org.springframework.web.servlet.tags
org.springframework.web.servlet.tags.form
If nothing else, those will show you how the spring developers wrote the spring tags.
What you could do is create a static method like this:
public static void autowireAllFor(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(...yourBeanFactory...);
bpp.processInjection(target);
}
and then for your tag you could do
public class YourTag extends TagSupport {
#Autowired
private SomeBean someBean;
public YourTag() {
YourHelperClass.autowireAllFor(this);
}
}
The obvious disadvantage of this approach is that you have to do this for every constructor, but as TagSupport only has one, it should not be a problem. You can go even one step further and create a helper superclass which always guarantees autowiring:
public class SpringTagSupport extends TagSupport {
public SpringTagSupport() {
super();
YourHelperClass.autowireAllFor(this);
}
}
The rest is as easy as extending your classes from SpringTagSupport.
First I write this:
public abstract class SpringSuportedTag extends SimpleTagSupport{
protected WebApplicationContext _applicationContext;
protected WebApplicationContext getSpringContext(){
PageContext pageContext = (PageContext) getJspContext();
if(_applicationContext==null){
_applicationContext = RequestContextUtils.getWebApplicationContext(
pageContext.getRequest(),
pageContext.getServletContext()
);
initCustomBeans();
}
return _applicationContext;
}
protected abstract void initCustomBeans();
/**
* Deprecated for inserting extra logic. Use {#link #doTagWithSpring()} instead.
*/
#Override
#Deprecated
public void doTag() throws JspException, IOException {
getSpringContext();
doTagWithSpring();
}
abstract void doTagWithSpring() throws JspException, IOException;
}
And usage:
public class SlotTag extends SpringSuportedTag {
// #Resource(name="userDetailHolder")
// not work here
UserDetailHolder userDetail;
private String slotname;
public String getSlotname() {
return slotname;
}
public void setSlotname(String slotname) {
this.slotname = slotname;
}
#Override
void doTagWithSpring() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
String userDetailCode = pageContext.getAttribute(InitWidgetUserTag.KAY_USERDETAIL, PageContext.PAGE_SCOPE).toString();
userDetail.init(userDetailCode);
String pageID = pageContext.getAttribute(InitWidgetUserTag.KAY_PAGEID, PageContext.PAGE_SCOPE).toString();
getJspContext().getOut().println("<b>slot for user:"+userDetail.getUserId()+"</b>");
}
#Override
protected void initCustomBeans() {
userDetail = (UserDetailHolder) getSpringContext().getBean("userDetailHolder");
}
}
It's work.
But than i found this:
Spring supported Tag Libraries. Truth in my progect I still use own solution.
Use :-
import org.springframework.web.servlet.tags.RequestContextAwareTag;
public class FetchTagNameTag extends RequestContextAwareTag {
// #Autowired
// private TaskService taskService;
#Override
protected int doStartTagInternal() throws Exception {
TaskService taskService= getRequestContext().getWebApplicationContext().getBean(TaskService.class);
return 0;
}

Categories