I have the following scenario:
class Super{
private List<String> someStringsThatWillBeDifferentForEveryInstancePerDerivedType;
}
#Component
class Derived1 extends Super{
#Autowired
private String name;
}
#Component
class Derived2 extends Super{
#Autowired
private Long configId;
}
I have a different List defined as Spring bean in xml for each derived class…call them listForDerived1 and listForDerived2. How can I wire these lists into my derived classes? I attempted constructor injection but I can't seem to find any luck injecting both the collection and the other deps.
You can use constructor injection with #Qualifier.
class Super {
private List<String> someStrings;
public Super(private List<String> someStrings) {
this.someStrings = someStrings;
}
}
#Component
class Derived1 extends Super {
#Autowired
public Derived1(#Qualifier("listForDerived1") List<String> listForDerived1, OtherBean bean) {
super(listForDerived1);
}
}
#Component
class Derived2 extends Super {
#Autowired
public Derived1(#Qualifier("listForDerived2") List<String> listForDerived1, OtherBean bean) {
super(listForDerived2);
}
}
Also see official Spring doc: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-autowired-annotation-qualifiers
Related
I have the following entity in spring boot application:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Audited
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#Table(name = "currency", catalog = "currency_db")
public class Currency implements java.io.Serializable {
#Autowired
Messages messages;
As for message, it just a container of spring MessageSource here it is:
#ApplicationScope
#Component
#Slf4j
public class Messages {
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource, Locale.ENGLISH);
log.info("Messages initialized");
}
public String get(String code) {
return accessor.getMessage(code);
}
}
I'm getting the following error when run mvn clean install. Any idea what I'm missing here?
org.hibernate.MappingException: Could not determine type for: com.company.currencyservice.Messages, at table: currency, for columns: [org.hibernate.mapping.Column(messages)]
It's looks like hibernate think it's a column. Thanks.
Entities are not Spring beans and therefor you cannot use dependency injection in entities.
If you want to access a Spring bean from within an entity you can use a helper class like this:
#Service
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T bean(Class<T> beanType) {
return context.getBean(beanType);
}
public static Object bean(String name) {
return context.getBean(name);
}
#Override
public void setApplicationContext(#SuppressWarnings("NullableProblems") ApplicationContext ac) {
context = ac;
}
}
Then you can use ApplicationContextProvider.getBean(Messages.class) to get access to the Messages.
I am having below mapper class in which I want to use CounterService. I am trying constructor injection but that's not working and null is printing.
#Mapper(componentModel = "spring", uses = CounterService.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public abstract class CarMapper {
private CounterService counterService;
public CarMapper(CounterService counterService) {
this.counterService = counterService;
}
public abstract Car dtoToEntity(CarDto carDto);
public CarDto entityToDto(Car car) {
System.out.println(counterService)
//....
return carDto;
}
}
Implementation class by mapStruct
#Component
public class CarMapperImpl extends CarMapper{
#Override
public Car dtoToEntity(CarDto carDto){
//...
}
}
If I use field injection using #AutoWired, that way it works fine. It means Spring doesn't support the constructor injection of abstract class. Is it because abstract class can't be instantiated directly and require a subclass to instantiate?
Is there any way mapStruct can create a constructor inside the implementation class as:
public CarMapperImpl(CounterService counterService){
super(counterService);
}
That way, constructor injection should work.
This has nothing to do with Spring. It was a deliberate decision done by the MapStruct team to not use super constructors.
What you can do though is to use setter injection.
#Mapper(componentModel = "spring", uses = CounterService.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public abstract class CarMapper {
private CounterService counterService;
public abstract Car dtoToEntity(CarDto carDto);
public CarDto entityToDto(Car car) {
System.out.println(counterService)
//....
return carDto;
}
#Autowired
public void setCounterService(CounterService counterService) {
this.counterService = counterService;
}
}
You can also use a Mapper Decorator like below. Also, always make sure to check the generated class.
#DecoratedWith(CarDecoratorMapper.class)
#Mapper(componentModel = "spring")
public interface CarMapper {
Car dtoToEntity(CarDto carDto);
CarDto entityToDto(Car car);
}
public abstract class CarDecoratorMapper implements CarMapper {
#Autowired
private CarMapper delegate;
#Autowired
private CounterService counterService;
public Car dtoToEntity(CarDto carDto) {
Car car = delegate.dtoToEntity(carDto);
car.setProperty(counterService.count(carDto));
return car;
}
public CarDto entityToDto(Car car) {
CarDto carDto = delegate.entityToDto(car);
carDto.setProperty(counterService.countDto(car));
return carDto;
}
}
You don't have to use setter injection you can do the same what Filip suggested and just use field injection. If you don't need a setter of course
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 have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}
I am using Spring 4 via Spring Boot 1.1.8 and have created a class to cache some data. The class relies on generics to work but I'm having trouble with Spring and autowiring this class as a bean in another service.
I get errors like this:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [orm.repository.BaseRepository] is defined: expected single matching bean but found 2: dataTypeRepository,propertyNameRepository
The class in question:
/**
* The purpose of this class is to get data from a cache backed by a database
* and if it does not exist to create it and insert into the database.
*/
#Service
public class CacheByName<TRepo extends BaseRepository, TItem extends BaseWithName> {
private final TRepo repo;
private final Class<TItem> itemClass;
private final Map<String, TItem> itemsCache; // TODO: change to better caching strategy
#Autowired
public CacheByName(TRepo repo, Class<TItem> itemClass) {
this.repo = repo;
this.itemClass = itemClass;
itemsCache = new HashMap();
}
public TItem getCreateItem(String name) {
TItem item = null;
if(itemsCache.containsKey(name)) {
item = itemsCache.get(name);
} else {
// try and load from db
item = (TItem) repo.findByName(name);
if(item == null) {
try {
item = itemClass.newInstance();
item.setName(name);
repo.saveAndFlush(item);
} catch (InstantiationException | IllegalAccessException ex) {
// TODO: log and handle better
return null;
}
}
itemsCache.put(name, item);
}
return item;
}
}
The class BaseRepository extends JpaRepository as follows. Other actual repositories extend this one.
#NoRepositoryBean
public interface BaseRepository<T extends Object, ID extends Serializable> extends JpaRepository<T, ID> {
public T findByName(String name);
}
The class BaseWithName is a MappedSuperclass that defines a name property and getters/setters for it. Other more concrete entity classes extend this.
I am trying to inject the CacheByName class into another service like the following. Note that I am defining the actual repository and entity class as generics in the constructor:
#Service
public class DataImporter extends BaseImporter {
private static final Logger log = LoggerFactory.getLogger(PropertyImporter.class);
private final PropertyNameRepository propertyNameRepo;
private final CacheByName<DataTypeRepository, DataType> dataTypeCache;
#Autowired
public PropertyImporter(RestTemplate restTemplateD5,
CacheByName<DataTypeRepository, DataType> dataTypeCache) {
super(restTemplateD5);
this.dataTypeCache = dataTypeCache;
}
.
.
.
}
My AppConfig.java looks like the following:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class AppConfig {
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public RestTemplate restTemplateD5() {
return RestTemplateFactory.createWithHttpBasicAuth(username, password);
}
}
I haven't been able to find much information about creating beans that use generics. I suspect I need to create another #Bean definition in my AppConfig but I wasn't able to implement anything that worked.
As BaseRepository is also a generic type, I think you missed to add the generic type there. That should help Spring to find a proper bean to inject:
public class CacheByName<TRepo extends BaseRepository<TItem, ? extends Serializable>, TItem extends BaseWithName>
This should also make the cast no longer needed:
item = repo.findByName(name);