引入一个简单的数值计算器的例子,首先创建计算器接口,然后提供一个简单的实现类,在实现类中,需要对传入参数进行验证,并且进行日志记录。
package com.aop.example;
public interface ArithmeticCalculator {
public double add(double a,double b);
public double sub(double a,double b);
}
package com.aop.example;
import com.sun.org.apache.commons.logging.Log;
import com.sun.org.apache.commons.logging.LogFactory;
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
private Log log=LogFactory.getLog(this.getClass());
public double add(double a, double b) {
// TODO Auto-generated method stub
log.info("the method add() begins with "+a+","+b);
validate(a);
validate(b);
double result = a+b;
System.out.println(a+"+"+b+" = "+result);
log.info("the method add() ends with "+result);
return result;
}
public double sub(double a, double b) {
// TODO Auto-generated method stub
log.info("the method add() begins with "+a+","+b);
validate(a);
validate(b);
double result = a-b;
System.out.println(a+"-"+b+" = "+result);
log.info("the method add() ends with "+result);
return result;
}
private void validate(double a) {
// TODO Auto-generated method stub
if(a<0){
throw new IllegalArgumentException("Illegal argument");
}
}
}
上述代码中,日志和验证属于非业务需求,将这类代码写入业务逻辑中,耦合性增大,并且带来了很多的重复性代码。如果日志和验证需求改变,必须修改所有模块。
[color=red]计算器应该只关心核心计算逻辑,需要将日志和验证关注点从其中分离出来。[/color]
采用动态代理的方法,建立InvocationHandler接口,并提供日志和验证实现类。
package com.aop.example;
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
public double add(double a, double b) {
// TODO Auto-generated method stub
double result = a+b;
System.out.println(a+"+"+b+" = "+result);
return result;
}
public double sub(double a, double b) {
// TODO Auto-generated method stub
double result = a-b;
System.out.println(a+"-"+b+" = "+result);
return result;
}
}
package com.aop.example;
import java.lang.reflect.Method;
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
package com.aop.example;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import com.sun.org.apache.commons.logging.Log;
import com.sun.org.apache.commons.logging.LogFactory;
public class CalculatorLoggingHandler implements InvocationHandler {
private Log log = LogFactory.getLog(this.getClass());
private Object target;
public CalculatorLoggingHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
log.info("the method "+ method.getName()+" begins with "+Arrays.toString(args));
Object result=method.invoke(target, args);
log.info("the method "+method.getName()+" ends with "+result);
return result;
}
public static Object createProxy(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new CalculatorLoggingHandler(target));
}
}
package com.aop.example;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CalculatorValidationHandler implements InvocationHandler {
private Object target;
public CalculatorValidationHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
for(Object arg:args){
validate((Double)arg);
}
Object result = method.invoke(target, args);
return result;
}
private void validate(double a){
if(a<0){
throw new IllegalArgumentException("illegal argument");
}
}
public static Object createProxy(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new CalculatorLoggingHandler(target));
}
}
最后做一下测试
package com.aop.example;
public class AopTest {
public static void main(String[] args){
ArithmeticCalculator arithmeticCalculatorImpl= new ArithmeticCalculatorImpl();
ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) CalculatorValidationHandler.createProxy
(CalculatorLoggingHandler.createProxy(arithmeticCalculatorImpl));
arithmeticCalculator.add(1.0, 2.0);
arithmeticCalculator.add(-2.2, 2.0);
arithmeticCalculator.sub(3.0, 2.0);
}
}
这里[color=red]使用验证代理包装日志代理形成了一个代理链[/color]。所有计算器的调用首先经过验证代理,然后再是日志代理。