How to inject bean via generic variable in spring - java

I have problem with injecting bean with generic types. Look at the example. I will inject to the service a repository which types takes from App class. Now i have exception:
No qualifying bean of type 'asd.IRepository' available: expected single matching bean but found 2: a,b
asd here is package, just for tests.
What can I do in this situation? Is any way to makes it?
public interface IRepository<T, V> {
void print();
}
#Component
public class A implements IRepository<String,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Component
public class B implements IRepository<Double,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Service
public class ServiceABC<V, T> {
#Autowired
private IRepository<V,T> repo;
public void print(){
repo.print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC<String, String> serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print();
}

It looks like you don't know in advance which implementation of your IRepository interface you will need. And you will know that at runtime. In this case it is a typical case for Factory pattern where you will have a IRepositoryFactory that will have a method thhat retrieves specific implementation by type (for example IRepositoryFactory.getInstance(String type); So in your ServiceABC you may use the IRepository to get specific bean at runtime. So Factory pattern may be an answer to your question. I also wrote an article that deals with this type of problem and proposes the idea of self-populating Factory (using Open source library that provides such utility). Here is the link to the article: Non-intrusive access to "Orphaned" Beans in Spring framework

You have to name your components and autowire by name:
#Component("A")
public class A implements IRepository<String,String> {...}
#Component("B")
public class B implements IRepository<Double,String> {...}
[...]
#Autowired
#Qualifier("B")
private IRepository repo;

Something like that?
#Controller
public class RepositoryFactory {
#Autowired
private IRepository<String, String> a;
#Autowired
private IRepository<Double, String> b;
public IRepository getRepository(String className) {
if(className.equalsIgnoreCase("a")) {
return a;
} else if(className.equalsIgnoreCase("b")) {
return b;
}
return null;
}
}
#Service
public class ServiceABC {
#Autowired
private RepositoryFactory repositoryFactory;
public void print(String className){
repositoryFactory.getRepository(className).print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print(A.class.getSimpleName());
}
}s

Related

Autowire static Interface Object - Spring boot 2.0 [duplicate]

Is there some way to use #Autowired with static fields. If not, are there some other ways to do this?
In short, no. You cannot autowire or manually wire static fields in Spring. You'll have to write your own logic to do this.
#Component("NewClass")
public class NewClass{
private static SomeThing someThing;
#Autowired
public void setSomeThing(SomeThing someThing){
NewClass.someThing = someThing;
}
}
#Autowired can be used with setters so you could have a setter modifying an static field.
Just one final suggestion... DON'T
Init your autowired component in #PostConstruct method
#Component
public class TestClass {
private static AutowiredTypeComponent component;
#Autowired
private AutowiredTypeComponent autowiredComponent;
#PostConstruct
private void init() {
component = this.autowiredComponent;
}
public static void testMethod() {
component.callTestMethod();
}
}
Create a bean which you can autowire which will initialize the static variable as a side effect.
Wanted to add to answers that auto wiring static field (or constant) will be ignored, but also won't create any error:
#Autowired
private static String staticField = "staticValue";
You can achieve this using XML notation and the MethodInvokingFactoryBean. For an example look here.
private static StaticBean staticBean;
public void setStaticBean(StaticBean staticBean) {
StaticBean.staticBean = staticBean;
}
You should aim to use spring injection where possible as this is the recommended approach but this is not always possible as I'm sure you can imagine as not everything can be pulled from the spring container or you maybe dealing with legacy systems.
Note testing can also be more difficult with this approach.
You can use ApplicationContextAware
#Component
public class AppContext implements ApplicationContextAware{
public static ApplicationContext applicationContext;
public AppBeans(){
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
then
static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);
Disclaimer This is by no means standard and there could very well be a better spring way of doing this. None of the above answers address the issues of wiring a public static field.
I wanted to accomplish three things.
Use spring to "Autowire" (Im using #Value)
Expose a public static value
Prevent modification
My object looks like this
private static String BRANCH = "testBranch";
#Value("${content.client.branch}")
public void finalSetBranch(String branch) {
BRANCH = branch;
}
public static String BRANCH() {
return BRANCH;
}
We have checked off 1 & 2 already now how do we prevent calls to the setter, since we cannot hide it.
#Component
#Aspect
public class FinalAutowiredHelper {
#Before("finalMethods()")
public void beforeFinal(JoinPoint joinPoint) {
throw new FinalAutowiredHelper().new ModifySudoFinalError("");
}
#Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
public void finalMethods() {}
public class ModifySudoFinalError extends Error {
private String msg;
public ModifySudoFinalError(String msg) {
this.msg = msg;
}
#Override
public String getMessage() {
return "Attempted modification of a final property: " + msg;
}
}
This aspect will wrap all methods beginning with final and throw an error if they are called.
I dont think this is particularly useful, but if you are ocd and like to keep you peas and carrots separated this is one way to do it safely.
Important Spring does not call your aspects when it calls a function. Made this easier, to bad I worked out the logic before figuring that out.
Generally, setting static field by object instance is a bad practice.
to avoid optional issues you can add synchronized definition, and set it only if private static Logger logger;
#Autowired
public synchronized void setLogger(Logger logger)
{
if (MyClass.logger == null)
{
MyClass.logger = logger;
}
}
:
Solution 1 : Using Constructor #Autowired For Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
public MyClass(MyService service) {
TestClass.service= service;
}
}
Solution 2 : Using #PostConstruct to set the value to Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
private MyService srv;
#PostConstruct
public void init() {
this.service= srv;
}
}
Refer here for more detail
I use private static inner Component: FieldSetter, to inject static field: MyBean, at last SelfDestroyBean will help me remove redundant FiledSetter bean
public final class MyClass {
private static MyBean myBean;
#Component
private static class FieldSetter extends SelfDestroyBean {
public FieldSetter(MyBean myBean) {
MyClass.myBean = myBean;
}
}
}
#SuppressWarnings("SpringJavaAutowiredMembersInspection")
public abstract class SelfDestroyBean {
#Autowired
private ApplicationContext context;
#PostConstruct
public void destroy() {
final String[] beanNames = context.getBeanNamesForType(this.getClass());
final BeanDefinitionRegistry registry =
((BeanDefinitionRegistry) context.getAutowireCapableBeanFactory());
for (String beanName : beanNames) {
registry.removeBeanDefinition(beanName);
}
}
}
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);

Spring Qualifier without if-else

I use Spring Framework. I want to make service for create an archive. I have two types of archive. I would like to know another way how to do it not using if-else statement.
Interface
public interface Archive {
public String makeArchive();
}
Implements 1
#Component
public class ArchiveRAR implements Archive {
#Override
public String makeArchive() {
return "I made archive RAR.";
}
}
Implements 2
#Component
public class ArchiveZIP implements Archive {
#Override
public String makeArchive() {
return "I made archive ZIP.";
}
}
Service
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
#Qualifier("archiveRAR")
Archive archiveRAR;
#Autowired
#Qualifier("archiveZIP")
Archive archiveZIP;
#Override
public void run(String... strings) throws Exception {
Report report = new Report("ZIP");
//HERE
if ("RAR".equals(report.getType())) {
System.out.println(archiveRAR.makeArchive());
} else if ("ZIP".equals(report.getType())) {
System.out.println(archiveZIP.makeArchive());
}
}
}
MainApp
#SpringBootApplication
public class MainApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(MainApp.class, args);
}
}
Thank you.
I suggest you to use here factory pattern instead of if-else statement:
#Component
public class ArchiveFactory {
#Autowired
private Map<String, Archive> archives;
public Archive getArhive(String archiveType) {
return archives.get(archiveType);
}
}
By this way your main method will be:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private ArchiveFactory archiveFactory;
#Override
public void run(String... strings) throws Exception {
Report report = new Report("ZIP");
Archive archive = archiveFactory.get(report.getType());
System.out.println(archive.makeArchive());
}
}
Of course you need also to define bean of type Map<String, Archive>.
One other way..
#Component("RAR")
public class ArchiveRAR implements Archive{...}
#Component("ZIP")
public class ArchiveZIP implements Archive {...}
And the service class to inject BeanFactory
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
BeanFactory beans;
#Override
public void run(String... strings) throws Exception {
String archiveType = "ZIP/RAR";
Archive archive = beans.getBean(archiveType,Archive.class);
archive.makeArchive();
}
There is a cleaner way to do it without basically if-else and String comparison if you can decide beforehand which one you would like to use: just pass the appropriate Archive reference to the Report bean when you create it and also define a method that will generate your archive:
#Component
class Report {
private Archive archive;
// declare that Archive dependency is required
#Autowired
public Report(#Qualifier("archiveRar") Archive archive) {
// or public Report(#Qualifier("archiveZip") Archive archive) {
// depending on which one you would like to inject and use
this.archive = archive;
}
public void generateArchive() {
archive.makeArchive();
}
}
Also annotate your 2 Archive implementations with meaningful qualifier names:
#Component
#Qualifier("archiveRar")
public class ArchiveRAR implements Archive {
#Override
public String makeArchive() {
return "I made archive RAR.";
}
}
and
#Component
#Qualifier("archiveZip")
public class ArchiveZIP implements Archive {
#Override
public String makeArchive() {
return "I made archive ZIP.";
}
}
The Service will now look simpler:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private Report report;
#Override
public void run(String... strings) throws Exception {
report.generateArchive();
}
}
If you need run-time injection, you can get rid of the if-else but not String value qualifier for the bean, for example by a accessing the Spring bean container using BeanFactory.
Keep the above code for Archive and its implementations and delete the Report class which will no longer be used. In the Service use this:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private BeanFactory beanFactory;
#Override
public void run(String... strings) throws Exception {
String qualifierValue = "archiveRar";
// or String qualifierValue = "archiveZip";
Archive archive = beanFactory.getBean(qualifierValue, Archive.class);
System.out.println(archive.makeArchive());
}
}

