用来记录一些原创性的总结
AOP的全称是Aspect Oriented Programming,翻译成中文是面向切面编程。它的主要思想是在程序正常执行的某一个点切进去加入特定的逻辑。AOP框架中对AOP支持最完整的是Aspectj,Spring Aop是基于Aspectj实现的专门针对于Spring自身支持的Aop,它的功能没有Aspectj那么完整,它只作用于Spring bean容器中bean对象的某个方法的执行。正如Spring官方文档所描述的那样,Spring Aop与Aspectj不是竞争关系,而是相互补充、相互完善的这么一个关系。
AOP框架的基本原理基本上都是通过代理的方式对目标对象达到切入式的切面编程的效果,Spring Aop也不例外。Spring Aop只能对它自身bean容器中定义的bean对象进行代理,这算是Spring Aop的一个限制,如果你的项目中不使用Spring的IOC,使用Spring的Aop显然是有点不那么合适的。Spring Aop中使用的代理有两种方式,一种是Jdk的动态代理,另一种是基于CGLIB实现的代理,当我们的bean对象实现了某一接口后,Spring默认将采用Jdk动态代理,当我们的bean对象没有实现接口时,默认将采用CGLIB代理,Spring也支持我们在bean对象实现了接口时也强制的使用CGLIB代理。Spring的Bean容器在初始化bean对象的时候就会判断对应的bean是否需要进行切面编程,即是否需要对其进行代理,如果需要,则初始化的时候就会把它初始化为一个代理对象。下面基于Jdk来看一个简单的示例,假设我们定义了如下这样一个代理工厂,其可以将一个普通的对象基于其实现的接口利用Jdk动态代理机制生成对应的代理对象。具体代码和说明请看如下代码,其中我们可以在目标对象执行特定的方法时加入一些特定的处理逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private static ProxyFactory instance = new ProxyFactory();
private ProxyFactory() {}
public static ProxyFactory getInstance() {
return instance;
}
@SuppressWarnings("unchecked")
public <T> T create(final T t) {
return (T) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
/**
* 当使用创建的代理对象执行其中的方法时,都会转换为调用与代理对象绑定的InvocationHandler对象的invoke方法,
* 这样我们就可以在这个方法里面对调用情况进行一些特定的处理逻辑
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在调用的方法是:" + method);
//1、加入对调用方法前的处理逻辑
//...
Object result = null;
try {
//2、正常的调用目标对象的目标方法
result = method.invoke(t, args);
//3、可加入正常调用后的处理逻辑
//...
} catch (Exception e) {
//4、可加入目标对象的方法调用抛出异常后的处理逻辑
//..
} finally {
//5、可加入目标对象的方法执行完成后的处理逻辑,此逻辑不论是否抛出异常都将执行
}
return result;
}
});
}
}
以下是基于上述代码进行的一个简单示例,具体如下,有兴趣的朋友也可以自己试一试。
@Test
public void test1() {
ProxyFactory proxyFactory = ProxyFactory.getInstance();
//创建一个实现了UserService接口的对象
UserService userService = new UserServiceImpl();
//创建一个基于userService对象的代理对象
UserService proxy = proxyFactory.create(userService);
//调用代理对象的某个方法
User user = proxy.findById(1);
System.out.println(user);
}
上述只是一个简单的示例,只是为了说明Spring Aop的大体原理,实际上Spring Aop的代理逻辑比这个要复杂很多,在初始化bean后它需要判断该bean是否需要创建代理对象,这通常都是BeanPostProcessor的功能。有兴趣的读者可以参考一下DefaultListableBeanFactory的preInstantiateSingletons方法,了解一下Spring bean的初始化过程,更详细的内容请参考AbstractApplicationContext.refresh方法。
在了解Spring Aop的用法前,我们需要先了解一下Spring Aop中的一些重要概念,这些概念的英文名称摘自Spring的官方文档,这些术语在本系列文章中出现时可能会以原始英文的形式出现。
Advice的类型主要有Before、After和Around三种,Before作用于JoinPoint执行前,After作用于JoinPoint执行后(After类型还可以细分),Around则可作用于JoinPoint执行前后,且JoinPoint的执行需要在Around类型的Advice中进行调用,具体如下:
Around Advice的功能是最强大的,所有其它Advice能够满足的需求使用Around Advice也都能够满足。但是Spring官方并推荐我们大量的使用Around Advice,而是使用最简单最能满足我们需要的那个Advice。比如如果你只是想简单的在Join Point执行完成后根据返回值来更新缓存,那你使用After Return Advice将比使用Around Advice更合适。这不但能够使你的程序更加的简单,也能减少你出错的机会(使用Around Advice时需要用户自己调用JoinPoint的proceed方法,让JoinPoint继续运行),更能减少程序运行的复杂度。
Spring AOP目前只支持对方法执行这样的JoinPoint进行特定的Advice处理,更确切的来说是只支持对Spring Bean容器里面的bean定义的方法执行进行切入特定的处理逻辑。如果你需要对属性的访问也进行拦截,也执行特定的Advice,那么你可以考虑使用Aspectj。还有一点需要注意的是切面类不会被自动代理,不能作为其它切面类作用的目标类,即使你配置的Poincut目标对象能包含对应的Aspect也不行。
(注:本文是基于Spring4.1.0所写)