Spring-AOP (2) Принцип реализации аннотации AspectJ

задняя часть Spring контейнер

в предыдущем постеSpring-AOP (1) Принцип реализацииМы узнали, как использовать ProxyFactory для создания прокси-объектов АОП, но этот процесс требует реализации некоторых интерфейсов и требует более сложной настройки. Поэтому после весны 2.0 предусмотрен более удобный способ. Используйте аннотацию @Aspect для объявления класса аспекта, а затем зарегистрируйте класс генерации прокси AnnotationAwareAspectJAutoProxyCreator с помощью аннотации @EnableAspectJAutoProxy. Давайте рассмотрим принцип

Как использовать @AspectJ весной

ткать

Ручное плетение

  1. Сначала нам нужно определить аспект
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();
}

Объявление Pointcut в форме @AspectJ

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

Pointcut в форме @AspectJ

AnnotationAwareAspectJAutoProxyCreator получает информацию об аннотации @Pointcut посредством отражения и создает ее экземпляр как объект AspectJExpressionPointcut внутри. AspectJExpressionPointcut реализует ClassFilter и MethodMatcher, а его внутренняя логика реализации делегируется PointcutParser, который в конечном итоге генерируется как экземпляр PointcutExpression (класс реализации PointcutExpressionImpl).

Совет в форме @AspectJ

Доступ к параметрам метода в точке соединения в определении Advice

Используйте 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() 

Принцип автоматической генерации прокси Spring AOP в виде @AspectJ

один пример

Мы используем демонстрацию аоп-спринга, сконфигурированного по аннотациям (в новой версии спринга рекомендуется использовать аннотации для настройки контейнера)

// 假设这是一个业务接口
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();
}

Анализируя демонстрационный механизм работы

Начните демистификацию с аннотации @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 (есть возможность проанализировать механизм импорта аннотации @Import про spring), которая импортирует конфигурационный класс 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 может автоматически генерировать прокси

структура класса

основной процесс

Анализ исходного кода
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. Идентификаторы @AspectJ, которые может использовать Spring AOP, такие как выполнение, внутри, это, цель, @аннотация и т. д.
  3. Описывает, как Spring использует аннотацию @EnableAspectJAutoProxy для включения функций Spring AOP и как автоматически генерировать прокси на основе класса @Aspect во внутреннем процессе генератора прокси AnnotationAwareAspectJAutoProxyCreator