AOP源码剖析之源码配置

在这里插入图片描述

AOP 基本概念

OOP是自上而下从controller-service-dao-数据库。
AOP(Aspect-Oriented Programming)面向切面编程。AOP工作原理一句话概括:通过代理模式为目标对象生产代理对象,并将横切逻辑插入到目标方法执行的前后。

术语

切面(Aspect)

切面是切点和通知的集合,一般单独作为一个类。通知和切点共同定义了关于切面的全部内容。
白话文理解:影响了多个类的公共行为封装到一个可重用模块。

切点(PointCut)

切点是对一系列代表同种功能(目的)的切入点(连接点)的统称,切点不是一个点,而是代表某一功能的一系列连接点的集合。通常使用明确的类或者方法名称,或是利用正则表达式定义所匹配的类和方法来指定这些切点。

通知(Advice)

通知就是我们要在切点执行的操作,就是我们要实现的目的,是要实现的功能的代码实现。一般通知又称为增强。
通知有物种类型:

  • Before 在方法被调用之前调用
  • After 在方法完成后调用通知,无论方法是否执行成功
  • After-returning 在方法成功执行之后调用通知
  • After-throwing 在方法抛出异常后调用通知
  • Around 通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为
    这里需要注意Around不是执行两次。如下代码,而是在pjp.proceed()方法前后的环绕。
@Around("controllerAspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        Object result = null;
        Transaction transaction = null;
        String uri = "";
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            ServletContext ctx = request.getSession().getServletContext();
            uri = request.getRequestURI().replace(ctx.getContextPath(), "").toLowerCase();
            uri = uri.substring(uri.lastIndexOf("/") + 1);
            if (uri.trim().matches("^\\d+$")) {
                String numberUri = uri.trim();
                uri = request.getRequestURI().replace(ctx.getContextPath(), "")
                        .replace("/", "").replace(numberUri, "")
                        .toLowerCase();
            }
            logger.info(String.format("controller path uri:%s", uri));
            transaction = Cat.newTransaction(TYPE, uri);

            Cat.logMetric(uri);
            // 执行方法
            result = pjp.proceed();
            transaction.setStatus(Transaction.SUCCESS);

            // 默认执行完方法不报错就是SUCCESS
            Cat.logEvent(TYPE, uri, Event.SUCCESS, "");
        } catch (Throwable e) {
            logger.error("方法执行失败 controller path=" + uri, e);
            transaction.setStatus(e);
            // 默认执行完报错就记失败
            Cat.logEvent(TYPE, uri, e.getMessage(), "");
            throw e;
        } finally {
            transaction.complete();
        }
        logger.info("controller path={} spent {} seconds", uri, stopWatch.elapsedTime());
        return result;
    }
连接点(JoinPoint)

连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时,抛出异常时,甚至修改一个字段时。切面代码可以利用这些点插入应用的正常流程中,并添加新的行为。Spring只支持方法的连接点

织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入。
Spring AOP的切面织入是在运行时被织入,原理是使用了动态代理技术,Spring支持两种方式生产代理对象:JDK动态代理和CGLIB,默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理

引入(Introduction)

添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口。

目标对象(Target Object)

包含连接点的对象。也被称作被通知或被代理对象。

AOP代理(AOP Proxy)

AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

源码分析

1、AOPNamespaceHandler中init是在什么时候调用的
2. 对于XML配置AOP加载过程已了解,那么对于SpringBoot配置AOP加载过程呢。

XML配置AOP加载过程。
3. AOP有几种配置方式,每种配置方式,源码都是怎么走向。

1、xml配置 aop:config标签使用分析

refresh
-> obtainFreshBeanFactory
-> refreshBeanFactory
-> AbstractRefreshableApplicationContext.loadBeanDefinitions(beanFactory)
-> …
-> XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
-> DefaultBeanDefinitionDocumentReader.parseBeanDefinitions

对于aop:config是自定义的标签不是defaultNamespace(默认是bean标签),那么走的是parseCustomElement方法

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

那么这个Handler就对应META/spring.handlers文件中。Spring各个jar包中的spring.handlers都会生效。
image.png
我们在看下NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);中的resolve方法,此方法拿到namespaceHandler之后会调用init方法。

@Override
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}

然后继续看parseCustomElement方法handler.parse方法,aop:config标签又交给了ConfigBeanDefinitionParser来解析.
首先findParseForElement获取parser然后进行调用parse方法。

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}

我们来看看ConfigBeanDefinitionParser的parse方法:大致是注册一个bean,以及切点、增强、切面的解析(pointcunt\advisor\aspect)

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		configureAutoProxyCreator(parserContext, element);

		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

configureAutoProxyCreator(parserContext, element);一路跟踪到如下

public static void registerAspectJAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
		//注册一个AspectJAwareAdvisorAutoProxyCreator,
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		//解析配置元素,决定代理的模式
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		//作为系统组件,把这个creator这个bean,放到Spring容器中,让Spring实力化,启动这个Creator
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

