Proxies
are used to achieve below things.
ArithmeticServiceImpl.java
MyInvocationHandler.java
Test.java
a. Control access to the
object
b. Remove the overhead
of cross cutting concerns.
In
any typical enterprise application authentication, logging, database connection
etc., are compulsory and these are implemented or used by almost all the service
classes. By using dynamic proxies, we can get rid of the duplication of cross
cutting concerns.
What is Dynamic
Proxy?
A
dynamic proxy class is a class that implements a list of interfaces specified
at runtime such that a method invocation through one of the interfaces on an
instance of the class will be encoded and dispatched to another object through
a uniform interface.
For
example, as you see below image, the calls to actual object are done via
InvocationHandler.
Let
me explain with an example.
How to create a proxy
object?
'java.lang.reflect.Procy'
class is used to create a proxy of an object.
public static Object
newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
Returns
an instance of a proxy class for the specified interfaces
Example
ArithmeticServiceImpl
arthObj = new ArithmeticServiceImpl();
ArithmeticService
arhServiceProxy = (ArithmeticService)
Proxy.newProxyInstance(ArithmeticServiceImpl.class.getClassLoader(), new
Class[] { ArithmeticService.class }, new MyInvocationHandler(arthObj));
After
running this code, the proxy variable 'arhServiceProxy' contains a dynamic
implementation of the ArithmeticService interface. All calls to the arhServiceProxy
will be forwarded to the handler implementation of the general
InvocationHandler interface.
MyInvocationHandler :
Implementation
of InvocationHandler interface
All
calls to the arhServiceProxy will be forwarded to the invoke() method of
MyInvocationHandler class.
Find
the below working application.
ArithmeticService.java
package com.sample.app.interfaces; public interface ArithmeticService { public int add(int a, int b); }
ArithmeticServiceImpl.java
package com.sample.app.interfaces.impl; import com.sample.app.interfaces.ArithmeticService; public class ArithmeticServiceImpl implements ArithmeticService { @Override public int add(int a, int b) { System.out.println("Calculating sum of " + a + " and " + b); return a + b; } }
MyInvocationHandler.java
package com.sample.app.invocationhandlers; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class MyInvocationHandler implements InvocationHandler { private Object obj; public MyInvocationHandler(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + " of " + method.getDeclaringClass() + " is getting executed" + " with arguments : " + Arrays.toString(args)); Object result = method.invoke(obj, args); System.out.println(method.getName() + " of " + method.getDeclaringClass() + " execution finished"); return result; } }
Test.java
package com.sample.app; import java.lang.reflect.Proxy; import com.sample.app.interfaces.ArithmeticService; import com.sample.app.interfaces.impl.ArithmeticServiceImpl; import com.sample.app.invocationhandlers.MyInvocationHandler; public class Test { public static void main(String args[]) { ArithmeticServiceImpl arthObj = new ArithmeticServiceImpl(); ArithmeticService arhServiceProxy = (ArithmeticService) Proxy.newProxyInstance(ArithmeticServiceImpl.class.getClassLoader(), new Class[] { ArithmeticService.class }, new MyInvocationHandler(arthObj)); int result = arhServiceProxy.add(10, 20); System.out.println(result); } }
When
you ran Test.java, you can able to see below messages in console.
add of interface
com.sample.app.interfaces.ArithmeticService is getting executed with arguments
: [10, 20]
Calculating sum of 10
and 20
add of interface com.sample.app.interfaces.ArithmeticService
execution finished
30
Let’s
enhance above application further like below.
LoggingFilter
: Log the information about the method execution
PerformanceFilter:
Log the execution time of the method execution
Find
the below working application.
Filter.java
package com.sample.app.filters; public interface Filter { /** * Called before execution of the actual method */ public void beforeExecution(); /** * Called after execution of the actual method */ public void afterExecution(); }
LoggingFilter.java
PerformanceFilter.java
package com.sample.app.filters; import java.lang.reflect.Method; import java.util.Arrays; public class LoggingFilter implements Filter { private Method method; private Object[] args; public LoggingFilter(Method method, Object[] args) { this.method = method; this.args = args; } @Override public void beforeExecution() { System.out.println("Method " + method.getName() + " of " + method.getDeclaringClass() + " is getting executed" + " with arguments : " + Arrays.toString(args)); } @Override public void afterExecution() { System.out.println("Method " + method.getName() + " of " + method.getDeclaringClass() + " execution finished"); } }
PerformanceFilter.java
package com.sample.app.filters; import java.lang.reflect.Method; public class PerformanceFilter implements Filter { private Method method; private long startTime; public PerformanceFilter(Method method) { this.method = method; } @Override public void beforeExecution() { startTime = System.nanoTime(); } @Override public void afterExecution() { System.out.println("Method " + method.getName() + " takes " + (System.nanoTime() - startTime) + " nanoSeconds"); } }
ArithmeticService.java
package com.sample.app.interfaces; public interface ArithmeticService { public int add(int a, int b); }
ArithmeticServiceImpl.java
MyInvocationHandler.java
package com.sample.app.interfaces.impl; import com.sample.app.interfaces.ArithmeticService; public class ArithmeticServiceImpl implements ArithmeticService { @Override public int add(int a, int b) { System.out.println("Calculating sum of " + a + " and " + b); return a + b; } }
MyInvocationHandler.java
package com.sample.app.invocationhandlers; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.*; import com.sample.app.filters.Filter; import com.sample.app.filters.LoggingFilter; import com.sample.app.filters.PerformanceFilter; public class MyInvocationHandler implements InvocationHandler { private Object obj; private boolean loggingFilter = false; private boolean perfFilter = false; private List<Filter> filters = new ArrayList<>(); private Method method; private Object[] args; public MyInvocationHandler(Object obj) { this.obj = obj; } public void addLoggingFilter() { loggingFilter = true; } public void addPerformanceFilter() { perfFilter = true; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { this.method = method; this.args = args; addFilters(); execBeforeFilters(); Object result = method.invoke(obj, args); execAfterFilters(); return result; } private void addFilters() { if (loggingFilter) { filters.add(new LoggingFilter(method, args)); } if (perfFilter) { filters.add(new PerformanceFilter(method)); } } private void execBeforeFilters() { for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); filter.beforeExecution(); } } private void execAfterFilters() { for (int i = filters.size() - 1; i > -1; i--) { Filter filter = filters.get(i); filter.afterExecution(); } } }
Test.java
package com.sample.app; import java.lang.reflect.Proxy; import com.sample.app.interfaces.ArithmeticService; import com.sample.app.interfaces.impl.ArithmeticServiceImpl; import com.sample.app.invocationhandlers.MyInvocationHandler; public class Test { public static void main(String args[]) { ArithmeticServiceImpl arthObj = new ArithmeticServiceImpl(); MyInvocationHandler invocationhandler = new MyInvocationHandler(arthObj); invocationhandler.addLoggingFilter(); invocationhandler.addPerformanceFilter(); ArithmeticService arhServiceProxy = (ArithmeticService) Proxy.newProxyInstance( ArithmeticServiceImpl.class.getClassLoader(), new Class[] { ArithmeticService.class }, invocationhandler); int result = arhServiceProxy.add(987654, 201801810); System.out.println("Result is : " + result); } }
When
you ran Test.java, you can able to see below messages in console.
Method
add of interface com.sample.app.interfaces.ArithmeticService is getting
executed with arguments : [987654, 201801810]
Calculating
sum of 987654 and 201801810
Method
add takes 327546 nanoSeconds
Method
add of interface com.sample.app.interfaces.ArithmeticService execution finished
Result is : 202789464
Result is : 202789464
No comments:
Post a Comment