Spring Java Config: configure already existing bean - spring

I want to configure in my #Configuration class bean which is already created by other library's autoconfiguration. I just need to change some fields in that class after it's being initialized.
But I can't find a proper way how to provide code block in #Configuration class and not using #Bean annotation. Is there an ideomatic way to do so in spring?

One way to do this:
#Configuration
class TestConfig {
#Autowired
private SomeBean someBean;
#PostConstruct
private void initSomeBean() {
// someBean.setProperty("qwe");
}
}
#PostConstruct annotation defines init-method, which is getting called after SomeBean is autowired. In this method you can adjust your bean

Do you want to import one Config in AnotherConfig on? It can be done via annotation placed on AnotherConfig:
import org.springframework.context.annotation.Import;
...
#Import(value = {Config.class})
public class AnotherConfig ... {}

Related

Spring annotation to initialize a bean during Autowire

Do we have a spring annotation that provides an option to initialize a bean (not a component) if not available through default constructor while autowiring?
If yes, that will be awesome. I am tired of initializing beans in some configuration class using default constructor and it occupies space.
I am doing this currently
#Bean
public Test test() {
return new Test();
}
Expecting:
Sometime like:
#Autowire(initMethodType=constructor)
private Test test:
If no, was there no real need of such annotation or any technical limitation?
You have to use #Bean annotation inside an #Configuration class.
Check the following link
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You could annotate your class with #Component, like this:
#Component
public class Test {
...
}
Whereever you need that class, you could then simply autowire it:
#Autowired
private Test test;
You would just have to make sure, that you use #ComponentScan to pick up that bean. You can find more information on that here.

Spring: What happens when we move #ComponentScan to another class in the package?

