zoukankan      html  css  js  c++  java
  • Spring源码之-AOP




    PS * 文中代码均为伪代码,本文基于spring 5.0 ,如有谬误,感谢指正!!!


    一、大话AOP

    1.AOP的概念

    • AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。

    • AOP技术利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    从概念上来说很清晰,仅对入门小白来说存在一些门槛。

    总结来说,AOP 提供的功能是 减少重复代码,通过切面来实现一些重复的逻辑代码。

    2.必要的准备工作

    个人认为阅读spring源码前务必请读一下常用设计模式,比如AOP 中最重要的设计模式:代理模式。

    什么是代理模式?

    • 代理模式为另一个对象提供一个替身或者占位符,以控制对这个对象的访问。也可使是视作代理对象让客户代码于实际对象解耦。使用代理模式创建代表对象,让代表对象控制对某对象的访问,被代理的对象可以是远程的对象(运行在不同JVM上,通过RMI实现)、创建开销大的对象(虚拟代理模式)或者需要安全控制的对象(保护代理模式)。

    可以简单认为,代理模式把<实际需要访问的对象>封装到了,一个<与实际对象实现了相同接口的对象>中,这个用于封装实际对象的外层对象,称之为 <代理对象>。对<被代理对象>的访问,全部通过<代理对象>进行。

    -- 助记定义
    <实际需要访问的对象>   即  被代理对象。
    <与实际对象实现了相同接口的对象>  即  代理对象。
    

    3.大话AOP

    那么AOP 具体是什么呢?

    对没有接触过AOP的,人来说,你跟他讲切面、切点、织入,这是非常抽象的。

    用最直白的话来说:AOP 实现的逻辑就是为原对象生成<代理对象>,处理<代理对象> 最终实现增强。[ JDK动态代理: InvocationHandler.invoke()、CGLIB代理: MethodInterceptor.intercept() ]

    实现AOP的方式

    1. 动态代理
    • JDK 动态代理

    • CGLIB 代理

      动态代理,在运行时动态织入增强,所以会对运行性能造成损耗。

    1. 静态代理
      在虚拟机加载字节码文件的时候,将增强内容织入到方法的字节码中,它提供了更细粒度的控制,对性能影响较小。

    二、动态AOP自定义标签

    全局搜索 AOP 配置项很容易定位到如下代码:

    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    

    最终接口方法parse 定位到如下代码

    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    			ParserContext parserContext, Element sourceElement) {
    
    		// 是否已经存在:自动代理创建器,已存在判断优先级,不存在创建默认类型
    		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    		//  处理 proxy-target-class 和 expose-proxy 属性
    		//  proxy-target-class  决定使用的代理类型:JDK动态代理,应用场景更加多元,通过实现接口代理,确定是性能不够强悍;CGLIB代理,通过继承目标类实现拓展,操作低层字节码,性能强悍。
    		//  expose-proxy :  目标对象内部的自我调用将无法实施切面中的增强,利用 expose-proxy 属性可以解决这个问题
    		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    		//  注册组件并通知,便于监听器做进一步的处理
    		//  beanDefinition 代表的是 AnnotationAwareAspectJAutoProxyCreator的类定义
    		registerComponentIfNecessary(beanDefinition, parserContext);
    	}
    

    上述代码主要实现如下功能:

    • 注册或升级 AnnotationAwareAspectJAutoProxyCreator 注解解析器

    • 处理 proxy-target-class 和 expose-proxy 属性

    1、JDK动态代理

    其代理对象必须实现某个接口,它通过在运行期间,创建接口的实现类来完成对目标对象的代理。

    2、CGLIB 代理

    它在运行期间生成的代理对象是针对目标类型拓展的子类。


    目标对象使用动态代理方式总结:
    • 目标对象实现了接口,默认使用JDK代理;

    • 目标对象实现了接口可以强制使用CGLIB代理;

    • 目标对象没有实现接口,必须使用CGLIB代理。

    	JDK 动态代理只能针对实现了接口的类生成代理。
    	CGLIB代理 为指定类生成一个子类,并覆盖其中的方法,需要被增强的类或方法不能定义为 final
    

    三、创建AOP代理

    AnnotationAwareAspectJAutoProxyCreator 类层次结构图
    pic1

    上一步实现了 AnnotationAwareAspectJAutoProxyCreator 的注册或升级,
    它实现了 BeanPostProcessor 接口[实现该接口的后处理器,在Bean实例化时都会调用其postProcessAfterInitialization() 方法]
    追踪代码可以发现,该方法由父类AbstractAutoProxyCreator 实现

    追踪代码发现:

    // 要么返回bean 要么返回被代理后的bean-proxy
    	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    			return bean;
    		}
    		// advisedBeans : 需要被增强的
    		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.
    		// 获取所有需要被增强的方法?
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {// 存在增强方法,则创建代理 
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
    			// 对获取到的需要增强的方法进行代理 
    			Object proxy = createProxy(
    					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));// 单例 
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    			return proxy;
    		}
    
    		this.advisedBeans.put(cacheKey, Boolean.FALSE);
    		return bean;
    	}
    

    它主要包含的内容是:

    1 . 获取增强方法 或者 增强器
    2 . 对获取到的增强进行代理

    本节剩下的内容都是在讲述他们

    关注获取增强方法的 getAdvicesAndAdvisorsForBean 方法,由 父类 AbstractAdvisorAutoProxyCreator 实现

    根据方法找到如下实现:

    
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    		// AnnotationAwareAspectJAutoProxyCreator 类覆盖了该方法 
    		List<Advisor> candidateAdvisors = findCandidateAdvisors();// 获取所有的增强 (所有  拦截||切点???)
    		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 寻找适用于当前 Bean 的增强 
    		extendAdvisors(eligibleAdvisors);
    		if (!eligibleAdvisors.isEmpty()) {
    			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    		}
    		return eligibleAdvisors;
    	}
    
    

    1.获取所有增强器

    public List<Advisor> findAdvisorBeans() {
    		// Determine list of advisor bean names, if not cached already.
    		String[] advisorNames = this.cachedAdvisorBeanNames;
    		if (advisorNames == null) {
    			// Do not initialize FactoryBeans here: We need to leave all regular beans
    			// uninitialized to let the auto-proxy creator apply to them!
    			// 获取 beanFactory 中的所有 注册的 beanName &&  声明为  @AspectJ
    			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    					this.beanFactory, Advisor.class, true, false);
    			this.cachedAdvisorBeanNames = advisorNames;
    		}
    		if (advisorNames.length == 0) {
    			return new ArrayList<>();
    		}
    		
    		List<Advisor> advisors = new ArrayList<>();// 记录增强 ??
    		for (String name : advisorNames) {
    			if (isEligibleBean(name)) {// 模板方法模式思想-钩子函数 : 校验 该bean 是否合格????
    				if (this.beanFactory.isCurrentlyInCreation(name)) { // 新 创建 ??
    					if (logger.isDebugEnabled()) {
    						logger.debug("Skipping currently created advisor '" + name + "'");
    					}
    				}
    				else {
    					// 提取 增强并缓存 
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
    				}
    			}
    		}
    		return advisors;
    	}
    

    2.寻找匹配的增强器

    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    		if (candidateAdvisors.isEmpty()) {
    			return candidateAdvisors;
    		}
    		List<Advisor> eligibleAdvisors = new ArrayList<>();
    		// 处理引介增强
    		for (Advisor candidate : candidateAdvisors) {
    			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
    				eligibleAdvisors.add(candidate);// 适合 ?? 拦截条件成立??
    			}
    		}
    		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    		for (Advisor candidate : candidateAdvisors) {
    			// 跳过引介增强(已处理)
    			if (candidate instanceof IntroductionAdvisor) {
    				// already processed
    				continue;
    			}
    			// 普通 bean 处理
    			if (canApply(candidate, clazz, hasIntroductions)) {
    				eligibleAdvisors.add(candidate);
    			}
    		}
    		return eligibleAdvisors;
    	}
    

    3.创建代理

    最后回到 wrapIfNecessary 方法,获取到增强方法后,将调用 createProxy 创建代理

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    			@Nullable 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);//  拦截器封装为 Advisor 
    		proxyFactory.addAdvisors(advisors);//  加入增强器 
    		proxyFactory.setTargetSource(targetSource);//  设置要代理的类
    		customizeProxyFactory(proxyFactory);//  定制化内容,钩子函数
    
    		proxyFactory.setFrozen(this.freezeProxy);
    		if (advisorsPreFiltered()) {
    			proxyFactory.setPreFiltered(true);
    		}
    
    		return proxyFactory.getProxy(getProxyClassLoader());//  获取代理
    	}
    

    最终指向 createAopProxy().getProxy();

    @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.");
    			}
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    

    查看代码很容易看到,ObjenesisCglibAopProxy 和 JdkDynamicAopProxy 都实现了接口 AopProxy

    四、创建AOP静态代理

    1.自定义标签

    全局搜索 AOP静态织入 配置项:load-time-weaver 可以发现如下的代码入口

    registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
    

    LoadTimeWeaverBeanDefinitionParser类层次结构图
    在这里插入图片描述

    查看该解析类,发现 经由父类把 parse 方法 转为了 doParse 方法

        // 以标签为标志,进行相关  《处理类》 的注册 
    	// parse 到 doParse的转化:  parse()   ->  parseInternal()  ->  doParse()
    	// 解析(×)  注册工具类(√) 
    	// 解析load-time-weaver 标签会产生一个 beanName为 loadTimeWerver 的 bean  class=org.springframework.context.weaving.DefaultContextLoadTimeWeaver
    	@Override
    	protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    		builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    
    		// 检查AspectJ 功能是否可用(load-time-weaver:-> aspectj-weaving<on off autodetect>)  {default=autodetect 检查 META-INF/aop.xml 是否存在}
    		if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {// aspectj-weaving
    			if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {//  org.springframework.context.config.internalAspectJWeavingEnabler
    				RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);// org.springframework.context.weaving.AspectJWeavingEnabler
    				parserContext.registerBeanComponent(
    						// org.springframework.context.config.internalAspectJWeavingEnabler
    						new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME)); // 封装 <处理类> AspectJWeavingEnabler 并注册   注册到容器中??
    				// BeanDefinitionHolder 
    			}
    
    			if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {// 检查是否已经注册成功
    				new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
    			}
    		}
    	}
    

    经过上述步骤,解析器 已经被封装为BeanDefinition并注册到了容器中,那么要怎样使用它呢,
    查看AbstractApplicationContext 类,如下为织入前的准备工作,将相关的后处理器链注入

    /**
    	 * Configure the factory's standard context characteristics,
    	 * such as the context's ClassLoader and post-processors.
    	 * @param beanFactory the BeanFactory to configure
    	 */
    	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		/*
    		* 其它无关代码不再列出
    		*/
    		
    		//  增加 对 AspectJ 的支持  AOP  相关后处理器  load-time-weaving  静态织入 
    		// 容器中查找 beanName为 loadTimeWeaver 的 类,如果存在即可说明 静态织入开关存在
    		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {// loadTimeWeaver 
    			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// 注册 aop 相关后处理器
    			// Set a temporary ClassLoader for type matching.
    			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    		}
    		/*
            * 其它无关代码不再列出
            */
    	}
    

    2.织入

    LoadTimeWeaverAwareProcessor 类层次结构图

    在这里插入图片描述

    初始化前会调用 后处理器 LoadTimeWeaverAwareProcessor 的 postProcessBeforeInitialization() 方法

    @Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    	    // 该后处理器 仅仅对 AspectJWeavingEnabler 类型的 bean 起作用 
    		if (bean instanceof LoadTimeWeaverAware) { // 它的实现类只有 AspectJWeavingEnabler   处理器类
    			LoadTimeWeaver ltw = this.loadTimeWeaver;//LoadTimeWeaver 实现类为:  DefaultContextLoadTimeWeaver
    			if (ltw == null) {
    				// 最终获得一个 DefaultContextLoadTimeWeaver 类型 bean  
    				ltw = this.beanFactory.getBean(ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
    			}
    			//  AspectJWeavingEnabler 持有 loadTimeWeaver 
    			((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);// 注入到   AspectJWeavingEnabler 的 loadTimeWeaver 属性
    		}
    		return bean;
    	}
    

    DefaultContextLoadTimeWeaver 类层次结构图
    在这里插入图片描述

    DefaultContextLoadTimeWeaver 实现了接口 BeanClassLoaderAware ,那么它的 setBeanClassLoader() 方法将会在初始化的时候被调用,
    从方法中可以看到,DefaultContextLoadTimeWeaver 类型的 bean 的loadTimeWeaver 属性设置为:InstrumentationLoadTimeWeaver 类型的bean,
    而最终实现静态AOP织入(修改方法字节码) 的就是它(InstrumentationLoadTimeWeaver)
    InstrumentationLoadTimeWeaver

    @Override
    	public void setBeanClassLoader(ClassLoader classLoader) {
    		LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
    		if (serverSpecificLoadTimeWeaver != null) {
    			this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
    		}
    		else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {// 检查当前虚拟机中的 Instrumentation 实例是否可用  
    			this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
    		}
    		else {
    			this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
                if (logger.isInfoEnabled()) {
                    logger.info("Using a reflective load-time weaver for class loader: " +
                            this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
                }
    		}
    	}
    

    最后,AspectJWeavingEnabler 实现了 BeanFactoryPostProcessor 接口,那么在所有bean解析结束后会调用其:
    postProcessBeanFactory()方法 -> enableAspectJWeaving()

    AspectJWeavingEnabler 类层次结构图
    在这里插入图片描述

    public static void enableAspectJWeaving(
    			@Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {
    
    		if (weaverToUse == null) {
    			if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {// 当前虚拟机的 Instrumentation 是否可用 
    				weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
    			}
    			else {
    				throw new IllegalStateException("No LoadTimeWeaver available");
    			}
    		}
    		weaverToUse.addTransformer(
    				new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
    	}
    

    最终,看到了实现的代码:

    /**
    	 * ClassFileTransformer decorator that suppresses processing of AspectJ
    	 * classes in order to avoid potential LinkageErrors.
    	 * @see org.springframework.context.annotation.LoadTimeWeavingConfiguration
    	 */
    	private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {
    
    		private final ClassFileTransformer delegate;
    
    		public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {
    			this.delegate = delegate;
    		}
    
    		@Override
    		public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
    				ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
    			if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
    				return classfileBuffer;
    			}
    			// 具体逻辑委托给代理 
    			return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
    		}
    	}
    
    • LoadTimeWeaverAwareProcessor.postProcessBeforeInitialization() 方法将 DefaultContextLoadTimeWeaver 注册到 AspectJWeavingEnabler 的 loadTimeWeaver 属性中
    • DefaultContextLoadTimeWeaver 实现了接口 BeanClassLoaderAware(实现了该接口的 bean 在 AbstractAutowireCapableBeanFactory.invokeAwareMethods 调用时,
      会去调用:BeanClassLoaderAware.setBeanClassLoader ,该方法逻辑内会把代表当前虚拟机的 InstrumentationLoadTimeWeaver实例,注册到 DefaultContextLoadTimeWeaver 的 loadTimeWeaver属性)

    Instrumentation 关联的是当前虚拟机实例,由它去处理静态织入(修改方法字节码)

    最终会存在如下的引用关系
    AspectJWeavingEnabler.loadTimeWeaver = DefaultContextLoadTimeWeaver.Instance();
    DefaultContextLoadTimeWeaver.loadTimeWeaver = InstrumentationLoadTimeWeaver.Instance();

    结语

    • OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    • 使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    引用文章
    [1] https://www.cnblogs.com/hongwz/p/5764917.html

  • 相关阅读:
    shell-变量的数值运算let内置命令
    shell-变量的数值运算符-计算双括号(())的使用
    shell-批量修改文件名及扩展名多案例
    shell-变量的字串应用技术
    一个MVVM前端扩展器
    测试一个mysql 悲观锁
    spring rest项目提示Request method 'PUT' not supported Method Not Allowed 405 错误
    Mysql 使用sql语句添加列,修改列默认值,添加类注释
    理解java泛型中的 上界extend 下界super
    mysql存储过程游标循环装载字符串SQL语句示例
  • 原文地址:https://www.cnblogs.com/bokers/p/14903065.html
Copyright © 2011-2022 走看看