I have a user defined annotation class as follows.
#Target({ TYPE, METHOD, PARAMETER, FIELD })
#Retention(RUNTIME)
#Qualifier
public #interface Message
{
Dest value();
public static enum Target { DEFAULT, TEST }
}
I use this annotation in the following way.
#Component
public class ProcessorBean implements Processor
{
#Autowired #Message(Message.Target.DEFAULT) Producer<Object, Object> messageProducer;
#Autowired
MessageConfig messageConfig;
Not sure, how to create a bean of ProcessorBean and inject Producer.
#Bean(name="DEFAULT")
public Producer<Object, Object> producer() {
return mock(Producer.class);
}
I tried the above one and it is throwing dependency error.
Thanks
I found the solution myself. Hope, this will be helpful for others.
#Message(Message.Target.DEFAULT)
#Bean
public Producer<Object, Object> producer() {
return mock(Producer.class);
}
Related
How to override #Configuation which is present under src/main/java with #TestConfiguration during unit tests?
#Configuration
public class AppConfig {
#Bean
public EmployeeService employeeService(){
return new EmployeeService();
}
}
#Component
public class ServerStartSetup implements CommandLineRunner {
#Autowired
private EmployeeService employeeService;
public void run(String... args) {
// do something with employee service
}
}
I would like to override the above bean with some below custom bean for testing purposes.
#TestConfiguration
public class TestAppConfig {
#Bean
public EmployeeService employeeService(){
return new FakeEmployeeService();
}
}
#SpringBootTest
#Import(TestAppConfig.class)
public class UnitTest {
}
However AppConfig does not seem to be skipped. That is , it throws an error saying that there is a bean with same name employeeService. If I rename bean method name in the TestAppConfig, it injects the bean created via AppConfig.
How to fix this.?
Note: One possible solution is using #Profile. I am looking for anything other than using Profiles.
I tested locally and found that changing the method name or #Bean to #Bean("fakeEmployeeService") and adding the #Primary annotation works.
#SpringBootTest
class DemoApplicationTests {
#Autowired
private EmployeeService employeeService;
#TestConfiguration
static class TestConfig {
//#Bean("fakeEmployeeService")
#Bean
#Primary
public EmployeeService employeeServiceTest() {
return new EmployeeService() {
#Override
public void doSomething() {
System.out.println("Do something from test...");
}
};
}
}
...
}
If we want to override a bean definition in #TestConfiguration, we need:
To use the same name as the overridden bean. (Otherwise it would be an "additional" bean and we could get conflict/'d have to qualify/primary)
Since spring-boot:2.1: spring.main.allow-bean-definition-overriding=true (set this in tests ONLY!plz!)
#ref
Then, with:
#TestConfiguration
public class TestAppConfig {
#Bean // when same name, no #Primary needed
public EmployeeService employeeService(){ // same name as main bean!
return new FakeEmployeeService();
}
}
We can do that:
#Import(TestAppConfig.class)
#SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class UnitTest {
... // EmployeeService will be "fake", the rest is from "main config"
You can mock the AppConfig bean in your test like this:
#MockBean
private AppConfig config;
Or, like you said, just use profiles.
Using SpringBoot, I have a Component bean that is defined as #Scope("protoype"). It also injects another prototype bean
The class is defined as
#Component
#Scope("prototype")
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(DataObj data) {
this.injectedBean = beanFactory.getBean(InjectedBean.class, data)
}
}
However, IntelliJ complains about the data field on the constructor: Could not autowire. No beans of 'DataObj' type found.. But DataObj is a POJO. I pass it in at runtime in order to create the bean. Am I defining the constructor incorrectly?
Update
Had the same problem doing it this way. It still wants to treat DataObj as a bean on the factory constructor class. Doesn't matter if I annotate the class with #Component or #Configuration
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
Also tried this example from that same link:
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
Update
I think I resolved this with some information in Spring Java Config: how do you create a prototype-scoped #Bean with runtime arguments?. Part of my problem is that I tried to put the factory bean in the Component itself, which doesn't work
In other words
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
In this cass, Spring tries to create a MyClass bean because of the #Component annotation, but another MyClass bean due to the #Bean annotation.
So I moved the #Bean to another class
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
This appears to work, but IntelliJ still complains about DataObj. This might be an Intellij issue
I have classes which implements MyInterface and their names are for example:
MyClassA, MyClassB etc.
How can I get the instance of the class by it's bean name? Something like:
context.getBean("myClassA")
context.getBean("myClassB")
Can I do it without configuring beans in the XML?
I want to use annotations
You can use qualifiers, e.g:
#Component
#Qualifier("classA")
public MyInterface ClassA {
return new ClassA();
}
#Component
#Qualifier("classB")
public MyInterface ClassB {
return new ClassB();
}
and use it like:
public class SomeClass {
#Autowired
#Qualifier("classA")
private MyInterface classA;
}
You have several options here. The easiest one would be using field names as a component name using #Autowire:
#Component("testClassA") // It is possible to omit explicit bean name declaration here since Spring will use a class name starting from lower case letter as a bean name by default. So just `#Component` should be sufficient here and below.
public TestClassA implements MyInterface {
}
#Component("testClassB")
public TestClassB implements MyInterface {
}
/*
* Note that field names are the same as the component names.
*/
#Component
public class TestClassWithDependencies {
#Autowired
private MyInterface testClassA;
#Autowired
private MyInterface testClassB;
}
Another option is to use qualifiers:
#Component
#Qualifier("testClassA")
public TestClassA implements MyInterface {
}
#Component
#Qualifier("testClassB")
public TestClassB implements MyInterface {
}
#Component
public class TestClassWithDependencies {
#Autowired
#Qualifier("testClassA")
private MyInterface testClassA;
#Autowired
#Qualifier("testClassB")
private MyInterface testClassB;
}
You could even create your own meta-annotations when you need to use the same qualifiers over and over again:
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier("testClassA")
public #interface TestClassACustomQualifier {
String value();
}
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier("testClassB")
public #interface TestClassBCustomQualifier {
String value();
}
#Component
public class TestClassWithDependencies {
#Autowired
#TestClassACustomQualifier
private MyInterface testClassA;
#Autowired
#TestClassBCustomQualifier
private MyInterface testClassB;
}
Much prettier, isn't it? One more option is to use #Resource from JSR-250 specification. As pointed out by #hovanessyan it's more JavaEE style of doing things, but still, I think it's a viable approach used on many projects:
#Component("testClassA")
public TestClassA implements MyInterface {
}
#Component("testClassB")
public TestClassB implements MyInterface {
}
#Component
public class TestClassWithDependencies {
#Resource(name="testClassA")
private MyInterface testClassA;
#Resource(name="testClassB")
private MyInterface testClassB;
}
More information you can get on https://www.sourceallies.com/2011/08/spring-injection-with-resource-and-autowired/, where discussed different approaches with tests added.
Hope this helps!
I think if above options don't suffice then factory implementation is one way to get instance on the fly -
#Component
public TestClassA implements MyInterface {
}
#Component
public TestClassB implements MyInterface {
}
define you factory this way -
public class MyInterfaceFactory extends AbstractFactoryBean<MyInterface> {
private String filter;
#Override
public Class<?> getObjectType() {
return MyInterface.class;
}
#Override
protected MyInterface createInstance() throws Exception {
MyInterface myInterface;
switch (filter)
{
case "1":
myInterface = new TestClassA();
break;
case "2":
myInterface = new TestClassB();
break;
default: throw new IllegalArgumentException("No such type.");
}
return myInterface;
}
}
and then your bean config -
#Configuration
public class FactoryBeanConfig {
#Bean(name = "myInterface")
public MyInterfaceFactory myInterfaceFactory() {
MyInterfaceFactory factory = new MyInterfaceFactory();
factory.setFilter("7070");
return factory;
}
}
I am trying to create my own #EnableXxx-style annotation (namely #EnableCustomizedPropertySources). For this the annotation imports the class CustomizedPropertySourcesConfiguration which in turn implements ImportAware, in order to have access to attributes of the #EnableCustomizedPropertySources annotation.
Annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(CustomizedPropertySourcesConfiguration.class)
public #interface EnableCustomizedPropertySources {
String externalFolderName();
String propertiesFileName();
(...)
}
Imported configuration class:
#Configuration
public class CustomizedPropertySourcesConfiguration implements ImportAware {
protected AnnotationAttributes enableCustomizedPropertySourcesAttributes;
#Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableCustomizedPropertySources.class.getName(), false);
this.enableCustomizedPropertySourcesAttributes = AnnotationAttributes.fromMap(attributes);
}
#Bean
public PropertySourcesPlaceholderConfigurer propertySource() {
return (...);
}
}
The problem is, the method setImportMetadata is not invoked by Spring when I annotate some #Configuration class with my #EnableCustomizedPropertySources annotation, so I cannot access the annotations attributes.
The ImportAware class (CustomizedPropertySourcesConfiguration) needs both:
#Configuration
#Component
I have RepositoryConfig extending Neo4jConfiguration. The latter sets up a number of beans with #Bean annotated methods. RepositoryConfigoverrides getGraphDatabaseService which is invoked before any fields in RepositoryConfig are autowired. That is a problem since I want to use the autowired stuff inside the getGraphDatabaseServicemethod.
#ConfigurationProperties(prefix = "neo4j")
public class RepositoryProperties {
[...]
}
#Configuration
#EnableNeo4jRepositories("com.foo.bar")
#EnableConfigurationProperties(RepositoryProperties.class)
public class RepositoryConfig extends Neo4jConfiguration {
#Autowired
private RepositoryProperties properties;
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
public GraphDatabaseService getGraphDatabaseService() {
[...] // properties is 'null' at this point
}
#PostContstruct
public void foo() {
[...] // properties is initiated OK here
}
}
Why is getGraphDatabaseServicebeing called before autowiring is complete? I guess it has to do with the inheritance... If I remove the inheritance then autowiring is complete at the time getGraphDatabaseServiceis called. I've also tried annotating the method with #DependsOn, with no luck.
Any ideas is much appreciated!
Yes, I have seen this too occasionally. I think there are two workarounds.
Option 1. Autowire the bean definition
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
#Autowired
public GraphDatabaseService getGraphDatabaseService() {
[...] // properties is 'null' at this point
}
Option 2. Inject the bean
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
public GraphDatabaseService getGraphDatabaseService(#Autowired RepositoryProperties properties) {
// can probably delete the Config member with this approach
[...] // properties is 'null' at this point
}