Oppositely Configured ConditionalOnProperty Beans Autowiring - java

I have two separate beans configured with two custom conditional annotations that should be exact opposites of each other and never both autowired at the same time, yet both are being autowired. These annotations will be used on many classes, so I want to be able to reuse them throughout the application without duplicating the #ConditionalOnProperty configuration. Are my conditionals wrong, or is there a better way to accomplish this in code so I don't have to rely on #Conditional annotations for optionally autowiring beans?
Conditional annotations:
#Inherited
#Documented
#ConditionalOnProperty(prefix = "my-prefix", value = "enable-this-thing", havingValue = "false", matchIfMissing = false)
public #interface ConditionalOnNotEnablingThisThing {
}
#Inherited
#Documented
#ConditionalOnProperty(prefix = "my-prefix", value = "enable-this-thing", havingValue = "true", matchIfMissing = true)
public #interface ConditionalOnEnablingThisThing {
}
Shared interface:
public interface SharedInterface {
void doSomething();
}
Implementing classes:
#Component
#ConditionalOnEnablingThisThing
class EnabledThingComponent implements SharedInterface {
//code
}
#Component
#ConditionalOnNotEnablingThisThing
class NotEnabledThingComponent implements SharedInterface {
//code
}
Class attempting to autowire that throws a multiple available bean exception:
#Component
public class MyConsumer {
private final SharedInterface sharedInterface;
public MyConsumer(SharedInterface sharedInterface) {
this.sharedInterface = sharedInterface;
}
//code
}

I would like to first start off with saying this is not an ideal solution, but it worked for my specific usecase without too much heavy lifting.
The configuration class code:
#Configuration(MYCONFIG_NAME_CONSTANT)
public class MyConfig {
private final GenericApplicationContext genericApplicationContext;
public MyConfig(GenericApplicationContext genericApplicationContext) {
this.genericApplicationContext = genericApplicationContext;
configureConditionalBeans();
}
private void configureConditionalBeans() {
if(//my conditional code) {
genericApplicationContext.registerBean(
//registration details for conditional bean
);
}
}
}
Components that need to autowire in these conditional beans:
#Component
#DependsOn(MYCONFIG_NAME_CONSTANT) //optional will always be empty without this
public class MyConsumingComponent {
private final Optional<[MyConditionalBean]> conditionalBeanOptional;
public MyConsumingComponent(Optional<[MyConditionalBean]> conditionalBeanOptional) {
this.conditionalBeanOptional = conditionalBeanOptional;
}
//more code here
}
The biggest caveat I found is that the beans autowired in this way are never found by #ConditionalOnBean annotations, even when the bean has the #DependsOn specified. This means that any additional conditional beans relying on these underlying beans being configured will need to be conditionally set up in the same way. This caused a large pain for me since the beans I was attempting to conditionally autowire were ones a Spring Boot library was looking for. This meant the Spring Boot dependent beans had to be manually configured as well. I would still be interested in a better solution than this, but this solved the immediate problem.

Related

How to execute a runnable instead of bean creation in Spring configuration?

There is a usual configuration creating beans and performing an action using some of them.
The intention of initialAction is just execute print("Hello...") if certain conditions are in place (MyCondition.class). Obviously this return null is only required to keep the ceremony of beans creation and can be misleading for developers reading the code.
Is there a more elegant and convenient way to express the intention "run code after certain beans created if a condition is true"?
#Configuration
class App{
#Bean
#Conditional(MyCondition.class)
Object initialAction() {
var a = firstBean();
var b = secondBean();
print("Hello beans: $s, %s".formatted(a, b))
return null;
}
MyEntity firstBean(){
...
}
MyEntity secondBean(){
...
}
// other beans
}
There is an annotation #PostConstruct from javax or jakarta packages that are invoked once the dependency injection is done. Sadly, this cannot be combined with the #Conditional annotation.
You can, however, create a dedicated #Configuration + #Conditional class using #PostConstruct:
#Configuration
#Conditional(MyCondition.class)
public InitialActionConfiguration {
#Autowired
#Qualifier("firstBean")
private MyEntity firstBean;
#Autowired
#Qualifier("secondBean")
private MyEntity secondBean;
#PostConstruct
public void postConstruct() {
var a = firstBean;
var b = secondBean;
print("Hello beans: $s, %s".formatted(a, b));
return null;
}
// initializations in other #Configuration class
}
Once you do this, both MyEntity beans got initialized (if properly defined as beans) and this configuration class autowires them and prints them out only if the condition defined by MyCondition is satisfied.

