How to exclude a class from Spring using new Condition interface? - java

Often we have tests and production environment different from each other. Depending on some configuration parameters, certain classes are not required to be registered as beans by container (while using spring). Is there a way to dynamically skip such classes from application context ?

If the class that should been used in the test is only available the classpath while testing, and your problem is about replacing injected classes, then you could annotate the class that is used in TESTs with #Primary annotation.
#Primary is a feature of spring that is much older then conditions and less powerful but it is really easy to use. Is says: when an injection point
could been fulfilled with two Beans then use the bean that is annotated with #Primary, instead throwing an exception that complains about ambiguous beans.
So when you add a bean in test scope with #Primary annotation, then this bean replaces the original bean in its injection points.

Using Spring 4 #Conditional annotation we can.
Check here for details of Spring Condition interface
First create a Class say- "ComponentScanCondition which implements Spring's "Condition" interface. And the only method "matches" returns false if the system property is not null or it is a "test" environment.
public class ComponentScanCondition implements Condition{
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metaData) {
return System.getProperty("environment")!=null && System.getProperty("environment").equals("test")? false:true;
}
}
Now with annotation- #Conditional(ComponentScanCondition.class) you can control the component scan on the classes you do not need in test environment.
In jUnit test class, set up a system property as below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class testMyClass{
#BeforeClass
public static void setUpBeforeClass() throws Exception{
System.setProperty("environment", "test");
}
#Test
public void testSomeMethod(){
}
}
And in the classes, not required in test, use #Conditional annotation.For example if you do not need UserProfile class in test environment, skip it as below:
#service("userProfile")
#Conditional(ComponentScanCondition.class)
public class UserProfile{
}
As in test class the system property "environment" is set to "test", matches method will return false and Spring will skip the class UserProfile to be scanned.
In prod environment this property will not be set and will be null so matches will return true and hence UserProfile (and those class with #Conditional) will be scanned by Spring to register as beans in the container.

Related

How to enforce Aspect implementation is loaded when its corresponding annotation is used?

I have a created an annotation that verifies whether certain security aspects are correct.
For example, #RequireClientCertificate, with an Aspect implementation RequireClientCertificateAspect that verifies whether the correct HTTP header is indeed passed in to the Spring REST controller.
This works totally fine, IF the RequireClientCertificateAspect is actually loaded, i.e. if its package is mentioned somewhere in #ComponentScan().
However, if someone forgets to add this package to #ComponentScan, or the aspect is moved to another package, or someone (accidentally) removes the package from #ComponentScan, the aspect bean isn't loaded, and the aspect is completely not applied.
I have this annotation in a common library, shared by several microservices, so it's easy for one of the microservices to accidentally get it wrong. In that case, no checking of the client certificate would be performed.
Question: How can I enforce that, if the #RequireClientCertificate annotation is used, its corresponding Aspect implementation is also loaded?
Simplified usage example:
#Controller
#RequestMapping(value = "/v1.0", produces = MediaType.APPLICATION_JSON_VALUE)
#RequireClientCertificate
public class SomeApiController {
#ResponseBody
#PostMapping("/get-token/")
public ResponseEntity<Token> getToken() {
return ResponseEntity.ok(...get token...);
}
}
Simplified version of the aspect:
#Aspect
#Component
public class RequireClientCertificateAspect {
#Around("execution(* (#RequireClientCertificate *).*(..))")
public Object requireClientCertificateAspectImplementation(ProceedingJoinPoint joinPoint) throws Throwable {
... verify request header ...
try {
return joinPoint.proceed();
finally {
... some other things I need to check ...
}
}
}
Things I've tried/considered:
I can detect 'usage' of the annotation by adding a static field with an initializer to the interface. For example:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface RestFactoryGatewaySecurityContext {
static public final boolean dummy = SomeClass.checkAspectIsLoaded();
}
However, such initializers are called very early, and I don't think Spring DI is 'up and running' far enough at that stage that I could even reliably determine whether the aspect bean is loaded.
Another option is to use #Autowired to inject the RequireClientCertificateAspect bean on the main app class explicitly. If somehow the bean isn't on the component scan, this will prevent Spring from instantiating the app.
So that does work, but requires someone to explicitly add this 'dummy' autowire, which in itself is easy to forget, in addition to being a bit 'ugly'.
If you use spring boot you can create your own starter.
Create file META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyCustomConfiguration
Then just add any validation you want to your configuration
#Configuration
public class MyCustomConfiguration{
}
You can #Autowired your RequireClientCertificateAspect into it, which will cause error if it isn't defined.
You can create method with #PostConstruct and do any validation you want.
If you went so far as creating custom starter, you can just initialize your bewns there.
More about it you can read here

Why is #Bean(initMethod="") not detecting given method in spring?

Edit Fixed by changing package.
I have this configuration file for spring framework
#Configuration
public class AppConfig {
#Bean(initMethod = "populateCache")
public AccountRepository accountRepository(){
return new JdbcAccountRepository();
}
}
JdbcAccountRepository looks like this.
#Repository
public class JdbcAccountRepository implements AccountRepository {
#Override
public Account findByAccountId(long
return new SavingAccount();
}
public void populateCache() {
System.out.println("Populating Cache");
}
public void clearCache(){
System.out.println("Clearing Cache");
}
}
I'm new to spring framework and trying to use initMethod or destroyMethod. Both of these method are showing following errors.
Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Could not find an init method named 'populateCache' on bean with name 'accountRepository'
Here is my main method.
public class BeanLifeCycleDemo {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new
AnnotationConfigApplicationContext(AppConfig.class);
AccountRepository bean = applicationContext.getBean(AccountRepository.class);
applicationContext.close();
}
}
Edit
I was practicing from a book and had created many packages for different chapters. Error was it was importing different JdbcAccountRepository from different package that did not have that method. I fixed it and it works now. I got hinted at this from answers.
Like you said, if you are mixing configurations types, it can be confusing. Besides, even if you created a Bean of type AccountRepository, because Spring does a lot of things at runtime, it can call your initMethod, even if the compiler couldn't.
So yes, if you have many beans with the same type, Spring can be confused an know which one to call, hence your exception.
Oh and by the way, having a Configuration creating the accountRepoisitory Bean, you can remove the #Repository from your JdbcAccountRepository... It is either #Configuration + #Bean or #Component/Repository/Service + #ComponentScan.
TL;DR
Here is more information and how Spring creates your bean : What object are injected by Spring ?
#Bean(initMethod = "populateCache")
public AccountRepository accountRepository(){
return new JdbcAccountRepository();
}
With this code, Spring will :
Detect that you want to add a Bean in the application Context
The bean information are retrieved from the method signature. In your case, it will create a bean of type AccountRepository named accountRepository... That's all Spring knows, it won't look inside your method body.
Once Spring is done analysing your classpath, or scanning the bean definitions, it will start instanciating your object.
It will therefor creates your bean accountRepository of type AccountRepository.
But Spring is "clever" and nice with us. Even if you couldn't write this code without your compiler yelling at you, Spring can still call your method.
To make sure, try writing this code :
AccountRepository accountRepository = new JdbcAccountRepository();
accountRepository.populateCache(); // Compiler error => the method is not found.
But it works for Spring... Magic.
My recommandation, but you might thinking the same now: If you have classes across many packages to answer different business case, then rely on #Configuration classes. #ComponentScan is great to kickstart your development, but reach its limit when your application grows...
You mix two different ways of spring bean declaration:
Using #Configuration classes. Spring finds all beans annotated with #Configuration and uses them as a reference to what beans should be created.
So if you follow this path of configuration - don't use #Repository on beans. Spring will detect it anyway
Using #Repository - other way around - you don't need to use #Configuration in this case. If you decide to use #Repository put #PostConstruct annotation on the method and spring will call it, in this case remove #Configuration altogether (or at least remove #Bean method that creates JdbcAccountRepository)
Annotate populateCache method with #PostConstruct and remove initMethod from #Bean. It will work.

Can I override a spring bean definition defined using classpath scan?

I have an application which has a very simple plugin architecture by the means of registering additional behaviour using spring classpath scanning ("Installing" the plugin happens by putting the plugin.jar on the classpath).
This works great for registering additional beans and it also works great for registering hooks like this:
// core.jar:
#Component
class CoreClass {
public void addListener(Listener listener) { /* ... */ }
}
// plugin.jar
#Component
class Plugin {
public Plugin(CoreClass coreClass) {
coreClass.addListener(new PluginListener());
}
}
However sometimes it is more appropriate to to replace an entire bean. This might look like this:
// core.jar:
#Component("someBean")
class CoreClass implements CoreInterface {
#Override
public void doStuff();
}
// plugin.jar
#Component("someBean")
#Order(HIGHEST_PRECEDENCE)
class Plugin implements CoreInterface {
#Override
public void doStuff();
}
In this case I want that both Plugin and CoreClass are discovered by classpath scan during startup, but then the CoreClass should be ignored, because the bean someBean has another definition with higher precedence.
I am aware that I could do this using XML (<import resource="classpath*:plugin-spring.xml" />), because XML allows overriding definitions. But is this also possible using annotations?
Edit: Note that I sometimes have multiple bean instances of the same type (with different properties) which I inject name-based. Thus I actually need to override the bean with a certain name.
Possible solution to use:
#Primary
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
This annotation is semantically equivalent to the element's primary attribute in Spring XML.
May be used on any class directly or indirectly annotated with #Component or on methods annotated with #Bean.
// core.jar:
#Component
class CoreClass implements CoreInterface {
#Override
public void doStuff();
}
// plugin.jar
#Component
#Primary
class Plugin implements CoreInterface {
#Override
public void doStuff();
}
http://memorynotfound.com/handling-multiple-autowire-candidates-with-spring-primary/
Or you could use :
#Configuration
#ImportResource( { "classpath*:core-spring.xml", "classpath*:plugin-spring.xml" } )
public class ConfigClass { }
I suspect that problem with annotations is not clear which order to override, if you define over notations same name you will end-up with ConflictingBeanDefinitionException spring will not allow you to have #Component or #Bean with same name. In xml you can manage override order that’s why you can do this over xml. In annotations you can't do that.
Any given Spring context can only have one bean for any given id or name. In the case of the XML id attribute, this is enforced by the schema validation. In the case of the name attribute, this is enforced by Spring's logic.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behavior depends on the ordering of the files when they get loaded by the context.
So while it's possible, it's not recommended. It's error-prone and fragile, and you'll get no help from Spring if you change the ID of one but not the other.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behavior depends on the ordering of the files when they get loaded by the context.

What happens in Spring if I use the #ActiveProfiles annotation on a configuration class instead use it on the class that defines my beans?

I am studying for the Spring Core certification and I have some doubts related to the use of profiles into JUnit tests.
So I know that if I annote a class in the following way:
#Profile("stub")
#Repository
public class StubAccountRepository implements AccountRepository {
private Logger logger = Logger.getLogger(StubAccountRepository.class);
private Map<String, Account> accountsByCreditCard = new HashMap<String, Account>();
/**
* Creates a single test account with two beneficiaries. Also logs creation
* so we know which repository we are using.
*/
public StubAccountRepository() {
logger.info("Creating " + getClass().getSimpleName());
Account account = new Account("123456789", "Keith and Keri Donald");
account.addBeneficiary("Annabelle", Percentage.valueOf("50%"));
account.addBeneficiary("Corgan", Percentage.valueOf("50%"));
accountsByCreditCard.put("1234123412341234", account);
}
public Account findByCreditCard(String creditCardNumber) {
Account account = accountsByCreditCard.get(creditCardNumber);
if (account == null) {
throw new EmptyResultDataAccessException(1);
}
return account;
}
public void updateBeneficiaries(Account account) {
// nothing to do, everything is in memory
}
}
I am declaring a service bean that belongs to the stub profile.
So, if my test class is something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=TestInfrastructureConfig.class)
#ActiveProfiles("stub")
public class RewardNetworkTests {
.....................................
.....................................
.....................................
}
it means that it will be used the beans bean that belong to the stub profile and the bean that not have a profile. Is it right or am I missing something?
What happens if instead use the #ActiveProfiles annotation on a class (whose instance will be a Spring bean) I use it on a Java Configuration Class?
Something like it:
#Configuration
#Profile("jdbc-dev")
public class TestInfrastructureDevConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
What exactly do? I think that all the beans configured in this class will belong to the jdbc-dev profile, but I am not sure about it. Can you give me more information about this thing?
Why I have to use the #Profile annotation on a **configuration class* instead annotate directly my beans?
Tnx
If you look at the JavaDoc of ActiveProfiles annotation, it contains this text:
ActiveProfiles is a class-level annotation that is used to declare which active bean definition profiles should be used when loading an ApplicationContext for test classes.
Meaning it is only supposed to be used to declare active Spring profiles for test classes. So If put it on a Configuration class it should have no effect.
And as for the #Profile annotation, it can be used on both method and class level. If you use it on method annotated with #Bean in configuration class, only that bean will belong to the profile. If you use it on configuration class, it will be applied to all the beans within the configuration class, if you use it on #Component class, the profile will be applied to the bean represented by that class.
#Profile annotation JavaDoc provides more detailed explanation of these rules.
Why I have to use the #Profile annotation on a **configuration class* instead annotate directly my beans?
Well if all beans in given configuration class should be active only for certain profile(s) then it makes sense to declare that globally on the configuration class to avoid having to individually specify the profile on all beans. But If you were to annotate all indiviudal beans it would work as well.
The #ActiveProfiles notation is a way of specifying which Spring profiles should be active in a particular context. It is useful for declaring which beans should be created and which configurations should be applied. An example of using the #ActiveProfiles notation would be:
#ActiveProfiles("test")
public class MyTestClass {
//test methods here
}
In this example, the "test" profile is active for the class MyTestClass. This means that any beans or configurations associated with the "test" profile will be applied when running any tests in this class.

