When to use #EnableHypermediaSupport? - spring

According to the Spring HATEOAS API,
Activates hypermedia support in the ApplicationContext. Will register infrastructure beans to support all appropriate web stacks based on selected HypermediaMappingInformation-type as well as the classpath.
My Spring Boot application (2.3.4) has the following dependencies:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
My SpringBootApplication class:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
// run this application as a Spring application
SpringApplication.run(MyApplication.class, args);
}
}
I've noticed my repositories are returning HALresponses via Spring Data REST without annotating my application class with #EnableHypermediSupport. When is this annotation needed? Do I not need it since I'm using Spring Data REST?

Spring Boot already auto configures for you if your dependencies contains spring-hateoas. The dependency spring-boot-starter-data-rest contains spring-hateoas.
File org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration has #EnableHypermediaSupport(type = HypermediaType.HAL).
/**
* {#link EnableAutoConfiguration Auto-configuration} for Spring HATEOAS's
* {#link EnableHypermediaSupport #EnableHypermediaSupport}.
*
* #author Roy Clarkson
* #author Oliver Gierke
* #author Andy Wilkinson
* #since 1.1.0
*/
#Configuration(proxyBeanMethods = false)
#ConditionalOnClass({ EntityModel.class, RequestMapping.class, RequestMappingHandlerAdapter.class, Plugin.class })
#ConditionalOnWebApplication
#AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
#EnableConfigurationProperties(HateoasProperties.class)
#Import(HypermediaHttpMessageConverterConfiguration.class)
public class HypermediaAutoConfiguration {
#Configuration(proxyBeanMethods = false)
#ConditionalOnMissingBean(LinkDiscoverers.class)
#ConditionalOnClass(ObjectMapper.class)
#EnableHypermediaSupport(type = HypermediaType.HAL)
protected static class HypermediaConfiguration {
}
}
Of course, you can override the default configuration.
#Configuration
#EnableHypermediaSupport(…)
class MyHypermediaConfiguration { … }

Related

Spring boot: #ConfigurationProperties not satisfied on test

I'm getting this exception on my test excution:
UnsatisfiedDependencyException: Error creating bean with name 'net.gencat.transversal.espaidoc.mongo.GridFSTest': Unsatisfied dependency expressed through field 'resourceProperties'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.gencat.transversal.espaidoc.ResourcesConfigProperties' available: expected at least 1 bean which qualifies as autowire candidate.
So, I think message is so clear enough: ResourcesConfigProperties is not satisfied.
My test:
RunWith(SpringRunner.class)
#SpringBootTest()
public class GridFSTest {
#Autowired
private GridFsTemplate gridFsTemplate;
#Autowired
private ResourcesConfigProperties resourceProperties;
public URL getHugeResource() {
try {
return Paths
.get(this.resourceProperties.getHuge())
.toUri()
.toURL();
} catch (MalformedURLException e) {
return null;
}
}
#Test
public void storeHugeFile() throws IOException {
URL resource = this.getHugeResource();
this.gridFsTemplate.store(
resource.openStream(),
resource.getPath(),
"mime"
);
}
}
and ResourcesConfigProperties is:
#ConfigurationProperties(prefix = "files")
public class ResourcesConfigProperties {
private String huge;
/**
* #return the huge
*/
public String getHuge() {
return huge;
}
/**
* #param huge the huge to set
*/
public void setHuge(String huge) {
this.huge = huge;
}
}
into my src/test/resources I have my application.properties file:
files.huge: /home/jcabre/Downloads/1GB.zip
Any ideas?
EDIT
Main Spring boot application:
#SpringBootApplication(
//scanBasePackages = { "cat.gencat.ctti.canigo.arch.web.rs" },
exclude = JmxAutoConfiguration.class
)
#EnableConfigurationProperties({
ApiProperties.class,
FileStoreProperties.class
})
#Import(RedisConfiguration.class)
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
TL;DR:
It is happening, because the #ConfigurationProperties are not managed by the application context you build in tests, although they will be load when the application launches, because you have #EnableConfigurationProperties on your app main class.
#EnableConfigurationProperties on main class only affect the application context you have when you bootRun, but not that in a #SpringBootTest. The application context you build in tests could be, under many circumstances, distinct with that of bootRun, just like in your case.
You can add #Component to make the context be aware of it, both in gradle bootRun and in gradle test. It's the easiest way, but not 100% the suggested way.
More proper way with details if you have time
Instead, you can add #EnableConfigurationProperties({Config1.class, Config2.class}) in a #SpringBootTest, to inject only some of the configuration properties class into the context, to avoid injection overhead.
It would be like:
//#RunWith(SpringRunner.class) // for JUnit4 in Spring
#ExtendWith(SpringExtension.class) // for JUnit5 in Spring.
#SpringBootTest
#EnableConfigurationProperties({
ResourcesConfigProperties.class,
})
#Data
public class ConfigsTest {
#Autowired
private ResourcesConfigProperties resourceConfigProperties;
...
}
Better yet, you can use #SpringBootTest(classes={}): classes within {} are those you want the application context of #SpringBootTest to manage(creation, initialization, loading properties from yaml files, and so on). Then you don't have to load all the context, but only part of it.
You can group all classes of #ConfigurationProperties in one class of #Configuration, and put it in the classes={} of #SpringBootTest, instead of repeating this list of #ConfigurationProperties everywhere. Something like:
//#RunWith(SpringRunner.class) // for JUnit4 in Spring
#ExtendWith(SpringExtension.class) // for JUnit5 in Spring.
#SpringBootTest(classes = {
TestConfiguration.class
})
#Data
public class ConfigsTest {
#Autowired
private ResourcesConfigProperties resourceConfigProperties;
...
}
TestConfiguration.java:
#EnableConfigurationProperties({
ResourcesConfigProperties.class,
})
#Configuration
public class TestConfiguration {
}
You need to add ResourcesConfigProperties to your EnableConfigurationProperties annotation in the main spring boot class, this will load and create a bean out of the ResourceConfigProperties for you
You could also add #Component to your ResourceConfigProperties if you do not want to add it to the EnableConfigurationProperties annotation.
When using the SpringBootTest or any slice test it will use whatever is annotated on, or beans defined within the main SpringBootApplication within the test context.
You also need to annotate ResourcesConfigProperties class with #Configuration as below, otherwise it will not create a bean of this class in the spring container.
#Configuration
#ConfigurationProperties(prefix = "files")
public class ResourcesConfigProperties {
private String huge;
/**
* #return the huge
*/
public String getHuge() {
return huge;
}
/**
* #param huge the huge to set
*/
public void setHuge(String huge) {
this.huge = huge;
}
}

