spring-AOP(二)实现原理之AspectJ注解方式

栏目: Java · 发布时间: 6年前

内容简介:在上一篇自动织入方式需要AnnotationAwareAspectJAutoProxyCreator类的支持,通过将AnnotationAwareAspectJAutoProxyCreator和需要的Aspect切面、以及目标对象声明在IOC容器中,在容器启动期间,AnnotationAwareAspectJAutoProxyCreator将自动为目标对象生成织入切面逻辑的代理对象Spring AOP支持以下的Pointcut表达式

在上一篇 spring-AOP(一)实现原理 我们了解了如何使用ProxyFactory来创建AOP代理对象,但其过程需要实现一些接口,并且需要一些比较复杂的配置。因此,在spring2.0之后,提供了一种较为便利的方式。 使用@Aspect注解声明一个切面类,之后通过@EnableAspectJAutoProxy注解来注册代理生成类AnnotationAwareAspectJAutoProxyCreator。下面我们来看一下其原理

spring中如何使用@AspectJ

织入方式

手动织入

  1. 首先需要定义一个Aspect
package com.luhc.springaop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @author luhuancheng
 * @date 2018/11/20
 */
@Aspect
public class PerformanceTraceAspect {

    @Pointcut("execution(* *..*method1()) || execution(* *..*method2())")
    public void pointcutName(){}

    @Pointcut("@annotation(AnyJoinpointAnnotation)")
    public void matchPointcut(){}

    @Before("matchPointcut()")
    public void before() {
        System.out.println("+++++++++@annotation++++++++++");
    }

    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        try {
            return joinPoint.proceed();
        } finally {
            System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
        }
    }

}
复制代码
  1. 通过AspectJProxyFactory手动织入
private static void manualWeaver() {
    // 手动织入
    AspectJProxyFactory weaver = new AspectJProxyFactory();
    weaver.setProxyTargetClass(true);
    // 声明目标对象
    weaver.setTarget(new Foo());
    // 声明切面
    weaver.addAspect(PerformanceTraceAspect.class);
    // 获取代理
    Object proxy = weaver.getProxy();
    // 执行已经织入切面逻辑的方法
    ((Foo) proxy).method1(new FlyImpl());
    ((Foo) proxy).method2();
}
复制代码

自动织入

自动织入方式需要AnnotationAwareAspectJAutoProxyCreator类的支持,通过将AnnotationAwareAspectJAutoProxyCreator和需要的Aspect切面、以及目标对象声明在IOC容器中,在容器启动期间,AnnotationAwareAspectJAutoProxyCreator将自动为目标对象生成织入切面逻辑的代理对象

  1. 声明配置
package com.luhc.springaop.aspect;

import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author luhuancheng
 * @date 2018/11/21
 */
@Configuration
public class AspectConfigure {

    /**
     * 自动织入器
     * @return
     */
    @Bean
    public AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
        AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator();
        // 默认为false,如果目标对象未实现接口的话,其代理对象也是通过cglib生成
        proxyCreator.setProxyTargetClass(false);
        return proxyCreator;
    }

    /**
     * 未实现接口的目标对象
     * @return
     */
    @Bean
    public Foo foo() {
        return new Foo();
    }

    /**
     * 切面
     * @return
     */
    @Bean
    public PerformanceTraceAspect performanceTraceAspect() {
        return new PerformanceTraceAspect();
    }

}
复制代码
  1. 从IOC容器中取出织入切面后的代理对象
private static void autoWeaver() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
    Foo foo = context.getBean(Foo.class);
    // 此时的foo对象,是以及经过AnnotationAwareAspectJAutoProxyCreator处理后的代理对象
    foo.method1(new FlyImpl());
    foo.method2();
}
复制代码

@AspectJ形式的Pointcut声明方式

Spring AOP支持以下的Pointcut表达式

// 任意包下的具有任意参数、任意返回值的任意方法
// @Pointcut("execution(* *..*(..))")

// com.luhc.springaop的任意子包下的任意类,springAOP只支持方法级别的JoinPoint,因此这个表达式将匹配指定类所声明的所有方法执行
// @Pointcut("within(com.luhc.springaop..*)")

// 匹配代理对象类型为Foo的所有方法级的JoinPoint
// @Pointcut("this(Foo)")

// 匹配目标对象类型为Fly的所有方法级的JoinPoint
// @Pointcut("target(Fly)")

