Spring & Servlet 3.0 Java Configuration Mess - java

Context:
I switched from XML based to Java based Spring configuration. My application has a JSP based web layer, Spring MVC, Spring Security and Hibernate as persistence provider.
I managed to separate the whole XML configuration in different config classes:
WebConfig - for the Spring MVC configurations;
PersistenceConfig - as the name states - for the JPA configuration;
ServiceConfig - only for the #Service and #Component annotated classes;
SecurityConfig - for the Spring Security Configuration.
For application initialization I have the SecurityInitializer and WebAppInitializer classes.
Here is some code:
WebConfig
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.demo.app.web"})
public class WebConfig extends WebMvcConfigurerAdapter { /* Bean initialization */ }
PersistenceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.dao"})
#EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = true)
public class PersistenceConfig { /* Bean initialization */ }
ServiceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.service", "com.demo.app.component"})
public class ServiceConfig { /* Bean initialization */ }
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { /* Bean initialization */ }
SecurityInitializer
#Order(1)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
WebAppInitializer
#Order(2)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {SecurityConfig.class, PersistenceConfig.class,
ServiceConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
And having to test the whole thing I have:
TestContext - abstract class that I think it sets up the basic context;
TestWebContext - extends TestContext and adds the WebCOnfig context. It's extended by all Controller tests;
DaoTest - extends TestContext and adds transaction management. It's extended by all DAO tests;
TestContext
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {PersistenceConfig.class, ServiceConfig.class, SecurityConfig.class})
public abstract class TestContext {
}
TestWebContext
#ContextConfiguration(classes = {WebConfig.class})
#WebAppConfiguration
public abstract class TestWebContext extends TestContext {
}
DaoTest
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional
public abstract class DaoTest extends TestContext {
}
Questions:
When should I put the WebConfig.class in the getServletConfigClasses() or in the getRootConfigClasses() or both? What is the difference?
Does it matter what is the order of the classes that are present in the getRootConfigClasses() and getServletConfigClasses() methods? I've seen somewhere that order matters for the initializers and people put #Order at them but what about the Config classes?
For the TestWebContext class I know that just adding #ContextConfiguration(classes = {WebConfig.class}) overrides the #ContextConfiguration from the base class but how can I achieve the context extension?
If I add another configuration class say CoreConfig (I had one). Then load spring application context from XML in it and add it to the classes in getRootConfigClasses():
Note: no duplicate beans with Config classes are present in the applicationContext.xml.
CoreConfig
#Configuration
#EnableScheduling
#ImportResource("classpath:applicationContext.xml")
public class CoreConfig { // No duplicate Beans load }
Which beans are loaded at first? The ones in applicationContext.xml or the ones from the Config classes?
Any other tips from practice that worked for you about the Java configuration are also highly appreciated!

The WebConfig is responsible for the servlet related beans and therefore loaded in the servlet context. Other beans that could be potentially shared from other servlets in the same application can go in the root context. A good start for me to learn about the difference between the contexts was What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
Practically it does not matter. Changing the order does not make any difference in my applications. I could not find any theoretical proof documented somewhere though
Have a look at
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/ContextHierarchy.html which contains code examples on how to "merge" the context configurations between parent-children
It shouldn't really matter since spring first loads all bean definitions and only afterwards it instantiates them by performing the dependency-injection in an order where beans that are properties or constructor arguments of other beans are initialized first. In cases where a bean depends to another but there is no property or constructor dependency between them for spring to understand the order, you can make use of the "depends-on" attribute
Regarding the usage of the config files I actually use a similar approach with yours. A change that you may find useful could be to only have a RootConfig.class loaded from the getRootConfigClasses() , and that RootConfig may import the SecurityConfig.class, PersistenceConfig.class and ServiceConfig.class along with any other functionality it may have. For example in my case it also loads an application.properties file using #PropertySource("classpath:application.properties") annotation and containing a PropertySourcesPlaceholderConfigurer bean

Related

Spring #ConditionalOnBean is not finding the the bean

I am using spring boot: 2.5.7
I have a starter in my application that is not working.
I opened the code in order to understand the reason and notice a weird behavior:
The starter code is:
AutoConfiguration:
#Configuration
#EnableConfigurationProperties(MyConfigProperties.class)
public class MyCompanyAutoConfiguration {
#Bean
#ConditionalOnBean(MyConfigProperties.class)
public MeterRegistry defaultNewRelicMeterRegistry(MyConfigProperties config) {
System.out.println("called method");
return MyCompanyNewRelicRegistry.builder(config).build();
}
}
PropertyBean
#Configuration
#ConfigurationProperties(prefix = "company.config.newrelic")
#ConditionalOnProperty(prefix = "company.config.newrelic", name = "apiKey")
#ConditionalOnClass(NewRelicRegistryConfig.class)
public class MyConfigProperties implements NewRelicRegistryConfig {
// getters and setters
...
}
Factory file (spring.factories)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.company.MyCompanyAutoConfiguration
When I start my application, MyCompanyNewRelicRegistry bean is not been created.
I find out that "ConditionalOnBean" is the problem, the code does not print "called method" when use this annotation. It is weird because MyConfigProperties bean is initialized (I saw calling the applicationContext.getBean after container have started).
When I remove the annotation #ConditionalOnBean, everything works well and the method is called.
Why ConditionalOnBean is not finding MyConfigProperties that time?
Note: all properties and classes used in other conditions are ok.
# UPDATE
I changed to:
#Configuration
#EnableConfigurationProperties
#ComponentScan
public class MyCompanyAutoConfiguration {
#Bean
#ConditionalOnBean(MyConfigProperties.class)
public MeterRegistry defaultNewRelicMeterRegistry(MyConfigProperties config) {
System.out.println("called method");
return MyCompanyNewRelicRegistry.builder(config).build();
}
}
The above example works.
I saw in log that spring try to load "defaultNewRelicMeterRegistry" first, when evaluate the conditionalBean, its not created yet.
When I remove the ConditionalOnBean the spring will load the property in order to inject it.
May the componentScan "force" spring to load all other beans before the declared bean in autoconfiguration class, make sense?

Spring Boot #Autowired MessageSource works in one classes and doesn't in another

I'm learning Spring boot framework (version 2.0.7.RELEASE) for developing web application. When I try to autowire MessageSource class it works for one classes, but doesn't for another:
Here is my WebConfig class:
package net.local.mis.phog.config;
#Configuration
public class WebConfig implements WebMvcConfigurer {
[...]
#Bean
public MessageSource messageSource() {
// This is the only place around application where messageSource is created
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
[...]
}
Everything works fine in controller classes, for example:
package net.local.mis.phog.controller;
#Controller
#Transactional
public class AlbumController {
[...]
#Autowired
private MessageSource messageSource; (works fine)
[...]
}
But when I try to inject messagSource in model classes it failes:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
[...]
#Autowired
private MessageSource messageSource; (null)
[...]
}
In spring - component autowired in one class but not in another Balaji Krishnan says: "You are not using spring to obtain Account instance - so spring did not get a chance to autowire it". Perhaps I should do something of the kind, but I cannot understand how.
Can anybody please help me.
Thank you, Mikhail.
For spring to leverage dependency injection, the beans must all be managed by spring. The AlbumModel object creation should be managed by spring, so that the MessageSource can be autowired. If AlbumModel should not be managed by spring and you want to create the object yourself (which I doubt because you annotated it with #Component) then you could also use constructor injection.
Whereas you can have something like this:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
[...]
private MessageSource messageSource;
[...]
#Autowired
public AlbumModel(MessageSource messageSource) {
this.messageSource = messageSource;
}
}
With the solution above, when you manually create an AlbumModel, you could pass in the MessageSource object, which is already autowired by the calling class (for example the controller or any service layer class). But also when AlbumModel creation is manage by spring it is advisable to use constructor injection. Read more about it on an article by a Spring contributor
Thank you guys for your answers.
Amit Bera, I create AlbumModel object just by calling its constructor:
package net.local.mis.phog.controller;
#Controller
#Transactional
public class AlbumController {
[...]
#Autowired
private JenreDao jenreDao;
#Autowired
private MessageSource messageSource;
[...]
#RequestMapping(value={"/album"},method=RequestMethod.GET)
public String albumShowHandler(Model model,
HttpServletRequest request,
#RequestParam(value="jenreIdSelected") Long jenreIdSelected,
#CookieValue(value="thumbnailOrder",defaultValue="ordDefault") String thumbnailOrder,
[...]) {
[...]
ThumbnailOrderAux thumbnailOrderAux = new ThumbnailOrderAux(thumbnailOrder);
JenreAux jenreAux = new JenreAux(jenreDao.getLstJenreModel(null,jenreIdSelected),jenreIdSelected);
AlbumModel albumModel = new AlbumModel(jenreAux,thumbnailOrderAux);
[...]
model.addAttribute("albumModel",albumModel);
return "album";
}
[...]
}
And here is code of AdminModel constructors:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
private JenreAux jenreAux;
private ThumbnailAux thumbnailAux;
[...]
public AlbumModel() {
super();
}
public AlbumModel(JenreAux jenreAux,ThumbnailOrderAux thumbnailOrderAux) {
super();
this.jenreAux = jenreAux;
this.thumbnailOrderAux = thumbnailOrderAux;
}
[...]
}
Nazeem, I added #Component annotation to AlbumModel class in hope that MessageSource bean would be injected. Unfortunately it was not.
I could pass MessageSource as argument and it works even if I don't annotate constructor #Autowired. If I understand it right MessageSource bean is injected to AlbumController because it managed by Spring, and it isn't injected to AlbumModel because it doesn't managed by Spring. Right? If so, is there any way I make Spring manage AlbumModel object?
I had the same problem on a spring 3.1.1-RELEASE old application. In my case during debug I've got two different MessageResource objects as if the wold have been instanziated twice.
I've solved moving the bean definition:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
from dispatcher-servlet.xml xml-based configuration file to the applicationContext.xml ones.
In other words, there exists two contexts: the application context and the dispatcher servlet contexts. The application context is the parent of the dispatcher servlet context (I guess that many servlet contexts may exists under the same application context if your application consists of many servlet).
Thus, beans defined in the application context are visible to the servlet context, but not vice versa. While the servlet context contains those beans that are specifically related to MVC, the application context is used to define broader beans like services.
Since #Controller annotation is a #Component annotated ones:
#Component
public #interface Controller
i guess Spring autowires beans in #Controller and #Component differently, looking for beans in the two different contexts mentioned above.

