I'm trying coding Spring's DI , just a simple example. There is a controller, and this #AutoWired is a Empty Annotation defined by me.
public class UserController {
#AutoWired
private UserServise userServise;// a empty interface
}
This is the code that implement Annotation injection:
UserController userController = new UserController();
Class<? extends UserController> clazz = userController.getClass();
Stream.of(clazz.getDeclaredFields()).forEach(field -> {
AutoWired annotation = field.getAnnotation(AutoWired.class);
if (annotation != null) {
field.setAccessible(true);
Class<?> type = field.getType();
try {
Object o = type.getDeclaredConstructor().newInstance();
field.set(userController, o);
} catch (Exception e) {
e.printStackTrace();
}
}
});
When the program runs into
Object o = type.getDeclaredConstructor().newInstance();
throws
java.lang.NoSuchMethodException: com.learning.servise.UserServise.<init>()
I guess program cannot find a constructor for a interface, So how can I create this instance for the injection?
I am not completely sure what you are trying to achieve. I'm assuming that UserService is an interface?
If so it cannot be instantiated. You must either a class which implements the interface.
So either write a class (can also be anonymous or lambda) or use a proxy:
Object instance = Proxy.newProxyInstance(type.getClassLoader(),
new Class<?>[]{type}, new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//implement your methods here
//determine which method you're in by checking the method arg
}
});
Don't know if this is what you're after, but it is my best guess.
But maybe you're going at this wrong. When you're trying to replicate Spring, it is important that you have a component or bean you can autowire. So you should probably focus on your #Bean annotation (or similar) first. You'd want some sort of registry which picks up annotated beans and then injects them into your Autowired fields. It seems you have this back-to-front.
You should first focus on registering beans to your framework and only when you have achieved that you should try to inject them.
Related
Say I have some Java class named SomeClassConfig in which I want to define a dependency injection like the following;
#Configuration
public class SomeClass {
#Bean
SomeOtherClass someOtherClass() {
FactoryClass factory = UtilFactoryClass.getDefaultFactory();
return factory.create();
}
}
Here UtilFactoryClass denotes some library that allows me to create a factory class, which in turn allows me to create an instance of the object I am interested in. My problem is that the above-mentioned default factory is initialized after some time, so I would like for the bean to be instantiated / injected after the instantiation the default FactoryClass instance. Is this possible? UtilFactoryClass has no knowledge of the Spring Boot framework, and I tried to apply the #DependsOn annotation, but realized it only allows for me to depend on other Beans.
The concrete 'getDefaultFactory' method I am trying to apply is the following: Link. It is associated to a library named Keycloak. My problem is that the factory obtained from this method is null until a certain point in the life cycle of the application.
Do you mean:
#Configuration
public class SomeClass {
#Bean
SomeOtherClass someOtherClass() { //argument injection also possible
return factory().create();
}
#Bean
FactoryClass factory() {
//check/wait for condition e.g.:
while (!UtilityFactory.isInitialized()) {
try {
Thread.sleep(1000);
// better: TimeUnit.XXX.sleep(), and even better use an (spring managed) executor
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
return UtilFactoryClass.getDefaultFactory();
}
}
"Let spring manage the factory"!? ;)
For the check/wait part, see: https://www.baeldung.com/java-delay-code-execution
I've got a Factory class in Java with some methods which return some Java Bean. All of these Java Beans have some DAO object as fields which are injected with the annotation #EJB. However in every case these DAO are all Null, so I suppose I've a problem with EJB injection. I use WebLogic for deploy. Any suggestions to resolve the issue?
//Factory class
public class Factory extends AbstractFactory {
#Override
public InterfaceService getService() {
return new ClassBean();
}
}
//Bean class
#Stateless(mappedName = "ClassBean")
#LocalBean
public class ClassBean implements IBeanService {
#EJB(beanName = "ClassDAO")
private ClassDAO classDAO;
public List<String> getList() throws ExpectedModelException {
return classDAO.getStringList(); //this one throws NullPointerException
}
Never create Enterprise-Beans using new.
The creation, caching, deletion,... is done by the container.
You must declare ClassDao as #Stateless or #Singleton, ... and the container will create and find it, hopefully if the names are correct.
The Factory is not necessary.
I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.
So, I have some generic components that use reflection to initialize themselves and by doing so, they require Class<T> objects at instanciation time. Those components use annotations in order to generate useful metadata and/or convert the object to another representation more appropriate for the task at hand.
I reduced my issue down to this sample component :
#Component
public class Instantiator<T> {
final Class<T> klass;
#Autowired
public Instantiator(Class<T> klass) {
this.klass = klass;
}
public T instantiate() {
try {
return klass.newInstance();
} catch (InstantiationException|IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Spring does not know how to automatically inject Class<T> instances, so I tried writing the following boilerplate code for each T for which I want Class<T> to be available.
#Bean
Class<Instantiatee> instantiateeClass() {
return Instanciatee.class;
}
It does not work.
Spring since version 4 has support for Autowiring generic types, but in my case, it has to infer what T is assigned to in Class<T>. Since by default Spring creates singleton beans and therefore could not possibly infer an appropriate T, I tried adding #Scope("prototype") but I ended up with a ClassCastException since the container does not know how to infer T anyway.
So, I removed the #Component annotation from Instantiator and settled on this workaround for each T I have :
#Bean
Instantiator<Instantiatee> instantiator() {
return new Instantiator<>(Instantiatee.class);
}
Do you know a workaround to make this work so that T will be inferred each time I want an Instantiator or another generic component depending on it ?
FYI, we are using spring 4.1.4 with boot.
I posted a more complete sample there : https://gist.github.com/anonymous/79e1a7ebe7c25c00a6c2.
Defining beans with #Bean, you give the bean name in the method name by default - in your case getInstanciateeClass. Also, when autowiring the default bean name is considered the parameter name, in your case klass. Because of this, Spring cannot match the beans, since they have different names and most probably there are more than one Class instances in the ApplicationContext. It does not matter if one is Class<Foo> and another one is Class<Bar>, Spring sees them as Class so it cannot do autowiring by type.
You can fix this by using the same default name both when defining the bean and when autowiring it.
#Autowired
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean
Class<Instanciatee> klass() {
return Instanciatee.class;
}
You can also specify the name of the bean in the #Bean annotation:
#Autowired
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean(name = "klass")
Class<Instanciatee> getInstanciateeClass() {
return Instanciatee.class;
}
Or you can also give the bean name when autowiring:
#Autowired
#Qualifier("getInstanciateeClass")
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean
Class<Instanciatee> getInstanciateeClass() {
return Instanciatee.class;
}
Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}