Execute CommandLineRunner outside #SpringBootApplication

This is based on https://spring.io/guides/gs/accessing-data-jpa/
I tried to move demo() in a different class in a different package (Application still on top of the filesystem hierarchy)
How do I make demo() run when i boot the project?
Application.java
package com.company.app
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
CommandLineRunner.java
package com.company.app.runner
public class Test {
#Bean
public CommandLineRunner demo() {
System.out.print("Run 1");
return (args) -> {
System.out.print("Run 2");
};
}
}
Add #Configuration to the Test class so that it is picked up when the classpath is scanned.
I haven't seen a Lambda for the CommandLineRunner before. Very nifty and saves having to create a class that specifically implements the CommandLineRunner.
Otherwise, you could implement CommandLineRunner to test and annotate as #Component.
#Component
public class ApplicationLoader implements CommandLineRunner {
#Override
public void run(String... strings) throws Exception {
System.out.print("Run 2");
}
}
* Update *
Answering the question in the comment with this update as I have more room to type...
#SpringBootApplication composes those other annotations as you indicated but those annotations are only applied to the specific class that it is defined on. The #Configuration annotation is telling Spring that the class defines beans that should be managed by the application context. The #ComponentScan tells spring to look through the classpath for classes that have specific annotations (e.g. #Component, #Service, #Configuration) and then act on those classes based on the type of annotation. the #EnableAutoConfiguration is the magic that loads appropriate beans based on the project dependencies (e.g. if mongo driver is on the classpath then create a MongoTemplate).

Configuring Spring to ignore dependencies annotated with #Inject