Spring boot 2.1 bean override vs. Primary

With Spring Boot 2.1 bean overriding is disabled by default, which is a good thing.
However I do have some tests where I replace beans with mocked instances using Mockito. With the default setting Tests with such a configuration will fail due to bean overriding.
The only way I found worked, was to enable bean overriding through application properties:
spring.main.allow-bean-definition-overriding=true
However I would really like to ensure minimal bean definition setup for my test configuration, which would be pointed out by spring with the overriding disabled.
The beans that I am overriding are either
Defined in another configuration that imported into my test configuration
Auto-discovered bean by annotation scanning
What I was thinking should work in the test configuration overriding the bean and slap a #Primary on it, as we are used to for data source configurations. This however has no effect and got me wondering: Is the #Primary and the disabled bean overriding contradictory?
Some example:
package com.stackoverflow.foo;
#Service
public class AService {
}
package com.stackoverflow.foo;
public class BService {
}
package com.stackoverflow.foo;
#Configuration
public BaseConfiguration {
#Bean
#Lazy
public BService bService() {
return new BService();
}
}
package com.stackoverflow.bar;
#Configuration
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean
public BService bService() {
return Mockito.mock(BService.class);
}
}
spring.main.allow-bean-definition-overriding=true can be placed in test configurations. If you need extensive integration testing, you will need to override beans at some point. It's inevitable.
Though the correct answer has already been provided, it implies that your bean will have different names. So, technically, it's not an override.
If you need a real override (because you use #Qualifiers, #Resources or something similar), since Spring Boot 2.X is only possible using the spring.main.allow-bean-definition-overriding=true property.
Update:
Be careful with Kotlin Bean Definition DSL. In Spring Boot it will require a custom ApplicationContextInitializer, like so:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans.initialize(context)
}
Now if you decide to override one of such DSL-based beans in your test via #Primary #Bean method, it will not do. The initializer will kick in after #Bean methods and you'd still get the initial, DSL-based bean in your tests even with #Primary on the test #Bean.
One other option would be to also create a test initializer for your tests and list them all in your test properties, like so(order matters):
context:
initializer:
classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
Bean Definition DSL also supports primary property via:
bean(isPrimary=true) {...}
- which you'll need to eliminate ambiguity when you try to inject a bean, however main:allow-bean-definition-overriding: true is not needed if you go pure DSL way.
(Spring Boot 2.1.3)
Overriding beans means that there may be only one bean with a unique name or id in the context. So you can provide two beans in the following way:
package com.stackoverflow.foo;
#Configuration
public class BaseConfiguration {
#Bean
#Lazy
public BService bService1() {
return new BService();
}
}
package com.stackoverflow.bar;
#Configuration
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean
public BService bService2() {
return Mockito.mock(BService.class);
}
}
If you add #Primary then primary bean will be injected by default in:
#Autowired
BService bService;
I make the testing beans available only in test profile, and allow overriding for just while testing, like this:
#ActiveProfiles("test")
#SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"})
class FooBarApplicationTests {
#Test
void contextLoads() {}
}
The bean I am mocking in the test configuration:
#Profile("test")
#Configuration
public class FooBarApplicationTestConfiguration {
#Bean
#Primary
public SomeBean someBean() {
return Mockito.mock(SomeBean.class);
}
}
It is allowed to override #Component with #Bean by default. In your case
#Service
public class AService {
}
#Component
public class BService {
#Autowired
public BService() { ... }
}
#Configuration
#ComponentScan
public BaseConfiguration {
}
#Configuration
// WARNING! Doesn't work with #SpringBootTest annotation
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean // you allowed to override #Component with #Bean.
public BService bService() {
return Mockito.mock(BService.class);
}
}

