Spring boot - scan packages - spring

I am starting to write an application in spring boot and below is how my package structure looks:
com.practice.spring.project.helloworld.HelloworldApplication.java
com.practice.spring.project.repository.EmployeeRepository.java
com.practice.spring.project.model.Employee.java
Below is how i had my application startup successfully,
#SpringBootApplication
#ComponentScan(basePackages = "com.practice.spring.project.DB", basePackageClasses = InitDatabase.class)
#EnableJpaRepositories(basePackages = "com.practice.spring.project.repository" , basePackageClasses = EmployeeRepository.class)
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
#Bean
public CommandLineRunner run(EmployeeRepository employeeRepository) throws Exception {
return (args) -> {
System.out.println("Calling it after the application context is all loaded up");
employeeRepository.save(new Employee("Ashwin", "Architect"));
};
}
}
My question is should I have to specify the base-packages & baseClasses for every class I add ? It would be tough if have 10 packages having 10 different classes.
Am sure there should be an easier way to scan and instantiate classes in different package.

Figured out a way - set the basePackages to com.practice.spring.project.*
#ComponentScan(basePackages = "com.practice.spring.project.*")

Related

How can I exclude a specific #Configuration class from my Spring Boot application?

Spring Boot 2.3.12 (I can't update to a newer version for reasons out of my control).
I have defined my main application class with specific scan base packages like this:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
}
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
What I'm trying to accomplish is both:
A) include a package outside the application's base package (hence the org.otherpackage.ComponentScanMarker.class reference in the #SpringBootApplication annotation)
and
B) exclude the HateoasConfiguration class completely*.
I've also tried this:
#SpringBootApplication
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
That results in HateoasConfiguration being loaded despite the excludeFilters.
Another option I tried:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = HateoasConfiguration.class
)
That results in an exception at startup with the message:
The following classes could not be excluded because they are not auto-configuration classes:
- org.springframework.hateoas.config.HateoasConfiguration
I can't get it to work, no matter what combination of annotation properties I try. Either HateoasConfiguration gets loaded despite the attempt to exclude it, or #Components in org.otherpackage don't get loaded. I've looked at a few different similar questions and answers, but none of them include the need for both goals.
How can I accomplish both needs, to include multiple base packages for component scanning, and exclude a specific #Configuration class that's on the classpath?
* This question really has nothing to do with Spring HATEOAS, it's just an example of a #Configuration class that is on the classpath but I want Spring Boot to ignore. Here are the annotations present on that class (source code here):
#Configuration(proxyBeanMethods = false)
#EnablePluginRegistries({ LinkDiscoverer.class })
public class HateoasConfiguration {
Have you tried this ?
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = { HateoasConfiguration.class }
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
I've been looking for a solution for a similar problem myself. Dropping it here for future developers.
The underlying problem is that #SpringBootApplication performs a full component scan itself, so your own #ComponentScan annotation does not have the desired effect.
The solution for me was to eject #SpringBootApplication and just do what it does internally (with our desired modifications) as it's just a convenience shorthand.
For your case, try:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
})
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}

#Async annotation does not works when war is deployed on JBoss

I have an application that contains an API Rest implemented as a Spring Boot application (1.5.18.RELEASE version)
This API contains a controller that executes a service method asynchronous. The method is marked with #Async annotation
The #EnableAsync annotation is set on my configuration class.
When i execute the application like a typical Spring Boot application, the method is executed asynchronous. if i generate a war (using maven) and this war is deployed on JBoss (6.4 Version), the same service is executed synchronous.
Could someone explain me this behavior? Should i add any type of configuration?
The source code is below:
The Spring Boot configuration
#SpringBootApplication
#EnableCustomConfiguration
#EnableCaching
#EnableScheduling
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
My custom annotation:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Import({CustomServicesConfiguration.class})
#Documented
public #interface EnableCustomConfiguration {
}
My configuration class:
#Configuration
#ComponentScan("com.bs.custom.api")
#EntityScan(basePackages = "com.bs.custom.api.domain", basePackageClasses = Jsr310JpaConverters.class)
#EnableJpaRepositories(basePackages = "com.bs.custom.api.repository")
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableAsync
public class CustomServicesConfiguration {
static {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
}
I've modified my WebApplication class to extends from SpringBootServletInitializer, as is defined on Spring Boot reference guide (thanks M.Deinum)
The final WebApplication class source code is below:
#SpringBootApplication
#EnableCustomConfiguration
#EnableCaching
#EnableScheduling
#EnableAsync
public class WebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebApplication.class);
}
}
Now, asynchronous methods run correctly on JBoss

