Building an AOP framework
Last updated
Last updated
In the example below we will write our own code to create AOP Framework using JDK Proxy without using any third-party libraries.
To use JDK Proxy we perform the following steps:
Create Invocation Handler: this class must implemnet java.lang.reflect.InvocationHandler. InvocationHandler is the interface implemented by the invocation handler of a proxy instance. When a method is called on a proxy instance, the method call is encrypted and sent to its invocation handler's calling method.
Create Proxy Instance : use Proxy.newProxyInstance() method provided by java.lang.reflect.Proxy factory method.
For example, we need to add some handling before and after the methods in the AccountService class are called.
Create Domain class.
package com.gpcoder.aop.account;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Account {
private String owner;
private String currency;
private int balance;
}
Create business classes:
package com.gpcoder.aop.account;
public interface AccountService {
void addAccount(Account account);
void removeAccount(Account account);
int getSize();
}
package com.gpcoder.aop.account;
import java.util.ArrayList;
import java.util.List;
public class AccountServiceImpl implements AccountService {
private List<Account> accounts = new ArrayList<>();
@Override
public void addAccount(Account account) {
System.out.println("addAccount: " + account);
accounts.add(account);
}
@Override
public void removeAccount(Account account) {
System.out.println("removeAccount: " + account);
accounts.remove(account);
}
@Override
public int getSize() {
System.out.println("getSize: " + accounts.size());
return accounts.size();
}
}
Create cross-cutting concern classes:
package com.gpcoder.aop.handler;
import java.lang.reflect.InvocationHandler;
public abstract class AbstractHandler implements InvocationHandler {
private Object targetObject;
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
public Object getTargetObject() {
return targetObject;
}
}
package com.gpcoder.aop.handler;
import java.lang.reflect.Method;
/**
* The class BeforeHandler provides a template for the before execution
*/
public abstract class BeforeHandler extends AbstractHandler {
/**
* Handles before execution of actual method.
*/
public abstract void handleBefore(Object proxy, Method method, Object[] args);
/*
* (non-Javadoc)
*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
handleBefore(proxy, method, args);
return method.invoke(getTargetObject(), args);
}
}
package com.gpcoder.aop.handler.impl;
import java.lang.reflect.Method;
import com.gpcoder.aop.handler.BeforeHandler;
/**
* This class provides implementation before actual execution of method.
*/
public class BeforeHandlerImpl extends BeforeHandler {
@Override
public void handleBefore(Object proxy, Method method, Object[] args) {
// Provide your own cross cutting concern
System.out.println("Handling before actual method execution");
}
}
package com.gpcoder.aop.handler;
import java.lang.reflect.Method;
public abstract class AfterHandler extends AbstractHandler {
/**
* Handles after the execution of method.
*/
public abstract void handleAfter(Object proxy, Method method, Object[] args);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(getTargetObject(), args);
handleAfter(proxy, method, args);
return result;
}
}
package com.gpcoder.aop.handler.impl;
import java.lang.reflect.Method;
import com.gpcoder.aop.handler.AfterHandler;
/**
* This class provides an implementation of business logic which will be
* executed after the actual method execution.
*/
public class AfterHandlerImpl extends AfterHandler {
@Override
public void handleAfter(Object proxy, Method method, Object[] args) {
// Provide your own cross cutting concern
System.out.println("Handling after actual method execution");
System.out.println("---");
}
}
Create AOP proxy-based class:
package com.gpcoder.aop.handler;
import java.lang.reflect.Proxy;
import java.util.List;
/**
* A factory for creating Proxy objects.
*/
public class ProxyFactory {
private ProxyFactory() {
throw new UnsupportedOperationException();
}
public static Object getProxy(Object targetObject, List<AbstractHandler> handlers) {
if (handlers != null && !handlers.isEmpty()) {
Object proxyObject = targetObject;
for (AbstractHandler handler : handlers) {
handler.setTargetObject(proxyObject);
proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), handler);
}
return proxyObject;
}
return targetObject;
}
}
Create application:
package com.gpcoder.aop;
import java.util.ArrayList;
import java.util.List;
import com.gpcoder.aop.account.Account;
import com.gpcoder.aop.account.AccountService;
import com.gpcoder.aop.account.AccountServiceImpl;
import com.gpcoder.aop.handler.AbstractHandler;
import com.gpcoder.aop.handler.ProxyFactory;
import com.gpcoder.aop.handler.impl.AfterHandlerImpl;
import com.gpcoder.aop.handler.impl.BeforeHandlerImpl;
/**
* This class to verify an AOP example using JDK proxy.
*/
public class AspectOrientedProgrammingInJdkExample {
public static void main(String[] args) {
List<AbstractHandler> handlers = new ArrayList<>();
handlers.add(new BeforeHandlerImpl());
handlers.add(new AfterHandlerImpl());
AccountService proxy = (AccountService) ProxyFactory.getProxy(new AccountServiceImpl(), handlers);
Account account = new Account("gpcoder", "USD", 100);
proxy.addAccount(account);
proxy.getSize();
proxy.removeAccount(account);
proxy.getSize();
}
}
Output:
Handling before actual method execution
addAccount: Account(owner=gpcoder, currency=USD, balance=100)
Handling after actual method execution
---
Handling before actual method execution
getSize: 1
Handling after actual method execution
---
Handling before actual method execution
removeAccount: Account(owner=gpcoder, currency=USD, balance=100)
Handling after actual method execution
---
Handling before actual method execution
getSize: 0
Handling after actual method execution
---