Automatic dynamic binding in spring - java

I am using spring MVC and would like to expose default validator for javascript to use.
I have a bunch of controllers extending a common abstract class and bunch of validators implementing a common interface. The situation is something like this:
public abstract class AbstractController {
protected Validator validator;
}
public class FooController extends AbstractController{}
public class BarController extends AbstractController{}
public interface Validator {}
public class FooValidator implementes Validator{}
public class BarValidator implementes Validator{}
I would like to automatically set the validator field for each concrete controller respectivelly (so that FooController.validator would be instance of FooValidator).
The matching should be done by class names automatically.

You could create a BeanPostProcessor to do this and register it in the application context. The post processor could look for AbstractController instances with the proper naming convention, generate the validator name, instantiate the validator object via reflection, and set it in the controller. Something like this:
public Object postProcessAfterInitialization(final Object bean, final String name) throws BeansException {
if (bean instanceof AbstractController) {
String controllerName = bean.getClass().getSimpleName();
if(controllerName.endsWith("Controller")) {
String validatorName = controllerName.replaceFirst("Controller$", "Validator");
try {
Class<?> validatorClass = Class.forName(validatorName);
Validator validator = (Validator)validatorClass.newInstance();
((AbstractController)bean).setValidator(validator);
} catch(Exception e) {
throw new FatalBeanException("Cannot instantiate validator", e);
}
}
}
return bean;
}
Alternatively, if the validators are registered as Spring beans because they need dependency injection or whatever, you could create a BeanFactoryPostProcessor (not a BeanPostProcessor) that finds all the controller bean definitions by type or name, then looks up matching validator bean definitions by type or name, and adds the matching validator to the property list of each controller bean. I don't have sample code for that, but hopefully you get the idea.

Couldn't you use something like this in your configuration:
<bean id="abstractControllerTemplate" abstract="true">
<property name="validator" ref="myFormValidator"/>
</bean>
...
<bean id="someOtherConcreteController" class="com.myproj.controllers.SomeOtherConcreteController" parent="abstractControllerTemplate">
<!-- other properties specific to this controller -->
</bean>

Related

Java Spring: getting the generic type

I have a parameterized class that implements the FactoryBean interface:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
What I want is to use this factory for spawning objects of different classes (all of these classes extend Dog). So I imagined that I could do something like the following:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
public ShepherdService(
#Autowired
DogFactory<Shepherd> shepherdFactory
) {
this.shepherdFactory = shepherdFactory;
}
// ...
}
Alas, I get the following error: Couldn't autowire. No beans of DogService<Shepherd> type found. :-(
How to inject it and use as DogFactory<Shepherd> or DogFactory<Corgi> (not just DogFactory)?
And another question. I also need to pass the Shepherd.class (or Corgi.class) to this bean, so it could "know" at run-time, objects of what exactly class should it produce. Is there a way to do that?
Or should I forget about FactoryBean and instantiate the factory as a regular class? Of course, I could do it this way:
DogFactory<Shepherd> shepherdFactory = new DogFactory<Shepherd>(Shepherd.class);
It would work perfectly, but I'd like to use FactoryBean as I use it for other factories in this project, so I would like to stick to FactoryBean.
Thanks in advance!
Update 1
Maybe I should clarify it more precise. What I need is a factory that could produce objects of different classes. All these classes should be extensions of the certain class (e.g., Shepherd and Corgi - all these classes extend Dog). And as the final result I actually need something like that:
public class DogService<T extend Dog> {
private DogFactory<T> dogFactory;
#Autowired
public DogService(DogFactory<T> dogFactory) {
this.dogFactory = dogFactory;
}
public T spawnDog(Color color) {
T dog = DogFactory.getObject(color);
return dog;
}
}
But it seems that I can't make a "universal" factory with the FactoryBean interface, so I can't do something like that:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
Instead of that I have to do something like the following:
public class ShepherdFactory implements FactoryBean<Shepherd> {
// ...
}
public class CorgiFactory implements FactoryBean<Corgi> {
// ...
}
Is that true?
Thanks!
What I want is to instantiate this factory in another class
If you mean "having the factory injected in the class instead of the bean it creates", you could inject the factory by prefixing its name with an ampersand:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
#Qualifier("&dogFactory")
public ShepherdService(DogFactory<Shepherd> shepherdService) {
this.shepherdService = shepherdService;
}
}
From the Spring documentation (section 3.8.3):
When you need to ask a container for an actual FactoryBean instance
itself, not the bean it produces, you preface the bean id with the
ampersand symbol & (without quotes) when calling the getBean method of
the ApplicationContext. So for a given FactoryBean with an id of
myBean, invoking getBean("myBean") on the container returns the
product of the FactoryBean, and invoking getBean("&myBean") returns
the FactoryBean instance itself.
Furthermore, my guess is that the FactoryBean is not picked up as a Spring bean as your snippets did not contain an XML config section, a Java config section, or a #Component annotation. I would explicitly declare the factory bean in a #Configuration annotated class, or annotate the factory class with #Component.
[edit]
But it seems that I can't make a "universal" factory with the
FactoryBean interface, so I can't do something like that:
You could still have a single Configuration class in which you declare all the factory beans.
#Configuration
public class DogFactoryConfig {
#Bean
public DogFactory<Shepherd> shepherdFactory() {
return new DogFactory<Shepherd>();
}
#Bean
public DogFactory<Corgi> corgiFactory() {
return new DogFactory<Corgi>();
}
}
And remove the #Component annotation from your DogFactory if it is present.

