Spring bean resolution based on unique constructors - java

Would be grateful if you could help me with the following.
Suppose I have the following classes and interface:
public interface BaseType {
public void method();
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class BaseTypeImpl implements BaseType {
private int num;
public BaseTypeImpl(int num) {
this.num = num;
}
#Override
public void method() {
System.out.println(num);
}
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class ChildBaseTypeImpl extends BaseTypeImpl {
String mes;
public ChildBaseTypeImpl(int num, String mes) {
super(num);
this.mes = mes;
}
#Override
public void method() {
super.method();
System.out.println(mes);
}
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class SecondaryTypeImpl implements BaseType {
private String str;
public SecondaryTypeImpl(String str) {
this.str = str;
}
#Override
public void method() {
System.out.println(str);
}
}
In result, I have 3 classes implementing 1 interface.
All the classes have non-default constructor with a different parameters.
Is there a way to make Spring lookup for a correct bean by BaseType interface based on the constructor parameters?
I want to do this:
ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
ctx.getBean(BaseType.class, 11); //Should return instance of BaseTypeImpl class object, since it has constructor taking int
or like this:
ctx.getBean(BaseType.class, 11, "hello!"); //Should return instance of ChildBaseTypeImpl
Trying to do this will result in exception:
Exception in thread "main"
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type 'springtest.BaseType' available: expected
single matching bean but found 3:
baseTypeImpl,childBaseTypeImpl,secondaryTypeImpl
It seems like there is no straightforward way to do this.
But maybe is it possible to get all classes, that are assignable from a BaseType, find appropriate constructors among them and then call getBean method with the *Impl class as a first parameter?
UPDATED
Thanks everyone for answering!
As pvpkiran mentioned, there is a way to get class of bean by bean name.
This opens the door for reflection, which solves the problem, here's the sample code:
public class App {
static ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
public static void main(String[] args) {
BaseType beanByInterface = getBeanByInterface(BaseType.class, "Hello!");
System.out.println(beanByInterface.getClass());
}
public static <T> T getBeanByInterface(Class<T> interf, Object... params) {
BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);
TypeFilter tf = new AssignableTypeFilter(interf);
s.addIncludeFilter(tf);
s.scan("springtest"); //Sample project package name
String[] beans = bdr.getBeanDefinitionNames();
for(String b : beans) {
Class<?> type = ctx.getType(b);
MAIN: for(Constructor cons : type.getConstructors()) {
if (cons.getParameterCount() == params.length) {
for (int i = 0; i < cons.getParameterCount(); i++) {
if (!params[i].getClass().equals(cons.getParameterTypes()[i])) { //Will fail comparing primitive and boxed types, just leaving like this for simplicity
continue MAIN;
}
}
return (T) ctx.getBean(type, params);
}
}
}
return null;
}
}
The code is buggy, but this is just to show the concept.
Having the beans classes I can get the right bean, that has the needed constructor.
This sample outputs "class springtest.SecondaryTypeImpl"
But I believe this should be the common problem that is covered in some utils.
I don't want to invent the bicycle, but still can't find the solution.
UPDATED 2
Seems like there is no such solution in existing libs, since it's not the best practice.
Anyway, here's the updated method, maybe someone will find it useful.
public static <T> T getBeanByInterface(Class<T> interf, Object... params) {
String[] beans = ctx.getBeanNamesForType(interf);
for(String beanName : ctx.getBeanNamesForType(interf)) {
Class<?> type = ctx.getType(beanName);
Class<?>[] paramTypes = new Class[params.length];
//Getting params types
for (int i = 0; i < params.length; i++) {
paramTypes[i] = params[i].getClass();
}
if (ConstructorUtils.getMatchingAccessibleConstructor(type, paramTypes) != null) {
return (T) ctx.getBean(type, params);
}
}
return null;
}

BeanFactory has following getBean() declaration
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException
As you can see here, there is no declaration for class type, bean name and arguments(which is what you are looking for).
But you can get the bean using bean name and arguments.
ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
ChildBaseTypeImpl childBaseTypeImpl = (ChildBaseTypeImpl) ctx.getBean("childBaseTypeImpl", 11, "hello");
BaseTypeImpl basetypeImpl = (BaseTypeImpl) ctx.getBean("baseTypeImpl", 11, );

Well, you can't use constructor arguments to differentiate your beans, as #alexlys noted. But you could do something different with the same result. Both approaches assumes that you could define what bean you need not in runtime but when you write the code.
You could create beans with unique names and then get desired bean by
name, not by interface
You could add some marker interfaces like
interface SecondaryType extends BaseType {//empty}. Each bean will implement only one specific interface so you could instaniate beans using such iterfaces.

Related

Annotation to default value

I have a visibility problem in projects, once my implementation is not visible as my interface. The visibility problem can not be solved.
I thought of a solution:
I wrote a note
#Target (ElementType.TYPE)
#Retention (RetentionPolicy.RUNTIME)
public #interface Register {
}
I use it in classes
#Register
public class ClientAlteracaoObserver implements Complement <ClientGestaoValue>, IClienteAlteracaoObserver {
}
I create an engine at project initialization that records in a container the implementations and interfaces
public class Injection implements ServletContextListener {
#Override
public void contextDestroyed( ServletContextEvent arg0 ) {
}
#Override
public void contextInitialized( ServletContextEvent arg0 ) {
System.out.println( "Agora as coisas vão ficar boas!!!!" );
Container container = Container.getInstance();
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( true );
scanner.addIncludeFilter( new AnnotationTypeFilter( Register.class ) );
Set< BeanDefinition > registers = scanner.findCandidateComponents( "br.com.syonet" );
for ( BeanDefinition bd : registers ) {
try {
Class< ? > clazz = Class.forName( bd.getBeanClassName() );
List< Class< ? > > interfaces = Arrays.asList( clazz.getInterfaces() );
for ( Class< ? > contract : interfaces ) {
container.register( contract, clazz );
}
} catch ( Exception e ) {
e.printStackTrace();
}
}
System.out.println( "GG garotão!" );
}
}
my container
public class Container {
private static final Container instance = new Container();
private final HashMap< String, Object> intances = new HashMap<>();
private Container() { }
public static Container getInstance() {
return instance;
}
public void register( Class<?> contract, Class<?> contractImpl ) throws InstantiationException, IllegalAccessException {
this.intances.put( contract.getName(), contractImpl.newInstance() );
}
#SuppressWarnings("unchecked")
public <T> T get( Class<T> interfac ) {
return ( T ) this.intances.get( interfac.getName() );
}
public static <T> T load( Class<T> interfac ) {
return ( T ) instance.get( interfac );
}
}
And finally I use this way:
class Test {
private IClienteAlteracaoObserver observer = Container.load(IClienteAlteracaoObserver.class);
}
But I would like to create the annotation #Inject to do the work of Container.load(IClienteAlteracaoObserver.class);
class Test {
private #Inject IClienteAlteracaoObserver observer;
}
While what you want is possible, I would not recommend it. Annotations are primarily meta-information, mostly processed at compile-time. For this, one writes an annotation processor, which must be passed to the compiler so it can process the annotations. Baeldung has a nice tutorial on annotation processing in which a simple builder is created through an annotation processor.
I propose an alternative in form of a static final variable:
public Class Test {
public static final String DEFAULT_FIELD_VALUE = "example";
private String field; // to ensure encapsulation, all attributes should be private
public Test() {
this(DEFAULT_FIELD_VALUE);
}
public Test(String field) {
this.field = field;
}
public String getField() {
return this.field;
}
public static void main(String... args) {
System.out.println(new Test().getField().equals("example"));
}
}
(Ideone live demo)
You can couple this approach with a static-block to make the configuration more flexible, by e.g. reading the default values from system variables or a config file.
If your objects start to become more complex, you can look at the Builder pattern for a more flexible solution.
A remark on your code: Due to data encapsulation, you do not want your class attributes accessible directly from the outside. Thus you should not set them public. Instead, write mutator methods.

Strategy - automatically register by interface type

I have a lot of actions. All actions works with some Object/Context that passed in all actions. I want to use pattern Strategy/Policy.
Here is examples in Kotlin:
interface Action {
val name: String
fun run(ctx: Context)
}
class Multiply: Action {
override name = "MULTIPLY"
override fun run(ctx: Context) {
writeToDb(ctx.id, ctx.number * 2)
}
}
class Substract
class SendNotification
etc...
So I want to register all strategies on startup. And select strategy from structure like Enum.
val action = selectAwaitingAction()
val ctx = selectCtxById(action.transaction_id)
perfromAction(ctx, actions.getByName(action.name))
fun performAction(ctx Context, action: Action) {
action.run(ctx)
}
My question is how register strategy by interface type?
Note: This is complete example. If you are looking only for automatic registration by interface type, scroll to last part of answer.
Strategy design pattern can be implemented using function tables. This stores implementations in Map<String,IImpl> where key is name of algorithm and value is concrete implementation of algorithm .
Common approach:
Consider class Context holding all parameters shared between imlementations of interface Solver.
public class Context extends HashMap<String,Object> {
public <T> T get(String key, Class<T> resultClass){
return resultClass.cast(get(key));
}
public <T> T getOrDefault(String key, T def, Class<T> resultClass){
return resultClass.cast(getOrDefault(key,def));
}
}
And interface Solver with required methods solve and name
public interface Solver {
void solve(Context context);
String name();
}
Then you can create implementations of Solver interface modifying shared Context object. I have created AddSolver and MultiplySolver in this example.
AddSolver.java:
public class AddSolver implements Solver {
#Override
public void solve(Context context) {
context.put("result", context.getOrDefault("result",0.0, Double.class) + context.get("add", Double.class));
}
#Override
public String name() {
return "+";
}
}
MultiplySolver.java
public class MultiplySolver implements Solver {
#Override
public void solve(Context context) {
context.put("result", context.getOrDefault("result",0.0, Double.class) * context.get("multiply", Double.class));
}
#Override
public String name() {
return "*";
}
}
Manual construction of Map<String,Solver>:
Implementations of interface Solver can be stored in HashMap<String,Solver>
#Test
public void testCustomFunctionMap(){
HashMap<String,Solver> functionMap = new HashMap<>();
functionMap.put("+", new AddSolver());
functionMap.put("*", new MultiplySolver());
Context context = new Context();
context.put("add", 2.0);
functionMap.get("+").solve(context);
TestCase.assertEquals(2.0, context.get("result", Double.class));
context.put("multiply", 3.0);
functionMap.get("*").solve(context);
TestCase.assertEquals(6.0, context.get("result", Double.class));
}
Automatic construction of Map<String,Solver>
There is more approaches, if you need costruct Map<String,Solver> automatically. A lot of them is mentioned in this question. I have used org.reflections library.
public class SolverScanner{
static HashMap<String, Solver> functionMap;
static {
functionMap = new HashMap<>();
Reflections reflections = new Reflections(SolverScanner.class.getPackage().getName());
for( Class<? extends Solver> clazz : reflections.getSubTypesOf(Solver.class)){
try {
Solver solver = clazz.newInstance();
functionMap.put(solver.name(), solver);
} catch (Exception e) {
throw new IllegalStateException("Cannot construct functionMap",e);
}
}
}
public static HashMap<String, Solver> getFunctionMap(){
return functionMap;
}
private SolverScanner(){}//disable instantiating
}
And usage:
#Test
public void testSolverScannerFunctionMap(){
HashMap<String,Solver> functionMap = SolverScanner.getFunctionMap();
Context context = new Context();
context.put("add", 2.0);
functionMap.get("+").solve(context);
TestCase.assertEquals(2.0, context.get("result", Double.class));
context.put("multiply", 3.0);
functionMap.get("*").solve(context);
TestCase.assertEquals(6.0, context.get("result", Double.class));
}

How to read annotation which is declared over a Java object?

How to read annotation which is declared over an object.
For e.g
Annotation :
AuthorInfo.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AuthorInfo {
String author() default "Dushyant Kumar";
String login() default "dushyantashu";
}
What I am trying to do :
Book.java
#Data
public class Book {
private int serialNo;
private String review;
}
Main.java
public class Main {
#AuthorInfo (
author = "Barry Allen",
login = "theflash"
)
private static Book book = new Book();
public static void main(String[] args) {
showAnnotation(book);
}
private static void showAnnotation(Object object) {
// How to get values of annotation declared over this object.
}
}
My usecase is to generate this generic showAnnotation() method, that's why param is Object. How to achieve this? From what I explored, I only got ways to read annotation if it's declared over a class, or declared over a member of a class. Isn't there a way where given an object, if some annotation is present over it can be read?
Thanks
You can give a try with generics and reflection. Assume the Book class is annotated with AuthorInfo like below:
#AuthorInfo(author = "Ram", login = "ram")
public class Book {
}
Suppose if you want to know whether AuthorInfo is present in the object of Book, you can do like below. This is straight forward solution to know whether specific annotation is present in an object.
public class TestAnnotation {
public static void main(String[] args) {
Book book = new Book();
showAnnotation(book);
}
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
if (aClass.isAnnotationPresent(AuthorInfo.class)) {
System.out.println("AuthorInfo annotation present");
AuthorInfo authorInfo = aClass.getAnnotation(AuthorInfo.class);
System.out.println(authorInfo.author());
System.out.println(authorInfo.login());
}
}
}
Suppose, if you want to know all annotations on that object, something like below helps:
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
for (Annotation annotation : aClass.getAnnotations()) {
System.out.println(annotation.toString());
}
}
You can retrieve the object class and then explore it. Via Reflection you could get its fields and methods also, and check if any has annotations on it.
Annotations can be read using Reflection API. Like
Class<Main> clazz = Main.class;
Method[] methods = clazz.getDeclaredMethods();
Field[] fields = clazz.getDeclaredFields();
for (Method method : methods) {
Annotation[] annotation = method.getDeclaredAnnotations();
}
for (Field field : fields) {
//This will get #AuthorInfo annotation on book
Annotation[] annotation = field.getDeclaredAnnotations();
//This will get #Data annotation on Book class
Annotation[] annotationsOnFieldClass = field.getClass().getDeclaredAnnotations();
}
clazz.getDeclaredAnnotations();

