I have the following code, where in i am trying to create a bean out of the return type of the method.
It give error while starting the application as below:
Error creating bean with name 'myMap' defined in class path resource
[com/test/MyServiceImpl.class]: No matching factory method found:
factory bean 'MyServiceImpl'; factory method 'myMap()'. Check that a
method with the specified name exists and that it is non-static.
Code:
#Configuration
public class MyServiceImpl implements MyService
{
#Autowired
private MyDao myDao;
#Override
#Bean
#Scope("singleton")
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public Map<String, String> myMap()
{
return myDao.getMapFromDB();
}
}
public interface MyService
{
Map<String, String> myMap()
}
My application is based spring mvc and I have added the relevant configuration in the xml.
<mvc:annotation-driven/>
<context:component-scan>
I'm not 100% sure what you try to do, but here's an approach which would work:
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public Map<String, String> myMap() {
return myDao.getMapFromDB();
}
}
interface MyService {
Map<String, String> myMap()
}
This would define your service implementation as a Spring-managed #Service. Your MyDao would automatically be injected and you can use it in your method.
If you want that your MyDao has a certain scope (singleton is already the default), than you would annotate the MyDao class.
If you want to write a configuration, you would need to do it like this:
#Configuration
class MyConfig {
public MyService myService() {
return new MyServiceImpl();
}
}
Related
I have the following class for a resource in my Spring Application
#RestController
#RequestMapping("/whatever")
public class SomeResource {
#Autowired
private CoolService coolService;
#RequestMapping(
path = "",
method = RequestMethod.GET)
#PreAuthorize("hasPerm(#coolService.resolve(#attribute))")
public void resource(#PathVariable("attribute") int attribute) {
...
}
And I want to call the bean implementing CoolService that has been autowired by the Spring context, because for CoolService I have two beans that get activated depending on the profile at startup.
public interface CoolService {
resolve(int attribute);
}
#Service
#Profile("super")
public interface SuperCoolService implements CoolService {
public Object resolve(int attribute){...}
}
#Service
#Profile("ultra")
public interface UltraCoolService implements CoolService {
public Object resolve(int attribute){...}
}
However it seems that Spring does not know which bean to use because there is no single bean just named CoolService, and inside the Preauthorize I can't write #superCoolService or #ultraCoolService because it is profile-dependant.
How can I achieve this?
If you want to define 2 bean implement same interface, then you can user annotation #Qualifier.
For example:
#Service
#Qualifier("service1")
public interface SuperCoolService implements CoolService {
public Object resolve(int attribute){...}
}
#Service
#Qualifier("service1")
public interface UltraCoolService implements CoolService {
public Object resolve(int attribute){...}
}
I have following configuration:
#Qualifier1
#Qualifier2
#Bean
public MyBean bean1(){...}
#Qualifier2
#Qualifier3
#Bean
public MyBean bean2(){...}
#Qualifier1
#Qualifier2
#Qualifier3
#Bean
public MyBean bean3(){...}
#Qualifier3
#Bean
public MyBean bean4(){...}
#Qualifier1
#Bean
public MyBean bean5(){...}
And it is the injection place:
#Qualifier2
#Qualifier3
#Autowired:
private List<MyBean> beans;
By default spring uses AND logic for each #Qualifier
So bean2 and bean3 will be injected.
But I want to have OR logic for that stuff so I expect beans bean1 bean2 bean3 and bean4 to be injected
How can I achieve it?
P.S.
#Qualifier annotation is not repeatable so I have to create meta annotation for each annotation:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Qualifier1 {
}
What if you used marker interfaces instead of qualifiers? For example:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
Then using this:
#Bean
public MyBean1 myBean1() {
//...
}
#Bean
public MyBean2 myBean2() {
//...
}
#Bean
public MyBean12 myBean12() {
//...
}
and this:
#Autowired private List<Marker1> myBeans;
You would get a list of myBean1 and myBean12 beans.
And for this:
#Autowired private List<Marker2> myBeans;
You would get a list of myBean2 and myBean12 beans.
Will this work?
UPDATE I
Custom FactoryBean
I implemented TagsFactoryBean class and #Tags annotation which you can use to solve your task (I hope :)).
First, mark your beans with #Tags annotation:
#Tags({"greeting", "2letters"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2letters"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags("greeting")
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags("parting")
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags("other")
#Bean
public Supplier<String> other() {
return () -> "other";
}
Then prepare TagsFactoryBean:
#Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
Here tags is an array of desired tags whose beans should be selected, type is a selected beans type, and generics is an array of generic types of the beans. The last parameter is optional and should be used only if your beans are generic.
Then you can use it with #Qualifier annotation (otherwise Spring injects all beans of Supplier<String> type):
#Autowired
#Qualifier("words")
private Map<String, Supplier<String>> beans;
The Map beans will contain three beans: hi, hello and other (their name are keys of the Map and their instances are its values).
More usage examples you can find in tests.
UPDATE II
Custom AutowireCandidateResolver
Thanks to #bhosleviraj recommendation, I implemented TaggedAutowireCandidateResolver that simplifies the process of autowiring the desired beans. Just mark your beans and the autowired collection with the same tags and you will get them injected into the collection:
#Autowired
#Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
#Configuration
static class Beans {
#Tags({"greeting", "2symbols", "even"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2symbols", "even"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags({"greeting", "5symbols", "odd"})
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags({"parting", "7symbols", "odd"})
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags({"other", "5symbols", "odd"})
#Bean
public Supplier<String> other() {
return () -> "other";
}
}
You can use not only the Map for injecting beans but also other Collections.
To make it work you have to register a CustomAutowireConfigurer bean in your application and provide it with TaggedAutowireCandidateResolver:
#Configuration
public class AutowireConfig {
#Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
More usage examples see in this Test.
Answer requires deep understanding of how autowiring resolution is implemented in Spring, so we can extend it.
I couldn't come up with any solution yet, but I can give you some pointers.
Possible candidate to extend is QualifierAnnotationAutowireCandidateResolver , override method that resolves to a qualified bean. And pass the custom autowire resolver to the bean factory.
You can clone source code and correct version branch from here:
https://github.com/spring-projects/spring-framework
There is a CustomAutowireConfigurerTests in spring-beans module, that might help you understand few things.
I guess you can't do it by using annotation.
What I'd use is the org.springframework.context.ApplicationContextAware Maybe you need to write some extra code but in this way you can solve your issue.
I'd implement a class like this:
#Component
public class SpringContextAware implements ApplicationContextAware {
public static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
public static synchronized ApplicationContext getCtx() {
return ctx;
}
}
Then in all beans where you need the OR logic you want you can do something like this:
#Autowired
private SpringContextAware ctxAware;
#PostConstruct
public void init() {
//Here you can do your OR logic
ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2")
}
Will this solve your issue?
Angelo
when i am using #autowire to inject my dependencies in Configuration
class its giving me as null please refer the code below .
#Configuration
public class DataSourceConfig {
#Autowired
AppService appService;
#Bean
public BeanDefinitionRegistryPostProcessor beanPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
createBeans(beanRegistry);
}
};
}
private void createBeans(BeanDefinitionRegistry beanRegistry,DataSourceConfigService ds) {
appService.getDbDetails();
appService is null here if i will call it using this way
BeanDefinitionRegistryPostProcessor beanPostProcessor(AppService
appService) then in AppServiceImpl class AppDao dependency will be null
}
}
//// Service
#Service
public class AppServiceImpl implements AppService{
#Autowired
AppDao ds;
#Override
public List<A> getDatabaseConfiguration() {
return ds.getDbDetails(); // here ds is null
}
}
//dao
#Repository
public class AppDaoImpl implements AppDao {
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
#Override
public List<A> getDbDetails() {
return nameParamJdbcTemplate.query(SELECT_QUERY, new DataSourceMapper()); // nameParamJdbcTemplate is null
}
// datasource config
#Configuration
public class DataSourceBuilderConfig {
#Bean(name = "dbSource")
#ConfigurationProperties(prefix = "datasource")
#Primary
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nameParamJdbcTemplate")
#DependsOn("dbSource")
#Autowired
public NamedParameterJdbcTemplate jdbcTemplate1(#Qualifier("dbSource") DataSource dbSource) {
return new NamedParameterJdbcTemplate(dbSource);
}
}
What i want is when ever my beanPostProcessor()
is executed i want all my dependent beans should be instantiated ie
#Autowired
AppService appService;
#Autowired
AppDao ds;
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
I am new to spring so any help or working examples would be great. Thanks
It is null because this #Configuration class also defines a BeanDefinitionRegistryPostProcessor that forces the context to create that bean very early on.
Because you are using field injection, the context has to resolve AppService bean but it can't yet because the post-processor have to be applied first.
Your configuration looks very complex so you may want to simplify it a bit:
Separate low-level infrastructure configuration from main configuration
Always define such post processor as public static method so that the context can invoke the #Bean method without having to construct the class first.
Is it possible to autowire a bean that does NOT have the given qualifier in spring? The use case would be to have a list of all beans, but exclude one:
#Autowired
#NotQualifier("excludedBean") // <-- can we do something like this?
List<SomeBean> someBeanList;
public class Bean1 implements SomeBean {}
public class Bean2 implements SomeBean {}
#Qualifier("excludedBean")
public class Bean3 implements SomeBean {}
In the example above someList should contain an instance of Bean1 and Bean2 but not Bean3.
(Remark: I'm aware that the opposite would work, i.e. add some qualifier to Bean1 and Bean2 and then autowire with that qualifier.)
EDIT: Some further clarifications:
All beans are in the spring context (also the one being excluded).
Configuration needs to be annotation-based, not xml-based. Therefore, e.g. turning off autowired-candidate does not work.
Autowire capability of the bean must remain in general. In other words, I want to exclude the bean from the injection point List<SomeBean> someBeanList;, but I want to autowire it somewhere else.
You can introduce you own annotation with meta annotations #Conditional and #Qualifier
#Target({ElementType.FIELD, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
#Conditional(MyCondition.class)
public #interface ExcludeBean {
and then introduce class where you can do your conditional logic
public class MyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !metadata.equals(ExcludeBean.class);
}
}
In your Configuration class
#Bean
#ExcludeBean
public BeanA beanA() {
return new BeanA();
}
You can also exclude bean from being candidate for autowiring by setting autowire-candidate on particular bean or by specifying default-autowire-candidates="list of candidates here"
main point it's bean for exclude is in context but not injected into some cases with exclude condition .
you can do exclude bean with qualifier with custom annotaion and BeanPostProcessor. (I did as example for simple case , for collection of bean type , but you can extend it)
annotaion for exclude :
#Component
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ExcludeBeanByQualifierForCollectionAutowired {
String qualifierToExcludeValue();
Class<?> aClass();
}
bean post processor with injection
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
#Component
public class ExcludeAutowiredBeanPostProcessor implements BeanPostProcessor {
#Autowired
private ConfigurableListableBeanFactory configurableBeanFactory;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
ExcludeBeanByQualifierForCollectionAutowired myAutowiredExcludeAnnotation = field.getAnnotation(ExcludeBeanByQualifierForCollectionAutowired.class);
if (myAutowiredExcludeAnnotation != null) {
Collection<Object> beanForInjection = new ArrayList<>();
String[] beanNamesOfType = configurableBeanFactory.getBeanNamesForType(myAutowiredExcludeAnnotation.aClass());
for (String injectedCandidateBeanName : beanNamesOfType) {
Object beanCandidate = configurableBeanFactory.getBean(injectedCandidateBeanName);
Qualifier qualifierForBeanCandidate = beanCandidate.getClass().getDeclaredAnnotation(Qualifier.class);
if (qualifierForBeanCandidate == null || !qualifierForBeanCandidate.value().equals(myAutowiredExcludeAnnotation.qualifierToExcludeValue())) {
beanForInjection.add(beanCandidate);
}
}
try {
field.set(bean, beanForInjection);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
and example:
public class ParentBean {}
public class Bean1Included extends ParentBean {}
public class Bean2Included extends ParentBean {}
public class Bean3Included extends ParentBean {}
#Qualifier("excludedBean")
public class BeanExcluded extends ParentBean {}
configuration
#Configuration
public class BeanConfiguration {
#Bean
public Bean1Included getBean1(){
return new Bean1Included();
}
#Bean
public Bean2Included getBean2(){
return new Bean2Included();
}
#Bean
public Bean3Included getBean3(){
return new Bean3Included();
}
#Bean
public BeanExcluded getExcludedBean(){
return new BeanExcluded();
}
#Bean
public ExcludeAutowiredBeanPostProcessor excludeAutowiredBeanPostProcessor(){
return new ExcludeAutowiredBeanPostProcessor();
}
}
and result:
#ExtendWith(SpringExtension.class) // assumes Junit 5
#ContextConfiguration(classes = BeanConfiguration.class)
public class ExcludeConditionTest {
#Autowired
private ApplicationContext context;
#Autowired
private BeanExcluded beanExcluded;
#ExcludeBeanByQualifierForCollectionAutowired(qualifierToExcludeValue = "excludedBean" , aClass = ParentBean.class)
private List<ParentBean> beensWithoutExclude;
#Test
void should_not_inject_excluded_bean() {
assertThat(context.getBeansOfType(ParentBean.class).values())
.hasOnlyElementsOfTypes(Bean1Included.class,
Bean2Included.class,
Bean3Included.class,
BeanExcluded.class);
assertThat(beansWithoutExclude)
.hasOnlyElementsOfTypes(Bean1Included.class,
Bean2Included.class,
Bean3Included.class)
.doesNotHaveAnyElementsOfTypes(BeanExcluded.class);
assertThat(beanExcluded).isNotNull();
}
}
There might be two cases :
case 1 : Bean3 in not in spring context;
case 2 : Bean3 is in spring context but not injected in some cases with #Autowired ,
if you need to exclude bean with Qualifier from context at all ,use
Condition. This bean is not registered in application conxtet if matches returns false. as result :
#Autowired List someBeanList; -- here injected all beans instanceof SomeBean and registered in application context.
from spring api
Condition A single condition that must be matched in order for a
component to be registered. Conditions are checked immediately before
the bean-definition is due to be registered and are free to veto
registration based on any criteria that can be determined at that
point.
autowired with qualifier :
2.1 if you want to exclude bean with the some qualifier from autowired
value in some bean/beans and in xml configuration you can use
autowire-candidate
2.2 also you can get
all autowired values by Setter Injection and filter only
beans that you need.
//no Autowired. Autowired in method
private List<ParentBean> someBeen = new ArrayList<>();
#Autowired
public void setSomeBeen(List<ParentBean> beens){
// if you use java 8 use stream api
for (ParentBean bean:beens) {
Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class);
if(qualifier == null ||!qualifier.value().equals("excludedBean")){
someBeen.add(bean);
}
}
}
2.3 you can use custome AutowiredAnnotationBeanPostProcessor :) and customise #Autowired for you requirements if you need something realy custom.
from spring api AutowiredAnnotationBeanPostProcessor :
Note: A default AutowiredAnnotationBeanPostProcessor will be
registered by the "context:annotation-config" and
"context:component-scan" XML tags. Remove or turn off the default
annotation configuration there if you intend to specify a custom
AutowiredAnnotationBeanPostProcessor bean definition.
Another way to do this, is creating a custom component with #Qualifier
#Component
#Qualifier
public #interface MyComponent {
public boolean isMock() default false;
}
#Autowired
#MyComponent(true)
List<SomeBean> mockList; // will inject just component with "isMock = true"
#Autowired
#MyComponent(false)
List<SomeBean> notMockList; // will inject just component with "isMock = false"
#MyComponent
public class Bean1 implements SomeBean {}
#MyComponent
public class Bean2 implements SomeBean {}
#MyComponent(isMock = true)
public class Bean3 implements SomeBean {}
Obs: this code is not tested, just giving an idea
I want to have one controller class, but 4 instances of it, each of instance will have own datasource and controller path, everything else (methods, validations rules, views names) will be the same;
So i need something like this :
class MyController{
private MyService service;
#RequestMapping("somework")
public String handleRequest(){
........
}
....................
}
Configuration class :
#Configuration
#EnableWebMvc
public class AppConfiguration {
#Controller // assuming it exists to get the
#RequestMapping('con1') // desired result
MyController controller1(){
MyController con = new MyController();
con.setService(service1Bean);
return con;
}
#Controller // assuming it exists to get the
#RequestMapping('con2') // desired result
MyController controller2(){
MyController con = new MyController();
con.setService(service2Bean);
return con;
}
...............................
}
No, you can't do this.
First, annotations are a set in stone at compile time. They are constant meta data that you cannot modify. So even though, they are accessible at run time through reflection, you cannot modify them.
Second, the #Controller annotation call only be used to annotate types. You cannot use it on a method. There is no corresponding annotation in Spring MVC that does what you want in your example. (You could always write your own.)
Finally, the Spring MVC stack registers your #Controller beans' methods as handlers mapping them to the various URL patterns you provide. If it tries to register a pattern that has already been registered, it fails because duplicate mappings are not allowed.
Consider refactoring. Create a #Controller class for each path you want but move the logic to a #Service bean which you can customize to use whatever data source you need.
You may achieve what you want by implementing an abstract superclass of
your controller, with constructor parameters for your service.
Then you should write derive your controllers from the abstract superclass,
with a constructor, where you inject your concrete service implementation:
public abstract class MyBaseController {
private MyService service;
public MyBaseController(final MyService service) {
this.service = service;
}
...
#RequestMapping("method1")
public ... method1( ... ) {
...
}
}
#Controller
#RequestMapping("con1")
public MyController1 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con1") final MyService service) {
super(service);
}
}
#Controller
#RequestMapping("con2")
public MyController2 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con2") final MyService service) {
super(service);
}
}
#Configuration
public class MyConfiguration {
#Bean(name = "con1")
public MyService serviceCon1() {
return ...;
}
#Bean(name = "con2")
public MyService serviceCon2() {
return ...;
}
}