spring-AOP(一)实现原理

spring内部使用了jdk动态代理、cglib(经过生成字节码的方式,继承目标类扩展目标类功能)两种方式实现了AOP框架。本篇先详细介绍spring内部的AOP概念实体、以后介绍spring AOP的使用方式和原理java

实现原理

spring内部使用了jdk动态代理、cglib这两种机制构建了整个AOP框架的基础正则表达式

JDK动态代理

咱们能够经过反射技术,为须要代理的目标对象,创造一个代理类出来,而且在代理类中执行咱们所须要的逻辑,如:统计方法执行时间、打印日志。
相对于cglib技术,JDK动态代理存在两个比较明显的缺点:spring

  1. 目标对象必须是经过实现接口的类,才能建立代理
  2. 在运行时,使用反射进行扩展目标对象功能,性能会略低于cglib字节码技术的实现方式
// 一个须要进行代理的接口
public interface Greeting {

    void sayHi(String name);

    void sayByte(String name);
}

// 接口实现类,即目标对象。
// 咱们须要在不改变该实现类代码的基础上,在执行接口方法时,进行一些额外的功能
public class GreetingImpl implements Greeting {

    @Override
    public void sayHi(String name) {
        System.out.println(name);
    }

    @Override
    public void sayByte(String name) {
        System.out.println(name);
    }
}

// 实现一个InvocationHandler,用于执行额外功能,而且调用目标对象的方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/** * created by luhuancheng on 2018/11/17 * @author luhuancheng */
public class LogInvocationHandler implements InvocationHandler {

    private Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before execute"); // 在执行目标对象方法以前,打印日志
        Object result = method.invoke(target, args); // 执行目标对象的方法
        System.out.println("After execute"); // 在执行目标对象方法以后,打印日志

        return result;
    }
}

// 建立动态代理
public class DynamicProxy {
    public static void main(String[] args) {

        Greeting greeting = new GreetingImpl();
        Object proxyInstance = Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), new Class[]{Greeting.class}, new LogInvocationHandler(greeting));
        Greeting proxy = (Greeting) proxyInstance;
        proxy.sayHi("luhuancheng");
        proxy.sayByte("luhuancheng");
    }
}
复制代码

cglib字节码技术

当咱们要代理的目标对象,并非由一个接口实现时。咱们没法经过JDK动态代理来进行代理对象的建立,这时候就须要cglib这种字节码技术的登场了。缓存

// 须要被代理的目标类,该类没有实现任何一个接口
public class Requestable {
    public void request() {
        System.out.println("request in Requestable without implementint any interface");
    }
}

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 实现cglib的方法拦截器接口
public class RequestableCallback implements MethodInterceptor {

    private static final String INTERCEPTOR_METHOD_NAME = "request";

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (INTERCEPTOR_METHOD_NAME.equals(method.getName())) {
            System.out.println("Before execute");
            Object result = methodProxy.invokeSuper(target, args);
            System.out.println("After execute");
            return result;
        }
        return null;
    }
}

import net.sf.cglib.proxy.Enhancer;

// cglib代理实现-动态字节码生成技术扩展对象行为(对目标对象进行继承扩展)
public class CglibProxy {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Requestable.class);
        enhancer.setCallback(new RequestableCallback());

        Requestable proxy = (Requestable) enhancer.create();
        proxy.request();
    }
}

复制代码

AOP概念实体

JoinPoint

JoinPoint,能够理解为执行切面动做的一个时机。如:构造方法调用时,字段设置时,方法调用时,方法执行时。但在spring AOP中,仅支持方法执行时的JoinPoint。app

类图

Pointcut

Pointcut,是咱们在开发AOP功能时,定义的一个帮助咱们捕捉系统中的相应JoinPoint的规则。框架

类图

  1. ClassFilter, 用于匹配被执行织入操做的对象。若是类型对于咱们要捕获的JoinPoint无关的话,可使用TruePointcut类
  2. MethodMatcher,用于匹配被执行织入操做的方法 。从大的分类能够分为两类:StaticMethodMatcher(不关心Pointcut具体参数)、DynamicMethodMatcher(关系Pointcut具体参数)
  3. StaticMethodMatcherPointcut继承了StaticMethodMatcher并且实现接口Pointcut,其自身能够做为Pointcut和MethodMatcher
  4. NameMatchMethodPointcut,根据指定的方法名和JoinPoint方法名进行匹配
  5. JdkRegexpMethodPointcut,使用正则表达式与JoinPoint方法名进行匹配
  6. AnnotationMatchingPointcut,根据目标类中是否存在指定类型的注解来匹配JoinPoint
  7. ComposablePointcut,能够进行逻辑运算的Pointcut
  8. DynamicMethodMatcherPointcut,继承了DynamicMethodMatcher并且实现接口Pointcut,其自身能够做为Pointcut和MethodMatcher

Advice

Advice,定义了AOP功能中的横切逻辑less

类图

BeforeAdvice

横切逻辑将在相应的JoinPoint执行以前执行ide

AfterAdvice

横切逻辑将在相应的JoinPoint执行以后执行。该接口又派生了两个子接口ThrowsAdvice、AfterReturningAdvicepost

ThrowsAdvice

横切逻辑将在相应的JoinPoint执行异常时执行性能

// ThrowsAdvice是一个没有定义方法的标记接口,可是在横切逻辑执行时,
// spring AOP内部会经过反射的方式,检测ThrowsAdvice实现类的方法。
// 咱们能够定义如下三个方法,分别处理不一样的异常
public class ExceptionBarrierThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(Throwable t) {
        // 普通异常处理逻辑
    }

    public void afterThrowing(RuntimeException e) {
        // 运行时异常处理逻辑
    }

    public void afterThrowing(Method method, Object[] args, Object target, ApplicationException e) {
        // 处理应用程序生成的异常
    }

}

/** * 业务异常类 */
class ApplicationException extends RuntimeException {

}
复制代码
AfterReturningAdvice

横切逻辑将在相应的JoinPoint正常执行返回时执行

MethodInterceptor

做为Spring AOP的环绕方法(Around Advice),能够拦截相应的JoinPoint方法执行,从而在JoinPoint执行的前、正常返回、执行后这三个地方进行横切逻辑的切入

public class PerformanceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            // 调用目标方法
            return invocation.proceed();
        } finally {
            System.out.println(String.format("cost time: %d", System.currentTimeMillis() - start));
        }
    }
}
复制代码

IntroductionInterceptor

Spring AOP框架中实现Introduction的接口。Introduction功能能够在不改变类的定义的状况下,为目标类增长新的接口或属性

Advisor(对应AOP世界中的切面,Aspect。)

Advisor,表明了spring中的Aspect。
分为两大类:PointcutAdvisor、IntroductionAdvisor

类图

PointcutAdvisor

PointcutAdvisor,默认存在三个实现类DefaultPointcutAdvisor、NameMatchMethodPointcutAdvisor、RegexpMethodPointcutAdvisor

DefaultPointcutAdvisor

DefaultPointcutAdvisor做为PointcutAdvisor比较通用的一个实现类,咱们能够为其设置Pointcut、Advice。伪代码以下:

Pointcut pointcut = ...
Advice advice = ...
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(advice);
复制代码
NameMatchMethodPointcutAdvisor

NameMatchMethodPointcutAdvisor,其内部持有一个NameMatchMethodPointcut实例做为pointcut。能够经过setMappedName(String mappedName)或者setMappedNames(String... mappedNames)操做pointcut,设置匹配的Pointcut方法名

RegexpMethodPointcutAdvisor

RegexpMethodPointcutAdvisor,其内部持有一个AbstractRegexpMethodPointcut实现类的实例pointcut(默认为JdkRegexpMethodPointcut),使用setPattern(String pattern)或者setPatterns(String... patterns)设置正则表达式用于匹配JoinPoint方法名

DefaultBeanFactoryPointcutAdvisor

DefaultBeanFactoryPointcutAdvisor,间接继承了BeanFactoryAware,其内部持有beanfactory。在指定advice时,能够经过方法setAdviceBeanName(String adviceBeanName)指定advice在beanfactory中的惟一name。以后在须要Advice时,将从beanfactory中获取,减小了容器启动初期Advice和Advisor之间的耦合

IntroductionAdvisor

DefaultIntroductionAdvisor

DefaultIntroductionAdvisor做为惟一的Introduction类型的Advice,只能使用于Introduction场景。

spring AOP织入原理

在spring AOP中,能够经过ProxyFactory、ProxyFactoryBean、AbstractAutoProxyCreator(及其实现类)来执行织入

ProxyFactory

咱们来看看ProxyFactory这个最基本的织入器是如何工做的。其步骤大体分为如下几步:

  1. 将目标对象传入构造器,实例化一个ProxyFactory
  2. 调用ProxyFactory相关方法,设置所需的参数。如:是否使用cglib优化setOptimize(true)、代理类setProxyTargetClass(true)
  3. 指定要织入的接口(这个步骤可选,代码内部会根据目标对象检测到接口)
  4. 实例化切面(即Advisor),设置切面逻辑(将Advice实现类实例设置进Advisor实现类中)
  5. 调用ProxyFactory.getProxy()获取代理类
  6. 执行代理类
