Please note that there are so many things you can use and my list is definitely not exhaustive. It just gives idea about a few good ones that come bundled with Java SE.
Reflection: Reflection is something that is absolutely essential for developing an enterprise framework. Most framework depend on configuration provided either in the form of XML or recently in the form of Java annotations. With the help of reflection, you can load a class dynamically (by using the name as a String), create its objects, access its properties and invoke its methods. Virtually all available frameworks use reflection.
Loading a class dynamically, creating its objects and calling its methods: An example program equals a thousand words (and I'm too lazy to type a thousand word). Hence, here is an example program.
package com.blogspot.debasishwebguide;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) {
try {
//Get a reference to the class
Class<?> cls=Class.forName("com.blogspot.debasishwebguide.ReflectionTestClass");
//Get a reference to the toString() method
Method toStringMethod=cls.getMethod("toString", new Class<?>[]{});
//Create a reference to the object
Object obj=cls.newInstance();
//Invoke the method
Object result=toStringMethod.invoke(obj, new Object[]{});
System.out.println(result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) {
try {
//Get a reference to the class
Class<?> cls=Class.forName("com.blogspot.debasishwebguide.ReflectionTestClass");
//Get a reference to the toString() method
Method toStringMethod=cls.getMethod("toString", new Class<?>[]{});
//Create a reference to the object
Object obj=cls.newInstance();
//Invoke the method
Object result=toStringMethod.invoke(obj, new Object[]{});
System.out.println(result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
package com.blogspot.debasishwebguide;
public class ReflectionTestClass {
public String toString(){
return "ReflectionTestClass";
}
}
public class ReflectionTestClass {
public String toString(){
return "ReflectionTestClass";
}
}
The code is commented to explain what's going on. The static method forName() in class java.lang.Class returns a reference to a given class. The method getMethod() in class java.lang.Class returns the reference to a method object as specified by the method name and argument list. You can also call getMethod() to get an array of Method objects all defined/declared in the Class or in the predecessors. However, only public methods are returned.
But, what if the class does not have a default constructor (i.e. a no argument constructor)? In that case, we need to get a reference to a Constructor, just like the way we got reference to a Method. The following code example shows it.
package com.blogspot.debasishwebguide;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTestConstructor {
public static void main(String[] args) {
try {
//Get a reference to the class
Class<?> cls=Class.forName("com.blogspot.debasishwebguide.ReflectionTestClassConstructorWithArguments");
//Get a reference to a Constructor
Constructor<?> constructor=cls.getConstructor(String.class);
//Create an instance
Object instance=constructor.newInstance("Debasish");
//Get reference to a method
Method sayItMethod=cls.getMethod("sayIt", String.class);
//Invoke the method
sayItMethod.invoke(instance, "Hello");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTestConstructor {
public static void main(String[] args) {
try {
//Get a reference to the class
Class<?> cls=Class.forName("com.blogspot.debasishwebguide.ReflectionTestClassConstructorWithArguments");
//Get a reference to a Constructor
Constructor<?> constructor=cls.getConstructor(String.class);
//Create an instance
Object instance=constructor.newInstance("Debasish");
//Get reference to a method
Method sayItMethod=cls.getMethod("sayIt", String.class);
//Invoke the method
sayItMethod.invoke(instance, "Hello");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
package com.blogspot.debasishwebguide;
public class ReflectionTestClassConstructorWithArguments {
private String name;
public ReflectionTestClassConstructorWithArguments(String name){
this.name=name;
}
public String toString(){
return "ReflectionTestClass";
}
public void sayIt(String helloMessage){
System.out.println(helloMessage+" "+name+"!");
}
}
public class ReflectionTestClassConstructorWithArguments {
private String name;
public ReflectionTestClassConstructorWithArguments(String name){
this.name=name;
}
public String toString(){
return "ReflectionTestClass";
}
public void sayIt(String helloMessage){
System.out.println(helloMessage+" "+name+"!");
}
}
Note that the methods in Class class are varargs. That means, they accept a variable number of arguments. In java, internally, those arguments are clubbed together into an array. It is also possible to pass an array of objects instead of the objects being passed separately.
Annotation: Now that we know about reflection, we can start learning annotation. Virtually all enterprise frameworks nowadays, like EJB 3, Spring 3, hibernate 3 (yes its amazing they all have same current major version number) all use annotation for configuration parallel to XML. Whether we should use annotations for configuration is still a burning question - you know, if we do that, we need to recompile our programs every time we change our configurations. However, in practice, there are a lot of configurations that never change in a project (For example, a stateless session bean and a stateful session bean are written completely differently. A stateless session bean does not maintain any instance variables, a stateful bean invariably does that. Hence, a session bean will not overnight change its statefulness, not at least without recompilation anyway. But for a EJB container, its just a matter of configuration). Annotation has a serious advantage over XML configuration that the developer sees the configurations right inside the code, which makes it really easy for him/her to understand.
Again, let us learn by example.
package com.blogspot.debasishwebguide;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValueList {
public String [] value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValueList {
public String [] value();
}
package com.blogspot.debasishwebguide;
public interface DynamicMethods {
@ValueList(value={"Value1","Value2"})
public String [] getValues();
public String [] anotherMethod();
}
public interface DynamicMethods {
@ValueList(value={"Value1","Value2"})
public String [] getValues();
public String [] anotherMethod();
}
package com.blogspot.debasishwebguide;
import java.lang.reflect.Method;
public class AnnotationTester {
public static void main(String[] args) {
//Get all public methods
Method [] methods=DynamicMethods.class.getMethods();
for(Method method:methods){
//Get a reference to the instance of annotation on the top of the method
ValueList valueList=method.getAnnotation(ValueList.class);
if(valueList!=null){//If the annotation was there
String [] values=valueList.value();//Get the array assigned to by "value=..."
System.out.println("Method: "+method);
for(String value:values){
System.out.println(value);
}
}
}
}
}
import java.lang.reflect.Method;
public class AnnotationTester {
public static void main(String[] args) {
//Get all public methods
Method [] methods=DynamicMethods.class.getMethods();
for(Method method:methods){
//Get a reference to the instance of annotation on the top of the method
ValueList valueList=method.getAnnotation(ValueList.class);
if(valueList!=null){//If the annotation was there
String [] values=valueList.value();//Get the array assigned to by "value=..."
System.out.println("Method: "+method);
for(String value:values){
System.out.println(value);
}
}
}
}
}
First, let us look at the definition of the annotation ValueList. An annotation is an interface, but not an ordinary interface. It is defined with the keyword @interface, to let the compiler know that we are defining an annotation instead of an interface.
Here we have defined only one method values() that returns an array of Strings. There is a restriction as to what a method in an annotation might return - primitive types, String, Class and single dimensional arrays of them. These methods must also not accept any arguments. In reality, what we are defining here is an attribute called value. What does that mean? Well, keep reading.
There are two annotations on the definition of the annotation. They configure the annotation we are defining. @Retention(RetentionPolicy.RUNTIME) asks the compiler to preserve the annotation in the generated class file. @Target(ElementType.METHOD) specifies that our annotation is only applicable on methods. Runtime annotations may also appear on classes and fields. Let us now check how the annotation is applied in DynamicMethods.java. Its simple, just put the @annotation before the method applied on. We are also providing the value for value attribute. Note that value almost looks like a field here. When we will access the value attribute from the annotation through reflection, the values assigned here would be available.
One more word here, the value attribute is the default attribute while using the annotation. Hence the code could also be written @ValueList({"Value1","Value2"}).
The annotation can be accessed through reflection as shown in AnnotationTester.java. The reflection classes Class, Method, Field, all have getAnnotation() method which can be used to access a particular annotation. To get all annotations, use getAnnotations().
Dynamic Proxy: Dynamic proxy is a way to dynamically implement a collection of interfaces. The implementation is provided by an instance of and implementation of InvocationHandler. Again, let's see an example.
package com.blogspot.debasishwebguide;
import java.lang.reflect.Proxy;
public class AnnotationProxyTester {
public static void main(String[] args) {
String className="com.blogspot.debasishwebguide.DynamicMethods";
Object proxy=getProxy(className);
DynamicMethods dynamicMethods=(DynamicMethods) proxy;
for(String value:dynamicMethods.getValues()){
System.out.println(value);
}
}
public static Object getProxy(String interfaceName) {
try {
//Get a reference to the interface class
Class<?> c=Class.forName(interfaceName);
//Create a proxy class for the interface.
Object proxy=Proxy.newProxyInstance(AnnotationProxyTester.class.getClassLoader(), new Class[]{c}, new ValuesInvocationHandler());
return proxy;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
import java.lang.reflect.Proxy;
public class AnnotationProxyTester {
public static void main(String[] args) {
String className="com.blogspot.debasishwebguide.DynamicMethods";
Object proxy=getProxy(className);
DynamicMethods dynamicMethods=(DynamicMethods) proxy;
for(String value:dynamicMethods.getValues()){
System.out.println(value);
}
}
public static Object getProxy(String interfaceName) {
try {
//Get a reference to the interface class
Class<?> c=Class.forName(interfaceName);
//Create a proxy class for the interface.
Object proxy=Proxy.newProxyInstance(AnnotationProxyTester.class.getClassLoader(), new Class[]{c}, new ValuesInvocationHandler());
return proxy;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
package com.blogspot.debasishwebguide;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ValuesInvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Get the valueList annotation
ValueList valueList=method.getAnnotation(ValueList.class);
if(valueList!=null&&method.getReturnType().equals(String [].class)){ //If the annotation is found
String [] values=valueList.value();
return values;
}
return null;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ValuesInvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Get the valueList annotation
ValueList valueList=method.getAnnotation(ValueList.class);
if(valueList!=null&&method.getReturnType().equals(String [].class)){ //If the annotation is found
String [] values=valueList.value();
return values;
}
return null;
}
}
package com.blogspot.debasishwebguide;
public interface DynamicMethods {
@ValueList({"Value1","Value2"})
public String [] getValues();
public String [] anotherMethod();
}
public interface DynamicMethods {
@ValueList({"Value1","Value2"})
public String [] getValues();
public String [] anotherMethod();
}
package com.blogspot.debasishwebguide;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValueList {
public String [] value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValueList {
public String [] value();
}
First let us check the getProxy() method. It simply gets a reference to the interface and then calls the Proxy.newInstance() to create an object of the implementation of the interface. Note that an array of interface is passed. So, multiple interfaces can be passed together. If the classes passed are not interfaces, an IllegalArgumentException is thrown.
An implementation of InvocationHandler (i.e. ValuesInvocationHandler) is also passed as an argument. Every method call to the proxy will be dedicated to this invocation handler's invoke () method. The object the proxy method is called on, is passed as the first argument of the invoke method. The arguments of the proxy method are passed as an array as the last argument of the invoke method. The rest I already explained.
Node the we have achieved a method injection - meaning we have created a method implementation simply by configuration. Where is XML? Although XML is heavily used to create configurations, parsing XML is too vast to fit in here. I may to write an article later.
6 comments:
awesome....really a learning experience...keep up sharing ur precise knowledge...ll watch d space fr more....
hey dude...its really good but big and boring....
hi sekhar....its for techies.....if u understand d technology it cant be boring...keep it up debasish....:)
really a good article!! keep writing..
Thanks for those inspiring comments. :)
A True Techie among thousands of mere mortals. Cheers! this blog is going into Dopelist@normalreaction
Post a Comment