Spring MVC. Define custom HttpMessageConverter over XML configuration file - java

After half a day googling I have managed to register a custom HttpMessageConverter over the configuration class. Like this:
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
#Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = super.requestMappingHandlerAdapter();
handlerAdapter.getMessageConverters().add(0, new CustomConverter());
return handlerAdapter;
}
}
Is there a way to do it using XML configuration file?

Here is the answer for Spring 3.2:
<mvc:annotation-driven >
<mvc:message-converters register-defaults="false">
<bean class="me.MyCustomMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="writeAcceptCharset" value="false"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Do not forget to define the mvc namespace: http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
I did not find, how to add the own converter on the top on the converters list. It is possible to redefine the converters stack using register-defaults attribute.
Standard stack is defined in the constructor of this Spring class RequestMappingHandlerAdapter. One can copy needed converters from there.

Related

How can I add my custom type converters to SpringBoot

Config my custom type converters by using (Spring 4.x)XML properties like this.
<mvc:annotation-driven conversion-service="factoryBean" />
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters">
<list>
<bean class="com.mvc.convertor.MyConvertor" />
</list>
</property>
</bean>
MyConvertor implements org.springframework.core.convert.converter.Converter.
And how can I config my custom type converters by using SpringBoot.I have tried many methods but failed.Hope any one can help me to resolve.Thanks!
As Stephane Nicoll pointed out spring boot should automatically pick up any converters registered as bean configuration in your application.
#Configuration
#EnableWebMvc
public class MyConfiguration {
#Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = ...
HttpMessageConverter<?> another = ...
return new HttpMessageConverters(additional, another);
}
}

Converting Spring XML to JavaConfig with Variable and Resource Import

I have a spring boot application where I am trying to convert the following spring xml config to java config:
<bean id="pageDAO" factory-bean="springWSDaoFactory"
factory-method="createPageDAO" lazy-init="true">
<constructor-arg type="java.lang.String" value="${cds.host}" />
<constructor-arg type="java.lang.Integer" value="${cds.port}" />
</bean>
<!-- CoreApi + plugins configuration -->
<import resource="classpath:coreAPI_SpringWSContext.xml"/>
<bean name="springWSDaoFactory" class="com.blan.torque.dao.springws.SpringWSDAOFactory" lazy-init="true">
<property name="serviceVersion" value="${service.version}"/>
<property name="securityKey" value="${service.key}"/>
</bean>
Here's what I have for javaconfig so far:
#Bean
public PageDAO pageDAO() {
return springWSDAOFactory().createPageDAO(null, null);
}
#Bean
public SpringWSDAOFactory springWSDAOFactory() {
SpringWSDAOFactory springWSDAOFactory = new SpringWSDAOFactory();
springWSDAOFactory.setServiceVersion(null);
springWSDAOFactory.setSecurityKey(null);
return springWSDAOFactory;
}
I have no idea how to implement <import resource...../> in Java let alone import the variables like ${cds.host}. I've put null everywhere as place holders. But any ideas on how to do this with annotations?
In your configuration class you can use the annotation #ImportResource instead of <import resource... />.
To read Strings from a property file, try to declare an String using #Value and the use the previously declared string.
For example:
#Value("${service.version}")
private String serviceVersion;
Then using it as parameter
springWSDAOFactory.setServiceVersion(this.serviceVersion);
I hope have helped you.

Inject spring bean into #Controller via applicationContext.xml

In my dispatcher-servlet.xml I defined a bean as follows:
<bean id="worplacementDAO" class="com.mycompany.maventestwebapp.db.dao">
<property name="dataSource" value="dataSource" />
</bean>
Is it possible to inject the bean into a controller via applicationContext configuration file, without using #Autowired?
Simple answer - No.
You can implement BeanPostProcessor to do something with your beans (e.g. inject dependency). Or you can manually register the bean as <bean> instead of letting component-scan do that for you. But that is all you can do.

How do I implicitly map folder views without breaking resource mappings?