Why do we use a qualifier when we can have a name for the bean?

Why do we use qualifiers with #Bean when we can have different names for different beans of the same type (class)?
#Bean
#Qualifier("fooConfig")
public Baz method1() {
}
Isn't the following code more clean?
#Bean("fooConfig")
public Baz method1() {
}
If I create two beans of the same type with different names (using #Bean annotation), then can we inject them specifically using the #Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?
#Bean("fooConfig")
public Baz method1(){
}
#Bean("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
If the above is true, then where do we use #Qualifier (with #Bean or #Component) instead of giving the bean a name as shown below?
#Bean
#Qualifier("fooConfig")
public Baz method1(){
}
#Bean
#Qualifier("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
Beans have names. They don't have qualifiers. #Qualifier is annotation, with which you tell Spring the name of Bean to be injected.
No.
Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the #Qualifier in that case. Spring is smart enough to find itself.
For example:
#Configuration
public class MyConfiguration {
#Bean
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with #Qualifier annotation.
SIDENOTE: #Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like #Bean("otherComponent").
You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?
This is you interface:
public interface TestRepository{}
This is your implementation 1:
#Repository
public class Test1Repository implements TestRepository{}
Your implementation 2:
#Repository
public class Test2Repository implements TestRepository{}
Now you are injecting it like:
private final TestRepository testRepository;
public TestServiceImpl(TestRepository testRepository) {
this.testRepository= testRepository;
}
QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with #Qualifier which class.
private final TestRepository testRepository;
public TestServiceImpl(#Qualifier("test1Repository") TestRepository testRepository) {
this.testRepository= testRepository;
}
I Prefer different method to not using #Qualifier
Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
Extends to each service
public interface FooService extends CommonFooBar {
}
public interface BarService extends CommonFooBar {
}
Then using it to your class
#Autowired
FooService fooService;
or
#Autowired
BarService barService;
so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.
I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?
Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.
The end result of this is:
Spring will name your beans based on the method used to create them.
If you import a bean, Spring will try to match on the bean name.
If you try to import a bean that does not match the name, Spring will attempt to match the class.
If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.
Lets not over-complicate Spring.
#Bean
mqConnectionFactory() {
ConnectionFactory connectionFactory = new MQXAConnectionFactory();
return connectionFactory;
}
#Bean
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
connectionFactory.setConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(cachingConnectionFactory);
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(pooledConnectionFactory);
...
return container;
}

What's the difference between #ComponentScan and #Bean in a context configuration?

There are at least 2 approaches to put Spring beans into a context configutation:
Declare a method with #Bean inside the configuration class.
Put #ComponentScan on the configuration class.
I was expecting that there are no difference between the 2 approaches in terms of the resulting Spring beans.
However, I found an example to demonstrate the difference:
// UserInfoService.java
public interface UserInfoService
{
#PreAuthorize("isAuthenticated()")
String getUserInfo ();
}
// UserInfoServiceTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy({
#ContextConfiguration(classes = TestSecurityContext.class),
#ContextConfiguration(classes = UserInfoServiceTest.Config.class)
})
public class UserInfoServiceTest
{
#Configuration
public static class Config
{
#Bean
public UserInfoService userInfoService ()
{
return new UserInfoServiceImpl();
}
}
#Autowired
private UserInfoService userInfoService;
#Test
public void testGetUserInfoWithoutUser ()
{
assertThatThrownBy(() -> userInfoService.getUserInfo())
.isInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
#Test
#WithMockUser
public void testGetUserInfoWithUser ()
{
String userInfo = userInfoService.getUserInfo();
assertThat(userInfo).isEqualTo("info about user");
}
The above code is to test the security annotation in the service UserInfoService. However, it will fail on testGetUserInfoWithoutUser(). The reason is that the bean userInfoService didn't get proxied by Spring Security. Therefore, the call userInfoService.getUserInfo() didn't get blocked by the annotation #PreAuthorize("isAuthenticated()").
However, if I replace the #Bean annotation with #ComponentScan, everything will start to work. That is, the bean userInfoService will get proxied and the call userInfoService.getUserInfo() will be blocked by the #PreAuthorize annotation.
Why are the approaches between the #Bean and #ComponentScan different? Did I miss something?
P.S. The full example is here and the above mentioned fix is here.
It is because #ContextHierarchy will create multiple spring contexts with parent children hierarchy. In your case, TestSecurityContext defines the bean configuration for the parent context while UserInfoServiceTest.Config defines for the children context.
If there is no #ComponentScan on the UserInfoServiceTest.Config , the security related beans are defined in the parent context which are invisible to the UserInfoService bean in the children context , and hence it does not get proxied by Spring Security.
On the other hand , if you define #ComponentScan on the UserInfoServiceTest.Config , it will also scan all the #Configuration beans from the package that containing UserInfoService (and all of its sub package) . Because TestSecurityContext is also inside this package and hence it get scanned and the security related beans are also configured for the children context. UserInfoService in children context will then get proxied by Spring Security.(Note : In this case ,both the parent and children context has their own set of security related beans)
By the way , if you just need a single context , you can simply use #ContextConfiguration :
#ContextConfiguration(classes= {TestSecurityContext.class,UserInfoServiceTest.Config.class})
public class UserInfoServiceTest {
public static class Config {
#Bean
public UserInfoService userInfoService() {
return new UserInfoServiceImpl();
}
}
}

Spring bean decoration without ambiguity errors

I have the following scenario: A factory interface with 2 implementations, while the second one used as decorator to the first one.
public final class BaseMailFactory implements MailFactory {
#Autowired
private final ClassA classA;
#Autowired
private final ClassB classB;
public Mail createMail(){
.
.
.
}
}
public final class MetricAwareMailFactory implements MailFactory {
private final MailFactory mailFactory;
public Mail createMail(){
var mail = mailFactory.createMail();
return new MetricsAwareMail(mail);
}
}
#Configuration
public class MailFactoryConfiguration {
#Bean
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(???);
}
}
The wrapped object previously instantiated through spring container (context), hence all auto wired fields populated successfully. After creation of the second implementation I am struggle to find an elegant way to initialize the first instance without adding multiple implementations to MailFactory interface which leads to application startup errors due to ambiguity.
I know that I can use qualifies for that but they pollute my code.
I am looking for a way to instantiate a class through spring but without actually register it as a bean, in older spring versions I get to use anonymous beans for such purposes.
I found the #Primary annotation useful here:
#Configuration
public class MailFactoryConfiguration {
#Bean
#Lazy
MailFactory baseMailFactory(){
return new BaseMailFactory();
}
#Bean
#Primary
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(baseMailFactory());
}
}
I such way, both beans will be created but the primary one will be selected in case of multiple implementations.

