I want to write a simple servlet in JBoss which will call a method on a Spring bean. The purpose is to allow a user to kick off an internal job by hitting a URL.
What is the easiest way to get hold of a reference to my Spring bean in the servlet?
JBoss web services allow you to inject a WebServiceContext into your service class using an #Resource annotation. Is there anything comparable that works in plain servlets? A web service to solve this particular problem would be using a sledgehammer to crush a nut.
There is a much more sophisticated way to do that. There is SpringBeanAutowiringSupportinside org.springframework.web.context.support that allows you building something like this:
public class MyServlet extends HttpServlet {
#Autowired
private MyService myService;
public void init(ServletConfig config) {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
}
This will cause Spring to lookup the ApplicationContext tied to that ServletContext (e.g. created via ContextLoaderListener) and inject the Spring beans available in that ApplicationContext.
Your servlet can use WebApplicationContextUtils to get the application context, but then your servlet code will have a direct dependency on the Spring Framework.
Another solution is configure the application context to export the Spring bean to the servlet context as an attribute:
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="jobbie" value-ref="springifiedJobbie"/>
</map>
</property>
</bean>
Your servlet can retrieve the bean from the servlet context using
SpringifiedJobbie jobbie = (SpringifiedJobbie) getServletContext().getAttribute("jobbie");
I've found one way to do it:
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
SpringifiedJobbie jobbie = (SpringifiedJobbie)context.getBean("springifiedJobbie");
Related
We have a existing system with Q2 Server and Spring MVC with following configuration. Q2 server was created when a Httpservlet is initiated and It worked perfectly and spring beans can be autowired withn ISORequestlistner. This is now being converted to Spring boot 2.3. Once I initiated the same Httpservlet using ServletRegistrationBean in Spring boot, Q2 server is initiated and can send requst to it. But auto-wiring is not working. Once I check. Once he request is processing inside the ISORequest listner, Spring context is not visible since Q2 server is using different class loader.
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="DownloadServer-A">
<attr name="port" type="java.lang.Integer">6400</attr>
<attr name="minSessions" type="java.lang.Integer">10</attr>
<attr name="maxSessions" type="java.lang.Integer">1100</attr>
<channel name="DownloadServer-A-Channel" class="org.jpos.iso.channel.NACChannel" logger="Q2"
packager="org.jpos.iso.packager.GenericPackager" header="6000010000">
<property name="packager-config" value="/app/repository/q2serverconfig/resources/Download_generic.xml" />
</channel>
<request-listener class="DownloadServerAListener" logger="Q2">
<property name="space" value="transient:default" />
<property name="queue" value="TransactionQueue" />
<property name="timeout" value="35000" />
</request-listener>
</server>
1st Try
Tried creating static ApplicationContext using ApplicationContextAware and tried it in the ISORequestListner. But It becomes null when TCP request received to Q2 server.
2nd Try
I tried several solutions like below github repo. But I didn't work.
https://github.com/vmantek/chimera
Have anyone tried to start ISO Server inside the Spring Application context as a bean? What I mean is that start ISO Server in #Configuration class with using Q2.start(). Q2.start will start in a separate class loader. I don't want it to happen.
I was looking for these few days and I was trying several ways.
The issue is that Spring is starting in a Specific class loader. But when you start Q2 Server as
Q2 q2Server = new Q2(<deploydir>);
q2Server.start();
Q2 server is starting in a different classloader. Therefore SpringContext was not available for auto wiring. SpringBeanAutowiringSupport depends on ContextLoader to retrieve the current application context, and always get null.
Workaround
You could register a bean that implements org.springframework.boot.context.embedded.ServletContextInitializer to retrieve the application context during startup().
#Configuration
public class WebApplicationContextLocator implements ServletContextInitializer {
private static WebApplicationContext webApplicationContext;
public static WebApplicationContext getCurrentWebApplicationContext() {
return webApplicationContext;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
webApplicationContext =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
}
}
Then you could implement self-autowiring in your ISORequestListener class.
#Component
public class ServiceImpl implements ISORequestListener {
#Autowired
private BackendService backendService;
public ServiceImpl() {
AutowiredAnnotationBeanPostProcessor bpp = new
AutowiredAnnotationBeanPostProcessor();
WebApplicationContext currentContext =
WebApplicationContextLocator.getCurrentWebApplicationContext();
bpp.setBeanFactory(currentContext.getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
}
Then auto wiring working perfectly. I was inspired by following answer.
Spring Boot register JAX-WS webservice as bean
I have spring based multimodule application. And in my DAO module the DB (embedded derby) is started and created by the class the implements ApplicationListener.
Problem that in the logs the huge stacktrace from Spring which say that there is no db(couldn't get connection).
Still, my application works without any problems. This stacktrace appeared before the ApplicationListener invoked and the db is created. Actually, I see it only when I am starting the application the first time on the machine, because the db created only this time, than it just used.
So my question is whow to avoid this exception in logs? Maybe there is spring or hibenate setup not connect to the db before the application context fully loaded? Or invoke the code that creates db by some other listener?
Well here is the way I do : the ROOT context contains the datasource, the dao, the service and the transaction manager. In XML config, the declaration of the database is :
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:url="jdbc:derby:/path/to/database;create=TRUE"
p:username="user" p:password="pwd"
p:driverClassName="org.apache.derby.jdbc.EmbeddedDriver"/>
it can then be used to declare a session factory for hibernate and an associated DAO as :
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
id="sessionFactory" p:dataSource-ref="datasource">
<!-- hibernate config -->
...
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
name="transactionManager" p:sessionFactory-ref="sessionFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="myDao" class="... .myDaoImpl" p:sessionFactory-ref="sessionFactory" .../>
That way all is created by spring, that ensures that the creation order is correct. Of course the same is possible in Java config with the same logic.
I suppose you are fetching some data from database from inside spring beans that are being created. Perhaps thru #PostConstruct or other way. Remember that until spring context is fully loaded some beans can have injected uninitialized beans.
So do not use DB, do not call any DAOs until you are sure that spring context is fully initialized.
To do such initial calls to DAOs try such patter that guarantees spring context completness:
#Component
public class SpringContextMonitor implements ApplicationListener<ApplicationEvent> {
#Autowired
private SomeDao dao;
...
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
onStart((ContextRefreshedEvent) event);
}
}
private void onStart(ContextRefreshedEvent event) {
// do your initialization here
dao.getSomething();
dao2.getSomething();
...
}
...
}
The onStart method in above example is place where you are sure that all beans are fully initialized
I need to inject a object of a java class in spring controller through applicaionContext.xml. My controller will be ,
#Controller
public class SpringController{
private MyClass obj;
}
I know I can do it with #Autowired annotation.
Is this really good to create a object for a controller through applicaionContext.xml ? Also can I inject a object of a class in controller using the <property> tag inside a <bean> tag ?
Is this really possible ? or please forgive me if it is a stupid question.
I need to know the possible ways for how to inject a object of a class in Spring controller ?
You can of course use #Autowired annotation to autowire the relationships, which can reduce the need to define the properties and constructor arguments for the controller in your applicationContext.xml file. And also to add a dependency to a class, you don't need to modify the configuration files.
But it has some disadvantages too, like if you use #Autowired, there will not be any explicit documentation for the wiring details between Spring managed beans. And to know the relationships between the beans, you have to go through your managed beans. But, if you use configuration files to define the relationships, the relationship details can be found in one place.
You can inject an object of a class into your controller through your applicaionContext.xml as below:
Constructor based injection:
#Controller
public class SpringController{
private MyClass obj;
public SpringController(MyClass obj){
this.obj=obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<constructor-arg ref="myClassImpl"></constructor-arg>
</bean>
Setter based injection:
#Controller
public class SpringController{
private MyClass obj;
public void setObj(MyClass obj){
this.obj=obj;
}
public MyClass getObj(){
return obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<property name="obj" ref="myClassImpl"></property>
</bean>
If you want to inject an object in a controller and you particularly want to you use xml,then instead of component scanning of Controller you should create a bean of the controller class of singleton scope in the application context.
Your controller class need not be annotated with #Controller.
you then have to you extend some Controller also like AbstractCommandController, AbstractController, AbstractFormController, AbstractWizardFormController, BaseCommandController, CancellableFormController, MultiActionController SimpleFormController, UrlFilenameViewController
Now to inject a particular object you can use Either Constructor and Setter based injection.
or you can use Autowring by name or type to auto inject the object.
Make sure that you have also declared the bean of that object also in Application Context.
After a DispatcherServlet has received a request and has done its work to resolve locales, themes and suchlike, it then tries to resolve a Controller, using a HandlerMapping. When a Controller has been found to handle the request, the handleRequest method of the located Controller will be invoked; the located Controller is then responsible for handling the actual request and - if applicable - returning an appropriate ModelAndView.
Thats it.
Actually, injection with xml and annotation is same behind the scene. Xml is old fashion while annotations are newer.
Basically, there are 2 types of injection types.
byName
Autowiring by property name. Spring container looks at the properties
of the beans on which autowire attribute is set to byName in the XML
configuration file. It then tries to match and wire its properties
with the beans defined by the same names in the configuration file.
You can give explicit names to beans both with xml and annotation.
#Service("BeanName")
#Component("BeanName")
#Controller("BeanName")
<bean name="BeanName" class="someclass"></bean>
and inject beans by using #Qualifier annotation.
#Autowired
#Qualifier("BeanName")
and with xml
<bean id="MyBean2" class="MyBean2 class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
byType
Autowiring by property datatype. Spring container looks at the
properties of the beans on which autowire attribute is set to byType
in the XML configuration file. It then tries to match and wire a
property if its type matches with exactly one of the beans name in
configuration file. If more than one such beans exists, a fatal
exception is thrown.
Default auto wiring mode is byType, so spring will look for matching type in auto wiring. However, older versions of Spring has default behavior none on injection. If you want to inject byType using xml, you should tell spring contaioner explicitly.
For example MyBean2 has a reference to MyBean, by setting autowired attribute to byType it handles injection automatically.
<bean id="MyBean" class="MyBean class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
<bean id="MyBean2" class="MyBean2 class"
autowire="byType">
</bean>
It also depends on where the injection take place in your code. There are 2 types, setter getter injection and constructor injection.
Note : There is no difference in #Controller since they are already in spring context.
See also
Spring Beans Auto wiring
I ran into such problem. I was getting "Ambiguous mapping found". (I use xml configuration as well and i am injecting a bean into my controller)
Then looking at my console i realized that my controller was being instantiated twice.
In more detailed look i noticed that my annotation
#Controller(value = "aController")
(Note value = "aController")
was different from my xml configuration where i was instatiating the same controller with different bean id
<bean id="aControleRRRRR" class="package.ControllerClassName"
p:property-ref="beanToInject" />
(Note id="aControleRRRRR")
So in conclusion your #Controller name (value = "aController") needs to be exactly the same as the name you give in the XML configuration (id="aControleRRRRR"), so that Spring can manage to distinct that they refer to the same bean (instance)
Hope this helps
I would like to use Spring as a JNDI provider. This means that I would like to configure a bean in my Spring context, which can be accessed via JNDI. This would look something like this:
<bean class="org.some.thing.here">
<property name="beans">
<map>
<entry key="w/t/f">
<bean class="some.thing.Else">
// rest ommitted
</bean>
</entry>
</map>
</property>
</bean>
Then, in my application (lets say a Controller), I want to be able to grab this bean via:
Context ctx = new InitialContext();
some.thing.Else bar = (some.thing.Else) ctx.lookup("w/t/f");
How could I go about doing this? I've looked at XBean, however the project looks out of date (doesn't work with Spring 3.0.X I don't think), and there is very little documentation.
Any other options? I would also considering rolling my own jndi provider class if it isn't too hard to do.
EDIT: I should add that I don't have an option using JNDI, I have a library we have to use which requires certain components to be loaded via JNDI. I would like to use Spring as the provider.
Why use JNDI at all? Just get the Spring ApplicationContext and get the bean from that.
Assuming you initialized Spring using ContextLoaderListener in your webapp, you should be able to retrieve the application context from the ServletContext. From there you can get any bean you declared in Spring.
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object bean = context.getBean(some.thing.Else.class);
If you have to use JDNI, then you can create a ServletContextListener that does something like the following in contextInitialized():
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object bean = context.getBean(some.thing.Else.class);
Context initCtx = new InitialContext();
Context springCtx = initCtx.createSubcontext("spring");
springCtx.bind("bean", bean);
Then, you should be able to lookup the Spring bean at "spring/bean" from the InitialContext.
Two things to note:
The context listener should probably also call initCtx.destroySubcontext("spring") in contextDestroy too.
The java:comp/env namespace is read-only (in Tomcat at least), so you can't put anything there.
Asker edit: Just a couple more points of clarity...
If you plan on referencing Spring beans via ApplicationContext, then you need a ContextLoaderListener defined in your web.xml. This must be defined before your custom listener class... like so:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>
org.example.sandbox.MyCustomServletContextListener
</listener-class>
</listener>
Also, you can get the ServletContext that getWebApplicationContext uses from the ServletContextEvent, like so:
#Override
public void contextInitialized(ServletContextEvent contextEvent) {
try {
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(contextEvent.getServletContext());
// get a bean named "myCalendar" from the application context
Calendar cal = (Calendar)appContext.getBean("myCalendar");
// bind via JNDI
Context initialContext = new InitialContext();
Context subCtx = initialContext.createSubcontext("sample");
subCtx.bind("calendar", cal);
} catch (NamingException e) { // ommitted }
}
AngerClown is right, don't bother with JNDI unless you need to provide references to other modules that insist on it. If you are in a webapp container like Tomcat, it will have a JNDI registry. Use that. If not inside a webapp container, it doesn't make sense to have JNDI anyway, since it's for J2EE environments.
Assuming you are inside a webapp, a better way to launch your app is to have the main class be a Spring bean that implements lifecycle interfaces (like InitializingBean) to get a call when it's time to start your app. By that point, your main application class will have been injected with all it's dependencies. This avoids the need to call methods on the ApplicationContext directly.
Even so, if you must call methods on the ApplicationContext and you are launched by Spring, you can implement BeanContextAware and get injected with the context.
Yet another way to write your own JndiExporter
https://blog.konstantinpavlov.net/2008/12/31/how-to-export-spring-bean-to-jndi/
We're currently adding some new features to an old webapp which was using only JSP without any framework for the front. We have added Spring recently, and we would like to autowire our beans in our modified JSP, while not rewriting everything to use SpringMVC, Struts2 or Tapestry5.
We're using autowiring by type, so it leads to get some code like this in the JSP, while previously getting the web application context ( as "wap") :
MyDao myDao = (MyDao) wap.getBeansOfType(MyDao.class).values().toArray()[0];
We would like not to use such a code but rather automagically inject our beans directly in our JSPs as we would in a business bean using #Autowired annotation.
In fact we're looking to the cleanest ways to inject our beans in our JSPs. What do you use ?
You can use Spring's ContextExposingHttpServletRequest:
HttpServletRequest decorator that
makes all Spring beans in a given
WebApplicationContext accessible as
request attributes, through lazy
checking once an attribute gets
accessed.
This would require your controller code to wrap the original HttpServletRequest in a ContextExposingHttpServletRequest, and then forward that to the JSP. It can either expose specific named beans, or every bean in the context.
Of course, this just shifts the problem from your JSPs to your controller code, but that's perhaps a more manageable problem.
You can't use #Autowired directly because both your jsps and servlets are instantiated by the servlet conainer. So they are not part of the spring context and hence their dependencies aren't injected.
You can:
move all code that to pure servlets, rather than in jsps - leave only presentation in the jsps.
use #Configurable on your servlets (and add a javaagent, as described in the linked docs)
Another way, is to make the servlet part of the current context manually. This is possible in both jsps and servlets:
public void init() {
WebApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());
AutowireCapableBeanFactory bf = ctx.getAutowireCapableBeanFactory();
bf.autowireBean(this);
}
This will resolve the #Autowired annotated dependencies.
Now, I'm not sure whether servlet containers are required to use only one instance of a servlet class. If not, you'd better place the above code in a getter-method for the dependency (getDao()) and if the #Autowired property is null (i.e. another instance of the servlet-class is used by the container) - perform the above operation.
That all said, really consider using a web framework (any of the ones you listed). Having logic in jsps is completely wrong, hard to support, hard to read, etc.
What about overriding jspInit() method and adding Autowiring support:
<%# page import="com.example.ExampleService"%>
<%# page import="org.springframework.beans.factory.annotation.Value"%>
<%# page import="org.springframework.beans.factory.annotation.Autowired"%>
<%# page import="org.springframework.web.context.support.SpringBeanAutowiringSupport"%>
<%!
public void jspInit()
{
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
getServletContext());
}
#Value("${example.property}")
private String someField;
#Autowired
private ExampleService exampleService;
%>
<% final Object data = exampleService.getSomething(someField); %>
I doubt that there is a clean way to inject dependencies into a JSP.
I think that the clean solution would be to start refactoring your code to get the business logic out of the JSPs, using either SpringMVC or one of the alternatives you cited.
Start with one or more minimalist controllers that simply pass the request to the JSPs with the injected beans as attributes; #skaffman's answer gives one way to do that, or you could do it more selectively. Then progressively migrate code out of the JSPs and into the controllers.
This isn't autowired, but Spring can expose your bean names into the request context, you just need to configure it in the viewResolver.
From: https://raibledesigns.com/rd/entry/spring_mvc_jstlview_and_exposecontextbeansasattributes
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>