Can #Configuration work without #componentScan (Spring JavaConfig #annotaion)

--Appconfig.java
#Configuration
public class AppConfig {
#Bean(name="helloBean")
public HelloWorld helloWorld() {
return new HelloWorldImpl();
}
}
--interface.java
public interface HelloWorld {
void printHelloWorld(String msg);
}
--ipml.java
public class HelloWorldImpl implements HelloWorld {
public void printHelloWorld(String msg) {
System.out.println("Hello! : " + msg);
--
}
--App.java
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
new AnnotationConfigApplicationContext(AppConfig.class);
HelloWorld obj = (HelloWorld) context.getBean(HelloWorldImpl.class);
obj.printHelloWorld("Spring3 Java Config");
}
}
My program can works, but my question is why I don't need to add #componentScan in Appconfig.java .
It seems to #Configuration and #Bean can be found by Spring whithout using #componentScan.
I thought if you want to use #annotation ,you must use #componentScan or
context:component-scan(xml),
am I right?
#ComponentScan allows spring to auto scan all your components with #Component annotated. Spring uses the base-package attribute, which indicates where to find components.
#Configuration is meta annotated with #Component, which marks it eligible for classpath scanning.
#Configuration (AppConfig class) is registered when you use
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
#Bean doesn't need #ComponentScan as all these beans are created explicitly when spring encounters this annotation.

Spring Boot: NoUniqueBeanDefinitionException between test and main

I have a SpringBoot main/Application.java class
#SpringBootApplication
#ComponentScan(value = "com.nfl.dm.shield", excludeFilters =
{
#ComponentScan.Filter(value = MemoryRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class Application {
final static Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
LOG.info("Booting application...");
SpringApplication.run(Application.class, args);
}
}
and a similar one for Test
#Configuration
#ComponentScan(basePackages = {"com.nfl.dm.shield"}, excludeFilters =
{
#ComponentScan.Filter(value = MySqlRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class ApplicationTestConfig {
}
The main code runs correctly. The test code throws NoUniqueBeanDefinitionException, appearing to not properly filter out the unwanted MySqlRepository component.
After over a day of trying many different ways to exclude the unwanted bean, the core issue turned out to be the fact that #ComponentScan was pulling in both Application and ApplicationTest, resulting in an additional scan for Application, resulting in the unwanted service being loaded.
The solution, add:
#ComponentScan.Filter(value = Application.class, type = FilterType.ASSIGNABLE_TYPE)
to the list in ApplicationTestConfig.java. So, when ApplicationTestConfig is loaded and triggers the component scan, it ignores Application (and all of Application's specific configurations).

Inject test beans into main method

I'm using JavaConfig to manage and wire Spring beans into my Java app. The Java application is a main method - and basically runs as a batch job, invoked via a bash file. Is there a way that I can use a different (test) config in my main method?
public static void main(String[] args) {
final ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// do Stuff
}
I have used the following annotations successfully before in my test classes:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
, but this does not work for "main" applications. Short of passing in the Spring context to use as an argument, not sure what I can do here. Thanks
You should be able to use profiles in your actual config class to do what you want as well.
By setting the desired Profile you can "inject" the different beans you want.
Your ApplicationConfig might look like:
#Configuration
#Import({
JndiDataConfig.class,
TestDataConfig.class,
)
public class ApplicationConfig {
...
where TestDataConfig looks (in part) like:
#Configuration
#Profile("test")
public class TestDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
and where JndiDataConfig looks like:
#Configuration
#Profile("production")
public class JndiDataConfig {
#Bean
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}

Resources