I have the following classes:
#ComponentScan
public class CDPlayerConfig {
#Autowired
private static CompactDisc cd;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CompactDisc cd = context.getBean(CompactDisc.class);
System.out.println(cd);
}
}
public interface CompactDisc {
void play();
}
#Component
public class SgtPeppers implements CompactDisc {
public void play() {
System.out.println("play song....");
}
}
When I run the class CDPlayerConfig, the program runs successfully. However, if I remove the ComponentScan annotation to CompactDisc interface or SgtPeppers I get the following error:
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'CompactDisc' available
I think that ComponentScan annotation marks the package where Spring looks for beans. CDPlayerConfig, CompactDisc and SgtPeppers are all placed in the same package, so allegedly moving ComponentScan annotation from one class to another should not make a difference.
If so, why do I get an error?
For #ComponentScan to work you have to "tell" spring where to search, or it must find it with help of other, already loaded, #ComponentScan annotated class (your class must be then annotated also with #Component, #Configuration etc. so it could be found).
In your case, you register application context in the first line of main method - you have specified there to load CDPlayerConfig.class which is #ComponentScan annotated so now spring can automatically find other beans in the package:
ApplicationContext context =
new AnnotationConfigApplicationContext(CDPlayerConfig.class);
If you want to move #ComponentScan to another class, you have to change class registered in AnnotationConfigApplicationContext to some #ComponentScan annotated class:
SgtPeppers:
#Component
#ComponentScan
public class SgtPeppers implements CompactDisc {
(...)
Main in CDPlayerConfig:
ApplicationContext context =
new AnnotationConfigApplicationContext(SgtPeppers.class);
Note you should register context from concrete classes (not interfaces).
Also, above sample would work even without #ComponentScan annotation on SgtPeppers, but then beans defined in other classes from the package wouldn't be found.

Spring automagic, #Autowired

#Autowired
Environment env;
#Value("${jdbcConnectionString}")
private String jdbcConnectionString;
The above works automagically in certain classes, however is null in similar classes in same package and with same annotations #Configuration/#Component.
I am trying to find out the proper approach to understand what spring does when while configuring various artifacts.
I am able to run stuff every now and then but any good resource to understand the magic is well appreciated.
PS. I am only interested in java-config based approach now.
Working :
package a.b.c;
#Configuration
public class AppConfig {
#Autowired
Environment env;
package a.b.d;
#Configuration
#EnableBatchProcessing
public class JobConfiguration {
#Autowired
private Environment env;
package a.b.L;
public class BatchJobListener implements Ordered, JobExecutionListener {
#Autowired
public Environment env;
Not working inside
package a.b.u
Tried to annotate classes with #Component/#Configuration
In order to autowire a bean, you need first to defined it in a Context.
#Configuration
public class ConfigOne {
#Bean
public String myBean(){
return "my bean";
}
}
The bean that you want to inject and the bean where the bean will be injected need to be in the same context. You can do it with:
JavaConfigApplicationContext context =
new JavaConfigApplicationContext(ConfigOne.class, ConfigTwo.class);
Or you can use #import to import one configuration class into another.
#Configuration
#Import(ConfigTwo.class)
public class ConfigOne {
UPDATE
What I meant was that probably you're not making the configuration in the ringht way. So all your beans where you're injecting the enviroment are not in the same context.
However, if you have configured well everything, it's possible that some classes are loading before enviroment. In this case you can use EnvironmentAware
#Configuration
#PropertySource("classpath:myProperties.properties")
public class MyConfiguration implements EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
public void myMethod() {
final String myPropertyValue = environment.getProperty("myProperty");
// ...
}
}
In your main class where you are initializing the spring boot application, do you have similar configuration:
#Configuration
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan("a.b") //Note that this scans the components where you have configured spring container backed objects
#PropertySource({
"classpath:someProperty1.properties",
"classpath:someProperty2.properties"
})
public class Main{
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
What this basically does is, it tells spring that this is a configuration class, and indicates this configuration class that it also triggers auto-configuration and component scanning (scan only these specific packages (a.b) and check if there are any annotations present for automatic bean detection, like: #Component, #Service, #Controller, #Repository). On detecting any class which have these stereotypes spring would create object(s) depending on the configuration. On creating these objects one may have autowired some objects or one could refer to some property defined in properties file. The config #PropertySource does this.
The package declaration in #ComponentScan should be the base package.

#Autowired dependencies are null in compile time weaving of #Aspect class [duplicate]

I have the following spring configuration:
<context:component-scan base-package="uk.co.mysite.googlecontactsync.aop"/>
<bean name="simpleEmailSender" class="uk.co.mysite.util.email.simple.SimpleEmailSenderImplementation"/>
<aop:aspectj-autoproxy/>
Then I have an aspect:
#Aspect
public class SyncLoggingAspect {
#Autowired
private SimpleEmailSender simpleEmailSender
#AfterReturning(value="execution(* uk.co.mysite.datasync.polling.Poller+.doPoll())", returning="pusher")
public void afterPoll(Pusher pusher) {
simpleEmailSender.send(new PusherEmail(pusher));
}
}
This aspect works (I can hit a breakpoint on afterPoll) but simpleEmailSender is null. Unfortunately I cannot find clear documentation on why this is. (For the record, my simpleEmailSender bean exists and is correctly wired into other classes) The following things confuse me:
Is context:component-scan supposed to be picking up #Aspect? If it is then surely it would be a spring managed bean, thus autowired should work?
If context:component-scan isn't for creating aspects, how is my aspect being created? I thought aop:aspectj-autoproxy just creates a beanPostProcessor to proxy my #Aspect class? How would it do this if it isn't a spring managed bean?
Obviously you can tell I don't have an understanding of how things should be working from the ground up.
The aspect is a singleton object and is created outside the Spring container. A solution with XML configuration is to use Spring's factory method to retrieve the aspect.
<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect"
factory-method="aspectOf" />
With this configuration the aspect will be treated as any other Spring bean and the autowiring will work as normal.
You have to use the factory-method also on Enum objects and other objects without a constructor or objects that are created outside the Spring container.
For Spring Boot to use #Autowired with AspectJ I have found the following method.
In configuration class add your aspect:
#Configuration
#ComponentScan("com.kirillch.eqrul")
public class AspectConfig {
#Bean
public EmailAspect theAspect() {
EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
return aspect;
}
}
Then you can successfully autowire your services in your aspect class:
#Aspect
public class EmailAspect {
#Autowired
EmailService emailService;
Another option is to add #Configurable to your aspect class instead of messing around with XML.
Configuring #Autowired with java config only (so no XML based configuration) requires a bit of extra work than just adding #Configuration to the class, as it also needs the aspectOf method.
What worked for me was creating a new class:
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And then use that in you aspect in conjunction with using #DependsOn #Configured and #Autowired:
#DependsOn("springApplicationContextHolder")
#Configuration
#Aspect
public class SomeAspect {
#Autowired
private SomeBean someBean;
public static SomeAspect aspectOf() {
return SpringApplicationContextHolder.getApplicationContext().getBean(SomeAspect.class);
}
The #DependsOn is needed because spring can't determine the dependency because the bean is used staticly.
I dont have 50 rep to comment on a question so here is another answer relating to #
Jitendra Vispute answer.
The official Spring doc mentions:
You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the #Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate #Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).Source: Spring '4.1.7.Release' documentation.
This would mean that adding a #Component annotation and adding the #ComponentScan on your Configuration would make #Jitendra Vispute's example work. For the spring boot aop sample it worked, though I did not mess around with context refreshing.Spring boot aop sample:
Application:
package sample.aop;
#SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {
// Simple example shows how an application can spy on itself with AOP
#Autowired
private HelloWorldService helloWorldService;
#Override
public void run(String... args) {
System.out.println(this.helloWorldService.getHelloMessage());
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleAopApplication.class, args);
}
}
The application should also run as plain Spring Framework application with the following annotations instead of #SpringBootApplication:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
and an AnnotationConfigApplicationContext instead of SpringApplication.
Service:
package sample.aop.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class HelloWorldService {
#Value("${name:World}")
private String name;
public String getHelloMessage() {
return "Hello " + this.name;
}
}
Monitor Aspect:
package sample.aop.monitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class ServiceMonitor {
#AfterReturning("execution(* sample..*Service.*(..))")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
}
This blog post explains it really well. Due to the fact that aspect singleton is created outside spring container you'd need to use factory-method=”aspectOf” that is only available after it is woven in by AspectJ ( not Spring AOP ) :
Notice factory-method=”aspectOf” that tells Spring to use a real
AspectJ ( not Spring AOP ) aspect to create this bean. So that after
the aspect is woven in it has an
“aspectOf” method.
So that :
No matching factory method found: factory method 'aspectOf()' - That
would mean that the aspect was not woven by AspectJ weaver.
From my experience with spring 3.1, if I don't use #Autowired but traditional setter for dependency injection, it gets injected and works as expected without aspectJ weaver. Although I'm encountering problems with the aspect being singleton... It results in 'perthis' instantiation model.
.
Add #Component to aspect class and your dependencies should get injected automatically.
and add context:component-scan for package where your aspect is in spring context file.
#Component
#Aspect
public class SomeAspect {
/* following dependency should get injected */
#Autowired
SomeTask someTask;
/* rest of code */
}
Use compile time weaving, see for plugin example at: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml
The following combination of annotation and Spring config works for me thanks to notes above by Tobias/Willie/Eric:
Class:
package com.abc
#Configurable
#Aspect
public class MyAspect {
#Autowired
protected SomeType someAutoWiredField;
}
XML:
<context:spring-configured />
<context:component-scan base-package="com.abc" />
#Configurable(autowire = Autowire.BY_TYPE)
Add this annotation to your Aspectj class. Then it will be handled by Spring IOC.
For Spring Boot using #Autowired in #Aspect,
my way is using spring.factories configuration file
create a file named spring.factories in src/main/resources
the file content is as following
org.springframework.boot.autoconfigure.EnableAutoConfiguration=pack.age.to.YourAspect,pack.age.to.YourDAO

Overriding Spring #PropertySource by #Import

I have a property test=default in class DefaultConfig, and I'm making them available using #PropertySource annotation.
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
I then want to be able to override to test=override, which is in a different properties file in class OverrideConfig, so I again use #PropertySource.
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {}
I configure a test to prove that it works.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={OverrideConfig.class})
public class TestPropertyOverride {
#Autowired
private Environment env;
#Test
public void propertyIsOverridden() {
assertEquals("override", env.getProperty("test"));
}
}
Except of course it does not.
org.junit.ComparisonFailure: expected:<[override]> but was:<[default]>
Maxing out debug, I can see what's happening:
StandardEnvironment:107 - Adding [class path resource [default.properties]] PropertySource with lowest search precedence
StandardEnvironment:107 - Adding [class path resource [override.properties]] PropertySource with lowest search precedence
It seems backwards. Am I making a simple mistake or misthinking this, or would you expect the properties defined by an #PropertySource in an #Import-ed configuration class to be overridden by properties defined in am #PropertySource in the #Import-ing class?
Here is a solution by Helder Sousa, written as a comment of the JIRA issue created by the OP:
[T]he behaviour available in spring xml (one xml importing another xml) is achievable using nested configurations:
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
#Configuration
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Configuration
#Import(DefaultConfig.class)
static class InnerConfiguration {}
}
With this setup, the properties will be gather in the proper order.
Today with Spring 4 you can use this:
#TestPropertySource(value="classpath:/config/test.properties")
And this can be used to use and eventually override properties for the junit test:
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(value="classpath:/config/test.properties")
I'm currently struggling with a similar case in Spring 3.1 but I'm using a different approach to override properties, because #PropertySource does not support optional property files:
#Configuration
#PropertySource("classpath:default.properties")
public class BaseConfig {
#Inject
private ApplicationContext context;
#PostConstruct
public void init() throws IOException {
Resource runtimeProps = context.getResource("classpath:override.properties");
if (runtimeProps.exists()) {
MutablePropertySources sources = ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources();
sources.addFirst(new ResourcePropertySource(runtimeProps));
}
}
...
It seems that #Import does not cause any specific order of #Configuration instantiation whatsoever besides the order dictated by normal bean dependencies. A way to force such an order is to inject the base #Configuration instance itself as a dependency. Could you try:
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Inject
private DefaultConfig defaultConfig;
...
}
Does this help?
And maybe the new ContextHierarchy annotation could be of help here also but I didn't try this one out so far.
You could enforce the loading order of your properties like this:
#Configuration
#PropertySource(value={"classpath:default.properties","classpath:override.properties"})
public class OverrideConfig {
...
}
I had a similar problem and succeeded in just declaring also the default property in my custom configuration:
#Configuration
#Import(DefaultConfig.class)
#PropertySource({"classpath:default.properties", "classpath:override.properties"})
public class OverrideConfig {}

Resources