I am turning old xml/java configuration into pure java config. In xml I used injection of parameters into configuration file like this:
<bean class="com.project.SpringRestConfiguration">
<property name="parameters" ref="parameters" />
</bean>
#Configuration
public class SpringRestConfiguration {
private Parameters parameters;
public void setParameters(Parameters parameters) {
this.parameters = parameters;
}
// #Bean definitions
...
}
Is it possible to inject Parameters in javaconfig? (Without the need of using autowiring!)
#Configuration
#Import(SpringRestConfiguration.class)
EDIT:
With #Import I can't see any chance to inject Parameters into SpringRestConfiguration
Basically you would need to use #Autowired but you can still use a name and not type interpretation like this:
#Configuration
public class SpringRestConfiguration {
#Autowired
#Qualifier("parameters") // Somewhere in your context you should have a bean named 'parameters'. It doesn't matter if it was defined with XML, configuration class or with auto scanning. As long as such bean with the right type and name exists, you should be good.
private Parameters parameters;
// #Bean definitions
...
}
This solves the confusion problem you mentioned when using #Autowired - there's no question here which bean is injected, the bean that is named parameters.
You can even do a little test, leave the parameters bean defined in the XML as before, use #Autowired, see that it works. Only then migrate parameters to #Configuration class.
In my answer here you can find a complete explanation of how you should migrate XML to #Configuration step by step.
You can also skip the private member altogether and do something like this:
#Configuration
public class SpringRestConfiguration {
#Bean
public BeanThatNeedsParamters beanThatNeedsParamters (#Qualifier("parameters") Parameters parameters) {
return new BeanThatNeedsParamters(parameters)
}
}
If I have understood your question properly, this is what you are trying to do :
#Component
public class SomeConfiguration {
#Bean(name="parameters")
public Parameters getParameters(){
Parameters parameters = new Parameters();
// add your stuff
return parameters;
}
#Bean(name="springRestConfiguration")
public SpringRestConfiguration springRestConfiguration(){
SpringRestConfiguration springRestConfiguration = new SpringRestConfiguration();
springRestConfiguration.setParametere(getParameters());
return springRestConfiguration;
}
}
and use it like :
ApplicationContext appContext = new AnnotationConfigApplicationContext(SomeConfiguration.class);
SpringRestConfiguration springRestConfiguration = (SpringRestConfiguration) appContext.getBean("springRestConfiguration");
Related
In XML config I can write (Class2 extends Class1):
<bean name="bean1" class="com.comp.Class1" primary="true"/>
<bean name="bean2" class="com.comp.Class2" primary="false"/>
But how can I reach the same effect using Java config? It's seems that place only one #Primary on bean1.
But then I got error "expected single matching bean but found 2" when autowiring by interface which implemented by Class1.
I am assuming you already know about qualifiers and your use-case is not to use that.
I think something like below should work in your case.
#Configuration
public class Config {
#Bean
public Employee JohnEmployee() {
return new Employee("John");
}
#Bean
#Primary
public Employee TonyEmployee() {
return new Employee("Tony");
}
}
MAIN JAVA CLASS
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
Employee employee = context.getBean(Employee.class);
System.out.println(employee);
I have developed a simple Annotation Interface
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomAnnotation {
String foo() default "foo";
}
then I test it annotating a Class
#CustomAnnotation
public class AnnotatedClass {
}
and call it using a method
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
logger.info(customAnnotation.foo());
}
and all works fine because it logs foo. I try also change the annotated class to #CustomAnnotation(foo = "123") and all works fine too, becuase it logs 123.
Now I want that the value passed to the annotation is retrieved by the application.properties, so I have changed my annotated class to
#CustomAnnotation(foo = "${my.value}")
public class AnnotatedClass {
}
but now the log returns the String ${my.vlaue} and not the value in application.properties.
I know that is possible use ${} instruction in annotation because I always use a #RestController like this #GetMapping(path = "${path.value:/}") and all works fine.
My solution on Github repository: https://github.com/federicogatti/annotatedexample
Spring Core-based approach
First off, I want to show you a standalone application that doesn't utilise Spring Boot auto-configurable facilities. I hope you will appreciate how much Spring does for us.
The idea is to have a ConfigurableBeanFactory set up with StringValueResolver which will be aware of our context (particularly, of the application.yaml properties).
class Application {
public static void main(String[] args) {
// read a placeholder from CustomAnnotation#foo
// foo = "${my.value}"
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String foo = customAnnotation.foo();
// create a placeholder configurer which also is a properties loader
// load application.properties from the classpath
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("application.properties"));
// create a factory which is up to resolve embedded values
// configure it with our placeholder configurer
ConfigurableListableBeanFactory factory = new DefaultListableBeanFactory();
configurer.postProcessBeanFactory(factory);
// resolve the value and print it out
String value = factory.resolveEmbeddedValue(foo);
System.out.println(value);
}
}
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface CustomAnnotation {
String foo() default "foo";
}
#CustomAnnotation(foo = "${my.value}")
class AnnotatedClass {}
Spring Boot-based approach
Now, I will demonstrate how to do it within your Spring Boot application.
We are going to inject ConfigurableBeanFactory (which has already been configured) and resolve the value similarly to the previous snippet.
#RestController
#RequestMapping("api")
public class MyController {
// inject the factory by using the constructor
private ConfigurableBeanFactory factory;
public MyController(ConfigurableBeanFactory factory) {
this.factory = factory;
}
#GetMapping(path = "/foo")
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String foo = customAnnotation.foo();
// resolve the value and print it out
String value = factory.resolveEmbeddedValue(foo);
System.out.println(value);
}
}
I don't like mixing up low-level Spring components, such as BeanFactory, in business logic code, so I strongly suggest we narrow the type to StringValueResolver and inject it instead.
#Bean
public StringValueResolver getStringValueResolver(ConfigurableBeanFactory factory) {
return new EmbeddedValueResolver(factory);
}
The method to call is resolveStringValue:
// ...
String value = resolver.resolveStringValue(foo);
System.out.println(value);
Proxy-based approach
We could write a method that generates a proxy based on the interface type; its methods would return resolved values.
Here's a simplified version of the service.
#Service
class CustomAnnotationService {
#Autowired
private StringValueResolver resolver;
public <T extends Annotation> T getAnnotationFromType(Class<T> annotation, Class<?> type) {
return annotation.cast(Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{annotation},
((proxy, method, args) -> {
T originalAnnotation = type.getAnnotation(annotation);
Object originalValue = method.invoke(originalAnnotation);
return resolver.resolveStringValue(originalValue.toString());
})));
}
}
Inject the service and use it as follows:
CustomAnnotation customAnnotation = service.getAnnotationFromType(CustomAnnotation.class, AnnotatedClass.class);
System.out.println(customAnnotation.foo());
You can't do something like directly as an annotation attribute's value must be a constant expression.
What you can do is, you can pass foo value as string like #CustomAnnotation(foo = "my.value") and create advice AOP to get annotation string value and lookup in application properties.
create AOP with #Pointcut, #AfterReturn or provided others to match #annotation, method etc and write your logic to lookup property for corresponding string.
Configure #EnableAspectJAutoProxy on main application or setting up by configuration class.
Add aop dependency: spring-boot-starter-aop
Create #Aspect with pointcut .
#Aspect
public class CustomAnnotationAOP {
#Pointcut("#annotation(it.federicogatti.annotationexample.annotationexample.annotation.CustomAnnotation)")
//define your method with logic to lookup application.properties
Look more in official guide : Aspect Oriented Programming with Spring
Make sure Annotated Class has #Component annotation along with #CustomAnnotation(foo = "${my.value}"), then Spring will recognize this class as Spring component and makes the necessary configurations to insert the value in.
You can use ConfigurableBeanFactory.resolveEmbeddedValue to resolve ${my.value} into the value in application.properties.
#CustomAnnotation(foo="${my.value}")
#lombok.extern.slf4j.Slf4j
#Service
public class AnnotatedClass {
#Autowired
private ConfigurableBeanFactory beanFactory;
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String fooValue = customAnnotation.foo().toString();
String value = beanFactory.resolveEmbeddedValue(fooValue);
log.info(value);
}
}
If you also want to resolve expressions you should consider using EmbeddedValueResolver.
EmbeddedValueResolver resolver = new EmbeddedValueResolver(beanFactory);
final String value = resolver.resolveStringValue(fooValue);
You can look at Spring's RequestMappingHandlerMapping to see how they do it, which is using a EmbeddedValueResolver. You can inject the bean factory into any spring component and then use it to build your own resolver:
#Autowired
public void setBeanFactory(ConfigurableBeanFactory beanFactory)
{
this.embeddedValueResolver = new EmbeddedValueResolver(beanFactory);
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String fooValue = customAnnotation.foo();
System.out.println("fooValue = " + fooValue);
String resolvedValue = embeddedValueResolver.resolveStringValue(fooValue);
System.out.println("resolvedValue = " + resolvedValue);
}
Assuming you set foo.value=hello in your properties, the output would look something like:
fooValue = ${foo.value}
resolvedValue = hello
I tested this with Spring Boot 2.0.2 and it worked as expected.
Keep in mind this is a minimal example. You would want to handle the error cases of missing annotations on the class and missing resolved value (if the value isn't set and there's no default).
To read property from application.propertie, one need to define PropertyPlaceholderConfigurer and map it with properties file.
XML based configuration:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations" value="classpath:application.properties" />
</bean>
For annotation based: one can use as below:
#Configuration
#PropertySource(
value{"classpath:properties/application.properties"},ignoreResourceNotFound=true)
public class Config {
/**
* Property placeholder configurer needed to process #Value annotations
*/
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
I'm not sure if I understand it correctly, but from what I got, is that I can use #Value annotations to read values from my application.properties.
As I figured out this works only for Beans.
I defined such a bean like this
#Service
public class DBConfigBean {
#Value("${spring.datasource.username}")
private String userName;
#Bean
public String getName() {
return this.userName;
}
}
When the application starts I'm able to retrieve the username, however - how can I access this value at runtime?
Whenever I do
DBConfigBean conf = new DBConfigBean()
conf.getName();
* EDIT *
Due to the comments I'm able to use this config DBConfigBean - but my initial problem still remains, when I want to use it in another class
#Configurable
public SomeOtherClass {
#Autowired
private DBConfigBean dbConfig; // IS NULL
public void DoStuff() {
// read the config value from dbConfig
}
}
How can I read the DBConfig in a some helper class which I can define as a bean
Thanks
As Eirini already mentioned you must inject your beans.
The #Value annotation only works on Spring beans.
There is another way of accessing configuration with #ConfigurationProperties.
There you define a class that holds the configuration.
The main advantage is, that this is typesafe and the configuration is in one place.
Read more about this:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-vs-value
You shouldn't instantiate your service with the new operator. You should inject it, for example
#Autowired
private DBConfigBean dbConfig;
and then dbConfig.getName();
Also you don't need any #Bean decorator in your getName() method
You just need to tell spring where to search for your annotated beans. So in your configuration you could add the following:
#ComponentScan(basePackages = {"a.package.containing.the.service",
"another.package.containing.the.service"})
EDIT
The #Value, #Autowired etc annotations can only work with beans, that spring is aware of.
Declare your SomeOtherClass as a bean and add the package config in your #Configuration class
#Bean
private SomeOtherClass someOtherClass;
and then
#Configuration
#ComponentScan(basePackages = {"a.package.containing.the.service"
"some.other.class.package"})
public class AppConfiguration {
//By the way you can also define beans like:
#Bean
public AwesomeService service() {
return new AwesomeService();
}
}
Wrap your DBConfig with #Component annotation and inject it using #Autowired :
#Autowired
private DBConfig dbConfig;
Just add below annotation to your DBConfigBean class:
#PropertySource(value = {"classpath:application.properties"})
I'm trying to imitate the behavior of the Spring XML alias in #Configuration class.
I have an alias in XML that looks like that:
<alias name="${com.some.bean}" alias="myBean"/>
I have a #Configuration class that looks like that:
#Configuration
public class MyConfig {
#Lazy
#Bean(name = "bean1")
public MyBean bean1() { return new MyBean(); }
#Lazy
#Bean(name = "bean2")
public MyBean bean2() { return new MyBean(); }
}
I want to load dynamically either bean1 or bean2 an give the one that's loaded the alias myBean. according to the resolution of com.some.bean property. It's easy to do with XMLs, yet I can't find the #Configuation equivalent.
Note: I do not want to use profiles because that's not how my system currently works and changing all the property resolution to use profiles instead of property files is not an option at the moment.
An ugly workaround is to define a #Bean which receives the property value as a #Value and performs the resolution itself
#Bean("myBean")
public MyBean myBean(#Value("${com.some.bean}") String value) {
if (value.equals("bean1"))
return bean1();
else if (value.equals("bean2"))
return bean2();
else {
throw new RuntimeException("nope, something went wrong");
}
}
When writing integration tests with Spring 3.1, I usually define the request scope to be a SimpleThreadScope, with the following XML context configuration:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope" />
</entry>
</map>
</property>
</bean>
To define the request scope to be backed by a prototype scope implementation, I thought to change the class to be an implementation of the prototype scope. However I was not able to locate any.
Looking at the Scope Interface Javadoc, in the section All Known Implementing Classes, I see listed: AbstractRequestAttributesScope, PortletContextScope, RequestScope, ServletContextScope, SessionScope, SimpleThreadScope... nothing that looks like a prototype scope.
How can I define the request scope as prototype for integration tests?
UPDATE: I've managed to make my integration tests pass by creating my own prototype scope, which I've defined as follow, so my question now is, whether the following implementation is correct, or it has to be fixed.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class PrototypeScope implements Scope {
private static final Log logger = LogFactory.getLog(PrototypeScope.class);
public Object get(String name, ObjectFactory objectFactory) {
return objectFactory.getObject();
}
public Object remove(String name) {
return null;
}
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("PrototypeScope does not support destruction callbacks. "
+ "Consider using a RequestScope in a Web environment.");
}
public Object resolveContextualObject(String key) {
return null;
}
public String getConversationId() {
return Thread.currentThread().getName();
}
}
UPDATE 2: I am using TestNG and my Integration tests look like this:
#Test
#ContextConfiguration(locations = { "classpath:META-INF/spring/test-context.xml" })
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class MyIntegrationTest extends AbstractTransactionalTestNGSpringContextTests {
#Resource
private MyBeanThatShouldBePrototype bean;
#Transactional
public void testCase() {
...
In fact it's working in a different way - Spring checks if bean is a prototype, and then clones its definition and just populate new bean, so there is no backing class for holding such beans. If you want to look at the implementation please visit:
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean and you will find:
if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
If you want to use prototype scope in tests you can just manually create this bean by invoking new MyObjectThatShouldBePrototype() and then configure it as a Spring bean by using AutowireCapableBeanFactory (injected/autowired into your test):
#Autowired
AutowireCapableBeanFactory beanFactory;
public MyObjectThatShouldBePrototype getBean() {
MyObjectThatShouldBePrototype bean = new MyObjectThatShouldBePrototype();
beanFactory.autowireBean(bean);
return bean;
}
Of course there are several ways to create beans - you can find then here http://www.kubrynski.com/2013/09/injecting-spring-dependencies-into-non.html
Something like this is not working for you?
#Test
#ContextConfiguration(locations = { "classpath:META-INF/spring/test-context.xml" },
classes = MyIntegrationTest.TestConfig.class)
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class MyIntegrationTest extends AbstractTransactionalTestNGSpringContextTests {
#Resource
private MyBeanThatShouldBePrototype bean; // protype bean produced by spring
#Transactional
public void testCase() {
...
}
#Configuration
public static class TestConfig {
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public MyBeanThatShouldBePrototype myBeanThatShouldBePrototype() {
return new MyBeanThatShouldBePrototype();
}
}
}
Probably you could go another way?
What about writing a beanfactorypostprocessor that changes the request scoped bean candidates to prototype instead?
I havent tried it myself but you should be able to apply this to any bean declared as request scoped and set the prototype flag.
In the spring context for your unit tests you define this processor and in the context for integration tests this postprocessor would not be around.