I want to use Spring to manage my JPA DAOs in web application and trying to follow the Spring website examples to figure out how to do it.
I am considering to use AnnotationConfigWebApplicationContext instead of AnnotationConfigApplicationContext to bootstrap Spring.
In the examples, it is used together with org.springframework.web.servlet.DispatcherServlet but I would like to use it without Spring MVC.
My web.xml looks following:
...
<web-app>
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.foo.Application</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
</web-app>
My configuration class looks following:
...
#Configuration
#ComponentScan(basePackages={"com.foo.service"})
public class Application {
#Bean
MessageService mockMessageService() {
return new MessageService() {
#Override
public String getMessages() {
return "Hello Spring World!";
}
};
}
}
Is it possible to inject mockMessageService in this example into my servlets or do I have to create a separate central hub for my web application where the services are called from by the servlets?
The problem is that if I have my servlets in package com.foo.service and try to use
#Autowired
to get dependancy or try some similar approach the service doesn't get injected and I don't know where's the problem. Is it possible to do it like that? Or should I add something for AnnotationConfigWebApplicationContext to work like that?
Servlets are managed by the servlet container and not by spring. Spring will not inject servlets, because it does not instantiate them.
If you want to get access to beans managed by spring within a servlet you must do it the same way as spring's DispatcherServlet does.
E.g. to get the WebApplicationContext from the ServletContext do
javax.servlet.ServletContext servletContext = ...;
WebApplicationContextUtils.getWebApplicationContext(servletContext);
This works because the ContextLoaderListener ensures that the WebApplicationContext is available as a ServletContext attribute.
Since the WebApplicationContext is an ApplicationContext you can autowire beans that have been instantiated outside the spring container.
For example
ApplicationContext appContext = ...;
AutowireCapableBeanFactory acbf = context.getAutowireCapableBeanFactory();
SomeBean someBean = new SomeBean();
acbf.autowireBean(someBean);
Just put the #Autowired annotation on fields of SomeBean.
A much easier way is to extend spring's org.springframework.web.servlet.FrameworkServlet and implement the doService method. Read the javadoc of the FrameworkServket for details.
If you have a servlet, you can wire it up on the init method using something like the below:
public class MyServlet implements javax.servlet.Servlet {
#Autowired
MyBean myBean;
#Override
public void init(ServletConfig arg0) throws ServletException {
final AutowireCapableBeanFactory autowireCapableBeanFactory=WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
}
//....
}
If I have have a lot of servlets, it can be handy to extract the init method into some reusable parent class e.g.
public class AutowiredServlet implements javax.servlet.Servlet {
#Override
public void init(ServletConfig arg0) throws ServletException {
final AutowireCapableBeanFactory autowireCapableBeanFactory=WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
}
}
and extend that class for every servlet that requires Autowiring.
Related
I am using Spring 4 (not Spring Boot) in the web application. I need to run some initialization code before any of the beans in the application context would be created. I tried to create implementation of org.springframework.context.ApplicationContextInitializer and register it in spring.factories but was not picked up for some reason. How can I do it?
As it turned out implementing of org.springframework.context.ApplicationContextInitializer was a right way. Because in my project I do not use Spring MVC implementation of this initializer should be registered in web.xml instead of spring.factories. Here is an example:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>my.company.MyContextInitializer</param-value>
</context-param>
This should work. If not, please post your code.
#Component
public class SampleBootstrap implements ApplicationListener<ContextRefreshedEvent> {
....
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Do Something();
}
}
It is possible to publish jersey rest service based on spring profile?
lets say as following example, how can I publish RegisterServices1 when using profile1?
public class ApiGWRestApplicationConfig extends ResourceConfig {
public ApiGWRestApplicationConfig() {
register(RegisterServicesApiGWInterface.class);
}
}
#Service
#Profile("profile1")
#Path(SystemConstants.REST_REGISTER)
public class RegisterServices1 implements RegisterServicesApiGWInterface {
}
#Service
#Profile("profile2")
#Path(SystemConstants.REST_REGISTER)
public class RegisterServices2 implements RegisterServicesApiGWInterface{}
web.xml
<servlet>
<servlet-name>jersey-servlet-kagw</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.ttech.tims.imos.web.ApiGWRestApplicationConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
So what you can do is get a hold of the ApplicationContext and use getBeansWithAnnotation(Path.class). This will give you all the resource instances that are part of the profile. Then you can register the instances.
I though it would be possible to inject the ApplicationContext into the ResourceConfig, but as mentioned in the comment above, it seems the creation of the ResourceConfig doesn't have access to it yet.
What I was able to get to work, is to use a JAX-RS Feature which also has access to registration methods, just like you have in the ResourceConfig. Using the Feature will give you access to the ApplicationContext
public class SpringProfilesFeature implements Feature {
#Inject
private ApplicationContext context;
#Override
public boolean configure(FeatureContext featureContext) {
Map<String, Object> resources = context.getBeansWithAnnotation(Path.class);
resources.values().forEach(resource -> featureContext.register(resource));
return true;
}
}
Then just register the feature with the ResourceConfig
public AppConfig() {
register(SpringProfilesFeature.class);
}
Remove any other registrations you have for all your resources. Just let the feature register them.
I've confirmed that this works. Not sure how you set your profile for the environment, but hopefully this is something you already know how to do.
I have a problem that I have a ClassA needs RoomService to be injected, and it works fine that I find in ClassA, the roomService's id is the same.
While for some reason, I need roomservice to create room instance based on some input param for me, so I use below config to achieve this:
#Configuration
#EnableAspectJAutoProxy
public class Application {
private static ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
public static ApplicationContext getApplicationContext(){
return ctx;
}
#Bean
public RoomService roomService(){
return new RoomService();//Singleton
}
#Bean
#Scope("prototype")
public AbstractRoom room(AbstractRoom.Mode roomMode){
RoomService roomService = (RoomService) ctx.getBean(RoomService.class);
LogUtil.debug("--------from application:" +roomService.id1);//here, I find the id is different every time
return roomService.newRoom(roomMode);
}
}
The problem is that I need RoomService to be singleton, but I find that in the Application.java , the ctx.getBean(roomService) always returns a different bean which has different id. Isn't Spring should reuse the same bean? Why is that?
Here is how I create a room in RoomService.java
public AbstractRoom createRoom(String userID,int playerCount,Mode roomMode){
ApplicationContext ctx =Application.getApplicationContext();
AbstractRoom room = (AbstractRoom)ctx.getBean(AbstractRoom.class,roomMode);
}
Update:
I tried reusing the same ctx and it does not work. One hint is that I find my constructor of RoomService() is called several times(I put a break point in it.) when I run tomcat to start it
Here is my web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>wodinow</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Please help!
Each time you retrieve your RoomService you are creating a new ApplicationContext instance. But singleton beans are only guaranteed to be singleton within a single instance of ApplicationContext.
So if you want the bean to be singleton you must use the same ApplicationContext instance each time you retrieve it.
From Spring documentation:
singleton: (Default) Scopes a single bean definition to a single
object instance per Spring IoC container.
Update 1
You can just call roomService() in your room() method to get the room service without creating application context and Spring will ensure that they are the same instance since it is marked as #Bean which has implicit singleton scope.
Update 2
Based on the updated question here are couple of issues with your code in general:
1. Do not create ApplicationContext in your configuration class. When you start your Spring application in Tomcat, application context is created for you by Spring. You just need to tell Spring which configuration classes it should register.
2. Remove the room() bean definition from your configuration class. Method for creating a room should be in RoomService. So for example if you needed to create a new room in Spring MVC controller you would inject the RoomService and call createRoom method on it. The injected service would be singleton. Example:
#Controller
#RequestMapping("/rooms")
public class RoomController {
#Autowired
private RoomService roomService;
#RequestMapping(value="/create", method=POST)
public String createRoom() {
roomService.createRoom(/* Parameters for room creation */);
return "redirect:/somelocation";
}
}
Try to rework your code based on these suggestions and it should work.
In my web application, I want to create Listener which will get notified when my server get started and all bean get loaded.
In that Listener, I want to call a service method.
I used ServletContextListener.
it has contextInitialized method but it does not work in my case. it get involked when server get started but before spring bean creation.
so I get instance of service class as null.
Is there other way to create Listener.
I would go for registering an instance of ApplicationListener in the Spring context configuration, that listens for the ContextRefreshedEvent, which is signalled when the application context has finished initializing or being refreshed. After this moment you could call your service.
Below you will find the ApplicationListener implementation (which depends on the service) and the Spring configuration (both Java and XML)that you need to achieve this. You need to choose the configuration specific to your app:
Java-based configuration
#Configuration
public class JavaConfig {
#Bean
public ApplicationListener<ContextRefreshedEvent> contextInitFinishListener() {
return new ContextInitFinishListener(myService());
}
#Bean
public MyService myService() {
return new MyService();
}
}
XML
<bean class="com.package.ContextInitFinishListener">
<constructor-arg>
<bean class="com.package.MyService"/>
</constructor-arg>
</bean>
This is the code for the ContextInitFinishListener class:
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ContextInitFinishListener implements ApplicationListener<ContextRefreshedEvent> {
private MyService myService;
public ContextInitFinishListener(MyService myService) {
this.myService = myService;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//call myService
}
}
You can use Spring's event handling. The event that you are looking for is probably ContextRefreshedEvent
Yes you need to add ContextLoaderListener in web.xml, only if you want to load other Spring context xml files as well while loading the app and you can specify them as
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
</param-value>
</context-param>
for more u can visit this link might helpful to you.
Click Here
I have a spring mvc project set up like so:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-contexts/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-contexts/configuration-context.xml</param-value>
</context-param>
It appears if I make a bean in configuration-context.xml and reference a bean in servlet-context.xml it cannot find it. Are these created as two different contexts? Why does this happen / work like this in general?
Yes there are two contexts stacked on each other (parent and child context).
The beans from the DispatcherServlet (servlet-context.xml) can access the beans from the ContextLoaderListener (configuration-context.xml), but not the other way around.
So put the basic stuff in the configuration-context.xml and the web related once into servlet-context.xml.
#See also this Stack Overflow question: ContextLoaderListener or not?
Spring container can definitely see the components decided by component scan base package of context and you can get the bean from the context.
There are two types of context in spring
1. Root context (ApplicationContext)
2. Servlet context (WebApplicationContext)
Visiblity of beans defined in rootContext to servletContext - YES
Beans defined in root context is always visible in all servlet contexts by default. for example dataSource bean defined in root context can be accessed in servlet context as given below.
#Configuration
public class RootConfiguration
{
#Bean
public DataSource dataSource()
{
...
}
}
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.pvn.mvctiles")
public class ServletConfiguration implements WebMvcConfigurer
{
#Autowired
private DataSource dataSource;
...
}
Visiblity of beans defined in servletContext to rootContext - yes*
(Why * in Yes)
1. Initialization of context order is rootContext first and servletContext next.
In the root context configuration class/xml if you try to get the bean defined in servletContext you will get NULL. (because servletContext is not initialized yet, hence we can say beans not visible while initialization of rootContext)
But you can get beans defined in servletContext after initialization of servletContext(you can get beans through application context)
you can print and confirm it by
applicationContext.getBeanDefinitionNames();
2. If you want to access beans of servlet context in the filter or in the another servlet context, add "org.springframework.web.servlet" base package to your root config class/xml
#Configuration
#ComponentScan(basePackages = "org.springframework.web.servlet" )
public class RootConfiguration
after adding you can get all below beans from application context
springSecurityConfig, tilesConfigurer, themeSource, themeResolver, messageSource, localeResolver, requestMappingHandlerMapping, mvcPathMatcher, mvcUrlPathHelper, mvcContentNegotiationManager, viewControllerHandlerMapping, beanNameHandlerMapping, resourceHandlerMapping, mvcResourceUrlProvider, defaultServletHandlerMapping, requestMappingHandlerAdapter, mvcConversionService, mvcValidator, mvcUriComponentsContributor, httpRequestHandlerAdapter, simpleControllerHandlerAdapter, handlerExceptionResolver, mvcViewResolver, mvcHandlerMappingIntrospector
If you want to get your custom beans from rootContext add base package value to rootContext component scan as given below.
#Configuration
#ComponentScan(basePackages = { "com.your.configuration.package", "org.springframework.web.servlet" })
public class RootConfiguration
Above given configuration will be helpful if you want injected dependency be available in your rootContext and can be accessed in your servlet-filter. For example If you catch exception in filter and want to send error response which is same as response sent by HttpMessageConverter but it is configured in servletContext, then you may want to access that configured converter to send the same response.
Note this, below autowiring will not work in servlet-filters
#Autowired
private ApplicationContext appContext;
this will not work in servlet filter, as filters are initialized before spring container got initialized.(Depends on order of filter and DelegatingProxyFilter)
So, to get applicationContext in filter
public class YourFilter implements Filter
{
private ApplicationContext appContext;
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
Filter.super.init(filterConfig);
appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
}
}
Hope it gives clear idea of how beans can be accessed between contexts.