In spring boot we may exclude specific auto-configuration classes such that they will never be applied. Example with annotation based configuration:
#SpringBootApplication(exclude = OneConfiguration.class)
What I want is to provide an environment variable with a value that will determine some auto configurations that will be excluded. Any ideas how to achieve that?
You can exclude this configuration class by default:
#SpringBootApplication(exclude = OneConfiguration.class)
then extend it and make it conditonal:
#Configuration
#ConditionalOnProperty(name = "one.configuration.condtion", havingValue = "some-value")
public class OneConfigurationConditional extends OneConfiguration { }
Create class extending Condition checking value of enviroment variable:
public class AutoConfigurationCondition extends Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getenv("EXCLUDE").equals("yes");
}
}
Then you can use it in Configuration class:
#Configuration
#Conditional(AutoConfigurationCondition.class)
#EnableAutoConfiguration(exclude = {OneConfiguration.class})
class AutoConfigurationExcludingConf {}
I managed to do what I wanted with profiles :-( So I have few files now - e.g. application-artemis.yaml and application-activemq.yaml. In each I've defined corresponding spring.autoconfigure.exclude property. I'm not proud with this solution though it works fine so far and looks more or less neat compared to other things that I did :-) .
What I've tried besides that:
Managing the value with an environment variable, e.g.:
spring:
autoconfigure:
exclude: ${ABC:com.example.Myautoconfiguration}
This does not work and I've even reported it as an issue. But it seems that I can't rely on expression for this property. Strange enough it works for other properties...
I've played around with the suggestion of #Randal Flagg but somehow I couldn't get it up and running - I'm using #SpringBootApplication, the documentation says that I can use EnableAutoConfiguration only once, etc.
I've tried with my own TypeFilter but this is also not an option - autoconfigurations have special treatment during component scan and the design does not seem very extensible there. At least I could not find a nice way to plug in.
Related
I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.
New to spring boot.
We have reusable beans in multiple base dependencies. Since, we need those reusable functionality, we are inheriting those jars in pom.xml.
Ex:
Jar1:
ClassName: UserInfo, RequestInterceptors, ClassName3, ClassName4, ClassNameN.
PackageName: com.somename1.base.comps
Jar2:
ClassName: UserInfo, RequestInterceptors, ClassName3, ClassName4, ClassNameN.
PackageName: com.somename2.base.comps
Since the class name is similar, creates conflict issue, when building.
Unfortunatly, we cannot able to change any class names, since someother teams are using these jar. No permission to create another version of these jars.
Is there any way to include both jar without excluding these conflicting components in #EnableAutoConfiguration or #ComponentScan
annotations?
Could someone share some advice here.
The solution here would be to name your beans. The reason for the conflict would be that the names of the classes are the same. For example:
#Configuration
public class Config{
public com.somename2.base.comps.UserInfo somename2UserInfo(){
return new com.somename2.base.comps.UserInfo();
}
public com.somename1.base.comps.UserInfo somename1UserInfo(){
return new com.somename1.base.comps.UserInfo();
}
}
The beans would then be named somename1UserInfo and somename2UserInfo and you can use these names instead when autowiring, e.g.:
public class SomeImpl{
#Autowired
#Qualifier("somename1UserInfo")
UserInfo userInfo;
/*
* Or you can do this
*/
#Resource(name = "somename1UserInfo")
UserInfo theSameUserInfo
}
This will allow you to autowire whichever bean should be used in whichever class. It may not be ideal to have to declare a qualifier for every place the classes are used, so if one implementation is primarily used, consider looking into the #Primary annotation.
I'm writing integration test which should works only with the specific profile. The problem is that I should get profile name from spring.profiles.active value in application.properties file. But I always get null due to real value from application.properties.
I've created for main method which explicitly put value into System.properties:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Autowired
public MyApplication(Environment environment) {
String property = "spring.profiles.active";
System.getProperties().setProperty(property, environment.getProperty(property));
}
But the value still is null.
In the test class I use following annotations:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = MyApplication.class)
#SpringBootTest(classes=MyApplication.class)
For loading context from the main class where I manually set the necessary value.
Also tried use #TestPropertySource("classpath:application.properties")
But nothing help. And I can't hardcode the value. It's should be get only from application.properties
UPDATE
It was my mistake. I've tried getValue from static method :/
Maybe someone know how I can disable class test by use this value?
The #IfProfileValue still return null and therefore doesn't fit.
UPDATE
I've realized that disabling is useless and better just use appropriate config using #SpringBootTest(classes=AppropriateConfigClass.class)
I'll choose gargkshitiz answer as solved, because he was the only one who tried to help.
You are halfway there. #TestPropertySource reads from src/test/resources. Add src/test/resources/application.properties with overridden values and you should be done.
Answer by #gargkshitiz is perfect !
In case you have only property spring.profiles.active to be read then you can mention it following way(it will save us from maintaining properties file).
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
value={"spring.profiles.active=yourActiveProfileName"})
Also you can specify multiple properties,
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
value={"spring.profiles.active=yourActiveProfileName",
"spring.some.other.property=propertyValue"})
You can drop webEnvironment = WebEnvironment.RANDOM_PORT if you don't need it.
I have a bunch of classes inside a Spring 4.2 project.
I'd like to have all of them annotated with #Xyz annotation. According to AspectJ documentation it could be done by
declare #type : x.y.z.* : #Xyz;
instruction.
But I have no clue where to place it.
I did some testing on my side and after some struggling, I looked for the concrete implementation. Sadly, #DeclareAnnotation exists but is not implemented.
We can see it here.
https://github.com/eclipse/org.aspectj/blob/V1_8_9/docs/adk15ProgGuideDB/ataspectj.xml#L1017
I thought it would be implemented sinced the annotation appeared in version 1.5.3. My bad.
Original answer (not working, AspectJ v1.8.9).
First you need to enable AspectJ in your configuration. For example, Java configuration :
#Configuration
#EnableAspectJAutoProxy
public class AopConfiguration {}
Then you create a new aspect with the #DeclareAnnotation annotation :
#Aspect
public class XyzAspect {
#DeclareAnnotation("x.y.z.*")
#Xyz class XyzClass {}
#DeclareAnnotation("x.y.z.MyClass.*(..)")
#Xyz void xyzMethod() {}
}
I'm using Spring 3.1 and bootstrapping an application using the #Configuration and #ComponentScan attributes.
The actual start is done with
new AnnotationConfigApplicationContext(MyRootConfigurationClass.class);
This Configuration class is annotated with
#Configuration
#ComponentScan("com.my.package")
public class MyRootConfigurationClass
and this works fine. However I'd like to be more specific about the packages I scan so I tried.
#Configuration
#ComponentScan("com.my.package.first,com.my.package.second")
public class MyRootConfigurationClass
However this fails with errors telling me it can't find components specified using the #Component annotation.
What is the correct way to do what I'm after?
Thanks
#ComponentScan uses string array, like this:
#ComponentScan({"com.my.package.first","com.my.package.second"})
When you provide multiple package names in only one string, Spring interprets this as one package name, and thus can't find it.
There is another type-safe alternative to specifying a base-package location as a String. See the API here, but I've also illustrated below:
#ComponentScan(basePackageClasses = {ExampleController.class, ExampleModel.class, ExmapleView.class})
Using the basePackageClasses specifier with your class references will tell Spring to scan those packages (just like the mentioned alternatives), but this method is both type-safe and adds IDE support for future refactoring -- a huge plus in my book.
Reading from the API, Spring suggests creating a no-op marker class or interface in each package you wish to scan that serves no other purpose than to be used as a reference for/by this attribute.
IMO, I don't like the marker-classes (but then again, they are pretty much just like the package-info classes) but the type safety, IDE support, and drastically reducing the number of base packages needed to include for this scan is, with out a doubt, a far better option.
Provide your package name separately, it requires a String[] for package names.
Instead of this:
#ComponentScan("com.my.package.first,com.my.package.second")
Use this:
#ComponentScan({"com.my.package.first","com.my.package.second"})
Another way of doing this is using the basePackages field; which is a field inside ComponentScan annotation.
#ComponentScan(basePackages={"com.firstpackage","com.secondpackage"})
If you look into the ComponentScan annotation .class from the jar file you will see a basePackages field that takes in an array of Strings
public #interface ComponentScan {
String[] basePackages() default {};
}
Or you can mention the classes explicitly. Which takes in array of classes
Class<?>[] basePackageClasses
You use ComponentScan to scan multiple packages using
#ComponentScan({"com.my.package.first","com.my.package.second"})
You can also use #ComponentScans annotation:
#ComponentScans(value = { #ComponentScan("com.my.package.first"),
#ComponentScan("com.my.package.second") })
I use:
#ComponentScan(basePackages = {"com.package1","com.package2","com.package3", "com.packagen"})
make sure you have added this dependency in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Use
#ComponentScan(basePackages = {"package1", "package2"})
define it at top before class.
Edit: the brackets must be around all the base packages, not a pair of brackets per package