'X.class' to get it's 'X' type?

I have written simple container that registers a class and it's interface and has a method to create object from that information like this:
public class DIContainer {
protected static DIContainer instance;
protected Hashtable<Class<?>, Class<?>> classMap;
protected DIContainer(){
this.classMap = new Hashtable<Class<?>, Class<?>>();
}
public static DIContainer getInstance(){
if (DIContainer.instance == null)
DIContainer.instance = new DIContainer();
return DIContainer.instance;
}
public void regClass(Class<?> interf, Class<?> classToReg){
this.classMap.put(interf, classToReg);
}
public Object create(Class<?> interf) throws Exception{
if(!this.classMap.containsKey(interf))
throw new Exception("No such class registered with "+interf.getName()+" interface");
return this.classMap.get(interf).newInstance();
}
}
But I want before creating new instance to bypass it to proxy, for it to create, so I have this proxy class:
public class MyProxy implements InvocationHandler
{
private Map map;
private Object obj;
public static Object newInstance(Map map, Object obj, Class[] interfaces)
{
return Proxy.newProxyInstance(map.getClass().getClassLoader(),
interfaces,
new MyProxy(map, obj));
}
public MyProxy(Map map, Object obj)
{
this.map = map;
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args) throws
Throwable
{
try {
return m.invoke(obj, args);
} catch (NoSuchMethodError e)
{
//Object result;
String methodName = m.getName();
if (methodName.startsWith("get"))
{
String name = methodName.substring(methodName.indexOf("get")+3);
return map.get(name);
}
else if (methodName.startsWith("set"))
{
String name = methodName.substring(methodName.indexOf("set")+3);
map.put(name, args[0]);
return null;
}
else if (methodName.startsWith("is"))
{
String name = methodName.substring(methodName.indexOf("is")+2);
return(map.get(name));
}
return null;
}
}
}
But for proxy class I need to provide type of class and it's interface, but I only have it's information with X.class. Can get the type (for example if it's class X) X, when I have X.class? Maybe I'm doing this the wrong way and I need to change something in order for it to work, but right now I figured I need to get that class type, so then I could provide it for proxy?
Because if I would right something like this:
X.class x;
I would get error. So I need to write like this X x;, but I only have X.class
Update:
To explain it simply, is it possible to get this:
X obj;
when you only have X.class (with X.class.newInstance it would instantiate it (like with new?), but I need not instantiated obj yet).
Update2
I tried this:
Object x = (Object) MyProxy.newInstance(map, this.classMap.get(interf).newInstance(), new Class[] {this.classMap.get(interf)});
But then I get this error:
Exception in thread "main" java.lang.IllegalArgumentException: class lab.X is not visible from class loader
My class X looks like this:
public class X implements I{
String name;
X(){}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
}
and it's interface looks like this:
public interface I {
public String getName();
public void setName(String name);
}
If I understand correctly, you are trying to instantiate an element of the class X.class? If that is the case, all you need to do is call X.class.newInstance().
java.lang.IllegalArgumentException: class lab.X is not visible from
class loader
Isn't this error message quite clear? You need to make sure that the same class loader is being used.
In here:
public static Object newInstance(Map map, Object obj, Class[] interfaces)
{
return Proxy.newProxyInstance(map.getClass().getClassLoader(),
interfaces,
new MyProxy(map, obj));
}
you shouldn't be using the class loader of the map, I'd think you should use class loader of the target object or pass the proper class loader as a separate argument.
I think there are other problems in your approach as well, such as not synchronizing your container creation and not using generics for your proxy type.
For the first part, class DIContainer, it would be better to use:
protected final Map<Class<?>, Class<?>> classMap;
protected DIContainer() {
classMap = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
}
public <I> void regClass(Class<I> interf, Class<? extends I> classToReg) {
this.classMap.put(interf, classToReg);
}
public <T> T create(Class<T> interf) throws Exception {
Class<?> implClass = classMap.get(interf);
if (implClass == null) {
throw new Exception("No such class registered with " + interf.getName()
+ " interface");
}
Constructor<?> c = implClass.getConstructor();
c.setAccessible(true); // If default constructor not public
return interf.cast(c.newInstance());
}
Safe typing, though still partly at run-time.
More or less obsolete Hashtable replaced by equivalent
Calling newInstance on the class bypasses exceptions thrown on getting the default constructor and doing that one's newInstance.
The second part of the question: I fail to understand it; the interface class(es) is what is proxied. In the create above you could easily proxy Class<T> and yield a (seemingly) T object. And you could delegate in the proxy class to an T object created as in the create above.

Suggestions on extending fit.RowFixture and fit.TypeAdapter so that I can bind/invoke on a class that keeps attrs in a map

TLDR: I'd like to know how to extend fit.TypeAdaptor so that I can invoke a method that expects parameters as default implementation of TypeAdaptor invokes the binded (bound ?) method by reflection and assumes it's a no-param method...
Longer version -
I'm using fit to build a test harness for my system (a service that returns a sorted list of custom objects). In order to verify the system, I thought I'd use fit.RowFixture to assert attributes of the list items.
Since RowFixture expects the data to be either a public attribute or a public method, I thought of using a wrapper over my custom object (say InstanceWrapper) - I also tried to implement the suggestion given in this previous thread about formatting data in RowFixture.
The trouble is that my custom object has around 41 attributes and I'd like to provide testers with the option of choosing which attributes they want to verify in this RowFixture. Plus, unless I dynamically add fields/methods to my InstanceWrapper class, how will RowFixture invoke either of my getters since both expect the attribute name to be passed as a param (code copied below) ?
I extended RowFixture to bind on my method but I'm not sure how to extend TypeAdaptor so that it invokes with the attr name..
Any suggestions ?
public class InstanceWrapper {
private Instance instance;
private Map<String, Object> attrs;
public int index;
public InstanceWrapper() {
super();
}
public InstanceWrapper(Instance instance) {
this.instance = instance;
init(); // initialise map
}
private void init() {
attrs = new HashMap<String, Object>();
String attrName;
for (AttrDef attrDef : instance.getModelDef().getAttrDefs()) {
attrName = attrDef.getAttrName();
attrs.put(attrName, instance.getChildScalar(attrName));
}
}
public String getAttribute(String attr) {
return attrs.get(attr).toString();
}
public String description(String attribute) {
return instance.getChildScalar(attribute).toString();
}
}
public class MyDisplayRules extends fit.RowFixture {
#Override
public Object[] query() {
List<Instance> list = PHEFixture.hierarchyList;
return convertInstances(list);
}
private Object[] convertInstances(List<Instance> instances) {
Object[] objects = new Object[instances.size()];
InstanceWrapper wrapper;
int index = 0;
for (Instance instance : instances) {
wrapper = new InstanceWrapper(instance);
wrapper.index = index;
objects[index++] = wrapper;
}
return objects;
}
#Override
public Class getTargetClass() {
return InstanceWrapper.class;
}
#Override
public Object parse(String s, Class type) throws Exception {
return super.parse(s, type);
}
#Override
protected void bind(Parse heads) {
columnBindings = new TypeAdapter[heads.size()];
for (int i = 0; heads != null; i++, heads = heads.more) {
String name = heads.text();
String suffix = "()";
try {
if (name.equals("")) {
columnBindings[i] = null;
} else if (name.endsWith(suffix)) {
columnBindings[i] = bindMethod("description", name.substring(0, name.length()
- suffix.length()));
} else {
columnBindings[i] = bindField(name);
}
} catch (Exception e) {
exception(heads, e);
}
}
}
protected TypeAdapter bindMethod(String name, String attribute) throws Exception {
Class partypes[] = new Class[1];
partypes[0] = String.class;
return PHETypeAdaptor.on(this, getTargetClass().getMethod("getAttribute", partypes), attribute);
}
}
For what it's worth, here's how I eventually worked around the problem:
I created a custom TypeAdapter (extending TypeAdapter) with the additional public attribute (String) attrName. Also:
#Override
public Object invoke() throws IllegalAccessException, InvocationTargetException {
if ("getAttribute".equals(method.getName())) {
Object params[] = { attrName };
return method.invoke(target, params);
} else {
return super.invoke();
}
}
Then I extended fit.RowFixture and made the following overrides:
public getTargetClass() - to return my class reference
protected TypeAdapter bindField(String name) throws Exception - this is a protected method in ColumnFixture which I modified so that it would use my class's getter method:
#Override
protected TypeAdapter bindField(String name) throws Exception {
String fieldName = camel(name);
// for all attributes, use method getAttribute(String)
Class methodParams[] = new Class[1];
methodParams[0] = String.class;
TypeAdapter a = TypeAdapter.on(this, getTargetClass().getMethod("getAttribute", methodParams));
PHETypeAdapter pheAdapter = new PHETypeAdapter(fieldName);
pheAdapter.target = a.target;
pheAdapter.fixture = a.fixture;
pheAdapter.field = a.field;
pheAdapter.method = a.method;
pheAdapter.type = a.type;
return pheAdapter;
}
I know this is not a neat solution, but it was the best I could come up with. Maybe I'll get some better solutions here :-)

Categories

Resources