Spring之AOP原理、代码、使用详解(XML配置方式)

时间:2021-01-20 21:34:50

  Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理、AOP代码以及AOP使用三个方向来讲AOP。先给出一张AOP相关的结构图,可以放大查看。

Spring之AOP原理、代码、使用详解(XML配置方式)

一、Spring AOP 接口设计

  1、PointCut (连接点,定义匹配哪些方法)

  首先打开 Spring 的源码,查看 PointCut 接口设计:

public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}

  该接口定义了2 个方法,一个成员变量。我们先看第一个方法:ClassFilter getClassFilter() ,该方法返回一个类过滤器,由于一个类可能会被多个代理类代理,于是Spring引入了责任链模式,另一个方法则是 MethodMatcher getMethodMatcher() ,表示返回一个方法匹配器,我们知道,AOP 的作用是代理方法,那么,Spirng 怎么知道代理哪些方法呢?必须通过某种方式来匹配方法的名称来决定是否对该方法进行增强,这就是 MethodMatcher 的作用。还有要给默认的 Pointcut 实例,该实例对于任何方法的匹配结果都是返回 true。

  我们关注一下 MethodMatcher 接口:

public interface MethodMatcher {
boolean matches(Method method, @Nullable Class<?> targetClass);
boolean isRuntime();
boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

  该接口定义了静态方法匹配器和动态方法匹配器。所谓静态方法匹配器,它仅对方法名签名(包括方法名和入参类型及顺序)进行匹配;而动态方法匹配器,会在运行期检查方法入参的值。静态匹配仅会判别一次,而动态匹配因为每次调用方法的入参都可能不一样,所以每次都必须判断。一般情况下,动态匹配不常用。方法匹配器的类型由isRuntime()返回值决定,返回false表示是静态方法匹配器,返回true表示是动态方法匹配器。

  总的来说, PointCut 和 MethodMatcher 是依赖关系,定义了AOP应该匹配什么方法以及如何匹配。

  2、Advice (通知,定义在链接点做什么)

  注意,Advice 接口只是一个标识,什么也没有定义,但是我们常用的几个接口,比如 BeforeAdvice,AfterAdvice,都是继承自它。我们关注一下 AfterAdvice 的子接口 AfterReturningAdvice :

public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
  该接口定义了一个方法,afterReturning,参数分别是返回值,目标方法,参数,目标方法类,在目标方法执行之后会回调该方法。那么我们就可以在该方法中执行我们的切面逻辑,BeforeAdvice 也是一样的道理。

  3、Advisor (通知器,将 Advice 和 PointCut 结合起来)

  有了对目标方法的增强接口 Advice 和 如何匹配目标方法接口 PointCut 接口后,那么我们就需要用一个对象将他们结合起来,发挥AOP 的作用,所以Spring 设计了 Advisor(通知器),经过我们刚刚的描述,我们应该知道了,这个 Advisor 肯定依赖了 Advice 和 PointCut,我们看看接口设计:

public interface Advisor {
Advice EMPTY_ADVICE = new Advice() {};
Advice getAdvice();
boolean isPerInstance();
}

  及其子接口:

public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}

  以上三个接口关系如图所示:

        Spring之AOP原理、代码、使用详解(XML配置方式)

二、手写AOP示例

  我们直接定义以上三个接口的实现类,实现AOP(此方式是为了理解AOP原理的AOP写作方式,非日常用的AOP的XML实现方式)。

  1、Pointcut 接口实现

package test;

import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut; public class TestPointcut implements Pointcut { @Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
} @Override
public MethodMatcher getMethodMatcher() {
return new MethodMatcher() { public boolean matches(Method method, Class<?> targetClass, Object[] args) {
if (method.getName().equals("test")) {
return true;
}
return false;
} public boolean matches(Method method, Class<?> targetClass) {
if (method.getName().equals("test")) {
return true;
}
return false;
} public boolean isRuntime() {
return true;
}
};
}
}

  只要方法名称是test则对该方法进行增强或者说拦截。

  2、AfterAdvice 实现

package test;

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice; public class TestAfterAdvice implements AfterReturningAdvice { @Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println(
"after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
}
}

  3、Advisor 通知器的实现

package test;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor; // 通知器
public class TestAdvisor implements PointcutAdvisor {
// 获取通知处理逻辑
@Override
public Advice getAdvice() {
return new TestAfterAdvice();
}
@Override
public boolean isPerInstance() {
return false;
}
// 获取切入点
@Override
public Pointcut getPointcut() {
return new TestPointcut();
}
}

  我们实现了 PointcutAdvisor 接口,返回我们刚才定义的两个类。完成了他们的组合。

  4、定义目标类 Targe