I have an EJB class in which I need to inject two beans - one should be injected by the EJB container and other is a Spring Container.
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#LocalBean
public class SomeClass {
#Inject
private EJBClass a;
#Autowired
private SpringComponent b;
}
Here, the Spring interceptor trying to intercept the injection of bean 'a' and it's getting failed. I want the EJB container to inject the bean 'a' and Spring container to inject bean 'b'.
Please show me a way out here.
By customizing the SpringBeanAutowiringInterceptor class, dependencies annotated with #Inject can be excluded from auto wiring.
To understand what happens behind the scene, have a look at source code of
SpringBeanAutowiringInterceptor.java -
/**
* Actually autowire the target bean after construction/passivation.
* #param target the target bean to autowire
*/
protected void doAutowireBean(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
configureBeanPostProcessor(bpp, target);
bpp.setBeanFactory(getBeanFactory(target));
bpp.processInjection(target);
}
At first line, of doAutowireBean, a new instance of AutowiredAnnotationBeanPostProcessor is created. Here set of annotations to be scanned for auto wiring dependencies are configured.
/**
* Create a new AutowiredAnnotationBeanPostProcessor
* for Spring's standard {#link Autowired} annotation.
* <p>Also supports JSR-330's {#link javax.inject.Inject} annotation, if available.
*/
#SuppressWarnings("unchecked")
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
Since, by default, the #Inject annotation is configured spring scans respective dependencies marked with #Inject and tries to auto wire them.
To exclude #Inject annotated dependencies write below custom class.
public class CustomSpringBeanAutowiringInterceptor extends SpringBeanAutowiringInterceptor {
/**
* Template method for configuring the
* {#link AutowiredAnnotationBeanPostProcessor} used for autowiring.
* #param processor the AutowiredAnnotationBeanPostProcessor to configure
* #param target the target bean to autowire with this processor
*/
protected void configureBeanPostProcessor(AutowiredAnnotationBeanPostProcessor processor, Object target) {
Set<Class> annotationsToScan = new HashSet<Class>();
annotationsToScan.add(Autowired.class);
annotationsToScan.add(Value.class);
processor.setAutowiredAnnotationTypes(annotationsToScan);
}
}
Here configureBeanPostProcessor hook is utilized to customize the bean post processor so as to include only those annotations which we require to be auto wired.
After applying this custom class as interceptor in code the desired behaviour can be achieved
#Stateless
#Interceptors(CustomSpringBeanAutowiringInterceptor.class)
#LocalBean
public class SomeClass {
#Inject
private EJBClass a;
#Autowired
private SpringComponent b;
}
Let know in comments if you face any issues. Also feel free to optimize the code as deemed fit and excuse any compilations / formatting issues.
Use #EJB annotation to inject EJB

Spring Boot Only Finds Beans in current and sub packages - how do you obtain reuse

Spring boot only find beans within the current packages or sub packages. I am creating several services that reuse beans and packages - how do I do this? Do I need to use the java implementation and forget autoscan?
Tim
You should use #ComponentScan and tell to spring all the packages you wan to scan. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html
You don't need to do that
ApplicationContext ctx = new AnnotationConfigApplicationContext(KafkaMqttBridgeConfig.class);
And instead to implement BeanFactoryAware use
#Autowired private KafkaMqttBridge bridge;
This should works
UPDATE
I have modified some classes and now it works.
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SpringBootTestController.java
#RestController
#RequestMapping("/service")
public class SpringBootTestController {
#Autowired
public SpringBootTestController(SpringBootTest springBootTest) {
springBootTest.fakeMethod();
}
/**
*
* #return the get method that return the service info and statistics
*/
#RequestMapping(method = RequestMethod.GET)
public String getServiceInfo() {
return "";
}
}

Spring Boot application is ignoring java config

I have a fairly simple Spring Boot app I am working on that uses a few Java Config classes. However, it seems that the configuration is not being picked up. I have break points all over, but nothing gets tripped. I even tossed a few RuntimeExceptions just to see if maybe my debugger was on the fritz.
In my main class, I have the standard Spring Boot main:
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As you can see, I tagged it with #ComponentScan and #EnableAutoConfiguration. The Application class lives at the root of the classpath. My understanding of the #ComponentScan annotation is that it will search for all configuration classes beneath it.
In a package one layer down I have all the config classes:
My "Common" configuration
#Configuration
#EnableJpaRepositories("com.codechimp.XXX.repository")
#EnableTransactionManagement
public class AppCommonConfig {
#Inject
private Environment environment;
/* Define common beans here like datasource and such */
}
And my Spring Security configuration
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Inject
private LocalXXXUserDetailsService localXXXUserDetailsService;
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(HttpSecurity)
*/
#Autowired
protected void configure(HttpSecurity http) throws Exception {
// Configure http
}
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configureGlobal(AuthenticationManagerBuilder)
*/
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
// Configure auth
}
}
However, when I run the app, it doesn't seem to call any of the methods in either of these config classes. It's as if they are being completely ignored. As I said, I have tried setting break points and even throwing a RuntimeException right in the beginning of all the methods like so:
if (true)
throw new RuntimeException("Break!");
Admittedly I have not had much experience with using Java Config, but I have been over the docs again and again and I am not seeing the missing piece(s).
I think you need your Application to be a #Configuration.
It's not a great idea to do a #ComponentScan from the default package (I assume that's what you mean by "the root of the classpath"). That would definitely switch some things off, but more seriously it causes a huge scan of all jars on your classpath, which is not a great idea (and can cause the app to fail).
You need to add
#SpringBootApplication
to your Spring Boot main class. From the docs:
/**
* Indicates a {#link Configuration configuration} class that declares one or more
* {#link Bean #Bean} methods and also triggers {#link EnableAutoConfiguration
* auto-configuration} and {#link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {#code #Configuration},
* {#code #EnableAutoConfiguration} and {#code #ComponentScan}.
*
* #author Phillip Webb
* #author Stephane Nicoll
* #since 1.2.0
*/

Resources