Spring 4 bean autowiring with generics - spring

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);

Related

how to autowire spring beans from a non-bean object created at runtime?

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);
...
}
}

Autowiring in implementation classes of an interface in Spring

I'm having a Spring boot application in which based on a variable, I need to call corresponding implementation classes of an interface. This is what I have right now:
public interface Parent{
public void call();
}
public class ABC implements Parent{
public void call(){
System.out.println("Called ABC");
}
}
public class XYZ implements Parent{
public void call(){
System.out.println("Called XYZ");
}
}
#Service("caller")
public class Caller{
#Autowired
protected OrderInfoRepository orderInfoRepository;
AnnotationConfigApplicationContext context;
public Caller(){
context = new AnnotationConfigApplicationContext(Config.class);
}
public void callMethod(String param){
Parent p = (Parent) context.getBean(param+"_Caller");
p.call();
}
}
#Configuration
public class Config{
#Bean(name="ABC_Caller")
public Parent getABC(){
return new ABC();
}
#Bean(name="XYZ_Caller")
public Parent getXYZ(){
return new XYZ();
}
}
#Repository
public interface MyRepo extends Repository<MyDAO, Long> {
// ....
}
Basically what I want to do is, based on the param passed to Caller.callMethod(), I want to add "_Caller" to the param, and call the corresponding implementation class. So, I'm defining a #Configuration class, where I define which implementation class to return. And then using the AnnotationConfigApplicationContext, I get the corresponding bean. This works fine.
The problem I'm having is, when I try to Autowire anything in the implementation classes, I get a NoSuchBeanDefinitionException. For instance, when I autowire in the implementation class ABC
public class ABC implements Parent{
#Autowired
MyRepo myRepo;
public void call(){
System.out.println("Called ABC");
}
}
I get Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.persistence.repositories.MyRepo] when I try to start the application. However, when I do the Autowiring in the Caller class instead, it works fine. I had asked a similar question a while back, but was not able to resolve it. Any ideas?

Spring Inject Collection From Superclass

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

Spring autowire trouble with generic parameter

I try to use generic parameter in autowire but it doesn't work. My goal is to create a generic JSON controller with Spring 4.1.3 and Hibernate
I have a generic abstract class , and I use it to create service via a model objec(the same as my DAO) as generic parameter.
The code of my AbstractService
public interface IGenericService<T extends Serializable> extends IOperations <T>{}
public interface IOperations<T extends Serializable> {
T findOne(final long id);
List<T> findAll();
void create(final T entity);
T update(final T entity);
void delete(final T entity);
void deleteById(final long entityId);
List<T> findByField(String field, String value);
T save(final T entity);
}
//MyAbstractService (generic service)
public abstract class AbstractService<T extends Serializable> implements
IGenericService<T> {
public static final Logger logger = LoggerFactory
.getLogger(AbstractService.class);
public AbstractService(){}
...
#Override
#Transactional
public T update( T entity) {
logger.debug("public T update( T entity)");
return getDao().update(entity);
}
...
}
Now I create a SecuredUserService with this abstract service
#Transactional
#Component //(value = "userService")
#Qualifier("userService")
public class UserService extends AbstractService<SecuredUser> implements
IUserService {
// I override the method upate of the abstract service
#Override
#Transactional
public SecuredUser update(SecuredUser user){
... // password encoding for example
}
}
public interface IUserService extends IGenericService<SecuredUser> {
T findOne(final long id);
...
}
In my JUnit test I made autowire with this code :
#Autowire
IGenericService<SecuredUser> userGenericService;
Or
#Autowire
IUserService userService;
At this point every thing is ok, I use the overrided method of userService and not those of abstractService. I pass my Junit Test. An I create a package.
Now I want to make generic spring mvc controller to handle common Json request GET/PUT/DELETE/POST :
//Generic Controller
public abstract class GenericSecuredController <MODEL extends Serializable> extends CommonSecuredController {
/**
* spring generic service retrieve by MODEL class type
*/
#Autowired
private IGenericService <MODEL> genericService;
/**
* Spring generic URI retrieve by MODEL class type
*/
#Autowired
private IGenericURI<MODEL> genericURI ;
...
}
// interface to manage URI in a generic way
public interface IGenericURI<MODEL extends Serializable> {
// root for the controller
public static String CONTROLLER_MAPPING="" ;
// path to the file system
public static String PATH_MAPPING = "";
// key to retrieve data in path
public static String PATH="{id}";
// Json REST SERVICE MappedUri
public static String JSON_DUMMY = "/dummy";
public static String JSON_GET = "/" + PATH;
public static String JSON_GET_ALL = "";
public static String JSON_CREATE = "";
public static String JSON_DELETE = "/" + PATH;
public static String JSON_UPDATE = "/" + PATH;
public static String HTML_VIEW = "/{view}.view.html";
public String getControllerMapping() ;
public String getPathMapping() ;
}
// The specific URI for the SecuredUser model object
#Component
public class SecuredUserURI implements Serializable, IGenericURI<SecuredUser> {
public static final String CONTROLLER_MAPPING = "/user";
public static final String PATH_MAPPING = "user";
public String getControllerMapping() {
return CONTROLLER_MAPPING;
}
public String getPathMapping() {
return PATH_MAPPING;
}
}
Now I could create a specific controller for SecuredUser like this :
public class UserController extends GenericSecuredController<SecuredUser> {
/**
* creator to set Class type for the GenericSecuredController<MODEL>
*/
public UserController() {
super(SecuredUser.class);
}
}
The problem appear at this point. The autowire of the
IGenericURI<MODEL>
work fine, but the autowiring with
IGenericService <MODEL> genericService;
doesn't use the overrided specific method of the userService but the abstract method with common behaviour!!!
So my question is :
Is it possible to autowire bean with generic parameter like in my example.
Maybe there is to many level for Spring autowiring .
Other information :
As workaround, I try to pass the userService as parameter of the contoller but, the same behaviour: the generic service use the abstract method.
UPDATE : If I autowire IGenericService genericService in the UserController and create a new handler, the specific service is call.
Thanks

requestfactory complain about find method

I have a spring (3.1) application with a service and dao layer.
I try to use requestfactory (gwt 2.4) withi this spring layer.
Here some of my class
My domain class
public class Account {
Long id;
String username;
// get, set
}
The bridge between spring and gwt
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return context.getBean(clazz);
}
}
My account proxy
#ProxyFor(value=Account.class, locator = AccountLocator.class)
public interface AccountProxy extends EntityProxy{
public Long getId();
public String getUsername();
public void setUsername(String userName);
public void setId(Long id);
}
RequestContext class
#Service(locator = SpringServiceLocator.class, value =AccountService.class)
public interface AccountRequest extends RequestContext {
Request<List<AccountProxy>> loadAllAccounts();
}
My requestFactory class
public interface AccountRequestFactory extends RequestFactory {
AccountRequest accountRequest();
}
My spring service
public interface AccountService {
public List<Account> loadAllAccounts();
}
#Service
public class AccountServiceImpl implements AccountService{
#Autowired
private AccountDAO accountDAO;
}
Account locator to avoid to put method in the entity
public class AccountLocator extends Locator<Account, Long> {
#Autowired
private AccountDAO accountDAO;
#Override
public Account create(Class<? extends Account> clazz) {
return new Account();
}
}
applicationContext.xml
<context:annotation-config />
<context:component-scan base-package="com.calibra" />
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
<bean id="accountDAO" class="org.calibra.server.dao.AccountDAOImpl"/>
The demo work but i get this error:
com.google.web.bindery.requestfactory.server.UnexpectedException: Could not find static method with a single parameter of a key type
Also on my AccountProxy i get this complain (a warning)
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method. Attempting to send a AccountProxy to the server will result in a server error.
I don't want to add a find methond in my domain class.
I tried to put this method in my spring service, but i get the same warning.
Edit with the Locator that work fine
Just strange i need to put bean in the applicationContext, context:annotation and context:component-scan seem useless
Any idea?
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method.
If you don't provide a find method of some kind, RequestFactory has no way of reconstituting objects when they get to the server - it can only create brand new ones, which prevents it from merging with existing data. Take this away, and you might as well have RPC again.
If you don't want static methods, provide a Locator instance which is able to find objects. From https://developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory#locators:
What if you don't want to implement persistence code in an entity itself? To implement the required entity locator methods, create an entity locator class that extends Locator:
public class EmployeeLocator extends Locator<Employee, Long> {
#Override
public Employee create(Class<? extends Employee> clazz)
{
return new Employee();
}
...
}
Then associate it with the entity in the #ProxyFor annotation:
#ProxyFor(value = Employee.class, locator = EmployeeLocator.class)
public interface EmployeeProxy extends EntityProxy {
...
}
You'll need to implement all of the methods, not just create - and the main one you are interested in is find(Class, Long). It may be possible to use one single Locator type for all proxies - as of 2.4.0 and 2.5.0-rc1 it is safe to fail to implement getDomainType(), and all of the other methods that need to know the exact type are provided with it as an argument.
Here is an example of what this can look like with JPA and Guice, but I think the idea is clear enough that it can be implemented with Spring and whatever persistence mechanism you are using. Here, all entities are expected to implement HasVersionAndId, allowing the locator to generalize on how to invoke getVersion and getId - you might have your own base class for all persisted entities.
(from https://github.com/niloc132/tvguide-sample-parent/blob/master/tvguide-client/src/main/java/com/acme/gwt/server/InjectingLocator.java)
public class InjectingLocator<T extends HasVersionAndId> extends Locator<T, Long> {
#Inject
Provider<EntityManager> data;
#Inject
Injector injector;
#Override
public T create(Class<? extends T> clazz) {
return injector.getInstance(clazz);
}
#Override
public T find(Class<? extends T> clazz, Long id) {
return data.get().find(clazz, id);
}
#Override
public Class<T> getDomainType() {
throw new UnsupportedOperationException();//unused
}
#Override
public Long getId(T domainObject) {
return domainObject.getId();
}
#Override
public Class<Long> getIdType() {
return Long.class;
}
#Override
public Object getVersion(T domainObject) {
return domainObject.getVersion();
}
}

Resources