Spring AOP (一)
一个有趣的案例(约定编程)
一个简单的业务(拼图1)
public interface HelloService {
void sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
if(name == null || name.trim().equals("")){
throw new RuntimeException("name is null");
}
System.out.println("hello," + name);
}
}
一个简单的拦截器(拼图2)
public interface Interceptor {
//事前方法
boolean before();
//事后方法
void after();
/**
* 取代原有事件方法
* @param invocation 回调参数,可以通过它的proceed方法,回调原有事件
* @return 原有事件返回对象
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
//是否返回方法。事件没有发生异常执行
void afterReturning();
//事后异常方法,当事件发生异常后执行
void afterThrowing();
//是否使用around方法取代原有方法
boolean userAround();
}
public class MyInterceptor implements Interceptor {
@Override
public boolean before() {
System.out.println("before......");
return true;
}
@Override
public void after() {
System.out.println("after......");
}
@Override
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
System.out.println("around before......");
Object obj = invocation.proceed();
System.out.println("around after......");
return obj;
}
@Override
public void afterReturning() {
System.out.println("afterReturning......");
}
@Override
public void afterThrowing() {
System.out.println("afterThrowing......");
}
@Override
public boolean userAround() {
return true;
}
}
around中Invocation
public class Invocation {
private Object[] params;
private Method method;
private Object target;
public Invocation(Object[] params, Method method, Object target) {
this.params = params;
this.method = method;
this.target = target;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException{
return method.invoke(target, params);
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
制定约定(绘画拼图模板)
当使用代理类获取到代理对象,然后强转为被代理类相应接口类型并调用相应方法时
- 先执行Interceptor中的before方法
- 如果Interceptor中的useAround方法返回true(这里默认为true),则执行around方法,由于Invocation回调参数的存在,会调用被代理类的目标方法;如果useAround方法返回false,则直接调用被代理类的目标方法
- 无论怎样,after方法都会执行
- 若发生异常,则调用afterThrowing方法;若无,则调用afterReturning方法
-
附上图
-
预测一下执行结果
before…
around before…
hello,tom
around after…
after…
afterReturning…
------------name is null-------------
before…
around before…
after…
afterThrowing…
将约定内容织入到约定流程中(将拼图块按一定规则填入拼图模板)
public class ProxyBean implements InvocationHandler{
private Object target = null;
private Interceptor interceptor = null;
/**
* 绑定代理对象
* @param target 被代理对象
* @param interceptor 拦截器
* @return 代理对象
*/
public static Object getProxyBean(Object target, Interceptor interceptor){
ProxyBean proxyBean=new ProxyBean();
proxyBean.target=target;
proxyBean.interceptor=interceptor;
Object proxy =
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyBean);
return proxy;
}
/**
* 处理代理对象方法逻辑
* @param proxy 代理对象
* @param method 当前方法
* @param args 运行参数
* @return 方法调用结果
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean exceptionFlag=false;
Invocation invocation=new Invocation(args, method, target);
Object retObj=null;
try {
if(this.interceptor.before()){
retObj=this.interceptor.around(invocation);
}else{
retObj=method.invoke(target, args);
}
} catch (Exception e) {
exceptionFlag=true;
}
this.interceptor.after();
if(exceptionFlag){
this.interceptor.afterThrowing();
}else{
this.interceptor.afterReturning();
return retObj;
}
return null;
}
public static void main(String[] args) {
HelloService proxyBean =
(HelloService) ProxyBean.getProxyBean(new HelloServiceImpl(), new MyInterceptor());
proxyBean.sayHello("tom");
System.out.println("------------name is null-------------");
proxyBean.sayHello(null);
}
}
执行代码验证
在这个例子中,JDK动态代理是关键,但我仍未弄明白动态代理内部的执行流程,如代理类对象执行相应方法时,怎么走的InvocationHandler中的invoke方法,其源码有点难懂
参考书籍
《深入浅出Spring Boot 2.x》