Spring 4 Component Inject with generic

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

How to collect several interfaces into a Collection in HK2?

I have my AbstractBinder and I bind several classes with the same interface. Let's say I bind Fish and Cat which both implement Animal interface.
What is the easiest/proper way of injecting them into a bean which takes Collection<Animal> ?
PS: Spring has equivalent in simply #Autowire List<Animal> and the collection is created and populated by Spring.
HK2 has IterableProvider<T>, as mentioned here in the documentation. You can get the service by name, by qualifier annotation, or just iterate over them, as it's an Iterable. Just for fun, here is a test.
public class IterableProviderTest {
public static interface Service {}
public static class ServiceOne implements Service {}
#QualAnno
public static class ServiceTwo implements Service {}
#Qualifier
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public static #interface QualAnno {
public static class Instance
extends AnnotationLiteral<QualAnno> implements QualAnno {
public static QualAnno get() {
return new Instance();
}
}
}
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(ServiceOne.class).to(Service.class).named("one");
bind(ServiceTwo.class).to(Service.class).qualifiedBy(QualAnno.Instance.get());
}
}
#Inject
private IterableProvider<Service> services;
#Test
public void test_IterableProvider() {
ServiceLocator locator = ServiceLocatorUtilities.bind(new Binder());
locator.inject(IterableProviderTest.this);
assertEquals(2, services.getSize());
Service serviceOne = services.named("one").get();
assertTrue(serviceOne instanceof ServiceOne);
Service serviceTwo = services.qualifiedWith(QualAnno.Instance.get()).get();
assertTrue(serviceTwo instanceof ServiceTwo);
}
}
UPDATE
For a List<Service> (to avoid HK2 InterablProvider), the only think I can think of is to use a Factory and inject the IterableProvider into it, and from there return the list. For example
public class Binder extends AbstractBinder {
#Override
protected void configure() {
...
bindFactory(ListServiceFactory.class).to(new TypeLiteral<List<Service>>(){});
}
}
public static class ListServiceFactory implements Factory<List<Service>> {
#Inject
private IterableProvider<Service> services;
#Override
public List<Service> provide() {
return Lists.newArrayList(services);
}
#Override
public void dispose(List<Service> t) {}
}
Yeah it's a little bit of extra work.
In the latest release of hk2 (2.4.0) you can
#Inject Iterable<Foo> foos;
That allows you to keep your pojo's without any hk2 API in them.
For more information see: Iterable Injection

