假设这样一个场景:
当我们在调用某些方法的时候,需要在调用方法前后,记录执行日志。
简单的做法肯定是在原业务方法的前后作修改,添加记录日志的代码。不过这肯定存在一些问题,比如,当你要记录日志的方法很多,或者偶尔需要修改记录日志的方法,等等。所以需要考虑在不对原有的业务方法产生影响的情况下,加入日志记录。
举个栗子说明下好了:
首先,定义一个业务接口类,一个业务实现类:
/**
* 业务接口类
* Created by Nemo on 2017/12/22.
*/
public interface IBusiness {
boolean doSomeThing() ;
}
/**
* 业务实现类
* Created by Nemo on 2017/12/22.
*/
public class Business implements IBusiness {
LogUtils log = LogUtils.getLog(Business.class);
public boolean doSomeThing() {
log.debug("这是一个业务逻辑。");
return true;
}
}
然后一般的做法:
/**
* 业务实现类
* Created by Nemo on 2017/12/22.
*/
public class Business implements IBusiness {
LogUtils log = LogUtils.getLog(Business.class);
public boolean doSomeThing() {
log.debug("开始记录日志。");
log.debug("这是一个业务逻辑。");
log.debug("记录日志结束。");
return true;
}
}
再看动态代理的实现:
咱们需要一个记录日志的切面类:
import com.nemo.framework.dao.utils.LogUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 打印日志的切面
* Created by Nemo on 2017/12/22.
*/
public class LogInvocationHandler implements InvocationHandler {
LogUtils log = LogUtils.getLog(LogInvocationHandler.class);
private Object target; //目标对象
LogInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.debug("方法执行前记录日志。");
//执行原有逻辑
Object rev = method.invoke(target, args);
log.debug("方法执行后记录日志。");
return rev;
}
}
然后测试调用:
/**
* Created by Nemo on 2017/12/22.
*/
public class ProxyTest {
public static void main(String[] args) {
IBusiness business = (IBusiness) getProxyInstence(IBusiness.class,new Business());
business.doSomeThing();
}
/**
* 得到被代理的对象
* @param clazz
* @param instence
* @return
*/
private static Object getProxyInstence(Class clazz,Object instence){
//需要代理的接口,被代理类实现的多个接口都必须在这里定义
Class[] proxyInterface = new Class[] { clazz };
//构建AOP的Advice,这里需要传入业务类的实例
LogInvocationHandler handler = new LogInvocationHandler(instence);
//生成代理类的字节码加载器
ClassLoader classLoader = ProxyTest.class.getClassLoader();
//织入器,织入代码并生成代理类
Object proxyBusiness = Proxy.newProxyInstance(classLoader, proxyInterface, handler);
//使用代理类的实例来调用方法。
return proxyBusiness;
}
}
可以看到,这里只需在调用的地方传入类定义以及实例,即可得到被代理的对象。然后强制转型,即可以调用相应的业务方法。
并且,在不破坏原来业务逻辑的前提下,日志由咱们的业务日志切面处理即可。
不过,弊端也是有的:
IBusiness business = (IBusiness) getProxyInstence(IBusiness.class,new Business());
可以看到,我们这里还是new Business()了。也就是说,目前暂时不能代理没有实现业务的接口类。
所以还需要解决下没有实现业务的接口类代理问题。