I want to use #Qualifier to dynamically specifying parameters? how to do it ?
#Qualifier("two") 'two' as a parameter ,can be 'one' 'three' or other.
Can i use aop dynamically design 'two'?
means I want to change the name of service with a #Qualifier by parameters.
the parameter from the url 'Token'.
case: url: http://localhost:8080/insert/order, token has a parameter: companyId = one
#RestController
public class ApiWebService {
#Autowired
#Qualifier("two")
//#Qualifier("one")
private BaseService baseService;
#GetMapping("insert/order")
public void test() {
baseService.insertOrder();
}
}
#Service("one")
public class CompanyOneService extends BaseService {
#Override
public void insertOrder() {
System.out.println("conpanyOne");
System.out.println("baseInsertOrder");
}
}
#Service("two")
public class CompanyTwoService extends BaseService {
#Override
public void insertOrder(){
System.out.println("companyTwo");
System.out.println("baseInsertOrder");
}
}
three
four
...
#Service
public class BaseService {
public void insertOrder(){
System.out.println("baseInsertOrder");
}
}
你好 !
No you cannot , mostly because the attribute in Java annotation does not allow to assign with variables.
Actually you want to choose an implementation to use based on some runtime conditions(i.e.companyId in your case). You can achieve it using factory pattern with #Configuration and #Bean which is much more elegant and easier to understand than your ugly AOP solution:
First define a factory:
#Configuration
public class ServiceFactory{
#Bean
public BaseService companyOneService(){
return new CompanyOneService();
}
#Bean
public BaseService companyTwoService(){
return new CompanyTwoService();
}
public BaseService getService(Integer companyId){
if(companyId == 1){
return companyOneService();
}else if(company==2){
return companyTwoService();
}else{
//blablablab
}
}
}
In the controller , inject the ServiceFactory to get the related Service based on the the company Id
#RestController
public class ApiWebService {
#Autowired
private ServiceFactory serviceFactory;
#GetMapping("insert/order")
public void test() {
Integer companyId = getCompanyIdFromToken(httpServletRequest);
BaseService service = serviceFactory.getService(companyId);
service.blablabla();
}
}
Inject (autowire) ApplicationContext into your class and use one of getBeans* method to find the exact bean you need.
aspect
#Aspect
#Component
public class ApiAspect {
#Pointcut("execution(* com.example.demo.control.ApiWebService.*(..))")
public void apiInputWebService() {
}
#Before("apiInputWebService()")
public void apiInputAuth(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes())
.getRequest();
String token = request.getHeader("Authorization");
//compangId can be from token
String compangId = "one";
Object target = joinPoint.getTarget();
Method method = target.getClass().getMethod("before", String.class);
method.invoke(target, compangId);
}
}
control
#RestController
public class ApiWebService {
private ApiService baseService;
#Autowired
private ApplicationContext applicationContext;
public void before(String company) {
baseService = (ApiService) applicationContext.getBean(company);
}
#GetMapping("insert/order")
public void test() {
baseService.insertOrder();
}
}
service
#Service
public class ApiService {
public void insertOrder(){
System.out.println("baseInsertOrder");
}
}
#Service("one")
public class CompanyOneService extends ApiService {
#Override
public void insertOrder() {
System.out.println("conpanyOne");
System.out.println("baseInsertOrder");
}
}
#Service("two")
public class CompanyTwoService extends ApiService {
#Override
public void insertOrder(){
System.out.println("companyTwo");
System.out.println("baseInsertOrder");
}
}
Related
My service has a #Controller with multiple APIs.
Each API accepts a specific kind of object.
I would like to inject a single interface into a controller class, but have different implementations of the interface depending on the type of the input argument - is that possible?
#Controller
public class ApiClass{
private final Service service;
public ApiClass(Service service) {
this.service = service;
}
public ResponseEntity<Response> apiFirst (Object1 object1) {
return ResponseEntity.ok(service.process(object1));
}
public ResponseEntity<Response> apiTwo (Object2 object2) {
return ResponseEntity.ok(service.process(object2));
}
}
public interface Service <T extends OwnObjectClass>{
void process (T object);
}
public class Implementation1 implements Service {
#Override
void process (Object1 object) {
--some code;
}
}
public class Implementation2 implements Service {
#Override
void process (Object2 object) {
--some code;
}
}
How to do it correctly so that for each implementation not to add a new injection to the ApiClass?
Spring will provide the primary bean for the interface implementation unless you use the #Qualifer annotation with the desired instance. The injected bean can not mutate to another instance.
If you don't want to use multiple injections in the controller, you can create a ServiceProvider and ask for a specific implementation each time.
Here is an example:
public class ApiClass{
private final ServiceProvider provider;
public ApiClass(ServiceProvider provider) {
this.provider = provider;
}
public ResponseEntity<Response> apiFirst (Object1 object1) {
return ResponseEntity.ok(provider.getService("Implementation1").process(object1));
}
public ResponseEntity<Response> apiTwo (Object2 object2) {
return ResponseEntity.ok(provider.getService("Implementation2").process(object1));
}
}
#org.springframework.stereotype.Service
public class ServiceProvider {
private Map<String, Service> services;
public ServiceProvider(List<Service> services) {
this.services = services.stream()
.collect(java.util.stream.Collectors.toMap(
Service::type,
service -> service
)
);
}
public Service getService(String type) {
return services.get(type);
}
}
interface Service<T extends OwnObjectClass> {
String type();
void process(T object);
}
#org.springframework.stereotype.Service("Implementation1")
class Implementation1 implements Service {
#Override
public String type() {
return "Implementation1";
}
#Override
public void process(OwnObjectClass object) {
}
}
#org.springframework.stereotype.Service("Implementation2")
class Implementation2 implements Service {
#Override
public String type() {
return "Implementation2";
}
#Override
public void process(OwnObjectClass object) {
}
}
You can change the string in the type for an Enum.
There is another way using HandlerMethodArgumentResolver where you can inject your dependency directly into the method definition.
Here is a nice article explaining it: https://reflectoring.io/spring-boot-argumentresolver/
I am new to spring boot. So I am working on a simple application where I have 2 services EnglishLanguageService and SpanishLanguageService. In the end, I would like to print Hello and Hola when the app runs but I am getting the error
Parameter 0 of constructor in controller.ConstructorInjectedController required a single bean, but 2 were found:
- profileEnglishLanguageService: defined in file [/Users/user/Downloads/depdency-injection-example/target/classes/services/EnglishLanguageService.class]
- profileSpanishLanguageService: defined in file [/Users/user/Downloads/depdency-injection-example/target/classes/services/SpanishLanguageService.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
I am not sure where I am going wrong. I will paste my code down below.
MainApplication
#SpringBootApplication
#ComponentScan(basePackages= {"controller" , "services"})
public class DepdencyInjectionExampleApplication {
public static void main(String[] args) {
// returns application context
ApplicationContext ctx = SpringApplication.run(DepdencyInjectionExampleApplication.class, args);
System.out.println(ctx.getBean(PropertyInjectedController.class).getGreeting());
System.out.println(ctx.getBean(ConstructorInjectedController.class).getGreetings());
}
ConstructorInjectedController class
#Component
public class ConstructorInjectedController {
private final GreetingService greetingService;
public ConstructorInjectedController(GreetingService greetingService) {
this.greetingService = greetingService;
}
public String getGreetings() {
return greetingService.sayGreetings();
}
PropertyInjectedControllerClass
#Controller
public class PropertyInjectedController {
public GreetingService greetingService;
public String getGreeting() {
return greetingService.sayGreetings();
}
}
GreetingService interface
public interface GreetingService {
String sayGreetings();
}
EnglishLanguageService class
#Service
public class EnglishLanguageService implements GreetingService {
private GreetingService greetingService;
public EnglishLanguageService(#Qualifier("english")
GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
EnglishServiceImpl
#Profile("english")
public class EnglishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hello";
}
SpanishLanguageService
#Service
public class SpanishLanguageService implements GreetingService {
private GreetingService greetingService;
public SpanishLanguageService(#Qualifier("spanish")
GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
SpanishLanguageServiceImpl
#Profile("spanish")
public class SpanishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hola";
}
}
You are using the annotations wrong. #Profile is used for something completely other than you think so it should be removed.
You are basically saying to spring "please inject a specific greeting service in class X please".
Right now spring doesn't know which bean you want since you have two different ones implementing the same interface. So we use the #Qualifier annotation to tag a bean, and also to target it during injection.
Fist your implementation should have either a #Service or a #Component so that spring finds it during the component scan, and instantiates it as a spring managed bean. This combined with a #Qualifier so that we tag it, so we later can pick this tag during injection:
#Component
#Qualifier("english")
public class EnglishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hello";
}
}
Here we inject the greeting service and use our qualifier to tell spring which bean we want to inject of the available ones.
#Service
public class EnglishLanguageService implements GreetingService {
private GreetingService greetingService;
public SpanishLanguageService(#Qualifier("english") GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
}
I have encountered with the following issue. I would like to create Spring #Component with generic
#Component
public class ResponseDtoValidator<DTO> {
public ResponseEntity<DTO> methodToInvoke(DTO dto) {
return Optional.ofNullable(dto).map(result -> new >ResponseEntity<>
(result, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); }
}
#Controller
public class SomeController {
#Inject
private ResponseDtoValidator<DTO1> responseDtoValidator1;
#Inject
private ResponseDtoValidator<DTO2> responseDtoValidator2;
public void someMethod() {
DTO1 dto1 = new DTO1();
DTO2 dto2 = new DTO2();
responseDtoValidator1.methodToInvoke(dto1);
responseDtoValidator2.methodToInvoke(dto2);
}
}
Can I inject this Component like above? Actually, I have tried and it seems to work properly, can you please confirm that I am correct or not?
Firstly, a spring bean can not be injected in itself.
In regards to your question,yes it is injectable but dont use generic signs when injecting. Just inject it normally. Generic type is send when a method of generic class is utilizing.
For instance;
#Component
public class ResponseDtoValidator<DTO> {
public void getAbc(List<DTO> aList) {}
}
Then;
public class Test {
#Autowired // or #Inject
private ResponseDtoValidator responseDtoValidator;
public void testMethod() {
responseDtoValidator.getAbc(List<EnterATypeInHere> aList);
}
}
I'm trying to do something like this (which works)
#Service("myService")
#Scope("session")
public class MyService {
public boolean hasAccessOnResource(String str)
{
return true;
}
#PreAuthorize("#myService.hasAccessOnResource(#str)")
public void execute(String str)
{
System.out.println("hello");
}
}
However, when the MyService implements an Interface and i call the execute over an interface, I get an exception.
At the controller (for ref)
#Controller
#Scope("session")
public class HomeController {
#Autowired
#Qualifier("myService")
MyServiceInterface service; // <- doesn't work case
//MyService service; <- working case
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpSession session)
{
service.execute("hello");
return "home";
}
}
The service implementing an interface..
#Service("myService")
#Scope("session")
public class MyService implements MyServiceInterface{
public boolean hasAccessOnResource(String str)
{
return true;
}
#PreAuthorize("#myService.hasAccessOnResource(#str)")
#Override
public void execute(String str)
{
System.out.println("hello");
}
}
The exception:
org.springframework.expression.spel.SpelEvaluationException: EL1004E:(pos 11): Method call: Method hasAccessOnResource(java.lang.String) cannot be found on com.sun.proxy.$Proxy52 type
What am I doing wrong? Is this the correct way? What is the approach i need to take if i want to PreAuthorize over this interface method?
I am attempting to figure out how to use a custom annotation and HK2 to inject something into a Resource method. Because I'm in a Spring webapp environment, I just piled on the existing helloworld-spring-webapp Jersey 2 example. My problem is, is the Resource method is called twice. The first time, the injection happens successfully, the second time, it does not.
InjectionResolver.resolve() method
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> root) {
return "THIS HAS BEEN INJECTED APPROPRIATELY";
}
Binder.configure() method
#Override
protected void configure() {
bind(SampleInjectionResolver.class).to(new TypeLiteral<InjectionResolver<SampleParam>>() {}).in(Singleton.class);
}
ResourceConfig registering of binder
public MyApplication () {
register(new SampleInjectionResolver.Binder());
...
JerseyResource.getHello()
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getHello(#SampleParam String inject) {
System.err.println("EXECUTING!");
System.err.println("*******************************INJECTED: " + inject);
return inject;
}
Server output from a SINGLE call
EXECUTING!
*******************************INJECTED: THIS HAS BEEN INJECTED APPROPRIATELY
EXECUTING!
*******************************INJECTED:
Have I missed a configuration somewhere? I can't figure out why it's being called twice. I'm assuming if I fix that, the issue of the InjectionResolver not working on the 2nd call will be a non-issue.
I faced with the exactly same issue - Twice call of the annotated resource method.
After deep investigation, I have found the way, how to use custom annotation in the Jersey 2.x.
Custom annotation class (TestMessage.java):
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER })
public #interface TestMessage {}
Custom annotation resolver (TestMessageResolver.java):
public class TestMessageResolver {
// InjectionResolver implementation
public static class TestMessageInjectionResolver extends ParamInjectionResolver<TestMessage> {
public TestMessageInjectionResolver() {
super(TestMessageValueFactoryProvider.class);
}
}
// ValueFactoryProvider implementation
#Singleton
public static class TestMessageValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public TestMessageValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator injector) {
super(mpep, injector, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) {
return null;
}
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
return "testString";
}
};
}
}
// Binder implementation
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(TestMessageValueFactoryProvider.class).
to(ValueFactoryProvider.class).
in(Singleton.class);
bind(TestMessageInjectionResolver.class).
to(new TypeLiteral<InjectionResolver<TestMessage>>(){}).
in(Singleton.class);
}
}
}
Custom annotation usage (JerseyResource.java):
#Path("jersey")
public class JerseyResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getMethod(#TestMessage String message) {
return "getMethod(), message=" + message;
}
}
Resolver registration(SpringJerseyApplication.java):
public class SpringJerseyApplication extends ResourceConfig {
public SpringJerseyApplication() {
register(JerseyResource.class);
register(new TestMessageResolver.Binder());
}
}
Hope it will be helpful :)