I have a spring mvc 3.0 web application and am trying to implement a generic solution for my initBinder methods. Rather than speciyfying the same initBinder code in all my contorllers for my date picker I am trying to do this (I do not have #Controller advice to help me in this version of spring)
#Component
public class MyPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
WebBindingInitializer wbi = ((RequestMappingHandlerAdapter) bean).getWebBindingInitializer();
if (wbi == null) {
wbi = new ConfigurableWebBindingInitializer();
((RequestMappingHandlerAdapter) bean).setWebBindingInitializer(wbi);
}
if (wbi instanceof ConfigurableWebBindingInitializer) {
((ConfigurableWebBindingInitializer) wbi).setPropertyEditorRegistrar(new MyPropertyEditorRegistrar());
}
}
return bean;
}
public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {
#Value("#{appProperties['date.format']}")
public String dateFormat = null;
/* (non-Javadoc)
* #see org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)
*/
public void registerCustomEditors(PropertyEditorRegistry registry) {
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
df.setLenient(false);
registry.registerCustomEditor(Date.class, new CustomDateEditor(df, true));
}
}
But my BeanPostProcessor doesn't seem to be getting called. Can someone help me understand how and when the BeanPostProcessor gets called?
p.s. I am using mvc:annotationdriven
thanks
Related
I'm using SpringBoot and I am trying to create a service layer for my web application but i cant make it work.
My classes look like this
ServiceFactory
#Service
public class ServiceFactory {
#Autowired
public static EncuestaService getEncuestaService()
{
return new EncuestaServiceImpl();
}
}
EncuestaService
public interface EncuestaService {
void crearEncuesta(Encuesta encuesta, Map<String,String> parametros);
}
EncuestaServiceImpl
#Service
public class EncuestaServiceImpl implements EncuestaService {
#Override
public void crearEncuesta(Encuesta encuesta, Map<String, String> parametros) {
CrearEncuesta nueva = new CrearEncuesta(encuesta,parametros);
nueva.execute();
}
}
CrearEncuesta
#Service
public class CrearEncuesta {
private Encuesta encuesta;
private Map<String,String> parametros;
#Autowired
private RespuestasRepository respuestasRepository;
#Autowired
private EncuestasRepository encuestasRepository;
public CrearEncuesta(Encuesta encuesta, Map<String,String> parametros) {
super();
this.encuesta = encuesta;
this.parametros = parametros;
}
public void execute()
{
encuestasRepository.save(encuesta);
}
}
Everytime I call ServiceFactory.getEncuestasService().crearEncuesta() from any Controller it returns me a NullPointerException.
From what I have been reading I should not be creating a new EncuestsaServiceImpl() in my ServiceFactory but I don't really know the correct way to do so. I would appreciate if anyone could help me out :P.
Edit:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Controller
#Controller
public class EncuestaController {
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
ServiceFactory.getEncuestaService().crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}
You will have to read a little bit more about dependency injection. The central principle in Spring Framework is dependency injection which should be used to avoid referencing beans (service implementations, repository implementations etc...) statically. Spring container also servers as a bean factory that will instantiate and inject (autowire) implementations to beans that need them.
Because Spring will instantiate service interface implementations for you, you don't need ServiceFactory. In your controller you need to add a reference (a field) to EncuestaService and annotate it as Autowired and Spring will wire in the implementation. And then you can just use it in your controller.
#Controller
public class EncuestaController {
#Autowired
EncuestaService encuestaService;
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
encuestaService.crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}
I have the following class:
public static class ARestRequestParam
{
String name;
LocalDate date; // joda type
}
And I want it to be deserialized from the following JSON which is processed by jackson.
{ name:"abc", date:"20131217" }
Actually, I want to deserialize any LocalDate field in any class with "yyyyMMdd" format, without duplicating the format string, without adding any setter method, without any XML configuration. (That is, annotation and Java code is preferable)
How can it be done?
Also, I also want to know the serialization part. that is, LocalDate -> "yyyyMMdd".
I've seen followings:
jackson-datatype-joda (https://github.com/FasterXML/jackson-datatype-joda)
custom serializer (public class JodaDateTimeJsonSerializer extends JsonSerializer { ... } - Spring #ResponseBody Jackson JsonSerializer with JodaTime)
#JsonCreator
#DateTimeFormat
But I don't know which is applicable, and which is most up-to-date.
BTW, I use Spring Boot.
UPDATE
Ok, I have managed to write working code for the deserialization part.
It is as follows:
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters)
{
converters.add(jacksonConverter());
}
#Bean
public MappingJackson2HttpMessageConverter jacksonConverter()
{
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ApiJodaModule());
converter.setObjectMapper(mapper);
return converter;
}
#SuppressWarnings("serial")
private class ApiJodaModule extends SimpleModule
{
public ApiJodaModule()
{
addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
}
}
#SuppressWarnings("serial")
private static class ApiLocalDateDeserializer
extends StdScalarDeserializer<LocalDate>
{
private static DateTimeFormatter formatter =
DateTimeFormat.forPattern("yyyyMMdd");
public ApiLocalDateDeserializer() { super(LocalDate.class); }
#Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
{
String s = jp.getText().trim();
if (s.length() == 0)
return null;
return LocalDate.parse(s, formatter);
}
throw ctxt.wrongTokenException(jp, JsonToken.NOT_AVAILABLE,
"expected JSON Array, String or Number");
}
}
}
I had to implement the deserializer myself, since the datetime format for the deserializer in jackson-datatype-joda cannot be altered. So, since I've implemented the deserializer myself, jackson-datatype-joda is not needed. (although I've copied pieces of its code)
Is this code Ok?
Is this up-to-date solution?
Is there any other easier way?
Any suggestion would be greatly appreciated.
UPDATE
Following Dave Syer's suggestion, I modified the source above as follows:
Removed 2 methods: configureMessageConverters(), jacksonConverter()
Added following method into WebMvcConfiguration class:
#Bean
public Module apiJodaModule()
{
return new ApiJodaModule();
}
But now it does not work. It seems apiJodaModule() is ignored.
How can I make it work?
(It seems that I should not have a class that has #EnableWebMvc to use that feature.)
The version I use is org.springframework.boot:spring-boot-starter-web:0.5.0.M6.
UPDATE
Final working version is as follows: (with other configurations I've done previously in the class that had #EnableWebMvc)
As Dave Syer mentioned, this will only work on BUILD-SNAPSHOT version, at least for now.
#Configuration
public class WebMvcConfiguration
{
#Bean
public WebMvcConfigurerAdapter apiWebMvcConfiguration()
{
return new ApiWebMvcConfiguration();
}
#Bean
public UserInterceptor userInterceptor()
{
return new UserInterceptor();
}
public class ApiWebMvcConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(userInterceptor())
.addPathPatterns("/api/user/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations("/")
.setCachePeriod(0);
}
}
#Bean
public Module apiJodaModule()
{
return new ApiJodaModule();
}
#SuppressWarnings("serial")
private static class ApiJodaModule extends SimpleModule
{
public ApiJodaModule()
{
addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
}
private static final class ApiLocalDateDeserializer
extends StdScalarDeserializer<LocalDate>
{
public ApiLocalDateDeserializer() { super(LocalDate.class); }
#Override
public LocalDate deserialize(JsonParser jp,
DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
{
String s = jp.getText().trim();
if (s.length() == 0)
return null;
return LocalDate.parse(s, localDateFormatter);
}
throw ctxt.mappingException(LocalDate.class);
}
}
private static DateTimeFormatter localDateFormatter =
DateTimeFormat.forPattern("yyyyMMdd");
}
}
Your code is OK, but if you use #EnableWebMvc in a Spring Boot app you switch off the default settings in the framework, so maybe you should avoid that. Also, you now have only one HttpMessageConverter in your MVC handler adapter. If you use a snapshot of Spring Boot you ought to be able to simply define a #Bean of type Module and everything else would be automatic, so I would recommend doing it that way.
I would like to serialize an object with jackson in spring MVC.
I have a controller which returns an ObjectTest1 which has a property ObjectTest2.
public class ObjectTest1{
private ObjectTest2;
// setters getters...
}
public class ObjectTest2{
private String value;
// setters getters...
}
public #ResponseBody ObjectTest1 test() throws IOException ...
I have a mapper and I have a serializer for ObjectTest2 and I've annotated the ObjectTest1.getObjectTest2 method with #JsonSerialize(using = ObjectTest2.class).
It works correctly!
But I want to use this serializer in a lot of Object, not just in ObjectTest1.
What should I do to avoid put annotation every getter method? Can use spring this serializer automatically for all properites which is ObjectTest2?
UPDATED:
I've already use this in my code:
<mvc:annotation-driven>
In ajax response Objects generated correctly as json.
Maybe I should try to explain another way.
So.
I have these objects:
public class DTO{
private InnerThing innerThing;
#JsonSerialize(using=ThingSerializer.class)
public InnerThing getThing(){...}
}
public class InnerThing{
private String value;
}
Generated json looks like:
{"innerThing":{"value":"something"}}
Afther when I've written a serializer, json is:
{"innerThing":"something"}
It is OK, but to get the second version of json I must annotate the getInnerThing method in DTO class with #JsonSerialize...
I don't want to annotate all methods where I use InnerThing as a property.
So my question is, can spring auto serialize every property which type is InnerThing?
By default, Spring will handle serialization and de-serialization of JSON automatically if you add Jackson to the classpath and you use either <mvc:annotation-driven> or #EnableWebMvc.
Links to the Spring Reference Docs:
Spring 3.0: <mvc:annotation-driven>
Spring 3.1: <mvc:annotation-driven> and #EnableWebMvc
You want Jackson to always use your custom JsonSerializer or JsonDeserializer to serialize/deserialize a specific type?
I ended up writing a custom Jackson module to let Jackson find serializers and deserializers that are Spring beans.
I am using Spring 3.1.2 and Jackson 2.0.6
Simplified version:
public class MyObjectMapper extends ObjectMapper {
#Autowired
public MyObjectMapper(ApplicationContext applicationContext) {
SpringComponentModule sm = new SpringComponentModule(applicationContext);
registerModule(sm);
}
}
Module:
public class SpringComponentModule extends Module {
private ApplicationContext applicationContext;
public SpringComponentModule(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override public String getModuleName() {
return "jackson-spring-component";
}
#Override public Version version() {
return SpringComponentModuleVersion.instance.version();
}
#Override
public void setupModule(SetupContext context) {
context.addSerializers(new SpringComponentSerializers(this.applicationContext));
context.addDeserializers(new SpringComponentDeserializers(this.applicationContext));
}
}
ComponentSerializer class:
public class SpringComponentSerializers extends Serializers.Base {
private ApplicationContext applicationContext;
public SpringComponentSerializers(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
Class<?> raw = type.getRawClass();
Map<String,JsonSerializer> beanSet = applicationContext.getBeansOfType(JsonSerializer.class);
for(String beanName : beanSet.keySet()) {
JsonSerializer<?> serializer = beanSet.get(beanName);
if(serializer.handledType().isAssignableFrom(raw)) {
return serializer;
}
}
return null;
}
}
I have problem with Abstract Factory pattern implementation on Spring. I am using Spring 3 MVC and Hibernate 3, which works fine if I don't use Abstract Factory Method pattern.
I am not sure what I have to add to the Controller to have access the Factory Class (CategoryFactory).
Is there anything missing in the Controller or bean initiating ?
class SectionsController extends MultiActionController {
/* Do I have to initiate the CategoryFactory here? */
public ModelAndView secList() throws Exception {
CategoryFactory.CategoryType type = CategoryFactory.CategoryType.valueOf("view");
modelMap.addAttribute("sectionList", CategoryFactory.findCategory(type).list(id));
return new ModelAndView("Form", modelMap);
}
}
Abstract Factory
public abstract class Category {
public abstract List list(int departId);
}
public class CategoryFactory {
public enum CategoryType { firstclass, secondClass, ... }
public static Category findCategory(CategoryType categoryType) {
// Create type specific Category implementation
}
}
It shoud be:
class SectionsController extends MultiActionController {
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public ModelAndView secList() throws Exception {
CategoryFactory.CategoryType type=CategoryFactory.CategoryType.valueOf("view");
modelMap.addAttribute("sectionList",CategoryFactory.findCategory(type).list(hibernateTemplate,id);
return new ModelAndView("Form", modelMap);
}
}
I am trying to learn TDD/BDD using NUnit and Moq.
The design that I have been following passes a DataService class to my controller to provide access to repositories.
I would like to Mock the DataService class to allow testing of the controllers.
There are lots of examples of mocking a repository passed to the controller but I can't work out how to mock a DataService class in this
scenerio.
Could someone please explain how to implement this?
Here's a sample of the relevant code:
[Test]
public void Can_View_A_Single_Page_Of_Lists()
{
var dataService = new Mock<DataService>();
var controller = new ListsController(dataService);
...
}
namespace Services
{
public class DataService
{
private readonly IKeyedRepository<int, FavList> FavListRepository;
private readonly IUnitOfWork unitOfWork;
public FavListService FavLists { get; private set; }
public DataService(IKeyedRepository<int, FavList> FavListRepository,
IUnitOfWork unitOfWork)
{
this.FavListRepository = FavListRepository;
this.unitOfWork = unitOfWork;
FavLists = new FavListService(FavListRepository);
}
public void Commit()
{
unitOfWork.Commit();
}
}
}
namespace MyListsWebsite.Controllers
{
public class ListsController : Controller
{
private readonly DataService dataService;
public ListsController(DataService dataService)
{
this.dataService = dataService;
}
public ActionResult Index()
{
var myLists = dataService.FavLists.All().ToList();
return View(myLists);
}
}
}
Create an interface like this:
public interface DataService
{
FavListService FavLists { get; }
void Commit();
}
Make your DataService implement this interface and your controller should depend on this interface. Problem solved :)
EDIT: This line of code:
dataService.FavLists.All().ToList();
is breaking the law of demeter and will be a pain to unit test your service. Create a method like AllFavList() on your service instead of all these chain of calls, it will be easier to mock.
EDIT2: How to mock you get property
dataService.SetupGet(d => d.FavLists).Returns(your_variable);