I am attempting to setup a very simple base configuration for a spring web application that can handle the following cases:
map resource root requests to /index, eg / maps to /index, /resource/ maps to /resource/index
map /static/** to /static/ (this is a resources view - css,js,images)
handle some specific request paths with controller mappings
map all other requests to a url based view, eg /resource/page maps to /WEB-INF/views/resource/page.jsp
In my current configuration what I have is:
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:view-controller path="/" view-name="index"/>
which works for static resources and root requests (/) but not for resource based root requests (/resource/). When attempting to handle all other requests (**/) I break handling of the static resources.
<mvc:view-controller path="**/*" />
Is there a way to do all of these things at the same time? It doesn't need to be an xml only solution, I'm fine with a code configuration or hybrid solution.
I have managed to find a solution by implementing my own Controller and Config.
view-servlet.xml
<mvc:resources mapping="/static/**" location="/static/"/>
<context:component-scan base-package="com.example.web.view"/>
com.example.web.view.ViewConfig
#Configuration
public class ViewConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/views/");
vr.setSuffix(".jsp");
return vr;
}
#Bean RequestMappingHandlerMapping requestMappingHandlerMapping(){
return new RequestMappingHandlerMapping();
}
#Bean RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setOrder(2); //process after mvc:resources
return adapter;
}
}
com.example.web.view.ViewController
#Controller
public class ViewController {
#RequestMapping(value = "**/")
public String all(HttpServletRequest request) {
String path = request.getRequestURI().replace(request.getContextPath(),"");
return path.endsWith("/") ? path +"index" : path;
}
}
The magic was with setting the order value on the adaptor in ViewConfig. Spring creates a default mapping handler that all RequestMappings found by component-scan are added to. The reason why my initial setup failed was that the ordering of this handler is to match before the one registered by mvc:resources. Creating my own adaptor and setting its order to be processed after mvc:resources allowed both to work.
Information regarding this can found in the spring documentation 19.9.1 Setting up the dispatcher for annotation support.
Of course the ViewConfig can also be implemented purely in xml:
<mvc:resources mapping="/static/**" location="/static/"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="order" value="2"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<context:component-scan base-package="com.example.web.view"/>
Update: Changed to RequestMappingHandlerMapping and RequestMappingHandlerAdapter from DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter to reflect changes made in Spring 3.1.x and above.

Spring MVC no default constructor found?

I'm having problems with my Spring controllers - I'm getting no default constructor found - but they do have a constructor which I am trying to created via the applicationContext.xml - heres the relevant bit:
<bean id="PcrfSimulator" class="com.rory.services.pcrf.simulator.PcrfSimulator" init-method="start">
</bean>
<bean id="CacheHandler" class="com.rory.services.pcrf.simulator.handlers.CacheHandler">
<constructor-arg index="0" type="com.rory.services.pcrf.simulator.CustomGxSessionIdCacheImpl">
<bean factory-bean="PcrfSimulator" factory-method="getGxSessionIdCache">
</bean>
</constructor-arg>
</bean>
Ie. I'm creating a bean first, and then trying to pass the result of a method call from that bean into the second bean's (CacheHandler) constructor.
Here'e the start of CacheHandler:
#Controller
public class CacheHandler {
private final CustomGxSessionIdCacheImpl gxSessionIdCache;
public CacheHandler(CustomGxSessionIdCacheImpl gxSessionIdCache) {
this.gxSessionIdCache = gxSessionIdCache;
}
Here's the error I'm getting:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheHandler' defined in URL [jar:file:/users/rtorney/Documents/apache-tomcat-7.0.25/webapps/PCRFSimulator-4.0/WEB-INF/lib/PCRFSimulator-4.0.jar!/com/rory/services/pcrf/simulator/handlers/CacheHandler.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.rory.services.pcrf.simulator.handlers.CacheHandler]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.rory.services.pcrf.simulator.handlers.CacheHandler.<init>()
Any help is much appreciated!
You should either define your beans in xml or annotate them, not both (if only to avoid errors like the one you're getting).
The problem here is that you're not autowiring constructor args, so spring doesn't know what to do with your controller. It knows it has to create a bean (#Controller annotation), but it doesn't know how (no default, nor autowired constructor).
You can try to do something like:
#Controller
public class CacheHandler {
private final CustomGxSessionIdCacheImpl gxSessionIdCache;
#Autowired
public CacheHandler(CustomGxSessionIdCacheImpl gxSessionIdCache) {
this.gxSessionIdCache = gxSessionIdCache;
}
and then in xml:
<bean id="gxSessionIdCache"
factory-bean="PcrfSimulator"
factory-method="getGxSessionIdCache"/>
So it will autowire constructor parameters.
Another option is to simply create default constructor and autowire gxSessionIdCache property.
You have to add an empty default constructor :
#Controller
public class CacheHandler {
private final CustomGxSessionIdCacheImpl gxSessionIdCache;
#Autowired
public CacheHandler(CustomGxSessionIdCacheImpl gxSessionIdCache) {
this.gxSessionIdCache = gxSessionIdCache;
}
But be carefull, because it seems that you are mixing annotation based configuration (#Controller) and XML configuration. In the example above, it uses the annotation based config (so please remove the bean declaration from your XML file).
You can also get this error if you haven't activated Spring's annotation-based config. Include this in your Spring Xml:
<context:annotation-config/>
Other posters have pointed out that you can get problems if you mix autowiring/component-scanning with explicit instantiation of beans. I had a similar problem with a web application that did that. I was able to fix the problem by telling the component-scanner not to automatically instantiate a bean of the crucial class. Like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=...>
<aop:aspectj-autoproxy />
<import resource="repository.xml" />
...
<context:component-scan base-package="com.example.webserver">
<context:exclude-filter type="regex" expression="MyRepositoryImpl" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" />
</context:component-scan>
</beans>
where repository.xml included the explicit bean instantiation:
<beans xmlns=...>
<bean id="password" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/comp/env/store/clientPassword" />
</bean>
<bean id="repository" class="com.example.webserver.datalayer.MyRepositoryImpl">
<constructor-arg ref="password" />
</bean>
...
</beans>

Categories

Resources