Autowiring Bean before initialization of another Bean

Spring question.
I have two questions related to spring.
If I declare bean like this:
#Service
public class Downloader {
#Bean
public String bean1() {
return "bean1";
}
}
Then if other classes will be autowiring "bean1" then method bean1 will be called several times? Or one instance of bean1 will be created and reused?
Second question. How to Autowire some other bean e.g. "bean2" which is String "externalBean" that can be used to construct bean1.
#Service
public class Downloader {
#Autowire
private String bean2;
#Bean
public String bean1() {
return "bean1" + this.bean2;
}
}
Currently I'm trying to Autowire this bean2 but it is null during bean1 call. Is there any mechanism that I can specify order of this. I don't know in what context looking for this kind of info in Spring docs.
Just simple #Bean annotation used sets the scope to standard singleton, so there will be only one created. According to the docs if you want to change you need to explicitly add another annotation:
#Scope changes the bean's scope from singleton to the specified scope
Then if other classes will be autowiring "bean1" then method bean1
will be called several times? Or one instance of bean1 will be created
and reused?
There will be only a single instance of bean1, as the implicit scope is Singleton (no #Scope annotation present).
Second question. How to Autowire some other bean e.g. "bean2" which is
String "externalBean" that can be used to construct bean1.
Being that it is a String, a #Qualifier might be required
#Bean
#Qualifier("bean2")
public String bean2() {
return "bean2";
}
Then
#Bean
public String bean1(#Qualifier("bean2") final String bean2) {
return "bean1" + bean2;
}
However, this works too.
Spring will be able to look at the name of the Bean and compare it to the parameter's one.
#Bean
public String bean2() {
return "bean2";
}
and
#Bean
public String bean1(final String bean2) {
return "bean1" + bean2;
}
The order is calculated automatically by Spring, based on a Bean dependencies.

Categories

Resources