基于接口
/** * 基于接口的织入 - JDK动态代理 */
private static void forInterface() {
    // 1. 须要被拦截的接口实现类
    ITask task = new TaskImpl();
    // 2. 实例化一个执行织入操做的ProxyFactory
    ProxyFactory weaver = new ProxyFactory(task);
    // 咱们也可让基于接口的织入,使用cglib的方式
    weaver.setProxyTargetClass(true);
    weaver.setOptimize(true);
    // 3. 指定须要织入的接口
    weaver.setInterfaces(ITask.class);
    // 4. 定义切面
    NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
    advisor.setMappedName("execute");
    // 5. 指定切面逻辑
    advisor.setAdvice(new PerformanceMethodInterceptor());
    // 6. 将切面添加到ProxyFactory实例中
    weaver.addAdvisor(advisor);
    // 7. 执行织入,返回织入后的代理实例
    ITask proxyObject = (ITask) weaver.getProxy();
    // 8. 调用接口,此时的执行逻辑中织入了切面逻辑
    proxyObject.execute();
    System.out.println(proxyObject);
    System.out.println(task);
}
复制代码
基于类
/** * 基于类的织入 - CGLIB */
private static void forClass() {
    // 1. 实例化一个执行织入操做的ProxyFactory,做为织入器
    ProxyFactory weaver = new ProxyFactory(new Excutable());
    // 2. 实例化Advisor(切面)
    NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
    advisor.setMappedName("execute");
    // 3. 为Advisor设置切面逻辑
    advisor.setAdvice(new PerformanceMethodInterceptor());
    // 4. 为织入器设置切面
    weaver.addAdvisor(advisor);
    // 5. 执行织入
    Excutable proxyObject = (Excutable) weaver.getProxy();
    // 6. 调用接口,此时的执行逻辑中织入了切面逻辑
    proxyObject.execute(); // 使用cglib,第一次执行须要动态生成字节码,效率比动态代理低。
    proxyObject.execute(); // 再次使用cglib,直接执行第一次调用生成的字节码,效率比动态代理高
    System.out.println(proxyObject.getClass());
}
复制代码
Introduction织入
private static void forIntroduction() {
    ProxyFactory weaver = new ProxyFactory(new DevloperImpl());
    weaver.setInterfaces(IDevloper.class, ITester.class);
    TesterFeatureIntroductionInterceptor advice = new TesterFeatureIntroductionInterceptor();
    weaver.addAdvice(advice);
// DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(advice);
// weaver.addAdvisor(advisor);
    Object proxy = weaver.getProxy();
    ((ITester) proxy).test();
    ((IDevloper) proxy).develSoftware();
}
复制代码

ProxyFactory内部机制

类图

AopProxy

AopProxy对使用不一样实现机制的代理进行了抽象。提供两个接口用于生成代理对象

Object getProxy();
Object getProxy(ClassLoader classLoader);
复制代码
ProxyConfig

提供了5个属性用于配置控制代理对象生成的过程

private boolean proxyTargetClass = false;

private boolean optimize = false;

boolean opaque = false;

boolean exposeProxy = false;

private boolean frozen = false;
复制代码
Advised

提供接口用于配置生成代理的必要配置信息,好比Advice、Advisor等

AopProxyFactory

做为AopProxy的抽象工厂

DefaultAopProxyFactory

AopProxyFactory默认实现,经过接口根据AdvisedSupport提供的配置信息建立代理

AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
复制代码
ProxyFactory

做为基本的织入器,继承了ProxyCreatorSupport(为了统一管理生成不一样类型的AopProxy,将生成逻辑抽象到了这个类中)。ProxyCreatorSupport持有了一个AopProxyFactory类型的实例,默认为DefaultAopProxyFactory

主流程

ProxyFactoryBean(容器内织入)

使用ProxyFactory咱们能够独立于spring的IOC容器来使用spring AOP框架。可是便于咱们管理Pointcut、Advice等相关的bean,咱们通常利用IOC容器来进行管理。在IOC容器中,使用ProxyFactoryBean来进行织入

类图

能够看出ProxyFactoryBean实现了接口FactoryBean,其实现以下:

public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
    
    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
		if (this.advisorChainInitialized) {
			return;
		}

        // 当拦截器名称列表不为空时,初始化advisor chain
		if (!ObjectUtils.isEmpty(this.interceptorNames)) {
            // 须要根据interceptorName从beanfactory中取到对应advisor的实例,因此beanfactory不能为null
			if (this.beanFactory == null) {
				throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
						"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
			}

			// Globals can't be last unless we specified a targetSource using the property...
			if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
					this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
				throw new AopConfigException("Target required after globals");
			}

			// Materialize interceptor chain from bean names.
			for (String name : this.interceptorNames) {
				if (logger.isTraceEnabled()) {
					logger.trace("Configuring advisor or advice '" + name + "'");
				}
                // 若是name以符号"*"结尾,则从beanfactory中获取beanname为name(去掉*)开头的全部Advisor、Interceptor类型的bean,注册为Advisor
				if (name.endsWith(GLOBAL_SUFFIX)) {
					if (!(this.beanFactory instanceof ListableBeanFactory)) {
						throw new AopConfigException(
								"Can only use global advisors or interceptors with a ListableBeanFactory");
					}
					addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
							name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
				}
                // 普通的name(即非*号匹配的bean),则直接从beanfactory获取,添加到advisor chain中
				else {
					// If we get here, we need to add a named interceptor.
					// We must check if it's a singleton or prototype.
					Object advice;
					if (this.singleton || this.beanFactory.isSingleton(name)) {
						// Add the real Advisor/Advice to the chain.
						advice = this.beanFactory.getBean(name);
					}
					else {
						// It's a prototype Advice or Advisor: replace with a prototype.
						// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
						advice = new PrototypePlaceholderAdvisor(name);
					}
					addAdvisorOnChainCreation(advice, name);
				}
			}
		}
        // 标记已初始化
		this.advisorChainInitialized = true;
	}

    private synchronized Object getSingletonInstance() {
		if (this.singletonInstance == null) {
            // 根据targetName从beanfactory中获取目标对象
			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");
				}
                // 自动识别目标对象的接口,设置到interfaces属性中
				setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
			}
			// Initialize the shared singleton instance.
			super.setFrozen(this.freezeProxy);
            // 生成代理对象
			this.singletonInstance = getProxy(createAopProxy());
		}
		return this.singletonInstance;
	}

    @Override
    public Object getObject() throws BeansException {
        // 初始化advisor chain
        initializeAdvisorChain();
        
        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();
        }
    }
}

复制代码

使用AbstractAutoProxyCreator实现类(自动织入)

从类图能够看出,全部的AutoProxyCreator都间接实现了接口InstantiationAwareBeanPostProcessor。而InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法提供了一个机会,让注册到beanfactory中的bean在实例化以前,能够有一个建立实例的嵌入逻辑。具体能够看下 springIOC容器实例化bean实例的过程分析
从类图能够看出,全部的AutoProxyCreator都间接实现了接口InstantiationAwareBeanPostProcessor。而InstantiationAwareBeanPostProcessor#postProcessAfterInitialization方法提供了一个机会,完成代理对象的建立,并缓存到容器中,供后续使用

BeanNameAutoProxyCreator

使用BeanNameAutoProxyCreator的伪代码

TargetClass target1 = new TargetClass();
TargetClass target2 = new TargetClass();

BeanNameAutoProxyCreator autoProxyCreator = new BeanNameAutoProxyCreator();
// 指定目标实例的beanname
autoProxyCreator.setBeanNames("target1", "target2");
// 指定Advice(切面逻辑)
autoProxyCreator.setInterceptorNames("a interceptor name");

// 完成以上配置的BeanNameAutoProxyCreator,注册到IOC容器时,将自动完成对target一、target2两个bean进行织入切面逻辑
复制代码

总结

总结一下:

  1. 咱们了解了spring aop中AOP实体的实现类:JoinPoint、Pointcut、Advice、Advisor(Aspect)等
  2. 使用ProxyFactory在容器外进行切面逻辑的织入
  3. 使用ProxyFactoryBean在容器管理Advice、Advisor实例bean的基础上,进行切面逻辑的织入
  4. 使用AbstractAutoProxyCreator的实现类,经过一些配置能够实如今容器启动时,自动生成代理类。免去了手动生成代理类的过程

如下内容就是spring aop框架的实现原理,能够看到建立aop的过程至关的繁琐,而且若是使用这种方式来建立代理类,织入切面逻辑的话,存在大量的模板代码。在spring2.0中,使用了一种全新的方法来简化咱们开发AOP的流程。咱们在下篇文章进行分析吧

相关文章
相关标签/搜索