Inject bean into enum

I have the DataPrepareService that prepare data for reports and I have an Enum with report types, and I need to inject ReportService into Enum or have access to ReportService from enum.
my service:
#Service
public class DataPrepareService {
// my service
}
my enum:
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
REPORT_3("name", "filename")
public abstract Map<String, Object> getSpecificParams();
public Map<String, Object> getCommonParams(){
// some code that requires service
}
}
I tried to use
#Autowired
DataPrepareService dataPrepareService;
, but it didn't work
How can I inject my service into enum?
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename");
#Component
public static class ReportTypeServiceInjector {
#Autowired
private DataPrepareService dataPrepareService;
#PostConstruct
public void postConstruct() {
for (ReportType rt : EnumSet.allOf(ReportType.class))
rt.setDataPrepareService(dataPrepareService);
}
}
[...]
}
weekens' answer works if you change inner class to static so spring can see it
Maybe something like this:
public enum ReportType {
#Component
public class ReportTypeServiceInjector {
#Autowired
private DataPrepareService dataPrepareService;
#PostConstruct
public void postConstruct() {
for (ReportType rt : EnumSet.allOf(ReportType.class))
rt.setDataPrepareService(dataPrepareService);
}
}
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
...
}
There is one another approach you may like to explore. However instead of injecting a bean into enum it associates a bean with an enum
Say you have an enum WidgetType and Widget class
public enum WidgetType {
FOO, BAR;
}
public class Widget {
WidgetType widgetType;
String message;
public Widget(WidgetType widgetType, String message) {
this.widgetType = widgetType;
this.message = message;
}
}
And you want to create Widgets of this type using a Factory BarFactory or FooFactory
public interface AbstractWidgetFactory {
Widget createWidget();
WidgetType factoryFor();
}
#Component
public class BarFactory implements AbstractWidgetFactory {
#Override
public Widget createWidget() {
return new Widget(BAR, "A Foo Widget");
}
#Override
public WidgetType factoryFor() {
return BAR;
}
}
#Component
public class FooFactory implements AbstractWidgetFactory {
#Override
public Widget createWidget() {
return new Widget(FOO, "A Foo Widget");
}
#Override
public WidgetType factoryFor() {
return FOO;
}
}
The WidgetService is where most of the work happens. Here I have a simple AutoWired field which keeps tracks of all the registered WidgetFactories. As a postConstruct operation we create a map of the enum and the associated factory.
Now clients could inject the WidgetService class and get the factory for the given enum type
#Service
public class WidgetService {
#Autowired
List<AbstractWidgetFactory> widgetFactories;
Map<WidgetType, AbstractWidgetFactory> factoryMap = new HashMap<>();
#PostConstruct
public void init() {
widgetFactories.forEach(w -> {
factoryMap.put(w.factoryFor(), w);
});
}
public Widget getWidgetOfType(WidgetType widgetType) {
return factoryMap.get(widgetType).createWidget();
}
}
Enums are static, so you have to figure out a way to access to the beans from a static context.
You can create a class named ApplicationContextProvider that implements the ApplicationContextAware interface.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware{
private static ApplicationContext appContext = null;
public static ApplicationContext getApplicationContext() {
return appContext;
}
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
this.appContext = appContext;
}
}
then add this your application context file:
<bean id="applicationContextProvider" class="xxx.xxx.ApplicationContextProvider"></bean>
after that you could access to the application context in a static way like this:
ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
it will be hard to control that the spring container is already up and running at the time the enum is instantiated (if you had a variable with this type in a test-case, your container will usually not be there, even aspectj autowiring won't help there). i would recommend to just let the dataprepare-service or something give you the specific-params with a lookup-method with the enum-parameter.
I think this what you need
public enum MyEnum {
ONE,TWO,THREE;
}
Autowire the enum as per usual
#Configurable
public class MySpringConfiguredClass {
#Autowired
#Qualifier("mine")
private MyEnum myEnum;
}
Here is the trick, use the factory-method="valueOf" and also make sure
lazy-init="false"
so the container creates the bean upfront
<bean id="mine" class="foo.bar.MyEnum" factory-method="valueOf" lazy-init="false">
<constructor-arg value="ONE" />
</bean>
and you are done!
Just pass it to the method manually
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
REPORT_3("name", "filename")
public abstract Map<String, Object> getSpecificParams();
public Map<String, Object> getCommonParams(DataPrepareService dataPrepareService){
// some code that requires service
}
}
As long as you call the method only from managed beans, you can inject it in these beans and pass the reference to the enum on each call.
Maybe you can use this solution ;
public enum ChartTypes {
AREA_CHART("Area Chart", XYAreaChart.class),
BAR_CHART("Bar Chart", XYBarChart.class),
private String name;
private String serviceName;
ChartTypes(String name, Class clazz) {
this.name = name;
this.serviceName = clazz.getSimpleName();
}
public String getServiceName() {
return serviceName;
}
#Override
public String toString() {
return name;
}
}
And in another class which you need the bean of the Enum :
ChartTypes plotType = ChartTypes.AreaChart
Object areaChartService = applicationContext.getBean(chartType.getServiceName());

Categories