Import by name in Spring Java configs - spring

Say I have 3 Spring/Maven projects:
api-spec: Contains interface MyService.
api-impl: Contains class MyServiceImpl which implements MyService. Also contains class MyServiceConfiguration which is a Spring #Configuration, that defines a bean of type MyServiceImpl.
main: Contains a Spring application setup with Spring JavaConfig (e.g. a #SpringBootApplication). It has a bean with an #Autowired MyService myService field, which works as its configuration class is annotated with #Import(MyServiceConfiguration.class).
I would like the main-project to have api-spec as a Maven compile dependency and to have api-impl as a runtime dependency (to prevent us from making "hard" dependencies from the main project to the api-impl project by mistake). This is not possible, because #Import takes an array of Classes - e.g.: #Import(MyServiceConfiguration.class). I would like something like #Import("my.package.MyServiceConfiguration") instead.
Using class path scanning is not an option (we have seen too many beans getting picked up by accident), and I would prefer not having to use XML files. We could use SpringApplicationBuilder.source(..) as it accepts a class name as a String - but I can't find a way to use that in my tests...

Compile time check is one of the advantages of java config, so I don't think that it's possible to do such thinks with Java. As for me you should use XML to handle this. It doesn't mean that you should do all your configuration in XML, most of the beans of your api-impl module can be in Java and just imported to XML where will be only beans that you are going to change in runtime.
If you don't want to use XML maybe you should consider to use Groovy config instead:
https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4

Related

Mock Bean for all JUnit Tests in Spring Boot

In Spring Boot, is there a way to mock a single bean for all existing JUnit tests, without changing the existing test classes (e.g., by adding an annotation or adding inheritance)? Like injecting a bean globally via configuration.
Assuming you are using #SpringBootApplication in your main sources to define the Spring Boot application, you'll already have component scanning enabled for everything in that package (including nested packages).
When running tests, the classes (typically) in src/test/java are also added to the classpath, and are therefore available to be scanned as well.
For example, if you defined your #SpringBootApplication at com.example.boot.MySpringBootApplication, then com.example.boot.MyTestConfiguration would be eligible for component scanning, even though the former is in src/main and the latter in src/test. Putting it in the src/test/java directory would ensure that it only has an effect while running tests.
You can then define any "global" beans you would like in that configuration.
Using the package/class names I provided:
// File: src/test/java/com/example/boot/MyTestConfiguration.java
#Configuration // this will get component-scanned
public class MyTestConfiguration {
#MockBean
MyBean myGlobalMockBean;
}
Then, so long as you don't omit that Configuration from the Context Configuration, the MockBean should always be present under test.

Conflict with config class in dependency (Spring AsyncConfig)

My team uses some starter code that's included in every internal Spring project by default. This is included as a dependency (not parent) in the pom.xml file of my project. This starter code contains a default implementation of a configuration interface which Spring does not allow duplicates of (AsyncConfigurer), but I need to create my own custom implementation. I am not sure how to resolve this. Is there a way for me to exclude this configuration class but keep the rest of the dependency? Or can I somehow keep the given config class, but modify its properties?
The specific exception I get is: java.lang.IllegalStateException: Only one AsyncConfigurer may exist
//Thank you
You can define the AsyncConfigurer on parameter excludes from #SpringBootApplication. Example:
#SpringBootApplication(exclude=AsyncConfigurer.class)
Notice that this will excludes the entire #Configuration .
Another solution is to use bean override, to define only the beans you need to replace.
Add this to your application.properties (or yaml):
spring.main.allow-bean-definition-overriding=true
And you will be able to override it.

Spring AOP aspect doesn't get applied if included from an external jar with different package name

I have a spring boot rest service that included an external project in pom as it's dependency. That external project is basically a jar that has spring AOP code.
The base package in my main application that includes this external jar with spring AOP code is x.y.z
The class in external jar where the #before advice is, is under the package a.b.c
With this class under a.b.c package, it doesn't get recognized by the main application where I want to use the spring aop implementation and apply the aspect. However, when I change it's package from a.b.c to x.y.z (which I really can't do in real life) it works fine.
I know that in spring boot service which happens to be the including service, it scans everything under root package given in the application class, x.y.z in this case and that is why aspect works fine if it's class is under x.y.z.
however, the problem is that this spring app jar will be used across multiple applications. So changing package name like this is not an option.
Is there a way to accomplish this without changing the package name of the class where spring app code is ?
Probably component scan is only activated for your application class packages by default. You can extend it to multiple packages, including the aspect package:
XML style configuration:
<context:component-scan base-package="x.y.z, a.b.c" />
Annotation style configuration:
#ComponentScan(basePackages = {"x.y.z", "a.b.c"})
Disclaimer: I am not a Spring user, only an AspectJ expert. I just knew that you can configure component scan, googled the syntax for you and hope it is correct.
Please define the bean (of jar project )inside main application. Give the #ComponentScan(basePackages = {"x.y.z", "a.b.c"}) as well as #EnableAspectJAutoProxy. Also include below piece of code.
ex:
` #Bean
public LoggingHandler loggingHandler()
{
return new LoggingHandler();
}`
Also annotate external jar code with:
`#Aspect
#Component
public class LoggingHandler {`
What #kriegaex suggests is correct. In addition to that, please make sure you are using #Component along with #Aspect. Since #Aspect is not a Spring annotation, Spring won't recognize it and hence your aspect won't be registered. So, using #Component is mandatory to getting aspects to work in Spring environment.

Test spring classes in separate gradle subproject

My project is separated into several gradle subprojects (modules). I have a module which contains several spring components/beans. I want to test these beans using junit, mockito and springboottest with features like autowired and mockbean. I am using
#RunWith(SpringJUnit4ClassRunner::class)
#SpringBootTest
annotations, but when I try to run a test I get
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
This happens because there is no main class (#SpringBootApplication) in this module.
One can avoid this by creating a mock main class like
#SpringBootApplication
class TestApp {
}
Is there a way to make it work without creating a mock main class?
If you want to run test in sub-module you need to define some configuration class. It can be #Configuration with #ComponentScan located in src/test/java root package of sub-module, so that it wouldn't pollute your production code.
With such test configuration, just use #SpringBootTest(classes=YourTestConfiguration.class).
Maybe you want to look at new annotation since Spring Boot 1.4.x called #TestConfiguration. That one is specifically tailored towards test only configs.

Spring-data-mongo: MongoRepository not being wired unless I add #Component annotation

I am having a little weird behavior with my spring-data-mongo where my repository package is not being scanned by the <mongo:repositories/> tag. I am using spring 3.2.3.RELEASE with spring-data-mongo 1.2.1.RELEASE.
I have a project called edowmis and in it there are 2 maven modules, datalayer and web which a webapp.I am using the datalayer in isolation so the other module can be ignored. I have an application context for datalayer
So I wanted to test my setup by writing a small Unit/Integration test but I've noticed I can't autowire my UserRepository because It says there isn't such a bean
Since I am using IntelliJ I can see certain visuals when things are ok and not ok. I've addec <context:component-scan/> to my application context but no result.
But when I add the #Component annotation it has started identifying the Class.
all information you might need is on pastie.org
Is the #component or #Repository really necessary or something is wrong with my configuration?
Yes, the #Component or #Repository is necessary. The scan simply indicates that spring should look for classes identified via annotations (#Component, #Repository, #Service) and load them as beans. If you don't use repository or component scan, you would have to manually instantiate all spring-managed beans via XML configuration or Java configuration.
You have to tell spring which classes to turn into beans as it doesn't assume everything in the classpath is supposed to be a spring-managed bean, which is why you need to use the annotations.

Resources