How can I test only one bean using existing application's spring configuration class?

In my code, I don't want to load all the beans defined in the XXApplicationConfig class.
XXApplicationConfig is a #Configuration annotated file which has bunch of spring beans defined.
So, I want to load only AppBean from XXApplicationConfig class while testing to reduce loading test time and also differentiate what I am testing. I also want to load the class using XXApplicationConfig class to make sure the bean configuration defined is correct as well.
This is my Test class ( modified ) to test AppBean class.
Could you let me know if this is the right approach and suggest how to make it better? Currently, this approach seems to be working. But, not sure if it is correct way of approaching it.
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationTest {
#Configuration
#PropertySources(value = {#PropertySource("classpath:test.properties")})
static class MyTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public XXApplicationConfig xxAppConfig() {
return new XXApplicationConfig();
}
#Bean
public CustomTestService customTestService() {
return new CustomTestService();
}
#Bean
public AppBean appBean() throws Exception {
return XXApplicationConfig().appBean();
}
}
#Autowired
private AppBean appBean;
#Test
public void testAppBean() {
test appBean.doSomething();
}
}
If you want to test just one object, just create one object of that class, using the constructor of that class. Spring beans are designed to be POJOs. The Spring context is just a convenient way of creating and connecting objects. Nothing stops you creating and connecting them yourself.
If you can instantiated the class you want to test and manually inject all the dependencies it required via constructor and/or setter getters, then you don't need to use Spring in your test.
However, if your bean:
uses private fields annotated with #Autowired or #Value without corresponding getters/setters.
depends on many other beans.
the behavior you want to test depends on Spring AOP/Proxies (you use #Transactional or #Cacheable for example).
Then you will need Spring to wired the bean. I personally prefer to define a a minimal #Configuration class for these cases.
Then again, if your bean meets the conditions on the list you should consider refactoring the bean to minimize its dependencies and facilitate testing.

Categories