I am trying to create a three-tier application with Spring, view, logic, data, more or less. The view depends on logic which depends on data.
How can I configure the Spring application in the view project such that the dependency graph is able to be resolved?
For example:
In the view layer:
#Controller
public class SomeView {
private final SomeService someService;
#Autowired
public SomeView(SomeService someService) {
this.someService = someService;
}
}
#Configuration
#EnableAutoConfiguration
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
In the logic layer:
#Component
public class SomeService {
private final SomeData someData;
#Autowired
public SomeService(SomeData someData){
this.someData = someData;
}
}
In the data layer:
#Component
public class SomeData {
}
This configuration is not able to boot because SomeService can't resolve SomeData because SomeData is not scanned in the view layers Application.java
when using #SpringBootApplication Spring boot uses default values. If you take a look at the #SpringBootApplication definition you will see that :
Many Spring Boot developers always have their main class annotated with #Configuration, #EnableAutoConfiguration and #ComponentScan. Since these annotations are so frequently used together (especially if you follow the best practices above), Spring Boot provides a convenient #SpringBootApplication alternative.
The #SpringBootApplication annotation is equivalent to using #Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes: [...]
That means :
#SpringBootApplication // same as #Configuration #EnableAutoConfiguration #ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also, it means that when using default values for #ComponentScan your packages sturctures shloud be as following :
com.example.model -> your entities
com.example.repositoriy -> your repositories
com.example.controller -> controllers
com.example -> MainApplication class
If not following this structure you should tell to the #ComponentScan the package where to find the components :
Example 1:
#Configuration
#EnableAutoConfiguration
#ComponentScan({"com.my.package.controller","com.my.package.domain"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Exemple 2 :
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackageClasses = {SomeService.class, SomeData.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also, i advice you to check this guide on how to structuring your code in a Spring Boot Application.
Related
I created Spring project via Spring Initializr with project following struct:
I defined property in application.properties file :
my.prop=testvalue
I inject this value into MyClass as following :
#Component
class MyClass {
#Value("${my.prop}")
private String myProp;
public String getMyProp() {
return myProp;
}
}
ConfigBeans defined as following:
package com.example.propertiesdemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ConfigBeans {
#Bean
public MyClass myLitoBean() {
return new MyClass();
}
}
PropertiesdemoApplication.java :
package com.example.propertiesdemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
ConfigBeans.class);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am expecting that after executing line
System.out.println(myClass.getMyProp());
will be printed value of myprop defined in application.properties (i.e testvalue), but after running (via Netbeans IDE) I get output :
${my.prop}
What was missed / wromg in this code ? Thanks in advance
You are creating MyClass bean twice.
Using #component annotation
using #bean annotation in the config class (use method name lowerCamelCase i.e. in your case myClass())
Create bean only once using any one of the above.
You dont need to create an application context in the main method like this. The presented code is a kind of mixture of "traditional" spring and spring boot. So you're kind of bypassing all the goodies that spring boot offers, among which is automatic application.properties loading.
If you're using spring boot (there is a #SpringBootApplication annotation) then it will create everything by itself.
Usually it should be something like this
public static void main(String[] args) {
SpringApplication.run(PropertiesdemoApplication.class, args);
}
Right, as Navnath Adsul said, you need the bean to be created once, and also, since you are using Spring Boot, you need to raise the context using a special method
#SpringBootApplication
public class PropertiesdemoApplication implements CommandLineRunner {
// Inject Bean
#Autowired
private MyClass myClass;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(PropertiesdemoApplication.class)
.run(args);
// or SpringApplication.run(PropertiesdemoApplication.class, args);
}
#Override
public void run(String[] args) throws Exception {
System.out.println(myClass.getMyProp());
}
}
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PropertiesdemoApplication.class, args);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am using SpringBoot 2.3.3.RELEASE and I have following web controllers and Services.
myapp
- controllers
- ProductController
- OrderController
- services
- ProductService
- OrderService
ProductController only depends on ProductService and OrderController only depends on OrderService.
Following is my SpringBoot main entrypoint class:
package com.sivalabs.myapp;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have an #WebMvcTest controller for testing ProductController as follows:
#WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productService;
//some tests
}
Everything works perfectly fine with this configuration.
I am trying to use some external library with Spring components which has different package name, so I want to override #ComponentScan as follows:
package com.sivalabs.myapp;
import com.somelib.BeanConfig;
#SpringBootApplication
#ComponentScan(basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
When I include #ComponentScan on my main entrypoint class and run my #WebMvcTest based test ProductControllerTest then in addition to ProductService SpringBoot is trying to initialise OrderService also. Ideally ProductControllerTest should not load OrderService as ProductController doesn't depend on OrderService. Is it a bug?
Workarounds:
If I use #ComponentScan the way it is used on #SpringBootApplication meta-annotation and include basePackageClasses it is working fine.
package com.sivalabs.myapp;
#SpringBootApplication
#ComponentScan(excludeFilters = { #Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), #Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Instead of adding #ComponentScan on main entrypoint class if I add another configuration class and add #ComponentScan on that class then it's working fine.
package com.sivalabs.myapp.config;
#Configuration
#ComponentScan(basePackageClasses = {BeanConfig.class})
public class AppConfig {
}
Is it a bug in component scanning process or is it working as expected?
It working as expected.
#SpringBootAplication already have #ComponentScan with filters, and redeclare it as in workaroud #1 doesn't make sense, right?
Problem With original code is that #ComponentScan overrides exclude filters applied inside #SpringBootApplocation annotation. And stuff is not excluded, so when #WebMvc tries to request part of context using filters it fails, and entire context is loaded.
Just check what beans are initialized for context in each scenario. And stick to solution 2. #SpringBootTest will scan packages where application class is places, and any additional packages should be scanned by configurations. That is even more flixible right? :)
The HystrixCommandAspect bean is declared in the HystrixCircuitBreakerConfiguration class but I would like to use my own custom implementation of HystrixCommandAspect and inject a different bean.
Application:
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableCircuitBreaker
#Import(HystrixConfiguration.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Configuration:
#Configuration
public class HystrixConfiguration {
#Bean
#Primary
public HystrixCommandAspect hystrixCommandAspect(){
return new com.hystrix.HystrixCommandAspect();
}
}
Custom HystrixCommandAspect:
package com.hystrix;
#Aspect
public class HystrixCommandAspect extends com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect {
...
}
However, when I use the annotation #EnableCircuitBreaker it uses the HystrixCircuitBreakerConfiguration and doesn't even load my own #Bean definition.
I upgraded spring to the latest release and this fixed the problem. I also noticed in the logs that it said it was overriding the bean provided in HystrixCircuitBreakerConfiguration.
I am consuming a spring boot project as jar inside another spring boot application using the maven dependency. I want to do the component scan of jar only if I enable a custom annotation from microservice.
#SpringBootApplication
//#ComponentScan({"com.jwt.security.*"}) To be removed by custom annotation
#MyCustomAnnotation //If I provide this annotation then the security configuration of the jar should be enabled.
public class MicroserviceApplication1 {
public static void main(String[] args) throws Exception {
SpringApplication.run(MicroserviceApplication1.class, args);
}
}
Please suggest some ideas.
In your library:
#Configuration
#ComponentScan(basePackages = { "com.jwt.security" })
public class MyCustomLibConfig{
}
#Retention(RUNTIME)
#Target(TYPE)
#Import(MyCustomLibConfig.class)
public #interface MyCustomAnnotation{
#AliasFor(annotation = Import.class, attribute = "value")
Class<?>[] value() default { MyCustomLibConfig.class };
}
So, in your application you can use the annotation
#SpringBootApplication
#MyCustomAnnotation //If I provide this annotation then the security configuration
of the jar should be enabled.
public class MicroserviceApplication1 {
public static void main(String[] args) throws Exception {
SpringApplication.run(MicroserviceApplication1.class, args);
}
}
You can use #Conditional to define configurations (see an example describe here). Some code from the source
#Configuration
public class MyConfiguration {
#Bean(name="emailerService")
#Conditional(WindowsCondition.class)
public EmailService windowsEmailerService(){
return new WindowsEmailService();
}
#Bean(name="emailerService")
#Conditional(LinuxCondition.class)
public EmailService linuxEmailerService(){
return new LinuxEmailService();
}
}
and conditional
public class WindowsCondition implements Condition{
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
You can use profiles. Just add the #Profile to your config class with scan of desired package.
One more alternative is described here.
#AutoconfigureAfter(B.class)
#ConditionalOnBean(B.class)
public class A123AutoConfiguration { ...}
I have a question, maybe simple, but I can not find out the solution.
I am using spring boot and added some annotation to the code like this:
#EnableEurekaClient
#SpringBootApplication
#EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
But in some other environment, for example, in production environment, we want to remove EurekaClient, but I do not want to manually remove it manually for each environment, instead, I want to use environment variable or command line parameter to control the behavior. I suppose to do this way:
#EnableEurekaClient(Enabled = {EnableEureka})
#SpringBootApplication
#EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Then I can easily start this application without touching the code.
Can anyone tell me if this is possible? If so, how can I do it?
Thanks
You would want to work with Spring Boot Profiles. Split out the #EnableEurekaClient to another #Configuration class and also add an #Profile("eureka-client") to the class. Then when starting up the application you can set a -Dspring.profiles.active=eureka-client for the environments other than production.
Example:
#SpringBootApplication
#EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
#Configuration
#EnableEurekaClient
#Profile("eureka-client")
public class EurekaClientConfiguration {
}
I prefer this method as you don't have to create an extra profile:
#Configuration
#EnableEurekaClient
#ConditionalOnProperty(name = "application.enabled", havingValue = "true", matchIfMissing = false)
public class EurekaClientConfiguration {
}