在bean实例化完成后,会调用BeanPostProcessor的postProcessAfterInitialization方法在其父类AbstractAutoProxyCreator中实现,

@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

跟踪wrapIfNecessary方法,会进入到createProxy方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}
 
		// Create proxy if we have advice.
        // 意思就是如果该类有advice则创建proxy,
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
          
            // 2.返回该proxy代替原来的bean
			return proxy;
		}
 
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

createProxy方法中最后一句proxyFactory.getProxy会进入到DefaultAopProxyFactory的createAopProxy方法。

protected Object createProxy(
			Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		for (Advisor advisor : advisors) {
			proxyFactory.addAdvisor(advisor);
		}

		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}
@Override
	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.");
			}
			//如果bean的类是接口或者类是JDK内部的代理类,则使用JDK的动态代理类。
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			//其他情况使用CGLIB来实现。
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

创建代理类后,其余过程与bean的生命周期基本一致。

2、注解配置 aop:aspectj-autoproxy标签使用分析

与aop:config标签大致类似,区别在于:

  1. 此标签交给AspectJAutoProxyBeanDefinitionParser去进行解析。
  2. AspectJAutoProxyBeanDefinitionParser解析时候注册的是AnnotationAwareAspectJAutoProxyCreator的bean。

3、SpringBoot配置AOP

SpringBoot项目自动化配置是读取spring-boot-autoconfigure项目中的/META-INF/spring.factories的文件,以及json配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
{
      "name": "spring.aop.auto",
      "type": "java.lang.Boolean",
      "description": "Add @EnableAspectJAutoProxy.",
      "defaultValue": true
    },

我们看下AopAutoConfiguration

@Configuration
//依赖于EnableAspectJAutoProxy与Aspect与Advice
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
//spring.aop.auto=true此类才会进行加载
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	//proxyTargetClass默认为false
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {

	}

}

那我们继续,来看EnableAspectJAutoProxy注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

导入了AspectJAutoProxyRegistrar配置我们来看AspectJAutoProxyRegistrar源码

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}

}

这里是不是很熟悉,相当于aop:aspectj-autoproxy标签使用,注册AnnotationAwareAspectJAutoProxyCreator。springboot加载过程就是这样。
仔细想想SpringBoot是基于注解的形式,那么肯定是有什么地方调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

4、AopNamespaceHandler中init方法

public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

一二行上面以及介绍过了,对于scoped-proxy可以理解为作用域代理。Spring中作用域默认是singleton.如果想使用其他除了单例模式以外的作用域,则需要添加scoped-proxy标签,默认走CGLIB代理。跟踪代码到ScopedProxyBeanDefinitionDecorator类中。

public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
		boolean proxyTargetClass = true;
		if (node instanceof Element) {
			Element ele = (Element) node;
			if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
				proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
			}
		}

		// Register the original bean definition as it will be referenced by the scoped proxy
		// and is relevant for tooling (validation, navigation).
		BeanDefinitionHolder holder =
				ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
		String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
		parserContext.getReaderContext().fireComponentRegistered(
				new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
		return holder;
	}

再看其createScopedProxy方法

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
			BeanDefinitionRegistry registry, boolean proxyTargetClass) {

		String originalBeanName = definition.getBeanName();
		BeanDefinition targetDefinition = definition.getBeanDefinition();
		String targetBeanName = getTargetBeanName(originalBeanName);

		// Create a scoped proxy definition for the original bean name,
		// "hiding" the target bean in an internal target definition.
		RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
		proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
		proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
		proxyDefinition.setSource(definition.getSource());
		proxyDefinition.setRole(targetDefinition.getRole());

		proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
		if (proxyTargetClass) {
			targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
		}
		else {
			proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
		}

		// Copy autowire settings from original bean definition.
		proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
		proxyDefinition.setPrimary(targetDefinition.isPrimary());
		if (targetDefinition instanceof AbstractBeanDefinition) {
			proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
		}

		// The target bean should be ignored in favor of the scoped proxy.
		targetDefinition.setAutowireCandidate(false);
		targetDefinition.setPrimary(false);

		// Register the target bean as separate bean in the factory.
		registry.registerBeanDefinition(targetBeanName, targetDefinition);

		// Return the scoped proxy definition as primary bean definition
		// (potentially an inner bean).
		return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
	}

对于创建ScopedProxyFactoryBean要看其中的setBeanFactory方法

@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		pf.setTargetSource(this.scopedTargetSource);

		Class<?> beanType = beanFactory.getType(this.targetBeanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
					"': Target type could not be determined at the time of proxy creation.");
		}
		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
		}

		// Add an introduction that implements only the methods on ScopedObject.
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

		// Add the AopInfrastructureBean marker to indicate that the scoped proxy
		// itself is not subject to auto-proxying! Only its target bean is.
		pf.addInterface(AopInfrastructureBean.class);

		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}

spring-configured不太了解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值