利用,代理的特性,我们可以制作一个拦截器,用来发出目标对象执行的通知
1、编写接口
一个五个通知,分别在业务执行的生命周期里:
before
:前置通知,业务开始执行时发出around
:环绕通知,业务正在执行时发出,业务就在这个通知里面执行afterReturning
:后置通知,业务执行结束时发出afterThrowing
:异常通知,业务执行异常时发出after
:最终通知,业务不管执行与否,都会被调用
/**
* 拦截器标准
* 代理方法的 生命周期
* @author PigIsDuc
*
*/
public interface Interceptor {
/**
*
* 前置通知
*
* 方法执行之前
* 如果返回为真true,那么后面的逻辑继续
* 返回为假false, 则方法不会执行
*
* @param target 目标对象
* @param proxy 代理对象
* @param method 目标对象的每一个方法
* @param args 执行方法的参数
* @return
*/
boolean before(Object target, Object proxy, Method method, Object[] args);
/**
*
* 环绕通知
*
* 方法在执行的时候
* 会得到方法执行的结果
* 方法如果真的执行了,只能在环绕通知里面执行
*
* @param target 目标对象
* @param proxy 代理对象
* @param method 目标对象的每一个方法
* @param args 执行方法的参数
* @return
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
Object around(Object target, Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
/**
* 后置通知
*
* 方法执行之后,在执行这个节点的时候,方法已经有返回值了,所以可以接收返回值的结果
*
* @param result 方法执行的结果
* @param proxy 代理对象
* @param method 目标对象的每一个方法
* @param args 执行方法的参数
*/
void afterReturning(Object result, Object proxy, Method method, Object[] args);
/**
*
* 异常通知
*
* 方法抛出异常之后
*
* @param e 方法所抛出的异常
* @param proxy 代理对象
* @param method 目标对象的每一个方法
* @param args 执行方法的参数
*/
void afterThrowing(Exception e, Object proxy, Method method, Object[] args);
/**
*
* 最终通知
*
* 最终,方法不管执行与否,都会被调用
*
* @param target 目标对象
* @param proxy 代理对象
* @param method 目标对象的每一个方法
* @param args 执行方法的参数
*/
void after(Object target, Object proxy, Method method, Object[] args);
}
2、编写真正的拦截器
/**
* 拦截器的实现类
* @author PigIsDuck
*
*/
public class InterceptorImpl implements Interceptor {
public boolean before(Object target, Object proxy, Method method, Object[] args) {
System.out.println("before InterceptorImpl");
return true;
}
public Object around(Object target, Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println("around InterceptorImpl");
// 执行,获得方法返回值
Object result = method.invoke(target, args);
return result;
}
public void afterReturning(Object result, Object proxy, Method method, Object[] args) {
System.out.println(" InterceptorImpl 后置");
}
public void afterThrowing(Exception e, Object proxy, Method method, Object[] args) {
System.out.println(" InterceptorImpl 异常");
e.printStackTrace();
}
public void after(Object target, Object proxy, Method method, Object[] args) {
System.out.println(" InterceptorImpl after");
}
}
3、改写代理对象的业务类,使其变成拦截器
public class ProxyProcessor implements InvocationHandler{
// 目标对象 / 代理对象
private Object target;
// 使用拦截器增强目标对象
private String interceptorName;
public ProxyProcessor() {
}
public ProxyProcessor(Object target, String interceptorName) {
this.target = target;
this.interceptorName = interceptorName; // 拦截器名称
}
/**
* 对代理对象中的所有方法的曾强逻辑
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 如果不启用拦截器
if(this.interceptorName == null) {
System.out.println("代理对象使用方法前");
result = method.invoke(target, args);
System.out.println("在" + new Date() + "\n使用了什么参数:" + Arrays.toString(args) + "\n调用了什么方法:" + method.getName());
System.out.println("代理对象使用方法后");
}else {
// 利用反射,启用了拦截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorName).newInstance();
try {
if(interceptor.before(target, proxy, method, args)) {
// 拦截器没有拦截出错误
result = interceptor.around(target, proxy, method, args);
interceptor.afterReturning(result, proxy, method, args);
}else {
// 拦截器拦截出错误
throw new RuntimeException("前置通知未通过,方法终止");
}
} catch (Exception e) {
interceptor.afterThrowing(e, proxy, method, args);
}finally {
interceptor.after(target, proxy, method, args);
}
}
return result;
}
}
4、使用拦截器
由于拦截器是使用代理编写的,所以,使用方式和代理的一样,只需要加入拦截器的地址就好
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
UserService userService2 = (UserService) ProxyFactory.bind(userService, "net.duck.spring.aop.reflect.InterceptorImpl");
System.out.println("users:" + userService2.queryAllUser());
结果:
init MockServlet1
before InterceptorImpl // 开始
around InterceptorImpl // 执行
net.duck.spring.ioc.dao.UserDao@5d11346a // 业务
InterceptorImpl 后置 // 后置
InterceptorImpl after // 最终
users:[User [username=duck, password=123456], User [username=pig, password=1asd56]]