Spring and serialization to Json - how to customize globally Jackson without Spring Boot - spring

I'm using clean Spring MVC framework (v5.3.21) without Spring Boot.
I was working with Gson library, which was used by Spring to serialize view models, returned with request methods.
public class Coffee {
String name = "n";
String brand = "b";
}
#RequestMapping(value={"/coffe"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Coffee getCoffee() {
return new Coffee();
}
Recently I added Jackson (v 2.13.3) on the classpath and I've noticed serialization works much different. First of all - in Gson non-private field where serialized by default, now they are not visible at the client side.
I know I can add annotation
#JsonAutoDetect(fieldVisibility = Visibility.NON_PRIVATE)
to all the model classes, or change fields to public (Jackson default visibility for fields is PUBLIC, as far as I found out).
But I would like to change just once, globally, in configuration, without rewriting code of many
I tried many options, but none of them doesn't work without Spring Boot.
Do you know to change this default setting with clean Spring?

You can create ObjectMapper bean which can be global for application
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.NON_PRIVATE));
return mapper;
}

Related

intercept request start and end in Vaadin 14 (Flow) with Spring Boot

I'm using vaadin-spring-boot-starter for integration of Vaadin Framework 14 and Spring Boot.
I would like to override the requestStart and requestEnd methods of the SpringServlet class to do the following things:
put stuff such as the current route / view path and current user ID into the SLF4J MDC in order to include it in each logging statement
log the duration of the request
In Vaadin 8 there was a SpringVaadinServlet class which I could replace by simply annotating my custom subclass with #SpringComponent("vaadinServlet").
This approach no longer works. The vaadin-spring integration contains SpringBootConfiguration which contains a direct call to the SpringServlet constructor:
#Bean
public ServletRegistrationBean<SpringServlet> servletRegistrationBean() {
String mapping = configurationProperties.getUrlMapping();
Map<String, String> initParameters = new HashMap<>();
boolean rootMapping = RootMappedCondition.isRootMapping(mapping);
if (rootMapping) {
mapping = VaadinServletConfiguration.VAADIN_SERVLET_MAPPING;
initParameters.put(Constants.SERVLET_PARAMETER_PUSH_URL,
VaadinMVCWebAppInitializer
.makeContextRelative(mapping.replace("*", "")));
}
ServletRegistrationBean<SpringServlet> registration = new ServletRegistrationBean<>(
new SpringServlet(context, rootMapping), mapping); // <-- HERE
registration.setInitParameters(initParameters);
registration.setAsyncSupported(configurationProperties.isAsyncSupported());
registration.setName(
ClassUtils.getShortNameAsProperty(SpringServlet.class));
return registration;
}
They should use a conditional bean here so we could replace it, but unfortunately they're not.
Just adding a custom ServletRegistrationBean with a copy of the above code (but the constructor call substituted with my own) doesn't work, even with #Primary.
So is there a better way to do what I want than to exclude the whole vaadin-spring autoconfiguration and copy everything in my own configuration bean? It works but I have to check if everything's still OK after each vaadin-spring upgrade.
You could add a VaadinServiceInitListener through which you can add a custom request handler. Alternatively you could use a Filter.

java.lang.NullPointerException in Spring Boot [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 6 years ago.
I was able to use RestTemplate and autowire it. However I want to move my rest template related part of code into another class as follows:
public class Bridge {
private final String BASE_URL = "http://localhost:8080/u";
#Autowired
RestTemplate restTemplate;
public void addW() {
Map<String, String> x = new HashMap<String, String>();
W c = restTemplate.getForObject(BASE_URL + "/device/yeni", W.class, x);
System.out.println("Here!");
}
}
And at another class I call it:
...
Bridge wb = new Bridge();
wb.addW();
...
I am new to Spring and Dependency Injection terms. My restTemplate variable is null and throws an exception. What can I do it how to solve it(I don't know is it related to I use new keyword)?
Using Bridge wb = new Bridge() does not work with dependency injection. Your restTemplate is not injected, because wb in not managed by Spring.
You have to make your Bridge a Spring bean itself, e.g. by annotation:
#Service
public class Bridge {
// ...
}
or by bean declaration:
<bean id="bridge" class="Bridge"/>
Just to add further to Jeha's correct answer.
Currently, by doing
Bridge wb = new Bridge();
Means that, that object instance is not "Spring Managed" - I.e. Spring does not know anything about it. So how can it inject a dependency it knows nothing about.
So as Jeha said. Add the #Service annotation or specify it in your application context xml config file (Or if you are using Spring 3 you #Configuration object)
Then when the Spring context starts up, there will be a Singleton (default behavior) instance of the Bridge.class in the BeanFactory. Either inject that into your other Spring-Managed objects, or pull it out manually e.g.
Bridge wb = (Bridge) applicationContext.getBean("bridge"); // Name comes from the default of the class
Now it will have the dependencies wired in.
If you want to use new operator and still all dependency injected, then rather than making this a spring component (by annotating this with #Service), make it a #Configurable class.
This way even object is instantiated by new operator dependencies will be injected.
Few configuration is also required. A detailed explanation and sample project is here.
http://spring-framework-interoperability.blogspot.in/2012/07/spring-managed-components.html

Spring - Retrieve all scanned packages

I'm creating a Spring Starter project and need to get all classes which are marked with a custom annotation. The annotated class is not a spring bean.
My current solution is to use the ClassPathScanningCandidateComponentProvider to find the required classes.
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomAnnotation.class));
candidates = scanner.findCandidateComponents("THE MISSING PACKAGE NAME");
The problem is that I'm currently provide an empty package String so that all packages/classes are scanned which slows the startup down.
I need to access the packages which are scanned by Spring to avoid the scanning of all packages and classes.
Is there a way to retrieve all packages programmatically which are scanned by Spring or is there an alternative solution to retrieve custom annotated classes which are not Spring beans.
Greets
One solution without the need to make a full classpath scan is to use the AutowiredAnnotationBeanPostProcessor:
private List<Class<?>> candidates = new ArrayList<>();
#Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(beanClass.isAnnotationPresent(YourAnnotation.class)){
candiates.add(beanClass));
System.out.println(beanClass);
return new Object();
}
}
#Bean
public CandiateHolder candidates() {
return new CandidateHolder(candidates);
}
You can check if the bean class which should be instantiated has the required annotation. If its the case you add the class to a property to expose it later as a bean. Instead of returning null you have to return an instance of a new Object. The returned object can be used to wrap the class in a proxy. Cause I don't need an instance I will return a simple new object. Its maybe a dirty hack but it works.
I have to use this kind of hack cause an instantiation of the needed object will result in an runtime error cause it has to be instantiated in the framework I use.

