I've been using XML based configuration for a while - we've got a Vaadin application with Spring used as DI, but we are not interested in DispacherServlet - only root context, which we use to inject global (not user owned dependencies).
The way it works
I've defined root-context.xml file with content:
<context:annotation-config />
<context:spring-configured />
<context:load-time-weaver />
<context:component-scan base-package="com.example" />
And my web.xml has in it:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Then, some of my classes are defined with #Component annotation and others with #Configurable (the latter mostly belong to user session, so require DI for each instance created with new keyword).
I've got context.xml file with line:
<Loader delegate="false" loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" />
And spring-instrument-tomcat-3.2.1.RELEASE.jar in Tomcat's lib directory.
All the dependencies are injected (with #Autowire) to my #Configurable classes correctly.
The way it doesn't work
Recently I've tried to get rid of root-context.xml and move context initialisation to Java #Configuration class.
I've created a class as follows:
#Configuration
#EnableSpringConfigured
#EnableLoadTimeWeaving
#ComponentScan("com.example")
public class BeansConfiguration
{
}
In addition I changed web.xml entries:
<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.example.spring.BeansConfiguration</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Unfortunately, strange things started to happen. Let me show you an example. Simplifying my class structure looks as follows:
#Component
public class ComponentA
{
}
#Configurable
public class BeanB
{
#Autowired
private ComponentA componentA;
}
#Configurable
public class BeanC
{
#Autowired
private ComponentA componentA;
private BeanB beanB;
public BeanC(BeanB beanB)
{
this.beanB = beanB;
}
}
#Configurable
public class Application
{
#Autowired
private ComponentA componentA;
public Application()
{
}
public void init()
{
BeanC beanC = new BeanC(new BeanB());
}
}
With the XML setup, when it does work, ComponentA is correctly injected by Spring into all my #Configurable objects.
Strangely, with annotation-only configuration BeanC doesn't get the ComponentA injected (it's always null), howewer BeanB and Application do get that!
Have you got any ideas why would it happen? As soon as I comment out lines in web.xml to go back to my previous (XML-based) configuration all starts to work.
My happy guess is that XML Spring entries register something more under the cover than their annotation based counterparts. I've spend half a day trying to find out, what could that be, but I gave up. I would appreciate any suggestions.
Related
I have created a new web project from scratch using JSF 2.3 and Weld as CDI implementation running on Tomcat 9. This works all fine.
Now I would like to add a dependency to a service library (Spring 4 + Hibernate 5) which implements e.g. the user authentication against a database. This is all existing code, so it cannot be modified.
I have already read many articles, but unfortunately no one really helped me. I am facing the issue that I do not know how to inject the Spring-Bean (4.3.12.RELEASE) into a JSF-CDI managed bean.
#Named
#RequestScoped
public class AuthenticationController extends AbstractBean {
#Inject
transient private IUserService _springUserService;
#PostConstruct
public void postConstruct() {
// _springUserService is null
}
}
The service implementation class is annotated as
#Repository(IUserService.BEAN_NAME)
public class UserService implements IUserService {
//...
}
In web.xmlI have configured the spring context loader:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-config-domain.xml
</param-value>
</context-param>
And in faces-config.xml I have configured the Spring-EL-Resolver
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
So which annotation do I need to have the Spring Bean injected?
Am I missing any additional configuration for CDI in order to have it finding the Spring bean?
Thanks in advance and best regards!
I am having a problem where the Spring WebApplicationContext appears to be ignoring the #Import annotation on a #Configuration-annotated config class in my web app. It is not a Spring MVC web app.
The problem occurs during processing of the contextInitialized event of a custom ServletContextListener which used to be able to successfully retrieve this bean when I was using XML configuration, but is now not finding this bean now that I have (apparently incorrectly) converted to the use of #Configuration-annotated classes.
The symptoms I see are:
During app startup, I see this output from the Spring framework:
INFO: No annotated classes found for specified class/package
[org.imaginary.spring.config.Instrumented]
Later, when my contextInitialized() method is invoked and I call getBean(), I get a NoSuchBeanDefinition exception
My config classes are factored in such a way that I have, for example, two high-level config classes, one for a "Production" configuration, and another for an "Instrumented" configuration. These top-level config classes are themselves completely empty, but they make use the #Import annotation to (I'm hoping) bring in the relevant bean definitions for that kind of configuration.
For example, here is the Instrumented configuration class:
package org.imaginary.spring.config;
import org.imaginary.spring.config.instrumentation.InstrumentationDependencies;
import org.imaginary.spring.config.servlets.ServletDependencies;
#Configuration
#Import( {InstrumentationDependencies.class, ServletDependencies.class } )
public class Instrumented
{
}
...for the purposes of this example, here is the InstrumentationDependencies config class, defined in a different package:
package org.imaginary.spring.config.instrumentation;
#Configuration
public class InstrumentationDependencies
{
#Bean
public IEventSink eventSinkImpl()
{
return new InstrumentationEventSinkImpl();
}
}
Here is (a stripped-down version of) the contextInitialized() method:
#Override
public void contextInitialized( ServletContextEvent ctxEvent )
{
try
{
if (_publisher == null)
{
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(ctxEvent.getServletContext());
_eventSink = (IEventSink)springContext.getBean("eventSinkImpl");
}
_eventSink.startReceiving();
}
catch ( Exception e )
{
// handle exception
}
}
Here are the relevant entries from my web.xml:
<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>org.imaginary.spring.config.Instrumented</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.imaginary.MyContextListener</listener-class>
</listener>
Any ideas what I have missed?
In this case, there is nothing wrong with the Spring configuration classes or configuration. The problem was in fact a build issue - the config classes had been added to a package that wasn't getting included in the main web app jar, so the class files weren't present. I was surprised that there wasn't a NoClassDefFoundError exception thrown, as this error leaves the impression that the class exists, it just isn't annotated:
No annotated classes found for specified class/package
[your.class.here].
I can see that the FilterDispatcher is called (by debugger), but it doesn't seem to find the service to call. I've got trouble grasping how RestEasy actually maps between resources defined in Spring and RestEasy.
Main story: Getting http://my.local.no:8087/rest/typeaheads/h only renders 404
web.xml:
...
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>restFilterDispatcher</filter-name>
<filter-class>org.jboss.resteasy.plugins.server.servlet.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>restFilterDispatcher</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
...
resteasy resource is set up by bean:
#Configuration
#ComponentScan(basePackageClasses = TypeaheadsRestService.class)
public class SpringConfig {
}
TypeaheadsRestService.java:
#Resource
#Path("/typeaheads")
public class TypeaheadsRestService {
#GET
#Path("/{search}")
#Produces(MediaType.APPLICATION_JSON)
public List<NameUrl> get(#PathParam("search") String search) {
...
}
}
The RestEasy SpringContextLoaderListener seem to be the missing part. I created a stripped down problem from RestEasy example and used it. For my somewhat more complex application however it would not work. That is probably because it overrides the deprecated createContextLoader-method. In Spring 3 ContextLoaderListener is an instance of ContextLoader. So I reimplemented it like this:
public class MyContextLoaderListener extends ContextLoaderListener {
private SpringContextLoaderSupport springContextLoaderSupport = new SpringContextLoaderSupport();
#Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
this.springContextLoaderSupport.customizeContext(servletContext, applicationContext);
}
}
I originally tried to do the customizeContext(...) in a bean initialisation. That worked in RestEasy 2.2.1.GA, but not in 2.3.4.FINAL.
I have a spring.xml file where in all the bean definitions are listed, where i have listed all the dependencies using beans, specified messageSource, dataSource etc. Also i have a class ApplicationContext class where iam using the context to get all the beans.
The code is ::
package models;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextClass {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
context.registerShutdownHook();
ATTModel attmodel = (ATTModel) context.getBean("att");
//ProjectModel project = (ProjectModel)context.getBean("project");
//project.call1();
attmodel.call();
System.out.println(context.getMessage("insertiondone",null, "Default greeting",null));
}
}
and i have Dao class where an applicationContext is used to access JDBCtemplate related bean. I have to develop a web application now using spring MVC and i need to use this applicationContext. How can i use these applicationContext classes in SpringMVC. I knw i need to use applicationcontextlisteners but where to write them ? Thanks..
You have two ways. In web.xml define this.
<servlet>
<servlet-name>yourapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
And to your WEB-INF folder add yourapp-servlet.xml with your beans and mvc configuration.
Other way is. In web.xml define this.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
And to your WEB-INF add applicationContext.xml with your beans.
You can also combine these approaches.
I use JSF and Spring. The JSF beans must be serializable by design. The JSF beans get injected with Spring beans that handle the db access. Thus these beans must be serializable as well. However simpleJdbcTemplate is not serializable so I get stack traces when restarting the servlet and sessions are lost. If I declare simpleJdbcTemplate to be transient, I don't get stack traces about "not serializable" but then simpleJdbcTemplate is sometimes NULL after a restart, which is even worse.
So what can I do so that the spring beans get injected with a "fresh" simpleJdbcTemplate when the servlet is restarted?
The classes look like this:
#Repository
public class Users implements Serializable
{
#Autowired
private SimpleJdbcTemplate simpleJdbcTemplate;
// ...
}
#ManagedBean
#SessionScoped
public class ThisBean implements Serializable
{
#ManagedProperty(value = "#{users}")
private Users users;
public void setUsers(Users users)
{
this.users = users;
}
// ...
}
Now some config files, just so that you see I didn't miss something standard.
The web.xml has this:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>Listeners.SessionListener</listener-class>
</listener>
The applicationContext.xml has this:
<context:annotation-config />
<context:component-scan base-package="SpringDB"/>
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/mysql"/>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg>
<ref bean="dataSource"/>
</constructor-arg>
</bean>
faces-config.xml has this:
<application>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
<application>
<message-bundle>jsf</message-bundle>
</application>
</application>
Why don't you try to pass in the DataSource reference instead of the JdbcTemplate. The common practice is to inject the data source and create the JdbcTemplate within the setter for the data source. You'll find the following example in the spring documentation:
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
You can also use #Autowired on the setDataSource method if you don't want to set up your beans with XML. For further details have a look at the JdbcTemplate best practices section of the excellent spring documentation.
Add scope="prototype" definition for simpleJDBCTemplate bean. Then Spring Framework will create new instance for every servlet instance. More info about bean scopes: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes