Constructor injection not work with qualifiers in #Statless bean - java

I encountered the next exciting problem. If I try to run the next code on Weblogic 12.2.1.3.0:
package hu.sample.bean;
import lombok.extern.slf4j.Slf4j;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.Random;
#Slf4j
#Stateless
public class Salutatory {
private Butler butler;
private Butler assistant;
public Salutatory() {}
#Inject
public Salutatory(#MainButler Butler mainButler,
#Assistant Butler assistant) {
this.butler = mainButler;
this.assistant = assistant;
log.debug("Call with {} {}", mainButler, assistant);
}
public String salute(String name) {
if (new Random().nextBoolean()) {
return butler.welcome(name + "1");
} else {
return assistant.welcome(name + "2");
}
}
}
package hu.sample.bean;
public interface Butler {
String welcome(String name);
}
package hu.sample.bean;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface MainButler {
}
package hu.sample.bean;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface Assistant {
}
package hu.sample.bean;
import javax.ejb.Stateless;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.inject.Named;
#Stateless
#MainButler
public class George implements Butler {
#Override
public String welcome(String name) {
return String.format("Hello, I'm Geroge, I'm the main butler. Welcom here %s! (%s)", name, this.getClass());
}
}
package hu.sample.bean;
import javax.ejb.Stateless;
#Stateless
#Assistant
public class Peter implements Butler {
#Override
public String welcome(String name) {
return String.format("Hello, I'm Peter, I'm an assistant. Welcom here %s! (%s)", name, this.getClass());
}
}
I get the next exception.:
weblogic.management.DeploymentException: CDI deployment failure:WELD-001408: Unsatisfied dependencies for type Butler with qualifiers #Default
at injection point [BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] public hu.sample.bean.Salutatory_mv77ws_Impl(Butler, Butler)
at hu.sample.bean.Salutatory_mv77ws_Impl.<init>(Salutatory_mv77ws_Impl.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
- Session bean [class hu.sample.bean.Peter with qualifiers [#Assistant #Any]; local interfaces are [Butler],
- Session bean [class hu.sample.bean.George with qualifiers [#MainButler #Any]; local interfaces are [Butler]
Ok, I understand this and I create a default implementation of Butler. But in this case the CDI injects default implementation every time, ignored the qualifiers. Why?
The qualifiers works well, if I use setter injection, field injection, etc...
If Salutatory is #WebServlet the constructor injection with qualifiers also works well.
But, why don't constructor injection with qualifiers works well in #Stateless bean?
Many thanks for your answers!

To inject an EJB it's best to use the javax.ejb.EJB annotation in an EJB injection context. (i.e. the injection point is an EJB itself)
In this case, you can use the simple name of the class as the beanName to choose which implementation to inject.
In your case, instead of qualifying the beans in the constructor, do so for each field.
#EJB(beanName="George")
private Butler butler;
#EJB(beanName="Peter")
private Butler assistant;

Related

Force autowire repository by name and not by type

I am trying to create a java + spring library (a seperate, reusable, application independent jar file) and use it in the application.
I show you a non-working example, that is already suitable to demonstrate my problem.
My problem is, that i have difficulties to autowire a repository by name and not by type.
The jar file logic is implemented here in one file called Library
package com.example;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToOne;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.stereotype.Service;
public class Library {
#MappedSuperclass
public static class ChildEntity {
public String childAttribute;
}
#MappedSuperclass
public static class RootEntity<T extends ChildEntity> {
public String rootAttribute;
#OneToOne
public T childEntity;
}
#Service
public static class RootEntityService<T extends ChildEntity> {
#Autowired
#Qualifier(Library.REPOSITORY_BEAN_NAME)
private RootRepository<T> repository;
public RootEntity<T> findMyEntity() {
return this.repository.findByChildEntity();
}
}
#NoRepositoryBean
public static interface RootRepository<T extends ChildEntity> extends Repository<T, Long> {
public RootEntity<T> findByChildEntity();
}
public final static String REPOSITORY_BEAN_NAME = "entityRepository";
}
As you can see i have a RootEntity and a ChildEntity with a OneToOne relation.
The RootRepository is defined as #NoRepositoryBean, since Repository cannot have generic parameters.
The RootService is referencing for the RootRepository and i am trying to autowire by name and not type - using the #Qualifier annotation.
Here comes the application itself:
package com.example;
import javax.persistence.Entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.stereotype.Repository;
import com.example.Library.ChildEntity;
import com.example.Library.RootEntity;
import com.example.Library.RootEntityService;
import com.example.Library.RootRepository;
public class Application implements ApplicationRunner {
#Entity
public static class RealChildEntity extends ChildEntity {
public String realChildAttribute;
}
#Entity
public static class RealRootEntity extends RootEntity<RealChildEntity> {
}
#Repository(Library.REPOSITORY_BEAN_NAME)
public static interface RealRootRepository extends RootRepository<RealChildEntity> {
}
public static void main(final String[] args) throws Exception {
SpringApplication application;
application = new SpringApplication(ApplicationConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
}
#Autowired
private RootEntityService<RealChildEntity> rootEntityService;
#Override
public void run(final ApplicationArguments args) throws Exception {
this.rootEntityService.findMyEntity();
}
}
The RealChildEntity has an application specific attribute.
I define the RealRootRepository to be a real repository, without generic parameters. I have also defined a bean name for this component to refer to.
I also have an ApplicationConfig class to define the service bean and the repo:
package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.example.Library.ChildEntity;
import com.example.Library.RootEntityService;
#Configuration
#EnableJpaRepositories(basePackageClasses = { Application.class })
public class ApplicationConfig {
#Bean(Library.REPOSITORY_BEAN_NAME)
public <T extends ChildEntity> RootEntityService<T> entityService() {
return new RootEntityService<T>();
}
}
If i execute this application, than spring gives me the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.example.Library$RootEntityService required a bean of type 'com.example.Library$RootRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
- #org.springframework.beans.factory.annotation.Qualifier(value="entityRepository")
Spring tells me, that RootEntityService required a bean of type. Why by type?
I want a repository by name
What am i doing wrong?
How can i have a library service without extending it just because of the repository;
Thanks for your help in advance

Implement Interface with custom method and JpaRepository

so I'm working on an API Rest, registration for students, using Domain Driven Design. So my problem is: I'm trying to add a custom method with the JpaRepository. My Project hierarchy is:
com.wizardry.witchcraft.domain.model.StudentModel;
com.wizardry.witchcraft.infraestructure.repository.CustomRepositoryImpl;
com.wizardry.witchcraft.domain.repository.IStudentRepository;
com.wizardry.witchcraft.domain.repository.ICustomRepository;
com.wizardry.witchcraft.api.controller.TesteController2;
com.wizardry.witchcraft.api.controller.StudentController;
So first I've created the method Implementation
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import com.wizardry.witchcraft.domain.model.StudentModel;
import com.wizardry.witchcraft.domain.repository.ICustomRepository;
#Repository
public class CustomRepositoryImpl implements ICustomRepository {
#PersistenceContext
private EntityManager manager;
#Override
public List<StudentModel> findCustom (String name){
//METHOD
}
}
And the Interface:
import com.wizardry.witchcraft.domain.model.StudentModel;
public interface ICustomRepository {
List<StudentModel> findCustom(String name);
}
And to test it out I created a new Controller to don't mess with my working one, TesteController2, and it worked fine. So my next step was extended the ICustomRepository in the IStudentRepository, made the changes in TesteController2 and then Spring won't find my findCustom method anymore, It tries to create the method as a JPA keyword and return and error. This is my repository interface:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.wizardry.witchcraft.domain.model.StudentModel;
#Repository
public interface IStudentRepository extends ICustomRepository, JpaRepository<StudentModel, Long> {
List<StudentModel> queryByName(String name, #Param ("id") Long school);
List<StudentModel> queryFirstByNameContaining(String name);
List<StudentModel> queryTop2ByNameContaining(String name);
int countByWizardingSchoolModelId(Long school);
}
And the TesteController2:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.wizardry.witchcraft.domain.model.StudentModel;
import com.wizardry.witchcraft.domain.repository.IStudentRepository;
#RestController
#RequestMapping(value = "/test")
public class TesteController2 {
#Autowired
private IStudentRepository iStudentRepository;
#ResponseStatus(HttpStatus.ACCEPTED)
#GetMapping
public List<StudentModel> findCustom2(String name) {
return iStudentRepository.findCustom(name);
}
}
PS: I have a Service Layer com.wizardry.witchcraft.domain.service.RegisterStudentService however
the method in question does not go through it(yet!) because I'm testing, I tried to pass by the service and see what happen but give the same ERROR.
*
ERROR: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'registerStudentService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'registerStudentService': Unsatisfied dependency expressed through field 'iStudentRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'IStudentRepository' defined in com.wizardry.witchcraft.domain.repository.IStudentRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.wizardry.witchcraft.domain.repository.ICustomRepository.findCustom(java.lang.String)! No property findCustom found for type StudentModel!
*
Thanks in Advance! I'm really lost here.
Just rename your class CustomRepositoryImpl to IStudentRepositoryImpl and it should work.
#Repository
public class IStudentRepositoryImpl implements ICustomRepository {
#PersistenceContext
private EntityManager manager;
#Override
public List<StudentModel> findCustom (String name){
//METHOD
}
}
Below is the documentation from Spring
Configuration If you use namespace configuration, the repository
infrastructure tries to autodetect custom implementations by scanning
for classes below the package we found a repository in. These classes
need to follow the naming convention of appending the namespace
element's attribute repository-impl-postfix to the found repository
interface name. This postfix defaults to Impl.
https://docs.spring.io/spring-data/data-commons/docs/current-SNAPSHOT/reference/html/#repositories.custom-implementations

Field in required a bean of type that could not be found consider defining a bean of type in your configuration

I'm getting the following error when trying to run my app:
Field edao in com.alon.service.EmployeeServiceImpl required a bean of
type 'com.alon.repository.EmployeeRepository' that could not be found.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type
'com.alon.repository.EmployeeRepository' in your configuration.
Project structure:
EmployeeRepository:
package com.alon.repository;
import com.alon.model.Employee;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface EmployeeRepository {
List<Employee> findByDesignation(String designation);
void saveAll(List<Employee> employees);
Iterable<Employee> findAll();
}
EmployeeServiceImpl:
package com.alon.service;
import com.alon.model.Employee;
import com.alon.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
private EmployeeRepository edao;
#Override
public void saveEmployee(List<Employee> employees) {
edao.saveAll(employees);
}
#Override
public Iterable<Employee> findAllEmployees() {
return edao.findAll();
}
#Override
public List<Employee> findByDesignation(String designation) {
return edao.findByDesignation(designation);
}
}
MyApplication:
package com.alon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class MyApplicataion {
public static void main(String[] args) {
SpringApplication.run(MyApplicataion.class, args);
}
}
As you have added spring-boot tag I guess you are using sprig data jpa. Your repository interfaces should extend org.springframework.data.repository.Repository (a marker interface) or one of its sub interfaces (usually org.springframework.data.repository.CrudRepository) for instructing spring to provide a runtime implementation of your repository, if any of those interfaces are not extened you'll get
bean of type 'com.alon.repository.EmployeeRepository' that could not
be found.
I assume you try to use spring data JPA. What you can check / debug is:
Is JpaRepositoriesAutoConfiguration executed? You can see this in the start up log in the debug log level
Does something change if you addionally add #EnableJpaRepositories with the corresponding basepackages.
Add #ComponentScan with the corresponding packages, normally #SpringBootApplication should do it, but just in case.
you can also check the autconfig documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html
EDIT: see comment from #ali4j: I did not see that it is the generic spring Repository interface and not the spring data interface
regards,WiPu

Can I access a named java bean?

I'm trying to create a Java EE application with #Named component.
import javax.inject.Named;
#Named
public class MyNamedBean {
public int myMethod() { ... }
}
But for some reasons, I need to access to this named java bean's methods from another java bean. So how can I do it ? Should I use #Inject annotation ?
I don't know what tag should be added. Please edit the question if needed.
Yes, the annotation #Inject can be used to inject the bean MyNamedBean to another bean, then access the method wanted from the injected bean.
Example :
Zoo.java
import javax.inject.Inject;
public class Zoo {
#Inject
private Dog dog;
public Zoo() {
}
public void talk() {
dog.talk();
}
}
Dog.java
import javax.inject.Named;
import javax.inject.Singleton;
#Named
#Singleton // optional
public class Dog {
public void talk() {
System.out.println("wowowo~ ฅ^•ﻌ•^ฅ");
}
}

#Qualifier does not work within Junit4

I try to improve my Spring knowledge by reading Spring in action 4.
When I've to to section, describing using of Qualifier annotation (3.3.2), i faced the problem.
To test this annotation in action, I wrote Dessert interface, which is implemented by 3 classes, creating in context using #Component annotation.
I also created class Taster, which "tastes" some dessert, autowired into by some qualifier.
When I run my application, using AnnotationConfigApplicationContext - everything works good. With SpringJUnit4ClassRunner - it does not. I guess I miss something in my test code, but I do not have enough knowledge to realize what.
Interface:
package bakery.intrface;
#FunctionalInterface
public interface Dessert {
void introduce();
}
Cake:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class Cake implements Dessert {
#Override
public void introduce() {
System.out.println("I am a cake!");
}
}
Cookie:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class Cookie implements Dessert {
#Override
public void introduce() {
System.out.println("I'm a cookie!");
}
}
Ice cream:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class IceCream implements Dessert {
#Override
public void introduce() {
System.out.println("I'm an ice cream!");
}
}
The class, consumes some bean, Taster:
package bakery;
import bakery.intrface.Dessert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
#Component
public class Taster {
private Dessert dessert;
public void taste(){
dessert.introduce();
}
#Autowired
#Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
}
Configuration:
package bakery.config;
import bakery.Bakery;
import bakery.Taster;
import bakery.desserts.Cake;
import bakery.intrface.Dessert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(basePackageClasses = Bakery.class)
public class BakeryConfig {
}
Run class:
package bakery;
import bakery.config.BakeryConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Bakery {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BakeryConfig.class);
String[] beans = context.getBeanDefinitionNames();
Taster taster = (Taster) context.getBean("taster");
taster.taste();
}
}
Test class:
package bakery;
import bakery.config.BakeryConfig;
import bakery.intrface.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertNotNull;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
#Autowired
Dessert dessert;
#Autowired
Taster taster;
#Test
public void contextInit(){
assertNotNull(dessert);
dessert.introduce();
}
#Test
public void tasterInit(){
assertNotNull(taster);
}
}
When I run the test, I'm getting the exception: No qualifying bean of type [bakery.intrface.Dessert] is defined: expected single matching bean but found 3: cookie,iceCream,cake.
There are 3 "Dessert" beans in your application context, you have to specify which one you want to wire.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
#Autowired
#Qualifier("iceCream") // <===================== you must specify which bean to be wired
Dessert dessert;
#Autowired
Taster taster;
This is to be expected.
The declaration
#Autowired
Dessert dessert;
is asking for a Dessert object. Dessert is the interface, and there are three implementing classes, Cookie, IceCream, and Cake. Since you haven't made it more explicit which of those implementations you want, Spring throws an error because it can't decide what to do.
If you need this in your test, you can do one of the following:
#Autowired
#Qualifier("iceCream")
Dessert dessert;
to get only the ice cream dessert,
OR
#Autowired
List<Dessert> desserts;
to get a list containing all the implementations.

Categories

Resources