How to restrict inner test #Configuration class to work only for this class?

Base test class for integration testing imports base configuration with component scan that includes almost all packages. In one test class I want to override some beans with Mocs, but this inner configuration is scanned and overrides beans for all tests. Is there some way to avoid this?
I've found the way I like mocking beans with by essentially having a separate MockObjectsConfig class with the mock objects I want using the standard Spring Context Configuration, and then import it alongside my real test config. You can also annotate your mock bean with #Profile and test with #ActiveProfiles if you need to prevent a conflict there.
#Configuration
#Profile("!test")
public class MyRealConfigClass {
#Bean
public YetAnotherClass yetAnotherClass() {
return new YetAnotherClass();
}
}
#Configuration
#Profile("test")
public class MockObjectsConfig {
#Bean
public YetAnotherClass yetAnotherClass() {
Mockito.mock(YetAnotherClass.class); // and add any thenReturns, answers, etc. here
}
}
Then include it in your test like so:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { MyRealConfigClass.class, MockObjectsConfig.class)
#ActiveProfiles({"test"})
public class MyJunitTest {
#Autowired
private RestController restController;
}
Then your mock bean will be used and not the real one from the production config.

How to enforce loading order of spring configuration classes?

I'm working with spring-boot on a multi module project (maven). Each module has it's own #Configuration class. Basically I do have the following layout
Module foo-embedded (runs just calls the SpringApplication.run()) method:
#Configuration
#EnableAutoConfiguration
#ComponentScan("de.foobar.rootpackage")
#Import({ApplicationConfig.class, RepositoryConfig.class, SecurityConfig.class})
public class FooApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FooApplication.class, args);
}
}
Module foo-common (contains all beans and spring-data-jpa initialization config)
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement(entityManagerFactoryRef="entityManagerFactory")
public class RepositoryConfig {
#Bean(destroyMethod = "shutdown")
public DataSource getDataSource() {
// returning a Hikari CP here
}
#Bean(name = "entityManagerFactory") // overriding spring boots default
public EntityManagerFactory getEntityManagerFactory() {
// returning a new LocalEntityManagerFactoryBean here
}
}
Module foo-security (containing spring-securiy configuration and related domain classes), which has a maven dependency on foo-common
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// configuring HTTP security and defining my UserDetailsService Bean
}
When I start the application using the FooApplication class, everything works as expected. The above mentioned UserDetailsServiceImpl get's autowired with my UserRepository which is being created through the #EnableJpaRepositories annotation.
Since I want to write some integration tests I've added a test clss to one of my modules.
Module foo-media (containing some domain related stuff plus test cases for that module)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {RepositoryConfig.class, SecurityConfig.class})
#WebAppConfiguration
#IntegrationTest
public class DirectoryIntegrationTest {
// my test code
}
When I run the test it seems that the SecurityConfiguration get's loaded before the RepositoryConfig.class does. Since the security config defined the UserServiceImpl which must be autowired, the test fails to start with a
NoSuchBeanDefinitionException telling me: No qualifying bean of type [com.foo.rootpackage.security.repository.UserRepository]
I already tried to add #DependsOn("UserRepository") at the bean definition of UserDetailsService, telling me that spring can't find a bean by that name.
Any hints or help would be greatly appreciated! Thanks in advance!
---- EDIT (since I was asked to provide more code) ----
For testing I do not use the actual RepositoryConfig.class, but have a TestRepositoryConfig.class in the common module. Looking like this
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = "de.foobar.rootpackage")
public class TestRepositoryConfig extends RepositoryConfig {
#Bean
#Override
public DataSource getDataSource() {
// returning the ds for testing
}
}
You can use #Order annotation on your configuration classes to define load ordering. But it's strange because Spring should resolve proper order - so please check if you property inject UserRepository in UserDetailsService
So I was able to solve this. As it pointed out it had nothing to do with the loading order of the configuration classes (which was my first thought).
As you may notice, the only Configuration that had a #ComponentScan annotation was the FooApplication.class
Spring was not able to find the Repositories, as it didn't know where to look for. Providing the basePackages attribute like this:
#EnableJpaRepositories(basePackages = "de.foobar.rootpackage")
at the TestRepositoryConfig.class did the trick here.

Categories