跟开振学习Spring AOP第一篇:开启约定编程之路
Posted ykzhen2015
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟开振学习Spring AOP第一篇:开启约定编程之路相关的知识,希望对你有一定的参考价值。
对于初学者也许Spring IoC相对好理解,你也许会觉得AOP就不好理解了,不要紧,这里博主带领一下大家理解AOP框架。在传统的Spring书籍中会对你介绍十分生涩难懂的,切面,切点,通知,织入等内容,好吧,我必须承认,这些对初学的码农确实很难理解,我们先把这些放下,这篇主要和大家玩约定编程。
首先我们先来一个简单到不能简单的服务接口:
package com.learn.spring.aop.service;
public interface HelloService
public void sayHello();
这个接口够简单了吧,不用我解释的,然后给出实现类:
package com.learn.spring.aop.service.impl;
import com.learn.spring.aop.service.HelloService;
public class HelloServiceImpl implements HelloService
@Override
public void sayHello()
System.out.println("hello world!!");
典型的hello world,初学者的问题也没有问题,好吧,假设上面你给你写的,下面是博主设计的,首先是一个拦截器接口:
package com.learn.spring.aop.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public interface Interceptor
// 前置通知
void before();
// 是否采用环绕通知
boolean useAround();
// 环绕通知
Object around(Object target, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
// 事后方法
void after();
// 异常返回方法
void afterThrowing();
// 正常返回方法
void afterReturning();
依据这个接口,你是否可以给出一个很简单的实现:
package com.learn.spring.aop.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyInterceptor implements Interceptor
@Override
public void before()
System.out.println("事前通知");
@Override
public boolean useAround()
return false;
@Override
public Object around(Object target, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
System.out.println("环绕通知");
//环绕通知
return method.invoke(target, args);
@Override
public void after()
System.out.println("事后通知");
@Override
public void afterThrowing()
System.out.println("异常返回通知");
@Override
public void afterReturning()
System.out.println("正常返回通知");
好吧,对于你而言,也不难吧。
好了,来点好玩的,然后我给你一个ProxyBean(com.learn.spring.aop.proxy.ProxyBean)的静态方法:
/**
* 绑定代理对象
* @param target 服务对象
* @param interceptor 拦截器
* @return 代理对象,该对象你可以强制转换为服务对象的接口类型
*/
public static Object getProxyBean(Object target, Interceptor interceptor)
然后我们暂且把这个方法返回的对象,记为proxy。
通过注释我们可以知道,我们如果使用ProxyBean.proxy(new HelloServiceImpl (), interceptor),就会返回一个对象proxy,按照约定:它可以通过强制转换为HelloService。例如:
HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
然后你就可以通过proxy去调用方法了,好了,来点好玩的。
如果你用proxy去调用方法,博主和你约定如下(这是一次很重要的约定哦):
1、首先调用拦截器的before方法;
2、如果拦截器的useAround方法,返回为true,则执行拦截器的around方法,不执行target对象原有的sayHello方法;
3、无论结果如何都会执行拦截器的after方法;
4、如果around方法有异常或者原有的target对象的sayHello方法有异常,则执行拦截器的afterThrowing方法,否则执行afterReturning方法。
好了,为了更好的说明,我们来个流程图。
好了,依据我们的约定,你的代码将会被织入这个流程中,假设你再开发中使用了这样的代码进行测试。
package com.learn.spring.aop.main;
import com.learn.spring.aop.proxy.MyInterceptor;
import com.learn.spring.aop.proxy.ProxyBean;
import com.learn.spring.aop.service.HelloService;
import com.learn.spring.aop.service.impl.HelloServiceImpl;
public class ProxyMain
public static void main(String[] args)
HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
proxy.sayHello();
那么其结果按照我们约定的流程图,就是:
事前通知
hello world!!
事后通知
正常返回通知
然后我们把MyInterceptor中的useAround方法,修改为返回true,于是就可以看到:
事前通知
环绕通知
hello world!!
事后通知
正常返回通知
跟着我们修改拦截器的around方法,让它抛出异常,如下所示
@Override
public Object around(Object target, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
System.out.println("环绕通知");
if (args == null || args.length == 0)
throw new RuntimeException("测试异常");
//环绕通知
return method.invoke(target, args);
于是得到这样的打印:
事前通知
环绕通知
事后通知
异常返回通知
这一切都是按照我们流程约定来的,你应该照着我们约定的流程图看这些结果。换句话说,在你知道前面的东西后,你就可以按照约定编程了,你是不需要知道博主是怎么做的,对吧。因为我们有约定,按约定就可以了,何必知道那么底层呢?这也是设计者的思维,设计者希望开发者的东西越简单就越好。吐舌头
那么博主是怎么做到将你开发的服务和拦截器织入到流程图中的呢?一切的奥妙当然是我给你的ProxyBean了,
不过在此之前你需要学习一下动态代理,可以看看博主的文章:
MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)
ps:在Spring中可以使用JDK动态代理,也可以玩CGLIB动态代理,在默认的情况下Spring会以这样的规则来服务:存在接口,使用JDK动态代理,因为JDK动态代理,必须需要接口,而不存在接口则使用CGLIB动态代理。
好了,我们现在公布ProxyBean的代码:(要懂得动态代理才能看懂,菜鸟补习去吧)
package com.learn.spring.aop.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
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;
Object retObj = null;
//前置通知
this.interceptor.before();
try
//判断是否启用环绕通知
if (this.interceptor.useAround())
//环绕通知
retObj = this.interceptor.around(target, method, args);
else
//调用目标对象原有方法
retObj = method.invoke(target, args);
catch (Exception ex)
//异常标志
exceptionFlag = true;
//事后通知
this.interceptor.after();
if (exceptionFlag)
//异常返回通知
this.interceptor.afterThrowing();
return null;
else
//正常返回通知
this.interceptor.afterReturning();
return retObj;
首先,在getProxyBean方法中,博主将你的服务对象target和拦截器保存起来,然后生成一个代理对象,并且委托给ProxyBean的invoke方法去执行,于是当你用proxy对象调用方法的时候,就会进入到invoke方法中了,然后博主把你的代码,按约定的那样织入到流程图中,于是你就可以按照我们的约定编程了。这里强烈建议初学的同学对invoke方法,这段代码进行一步步调试。这样你理解AOP就快很多。
好了,今天谈了约定编程,如果是我来设计,我倒不希望你知道ProxyBean的源码的内容,值需要知道我和你约定的内容,就可以了,这就叫做封装掉,最难理解的内容。
你可以看到,按照约定的规则,博主就可以将你的代码织入到约定的流程中,同样的Spring也是可以做到的,这就是AOP的内容,而实际上你调试这个例子,你就基本懂得了AOP,下一篇,我们把这篇的概念换一下,你就知道原来AOP也就是那么回事。
以上是关于跟开振学习Spring AOP第一篇:开启约定编程之路的主要内容,如果未能解决你的问题,请参考以下文章