Custom nested actuator endpoint

Using Spring Boot 1.0, I was able to customize the actuator endpoints as follows...
endpoints.beans.id=foo/springbeans
This would expose the spring beans endpoint at /foo/springbeans. However, in the latest Spring Boot this is not possible due to the following code in the AbstractEndpoint...
#NotNull
#Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String id;
I tried using the underscore, but that just exposes the endpoint at /foo_springbeans. This lead me to try to add a view controller so I could at least redirect or forward to the default endpoint, but I couldn't find an easy way to do that either. How can I configure the endpoint or a redirect?
After opening an issue with Spring Boot and being told to simply move the entire management context as suggested by Rafal, I was able to achieve what I was looking for, albeit with more code than I'd like. I created a custom MvcEndpoint as follows...
#Named
public class MyCustomHealthCheck extends EndpointMvcAdapter {
private HealthEndpoint delegate;
#Inject
public MyCustomHealthCheck(HealthEndpoint delegate) {
super(delegate);
this.delegate = delegate;
}
#ResponseBody
#RequestMapping(value = "/springbeans", method = GET)
public Health foo() {
return delegate.invoke();
}
}
The code above creates the /springbeans path underwhatever path the HealthEndpoint is mapped to, which is fine enough for my usecase. If I wanted it mapped to an entirely separate path, I would have needed to create a dummy endpoint and stick this MvcEndpoint under that.
For Spring 1.x Following property should help you:
endpoints.beans.path: /foo/springbeans
You can use it with any standard endpoint and if you want to use it with custom endpoint that extends AbstractEndpoint then you need additional annotation:
#ConfigurationProperties(prefix = "endpoints.customEndpoint")
and then use property:
endpoints.customEndpoint.path: /custom/endpoint

How do I add things to the /info endpoint in spring boot programmatically?

How do I add things to the /info endpoint in Spring Boot programmatically? The documentation states that this is possible for the /health endpoint through the use of HealthIndicator interface. Is there something for the /info endpoint as well?
I would like to add operating system name and version and other runtime info there.
In Spring Boot 1.4, you are able to declare InfoContributer beans to make this a whole lot easier:
#Component
public class ExampleInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("key", "value"));
}
}
See http://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/htmlsingle/#production-ready-application-info-custom for more info.
The accepted answer actually clobbers the InfoEndpoint and does not add to it.
One way I found to add to the info is, in a #Configuration class, add an #Autowired method that adds extra properties following the info.* conventions to the environment. Then InfoEndpoint will pick them up when its invoked.
You can do something like the following:
#Autowired
public void setInfoProperties(ConfigurableEnvironment env) {
/* These properties will show up in the Spring Boot Actuator /info endpoint */
Properties props = new Properties();
props.put("info.timeZone", ZoneId.systemDefault().toString());
env.getPropertySources().addFirst(new PropertiesPropertySource("extra-info-props", props));
}
One way to do what you want (in the event that you have totally custom properties you need to display) is to declare a bean of type InfoEndpoint which will override the default.
#Bean
public InfoEndpoint infoEndpoint() {
final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(map);
}
As you can see from the code above, the map can contain whatever info you need.
In the event that the data you want to show can be retrieved by the environment and is not custom, you do not need to override the InfoEndpoint bean, but you can simply add properties to the properties file with a prefix of info. One example where the OS name is evaluated is the following:
info.os = ${os.name}
In the code above, Spring Boot will evaluate the right-hand expression before returning the property in the /info endpoint.
A final note is that there is a ton of environment information that is already available in the /env endpoint
Update
As pointed out by #shabinjo, in newer Spring Boot versions there is no InfoEndpoint constructor that accepts a map.
You can however use the following snippet:
#Bean
public InfoEndpoint infoEndpoint() {
final Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(new MapInfoContributor(map));
}
The code above will completely override the default info that would end-up in /info.
To overcome this issue one could add the following bean
#Bean
public MapInfoContributor mapInfoContributor() {
return new MapInfoContributor(new HashMap<String, Object>() {{
put("test", "value");
}});
}
It should be possible to add a custom PropertySource inside an ApplicationListener to add custom info.* properties to the environment (see this answer for an example: How to Override Spring-boot application.properties programmatically)

Resources