package test;

public class TestTarget {

    public void test() {
System.out.println("target.test()");
} public void test2() {
System.out.println("target.test2()");
}
}

  该目标的实现是2个方法,分别打印自己的方法名。

  5、定义XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="testAdvisor" class="test.TestAdvisor"></bean>
<bean id="testTarget" class="test.TestTarget"></bean>
<bean id="testAOP" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName">
<value>testTarget</value>
</property>
<property name="interceptorNames">
<list>
<value>testAdvisor</value>
</list>
</property>
</bean>
</beans>
  我们定义了3个bean,上面两个testAdvisor、testTarget是我们刚刚定义的,下面一个ProxyFactoryBean 是什么呢?首先他是一个 FactoryBean,我们在学习 IOC 的时候知道, FactoryBean 是Spring 留给我们扩展用的,实现该接口的类可以自定类的各种功能。ProxyFactoryBean 当然也实现了自己的很多自定义功能。ProxyFactoryBean 也是Spring IOC 环境中创建AOP 应用的底层方法,Spring 正是通过它来实现对AOP的封装。这样我们更加接近Spring的底层设计。而该类需要注入两个属性一个目标类,一个拦截类,ProxyFactoryBean 会生成一个动态代理类来完成对目标方法的拦截。

  6、定义测试类

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext; public class TestAOP { public static void main(String[] args) {
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(
"spring-context/src/test/java/test/beans.xml");
TestTarget target = (TestTarget) applicationContext.getBean("testAOP");
target.test();
System.out.println("----------------");
target.test2();
}
}

  查看输出结果:

target.test()
after TestTarget.test()
----------------
target.test2()

  可以看到因为我们只配置了在test名称的方法之后打印该方法的名称和该目标类的名称,而test2 则没有配置,因此也就没有打印。

三、深入 AOP 源码实现

  我本地配置对service配置了AOP,代码如下:

public class Test {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:aop/args/applicationContext.xml");
TestService service = context.getBean("service", TestService.class);
System.out.println(service.getClass().getName());
service.test("test execution");
}
}

  在调用时,可以通过debug看到service返回的是一个JDK生成的代理对象:JdkDynamicAopProxy,而不是我们自己定义的一个TestServiceImpl的实例,也就是说, FactoryBean 确实能够在IOC容器中做一些定制化。

      Spring之AOP原理、代码、使用详解(XML配置方式)

  配置了AOP的实例类在IOC阶段就已经注册了一个带有AOP功能的代理类,那么这个代理对象是如何生成的呢?

  首先进入抽象类 AbstractApplicationContext 的getBean 方法,从容器或获取 Bean,再调用 doGetBean 方法,看看该方法实现:

//获取IoC容器中指定名称的Bean
public Object getBean(String name) throws BeansException {
//doGetBean才是真正向IoC容器获取被管理Bean的过程
return doGetBean(name, null, null, false);
}
//获取IoC容器中指定名称和类型的Bean
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
//doGetBean才是真正向IoC容器获取被管理Bean的过程
return doGetBean(name, requiredType, null, false);
}
//获取IoC容器中指定名称和参数的Bean
public Object getBean(String name, Object... args) throws BeansException {
//doGetBean才是真正向IoC容器获取被管理Bean的过程
return doGetBean(name, null, args, false);
}
//获取IoC容器中指定名称、类型和参数的Bean
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
//doGetBean才是真正向IoC容器获取被管理Bean的过程
return doGetBean(name, requiredType, args, false);
}
//真正实现向IoC容器获取Bean的功能,也是触发依赖注入功能的地方
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖,如果指定的是别名,将别名转换为规范的Bean名称
final String beanName = transformedBeanName(name);
Object bean;
//先从缓存中取是否已经有被创建过的单态类型的Bean,对于单态模式的Bean整个IoC容器中只创建一次,不需要重复创建
Object sharedInstance = getSingleton(beanName);
//IOC容器创建单态模式Bean实例对象
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
//如果指定名称的Bean在容器中已有单态模式的Bean被创建,直接返回已经创建的Bean
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
//注意:BeanFactory是管理容器中Bean的工厂,而FactoryBean是创建创建对象的工厂Bean,两者之间有区别
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
    //缓存没有正在创建的单态模式Bean