// 匹配传入参数类型为Fly和Foo的所有方法执行的JoinPoint,不关心方法在哪个类中定义
// @Pointcut("args(Fly,Foo)")

// @within  @target的区别在于@within是静态匹配、@target是在运行时动态匹配
// 匹配所有被注解AnyJoinpointAnnotation标注了的类的所有方法级的JoinPoint
// @Pointcut("@within(AnyJoinpointAnnotation)")

// 匹配所有目标对象呗注解AnyJoinpointAnnotation标注了的类的所有方法级的JoinPoint
// @Pointcut("@target(AnyJoinpointAnnotation)")

// 匹配方法参数类型被注解AnyJoinpointAnnotation标注了的所有方法级的JoinPoint
// @Pointcut("@args(AnyJoinpointAnnotation)")

// 匹配方法被注解AnyJoinpointAnnotation标注了的所有方法级的JoinPoint
// @Pointcut("@annotation(AnyJoinpointAnnotation)")

// 可以使用 || 和 && 来表达pointcut之间的逻辑运算
// @Pointcut("execution(* *..*method1()) || execution(* *..*method2())")
复制代码

剖开@AspectJ在SpringAOP中的真相

@AspectJ形式的Pointcut

AnnotationAwareAspectJAutoProxyCreator通过反射获取到@Pointcut注解的信息,在内部实例化为AspectJExpressionPointcut对象。 AspectJExpressionPointcut实现了ClassFilter、MethodMatcher,其内部实现逻辑代理给了PointcutParser,最终生成为PointcutExpression(PointcutExpressionImpl实现类)实例

@AspectJ形式的Advice

在Advice定义中访问Joinpoint处的方法参数

使用org.aspectj.lang.JoinPoint

将Advice方法的第一个参数声明为org.aspectj.lang.JoinPoint类型,我们可以通过调用org.aspectj.lang.JoinPoint相关方法获取需要的数据

@Before("matchPointcut()")
public void before(org.aspectj.lang.JoinPoint joinPoint) {
    // 获取方法名
    System.out.println(joinPoint.getSignature().getName());
    System.out.println("+++++++++@annotation++++++++++");
}
复制代码

使用args标志符绑定

// 可以同时使用标识符和JoinPoint,但是JoinPoint必须放在第一个参数位置上
@Before("matchPointcut() && args(name)")
public void before(JoinPoint joinPoint, String name) {
    System.out.println("获取到Joinpoint上的入参:" + name);
    System.out.println("获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
}
复制代码

捕获异常@AfterThrowing

@AfterThrowing(pointcut = "matchPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, RuntimeException e) {
    System.out.println("方法:" + joinPoint.getSignature().getName() + "发生异常:" + e.getMessage());
}
复制代码

捕获返回值@AfterReturning

@AfterReturning(pointcut = "pointcutName()", returning = "result")
public void afterReturning(JoinPoint joinPoint, String result) {
    System.out.println("方法:" + joinPoint.getSignature().getName() + "获得返回值:" + result);
}
复制代码

方法正常执行完成后(未抛出异常)@After

@After("pointcutName()")
public void after(JoinPoint joinPoint) {
    System.out.println("方法:" + joinPoint.getSignature().getName() + ": 执行完毕");
}
复制代码

@Around环绕方法

@Around与其他几个Advice注解不同,在@Around方法中,第一个参数必须为org.aspectj.lang.ProceedingJoinPoint

@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();

    try {
        return joinPoint.proceed();
    } finally {
        System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
    }
}
复制代码

公开当前调用的代理对象

// 在目标对象方法中,通过此方法可以获取当前目标对象的代理对象
AopContext.currentProxy() 
复制代码

@AspectJ形式的Spring AOP代理自动生成原理

一个例子

我们使用注解配置的一个spring aop的demo(新版本的spring中推荐使用注解来配置容器)

// 假设这是一个业务接口
public interface Fly {
    void fly();
}

// 业务接口实现
public class FlyImpl implements Fly {
    @Override
    public void fly() {
        System.out.println("++++++++++++++++ Fly ++++++++++++++++");
    }
}

// 声明一个切面
@Aspect
public class PerformanceTraceAspect {

    // 匹配任意返回值、任意包下的、任意参数的fly方法。在这个demo中,将匹配到com.luhc.springaop.aspect.FlyImpl#fly这个方法
    @Pointcut("execution(* *..*fly(..))")
    public void pointcutName(){}

    // 声明切入的前置逻辑
    @Before("pointcutName()")
    public void before(JoinPoint joinPoint) {
        // 可以通过JoinPoint获取到pointcut方法的详细信息
        System.out.println("Before --> 获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
    }

    // 声明切入的后置逻辑
    @After("pointcutName()")
    public void after(JoinPoint joinPoint) {
        // 可以通过JoinPoint获取到pointcut方法的详细信息
        System.out.println("After --> 获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
    }

    // 声明切入的环绕逻辑(即在方法执行前后切入逻辑)
    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        try {
            // 调用执行链
            return joinPoint.proceed();
        } finally {
            System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
        }
    }
}

// 配置类
@Configuration
// 启用AOP
@EnableAspectJAutoProxy
public class AspectConfigure {

    /**
     * 实现接口的目标对象
     * @return
     */
    @Bean
    public Fly fly() {
        return new FlyImpl();
    }

    /**
     * 切面
     * @return
     */
    @Bean
    public PerformanceTraceAspect performanceTraceAspect() {
        return new PerformanceTraceAspect();
    }

}

// 运行应用
public class AspectJDemo {
    // 使用配置类,初始化容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
    // 从容器中获取业务接口(此时已经是被处理过的代理对象,即已经切入了切面逻辑)
    Fly fly = context.getBean(Fly.class);
    fly.fly();
}
复制代码

剖析demo运行机制

从@EnableAspectJAutoProxy注解开始解开神秘面纱

注解EnableAspectJAutoProxy的定义

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

	// 是否使用cglib来生成代理
	boolean proxyTargetClass() default false;

	// 是否将代理绑定到ThreadLocal,后续在目标类中可以使用AopContext.currentProxy()来获取代理对象
	boolean exposeProxy() default false;

}
复制代码

AspectJAutoProxyRegistrar配置类

重点在其元注解@Import上(有机会再分析一下关于spring的@Import注解导入机制),其导入了配置类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) {
        // 重点!!此处向容器注入了AnnotationAwareAspectJAutoProxyCreator类
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

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

}
复制代码

注册过程

public abstract class AopConfigUtils {
    /**
	 * Stores the auto proxy creator classes in escalation order.
	 */
	private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>();

	/**
	 * Setup the escalation list.
     * 在spring中,默认存在三个代理生成类。优先级别从上到下排序,越往后优先级越高
	 */
	static {
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // 最高优先级
	}

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                // 如果容器当前已经注册了代理生成器类,则比较其与AnnotationAwareAspectJAutoProxyCreator的优先级。取优先级最高的那个作为代理生成器注册在容器中。
                // 显然AnnotationAwareAspectJAutoProxyCreator被注册到容器中
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}
}
复制代码

注册流程总结

  1. @EnableAspectJAutoProxy注解导入了配置类AspectJAutoProxyRegistrar
  2. 配置类AspectJAutoProxyRegistrar调用了AopConfigUtils来注册代理生成器AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator如何做到自动生成代理

类结构

spring-AOP(二)实现原理之AspectJ注解方式

主流程

spring-AOP(二)实现原理之AspectJ注解方式

源码分析

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    @Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		if (beanName != null) {
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if (targetSource != null) {
				this.targetSourcedBeans.add(beanName);
				Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
				Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
				this.proxyTypes.put(cacheKey, proxy.getClass());
				return proxy;
			}
		}

		return null;
	}

    // 生成代理对象
    @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;
	}

    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.
        // 获取切面Advisor
		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;
	}

    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
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

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

        // 生成代理
		return proxyFactory.getProxy(getProxyClassLoader());
	}

}
复制代码

总结

  1. 描述了@AspectJ形式的Spring AOP如何使用
  2. spring AOP可以使用的@AspectJ标识符,如execution、within、this、target、@annotation等
  3. 描述了spring内部是如何使用@EnableAspectJAutoProxy注解来启用Spring AOP功能的,以及代理生成器AnnotationAwareAspectJAutoProxyCreator的内部流程时如何根据@Aspect类,来自动生成代理的

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Collective Intelligence

Programming Collective Intelligence

Toby Segaran / O'Reilly Media / 2007-8-26 / USD 39.99

Want to tap the power behind search rankings, product recommendations, social bookmarking, and online matchmaking? This fascinating book demonstrates how you can build Web 2.0 applications to mine the......一起来看看 《Programming Collective Intelligence》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具