Why i am not getting No unique bean of type in Autowiring in spring

Person.java
#Controller
public class Person
{
#Autowired
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}
Ability.java
#Controller
public class Ability
{
void printMessasge(){
System.out.println("I print message");
}
}
spring.xml
<bean id="invisible" class="com.mkyong.common.Ability" >
</bean>
<context:component-scan base-package="com.mkyong" />
App.java
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring.xml");
Person person = (Person) context.getBean("person");
person.printMessage( );
}
In the above example i have defined two beans of Ability class one using #Controller and one in xml file. According to Autowire by type i should get
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException:
...
No unique bean of type [com.mkyong.common.Ability] is defined:
But i am getting proper output. Why?
And if i create an interface which Ability class implements then i would get UnsatisfiedDependencyException exception. Like this:
Parent.java
public interface Parent{
public void printMessasge();
}
Ability.java
#Controller
public class Ability implements Parent
{
void printMessasge(){
System.out.println("I print message");
}
Person.java
#Controller
public class Person
{
#Autowired
Parent parent;
public void printMessage(){
parent.printMessasge();
}
public void setOutputGenerator( Parent parent) {
this.parent= parent;
}
}
By default Spring indeed matches by type, but in case of multiple matching beans, it falls back to matching by name. The reference (6.9.4 Fine-tuning annotation-based autowiring with qualifiers) says:
For a fallback match, the bean name is considered a default qualifier value.
Using the #Qualifier annotation makes the autowiring by type and additionally by name more explicit.
You are not going to get a UnsatisfiedDependencyException with this configuration because Spring is smart enough to also look at the name of your required dependency, as #Adam writes in his answer. So even if the default autowiring mode is by type and there are two beans of the same type for that dependency, Spring is able to solve the conflict.
This behavior is described in the official documentation, 6.9.4 Fine-tuning annotation-based autowiring with qualifiers section.
More details about autowiring modes can be found 6.4.5 Autowiring collaborators.
If you change the name of Person.ability field to something else, like Person.ability2, then Spring will throw a org.springframework.beans.factory.NoUniqueBeanDefinitionException. It cannot decide which bean it should wire for the Person.ability2 field because:
There are two beans of com.mkyong.common.Ability type in the context
Field name does not match with either bean names
EDIT: in the second case you mention (with the interface), Spring throws the exception for the same bean naming reason I explained above.
Official documentation references:
6.4.5 Autowiring collaborators
6.9.4 Fine-tuning annotation-based autowiring with qualifiers
you have two ambiguous beans defined, when spring try find the bean via type it finds two beans, one via scan other explicitly defined in spring.xml. use #Qualifier to mark the correct bean loaded in Person class. So probably you want like this,
#Controller
public class Person
{
#Autowired
#Qualifier("invisible")
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}

Spring annotations confusion

i am really confused with spring annotations.
where to use # Autowired, where class is # Bean or # Component,
i understand we cannot use
Example example=new Example("String");
in Spring
but how alone
#Autowired
Example example;
will solve the purpose?
what about Example Constructor ,how spring will provide String value to Example Constructor?
i went through one of the article but it does not make much sense to me.
it would be great if some one can give me just brief and simple explanation.
Spring doesn't say you can't do Example example = new Example("String"); That is still perfectly legal if Example does not need to be a singleton bean. Where #Autowired and #Bean come into play is when you want to instantiate a class as a singleton. In Spring, any bean you annotate with #Service, #Component or #Repository would get automatically registered as a singleton bean as long as your component scanning is setup correctly. The option of using #Bean allows you to define these singletons without annotating the classes explicitly. Instead you would create a class, annotate it with #Configuration and within that class, define one or more #Bean definitions.
So instead of
#Component
public class MyService {
public MyService() {}
}
You could have
public class MyService {
public MyService() {}
}
#Configuration
public class Application {
#Bean
public MyService myService() {
return new MyService();
}
#Autowired
#Bean
public MyOtherService myOtherService(MyService myService) {
return new MyOtherService();
}
}
The trade-off is having your beans defined in one place vs annotating individual classes. I typically use both depending on what I need.
You will first define a bean of type example:
<beans>
<bean name="example" class="Example">
<constructor-arg value="String">
</bean>
</beans>
or in Java code as:
#Bean
public Example example() {
return new Example("String");
}
Now when you use #Autowired the spring container will inject the bean created above into the parent bean.
Default constructor + #Component - Annotation is enough to get #Autowired work:
#Component
public class Example {
public Example(){
this.str = "string";
}
}
You should never instantiate a concrete implementation via #Bean declaration. Always do something like this:
public interface MyApiInterface{
void doSomeOperation();
}
#Component
public class MyApiV1 implements MyApiInterface {
public void doSomeOperation() {...}
}
And now you can use it in your code:
#Autowired
private MyApiInterface _api; // spring will AUTOmaticaly find the implementation

How To Access Spring Bean name?

#Named("myUniqueName")
public class ReportDashboardDao implements DashboardDAO{
//STUFF
}
how can i access the string inside #Named tag when i am injecting DashboardDAO like this :
#Named
public class DshboardDaoConsumer(){
#Inject List<DashboardDAO> dashboardDAO;
//STUFF
}
Use a Map instead
#Inject
Map<String, DashboardDao> dashBoardDaos;
This will inject a Map with bean names as keys and daos as values.
Of course, you could also read the annotation value from class instances.
You can't. You're injecting by type. After injection has been done, Spring does not leave behind any relation between the bean's object and the bean's name.
You might want to check out ApplicationContext#getBeanNamesByType() depending on what you want to do.
By implementing BeanNameAware.
#Named("myUniqueName")
public class ReportDashboardDao implements DashboardDAO, BeanNameAware{
//STUFF
private String beanName;
#Override
public Void setBeanName(String beanName) {
this.beanName = beanName;
}
}
So that Spring can inject the beanName into the bean. If you add a public String getBeanName(); in your DashboardDAO interface, DashboardDaoConsumer will be able to obtain it.
In this particular case, Spring will inject the name you specified in the annotation.

Correct way to get beans from applicationContext Spring

I have a factory that creates instances:
public class myFactory {
public static getInstace() {
switch(someInt) {
case 1:
return new MySpringBean();
case 2:
return new MyOtherSpringBean();
}
}
}
I need to return a new instance of the beans that are "managed" by Spring bc they have Transactional business logic methods. I have read in many posts here that I should not use getBean method to get a singleton or a new instance from the applicationContext. But I cannot find the proper way to do it for my case. I have used #Resource and it seems to work but it doesn't support static fields.
Thanx
There are many ways to achieve this in spring, the most obvious way given the factory class that you have is to use JavaConfig. If you used the spring enabled JavaConfig annotations you could do the following to construct your beans and add them to the application context:
#Configuration
public class myFactory {
#Bean
public static getInstance() {
switch(someInt) {
case 1:
return new MySpringBean();
case 2:
return new MyOtherSpringBean();
}
}
}
One way is create a factory class and store the instances of the beans (each implementing a common interface MyBean) as values under some key in a map (beans):
public class MyBeanFactory {
private Map<Integer, MyBean> beans;
public MyBean create(Integer which) {
if (which != null)
return beans.get(which);
else
throw new IllegalArgumentException("Unknown bean");
}
public void setBeans(Map<Integer, MyBean> beans) {
this.beans = beans;
}
}
In your Spring applicationContext.xml now create bean a of the factory and set the beans:
<beans...>
<bean id="myBeanFactory" class="foo.bar.MyBeanFactory">
<property name="beans">
<map>
<entry key="1">
<bean class="foo.bar.MyBeanA" />
</entry>
<entry key="2">
<bean class="foo.bar.MyBeanB" />
</entry>
</map>
</property>
</bean>
</beans>
Finally you can inject your bean factory as usual and get instances from like e.g. this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext.xml" })
public class MyBeanFactoryTest {
#Autowired
private MyBeanFactory myBeanFactory;
#Test
public void test() {
Assert.assertTrue(myBeanFactory.create(1) instanceof MyBeanA);
Assert.assertTrue(myBeanFactory.create(2) instanceof MyBeanB);
}
}
You implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories.
The FactoryBean interface is a point of pluggability into the Spring IoC container's instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean, write the complex initialization inside that class, and then plug your custom FactoryBean into the container.
The FactoryBean interface provides three methods:
Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.
boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise.
Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance
Take a look at Spring Docs

Categories