//缓存中已经有已经创建的原型模式Bean,但是由于循环引用的问题导致实例化对象失败
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//对IoC容器中是否存在指定名称的BeanDefinition进行检查,首先检查是否能在当前的BeanFactory中获取的所需要的Bean
    //如果不能则委托当前容器的父级容器去查找,如果还是找不到则沿着容器的继承体系向父级容器查找
BeanFactory parentBeanFactory = getParentBeanFactory();
//当前容器的父级容器存在,且当前容器中不存在指定名称的Bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//解析指定Bean名称的原始名称
String nameToLookup = originalBeanName(name);
if (args != null) {
//委派父级容器根据指定名称和显式的参数查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
//委派父级容器根据指定名称和类型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
//创建的Bean是否需要进行类型验证,一般不需要
if (!typeCheckOnly) {
//向容器标记指定的Bean已经被创建
markBeanAsCreated(beanName);
}
//根据指定Bean名称获取其父级的Bean定义,主要解决Bean继承时子类合并父类公共属性问题
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//获取当前Bean所有依赖Bean的名称
String[] dependsOn = mbd.getDependsOn();
//如果当前Bean有依赖Bean
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
//递归调用getBean方法,获取当前Bean的依赖Bean
getBean(dependsOnBean);
//把被依赖Bean注册给当前依赖的Bean
registerDependentBean(dependsOnBean, beanName);
}
}
//创建单态模式Bean的实例对象
if (mbd.isSingleton()) {
//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
//创建一个指定Bean实例对象,如果有父级继承,则合并子//类和父类的定义
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//显式地从容器单态模式Bean缓存中清除实例对象
destroySingleton(beanName);
throw ex;
}
}
});
//获取给定Bean的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//IoC容器创建原型模式Bean实例对象
else if (mbd.isPrototype()) {
//原型模式(Prototype)是每次都会创建一个新的对象
Object prototypeInstance = null;
try {
//回调beforePrototypeCreation方法,默认的功能是注册当前创//建的原型对象
beforePrototypeCreation(beanName);
//创建指定Bean对象实例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//回调afterPrototypeCreation方法,默认的功能告诉IoC容器指//定Bean的原型对象不再创建了
afterPrototypeCreation(beanName);
}
//获取给定Bean的实例对象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//要创建的Bean既不是单态模式,也不是原型模式,则根据Bean定义资源中配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中比较常用
//如:request、session、application等生命周期
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
//Bean定义资源中没有配置生命周期范围,则Bean定义不合法
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
//这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
Object scopedInstance = scope.get(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
//获取给定Bean的实例对象
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
//对创建的Bean实例对象进行类型检查
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}
  该方法会进入到第一个if块中的 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null) 方法中,而 getObjectForBeanInstance 方法则会先判断缓存是否存在,如果不存在,则进入父类的 getObjectForBeanInstance 方法,我们看看该方法实现:
//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
//容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean,也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象
//如果调用本身就想获得一个容器的引用,则指定返回这个工厂Bean实例对象
//如果指定的名称是容器的解引用(dereference,即是对象本身而非内存地址),且Bean实例也不是创建Bean实例对象的工厂Bean
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
//如果Bean实例不是工厂Bean,或者指定名称是容器的解引用,调用者向获取对容器的引用,则直接返回当前的Bean实例
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
//处理指定名称不是容器的解引用,或者根据名称获取的Bean实例对象是一个工厂Bean,使用工厂Bean创建一个Bean的实例对象
Object object = null;
if (mbd == null) {
//从Bean工厂缓存中获取给定名称的Bean实例对象
object = getCachedObjectForFactoryBean(beanName);
}
//让Bean工厂生产给定名称的Bean对象实例
if (object == null) {
FactoryBean factory = (FactoryBean) beanInstance;
//如果从Bean工厂生产的Bean是单态模式的,则缓存
if (mbd == null && containsBeanDefinition(beanName)) {
//从容器中获取指定名称的Bean定义,如果继承基类,则合并基类相关属性
mbd = getMergedLocalBeanDefinition(beanName);
}
//如果从容器得到Bean定义信息,并且Bean定义信息不是虚构的,则让工厂Bean生产Bean实例对象
boolean synthetic = (mbd != null && mbd.isSynthetic());
//调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,实现工厂Bean生产Bean对象实例的过程
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
  首先判断是否是 Bean 引用类型并且是否是 Factory 类型,很明显不是Bean 引用类型(Bean引用类型指的是IOC在解析XML文件 的时候,会有 ref 属性,而这个ref 对象还没有实例化,则暂时创建一个Bean引用类型的实例,用于在依赖注入的时候判断是否是Bean的属性类型,如果是,则从容器中取出,如果不是,则是基本类型,就直接赋值),然后进入下面的if判断,很明显会直接跳过。进入下面的 getCachedObjectForFactoryBean(beanName) 方法,从缓存中取出,很明显,第一次肯定返回null,继续向下,进入if块,重点在 object = getObjectFromFactoryBean(factory, beanName, !synthetic) 方法,我们进入该方法查看:
//Bean工厂生产Bean实例对象
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
//Bean工厂是单态模式,并且Bean工厂缓存中存在指定名称的Bean实例对象
if (factory.isSingleton() && containsSingleton(beanName)) {
//多线程同步,以防止数据不一致
synchronized (getSingletonMutex()) {
//直接从Bean工厂缓存中获取指定名称的Bean实例对象
Object object = this.factoryBeanObjectCache.get(beanName);
//Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象
if (object == null) {
//调用Bean工厂的getObject方法生产指定Bean的实例对象
object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
//将生产的实例对象添加到Bean工厂缓存中
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
return (object != NULL_OBJECT ? object : null);
}
}
//调用Bean工厂的getObject方法生产指定Bean的实例对象
else {
return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
}
}

  该方法还是先重缓存中取出,然后进入 doGetObjectFromFactoryBean(factory, beanName) 方法,我们看看该方法:

//调用Bean工厂的getObject方法生产指定Bean的实例对象
private Object doGetObjectFromFactoryBean(
final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
//实现PrivilegedExceptionAction接口的匿名内置类,根据JVM检查权限,然后决定BeanFactory创建实例对象
object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
//调用BeanFactory接口实现类的创建对象方法
return factory.getObject();
}
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//调用BeanFactory接口实现类的创建对象方法
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
//创建出来的实例对象为null,或者因为单态对象正在创建而返回null
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
//为创建出来的Bean实例对象添加BeanPostProcessor后置处理器
if (object != null && shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
}
}
return object;
}
  该方法会直接进入 object = factory.getObject() 行,也就是 ProxyFactoryBean 的 getObject 方法,Spring 允许我们从写 getObject 方法来实现特定逻辑,继续看看该方法实现:
public Object getObject() throws BeansException {
initializeAdvisorChain();// 为代理对象配置Advisor链
if (isSingleton()) {// 单例
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}// 非单例
return newPrototypeInstance();
}
}
  该方法很重要,首先初始化通知器链,然后获取单例,这里返回的就是我们最初看到的JDK动态代理。这里的初始化过滤器链的重要作用就是将通知连接起来,基本实现就是循环我们在配置文件中配置的通知器,按照链表的方式连接起来。然后判断是否是单例,然后我们着重关注下面的方法 getSingletonInstance();
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
  该方法是同步方法,防止并发错误,因为有共享变量。首先返回一个包装过的目标对象,然后是否含有接口,我们的目标类实现了接口,因此截图也有展示interfaces,进入if块,从目标类中取出所有接口并设置接口。接下来重要的一行是 getProxy(createAopProxy()),先创建AOP,再获取代理。我们先看 crateAopProxy。
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}

  该方法返回一个AopProxy 类型的实例,我们看看该接口:

public interface AopProxy {
Object getProxy();
Object getProxy(@Nullable ClassLoader classLoader);
}

  该接口定义了两个重载方法,我们看看它有哪些实现:

        Spring之AOP原理、代码、使用详解(XML配置方式)

  这是该接口的继承图,分别是 JdkDynamicAopProxy 动态代理和 CglibAopProxy 代理。而 JdkDynamicAopProxy 实现了 InvocationHandler 接口,如果熟悉Java 动态代理,应该熟悉该接口,实现了该接口的类并实现invoke方法,再代理类调用的时候,会回调该方法。实现动态代理。

  我们继续看 createAopProxy 方法,该方法主要逻辑是创建一个AOP 工厂,默认工厂是 DefaultAopProxyFactory,该类的 createAopProxy 方法则根据 ProxyFactoryBean 的一些属性来决定创建哪种代理:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

  看到只要实现类接口,则创建JDK代理,否则则是Cglib代理。最终创建了一个 JdkDynamicAopProxy代理。

  我们回到 ProxyFactoryBean 类的 getProxy 方法,当 createAopProxy 返回一个JDK 代理的后,则调用 getProxy 方法获取一个代理对象,我们看看该方法的JdkDynamicAopProxy实现:

public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
} Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

  该方法就是用JDK自带的proxy返回一个proxy代理,接下来我们看看另一种Cglib的实现:

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
} try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
} // Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}

  该方法基本上就是使用了Cglib 的库的一些API最后通过字节码生成代理类,比如 Enhancer 增强器,。总之,我们已经知道了Spring 是如何生成代理对象的,主要的通过 ProxyFactoryBean 来实现。

  最后,返回代理类,执行代理类的方法。完成切面编程。

四、AOP代理类调用流程

  由3已经知道,AOP直接返回了一个代理类,那么这个代理类如何调用方法实现AOP的呢?
  以JdkDynamicAopProxy为例,通过debug可以知道在调用到具体的方法时,实际调用了JdkDynamicAopProxy类的invoke方法。

  1、JdkDynamicAopProxy.invoke()

@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// 获取代理对象中的目标源对象。 相当于 Service 实现类。
TargetSource targetSource = this.advised.targetSource;
Object target = null; Integer var9;
try {
// 判断逻辑目的是避免代理对象执行出现RuntimeException
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
Boolean var19 = this.equals(args[0]);
return var19;
} if (this.hashCodeDefined || !AopUtils.isHashCodeMethod(method)) {
if (method.getDeclaringClass() == DecoratingProxy.class) {
Class var18 = AopProxyUtils.ultimateTargetClass(this.advised);
return var18;
}
//returnvalue, 定义返回结果数据的引用。
Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
} if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} //incaseitcomesfromapool. 目标对象获取。目标对象的类对象
target = targetSource.getTarget();
Class<?> targetClass = target != null ? target.getClass() : null;
// Get the interception chain for this method. 获取代理需要在目标方法执行前后,切入的拦截器链。 关注此方法
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 如果代理对象没有需要切入的拦截器,执行目标对象中的方法。
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
// 创建一个执行器,加入拦截信息,并按照顺序执行拦截代码和目标对象中的方法。
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 方法执行。按照顺序执行拦截代码和目标对象中的方法。关注此方法
retVal = invocation.proceed();
} // Massage return value if necessary. 获取目标对象中方法的返回结果类
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
} Object var13 = retVal;
return var13;
} var9 = this.hashCode();
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
} if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
} }
return var9;
}

  2、AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// 方法匹配信息, 获取 spring 容器中的缓存。
AdvisedSupport.MethodCacheKey cacheKey = new AdvisedSupport.MethodCacheKey(method);
// 从已知的缓存中获取方法缓存匹配信息。
List<Object> cached = (List)this.methodCache.get(cacheKey);
if (cached == null) {
// 查询代理对象需要执行的拦截信息。 关注此方法
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
// 保存缓存数据,为后续其他代码提供缓存内容。
this.methodCache.put(cacheKey, cached);
}
return cached;
}

  3、DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {
List<Object> interceptorList = new ArrayList(config.getAdvisors().length);
Class<?> actualClass = targetClass != null ? targetClass : method.getDeclaringClass();
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
// 通知注册器。spring 容器会将配置好的所有通知使用注册器管理。
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] var8 = config.getAdvisors();
int var9 = var8.length; // 从配置信息中获取通知对象。
for(int var10 = 0; var10 < var9; ++var10) {
Advisor advisor = var8[var10];
MethodInterceptor[] interceptors;
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor)advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
MethodInterceptor[] var15 = interceptors;
int var16 = interceptors.length; for(int var17 = 0; var17 < var16; ++var17) {
MethodInterceptor interceptor = var15[var17];
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor)advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} return interceptorList;
}

  4、ReflectiveMethodInvocation.proceed()

public Object proceed() throws Throwable {
// 开始执行代理方法。包含通知方法和目标对象中的真实方法。
// 判断当前代理是否还有需要执行通知。如果没有通知,执行目标代码。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
} else {
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}

  综合以上给出JdkDynamicAopProxy的时序图:

Spring之AOP原理、代码、使用详解(XML配置方式)

五、AOP常见XML配置

  1、execution 表达式

  语法格式:execution(返回类型 包名.类名.方法名(参数表))

  如:execution(java.lang.String com.xxx.service.AService.test(java.lang.Integer))

  在类型 com.xxx.service.AService 中有方法 test,且参数为 Integer,返回类型为 String 时增加切面。

  如:execution(* com.xxx.AService.*(..)),代码:

<bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
<bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
<aop:config>
<!-- com.sxt.aop包中任意类型任意方法都作为连接点。 -->
<aop:pointcut expression="execution(* com.sxt.aop.*.*(..))" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

  com.xxx.AService 类型中的任意方法,任意类型返回结果,参数表不限定,都增加切面。 应用:最常用 。也是相对最通用。根据方法执行的标准,定义切点。如:事务处理,日志处理。

  2、target表达式

  以目标对象作为切点的表达式定义方式。

  语法: target(包名.接口名)

  如: target(com.xxx.IA) - 所有实现了 IA 接口的实现类,作为代理的目标对象,会自动增加通知的织入,实现切面。代码:

<bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
<bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
<aop:config>
<!-- 任意实现TestService接口的目标对象,都作为连接点 -->
<aop:pointcut expression="target( com.sxt.aop.TestService )" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

  应用:为某一个具体的接口实现提供的配置。如:登录 。登录的时候需要执行的附属逻辑是比较多的。在不同的业务流程中,附属逻辑也不同。如:电商中,可能在登录的时候,需要去执行购物车合并。

  3、this 表达式

  实现了某接口的代理对象,会作为切点。 和 target 很类似,但是更粗粒度,因为一个代理对象可能实现多个接口,意味着可以对应包含多个target。

  语法: this(包名.接口名),如代码:

<bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
<bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
<aop:config>
<!-- 实现接口TestService的任意代理对象都作为连接点 -->
<aop:pointcut expression="this( com.sxt.aop.TestService )" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

  如: this(com.xxx.IA) - 代理对象 Proxy 如果实现了 IA 接口,则作为连接点。 应用:针对某个具体的代理提供的配置。比 target 切点粒度细致。因为目标对象可以多实现。代理对象可以针对目标对象实现的多个接口的某一个接口,提供特定的切点 。如:银 行中的登录,银行中的帐户种类非常多。且有交叉。如:借记卡,贷记卡,借记还贷卡,贷 记还贷卡等。可以针对还贷接口提供一个切点,做还贷信息的记录等。

  4、within 表达式

  以包作为目标,定义切点。

  语法: within(包名.*)- 代表在包中的任意接口或类型都作为切点。如代码:

<bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
<bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
<aop:config>
<!-- com.sxt.aop包中的任意位置作为连接点 -->
<aop:pointcut expression="within( com.sxt.aop.* )" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

  应用: 针对某一个包提供的切点,粒度比 target 粗糙。如:某包中的所有接口都需要执行某附属逻辑。如:电商平台中的下订单。下订单服务中可能需 要特定的逻辑(时间戳校 验,库存检查等),这些逻辑,是其他业务线中不需要提供切面的。

  5、args 表达式

  以参数标准作为目标,定义切点。

  语法: args(类型,类型) - 代表方法的参数表符合要求的时候,作为切点。参数表是有顺序的。代码如:

<bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
<bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
<aop:config>
<!-- 任意只有唯一参数,且参数类型为字符串的,都作为连接点 -->
<aop:pointcut expression="args( java.lang.String )" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

  应用:主要应用在参数校验中。如:登录的时候必须传递两个字符 串参数(登录名和密 码)。可以使用 args 来限定。 配合这 execution 实现。 如: execution( * xxxx.*.login(..) ) args(string,string)。 是使用频率最低的表达式。

六、注解方式AOP概述

  而对于注解方式,通过分析源码我们知道注解方式和 XML 配置方式的底层实现都是一样的,都是通过继承 ProxyCreatorSupport 来实现的,不同的通过扩展不同的 Spring 提供的接口,XML 扩展的是FactoryBean 接口, 而注解方式扩展的是 BeanPostProcessor 接口,通过Spring 的扩展接口,能够对特定的Bean进行增强。而 AOP 正式通过这种方式实现的。

  大致流程为:

  纵观过程:实际就是为bean创建一个proxy,JDKproxy或者CGLIBproxy,然后在调用bean的方法时,会通过proxy来调用bean方法

重点过程可分为:

1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中;

2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy;

3)调用bean方法时通过proxy来调用,proxy依次调用增强器的相关方法,来实现方法切入。

参考资料:

  SpringBoot2 | Spring AOP 原理深度源码分析

  深入理解Spring 之 源码剖析AOP(注解方式)

  Spring源码深度解析(AOP功能源码解析)