In the last spring-AOP (PART 1) implementation Principles, we learned how to use ProxyFactory to create AOP proxy objects, but the process requires some interface implementation and some complex configuration. Therefore, after spring2.0, a more convenient approach is provided. Using the @ Aspect annotations declared a plane class, after the @ EnableAspectJAutoProxy generated class AnnotationAwareAspectJAutoProxyCreator annotations to registration agency. Let’s see how it works
How do YOU use @AspectJ in Spring
Woven into the way
Manual weaving
- First you need to define an 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
* @date2018/11/20 * /
@Aspect
public class PerformanceTraceAspect {
@Pointcut("execution(* *.. *method1()) || execution(* *.. *method2())")
public void pointcutName(a){}
@Pointcut("@annotation(AnyJoinpointAnnotation)")
public void matchPointcut(a){}
@Before("matchPointcut()")
public void before(a) {
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)); }}}Copy the code
- Manually woven through AspectJProxyFactory
private static void manualWeaver(a) {
// Manually weave
AspectJProxyFactory weaver = new AspectJProxyFactory();
weaver.setProxyTargetClass(true);
// Declare the target object
weaver.setTarget(new Foo());
// Declare the cut
weaver.addAspect(PerformanceTraceAspect.class);
// Get the proxy
Object proxy = weaver.getProxy();
// Execute the method already woven into the aspect logic
((Foo) proxy).method1(new FlyImpl());
((Foo) proxy).method2();
}
Copy the code
Automatic weave
Automatic weaving way need AnnotationAwareAspectJAutoProxyCreator kind support, By AnnotationAwareAspectJAutoProxyCreator and necessary Aspect Aspect, as well as the target object statement in the IOC container, the container during startup, The target object AnnotationAwareAspectJAutoProxyCreator will automatically generate the proxy object of weaving section of logic
- The statement configuration
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
* @date2018/11/21 * /
@Configuration
public class AspectConfigure {
/** * automatic weavers *@return* /
@Bean
public AnnotationAwareAspectJAutoProxyCreator proxyCreator(a) {
AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator();
// The default is false. If the target object does not implement an interface, its proxy object is also generated by cglib
proxyCreator.setProxyTargetClass(false);
return proxyCreator;
}
/** * Target object of unimplemented interface *@return* /
@Bean
public Foo foo(a) {
return new Foo();
}
/** ** section *@return* /
@Bean
public PerformanceTraceAspect performanceTraceAspect(a) {
return newPerformanceTraceAspect(); }}Copy the code
- Extract the proxy object woven into the cut from the IOC container
private static void autoWeaver(a) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
Foo foo = context.getBean(Foo.class);
/ / foo object at this time, and after dealing with the AnnotationAwareAspectJAutoProxyCreator proxy objects
foo.method1(new FlyImpl());
foo.method2();
}
Copy the code
The @aspectJ form of the Pointcut declaration
Spring AOP supports the following Pointcut expressions
// Any method in any package with any parameters and any return values
// @Pointcut("execution(* *.. * (..) )"
// any class under any subpackage of com.luhc.springaop. Springaop only supports JoinPoint at the method level, so this expression will match all method implementations declared by the specified class
// @Pointcut("within(com.luhc.springaop.. *) ")
// Matches all method-level JoinPoints whose proxy object type is Foo
// @Pointcut("this(Foo)")
// Match all method level JoinPoints whose target object type is Fly
// @Pointcut("target(Fly)")
// Match JoinPoint for all methods that pass in arguments of type Fly and Foo, regardless of which class the method is defined in
// @Pointcut("args(Fly,Foo)")
// @within @target differs in that @within is a static match and @target is a dynamic match at runtime
// match all method-level joinpoints of classes annotated with AnyJoinpointAnnotation
// @Pointcut("@within(AnyJoinpointAnnotation)")
// match all target objects by annotating all method-level joinpoints of the classes provided by AnyJoinpointAnnotation
// @Pointcut("@target(AnyJoinpointAnnotation)")
// Match method parameter types annotated AnyJoinpointAnnotation for all method-level JoinPoints
// @Pointcut("@args(AnyJoinpointAnnotation)")
// The matching method is annotated AnyJoinpointAnnotation for all joinPoints at the method level
// @Pointcut("@annotation(AnyJoinpointAnnotation)")
/ / you can use the | | and && to express the logic operations between pointcut
// @Pointcut("execution(* *.. *method1()) || execution(* *.. *method2())")
Copy the code
Disentangle @aspectj in SpringAOP
Pointcut in the form of @aspectj
AnnotationAwareAspectJAutoProxyCreator through reflection to obtain the information of the @pointcut annotation, internally AspectJExpressionPointcut object instance. AspectJExpressionPointcut ClassFilter, MethodMatcher, its internal implementation logic agent gave PointcutParser, At most, it becomes an instance of PointcutExpression(PointcutExpression ImpL implementation class)
Advice in the form of @aspectj
Access method arguments at Joinpoint in the Advice definition
Using org. Aspectj. Lang. JoinPoint
Will be the first parameter to the Advice method statement for org. Aspectj. Lang. JoinPoint type, we can call org. Aspectj. Lang. JoinPoint relevant methods obtain the data needed
@Before("matchPointcut()")
public void before(org.aspectj.lang.JoinPoint joinPoint) {
// Get the method name
System.out.println(joinPoint.getSignature().getName());
System.out.println("+++++++++@annotation++++++++++");
}
Copy the code
Bind using the ARGS identifier
// You can use both the identifier and JoinPoint, but JoinPoint must be placed on the first parameter
@Before("matchPointcut() && args(name)")
public void before(JoinPoint joinPoint, String name) {
System.out.println("Get the input parameter to Joinpoint:" + name);
System.out.println("Get Joinpoint method name:" + joinPoint.getSignature().getName());
}
Copy the code
Catch an exception @afterThrowing
@AfterThrowing(pointcut = "matchPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, RuntimeException e) {
System.out.println("Method:" + joinPoint.getSignature().getName() + "An exception occurs:" + e.getMessage());
}
Copy the code
Capture the return value @afterRETURNING
@AfterReturning(pointcut = "pointcutName()", returning = "result")
public void afterReturning(JoinPoint joinPoint, String result) {
System.out.println("Method:" + joinPoint.getSignature().getName() + "Get return value:" + result);
}
Copy the code
After the method completes normally (without throwing an exception) @after
@After("pointcutName()")
public void after(JoinPoint joinPoint) {
System.out.println("Method:" + joinPoint.getSignature().getName() + ": Execution completed");
}
Copy the code
@around wrap method
@ Around, unlike several other Advice notes, in @ Around approach, the first argument must be a 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)); }}Copy the code
Exposes the currently invoked proxy object
// In the target object method, this method is used to get the proxy object of the current target object
AopContext.currentProxy()
Copy the code
Principle of automatic generation of Spring AOP proxies in the form of @AspectJ
A case in point
A demo of Spring AOP configured using annotations (new versions of Spring recommend using annotations to configure containers)
// Suppose this is a business interface
public interface Fly {
void fly(a);
}
// Business interface implementation
public class FlyImpl implements Fly {
@Override
public void fly(a) {
System.out.println("++++++++++++++++ Fly ++++++++++++++++"); }}// Declare an aspect
@Aspect
public class PerformanceTraceAspect {
// Match the fly method with any return value, any package, and any parameters. In this demo, will match to com. The luhc. Springaop. Aspect. FlyImpl# fly this method
@Pointcut("execution(* *.. *fly(..) )")
public void pointcutName(a){}
// Declare the cut before the logic
@Before("pointcutName()")
public void before(JoinPoint joinPoint) {
// You can use JoinPoint to obtain the pointcut method details
System.out.println("Before --> get Joinpoint method name:" + joinPoint.getSignature().getName());
}
// Declare the post-logic of the cut
@After("pointcutName()")
public void after(JoinPoint joinPoint) {
// You can use JoinPoint to obtain the pointcut method details
System.out.println("After --> get Joinpoint method name:" + joinPoint.getSignature().getName());
}
// Declare the enclosing logic of the cut (i.e. the cut logic before and after the method execution)
@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
// Call the execution chain
return joinPoint.proceed();
} finally {
System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); }}}/ / configuration class
@Configuration
/ / enabled AOP
@EnableAspectJAutoProxy
public class AspectConfigure {
/** * Implements the target object of the interface *@return* /
@Bean
public Fly fly(a) {
return new FlyImpl();
}
/** ** section *@return* /
@Bean
public PerformanceTraceAspect performanceTraceAspect(a) {
return newPerformanceTraceAspect(); }}// Run the application
public class AspectJDemo {
// Use the configuration class to initialize the container
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
// Get the business interface from the container (at this point it is already a proxy object that has been processed, i.e. cut into the aspect logic)
Fly fly = context.getBean(Fly.class);
fly.fly();
}
Copy the code
Analyze the Operating mechanism of Demo
Start with the @enableAspectJAutoProxy annotation
Annotation EnableAspectJAutoProxy definition
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
// Whether to use cglib to generate proxies
boolean proxyTargetClass(a) default false;
// Whether to bind the proxy to ThreadLocal, you can then use aopContext.currentProxy () in the target class to fetch the proxy object
boolean exposeProxy(a) default false;
}
Copy the code
AspectJAutoProxyRegistrar configuration class
Focus on its yuan notes @ Import (have a chance to look at the @ Import annotations about spring Import mechanism), Import the configuration class 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) {
// key!! The injected AnnotationAwareAspectJAutoProxyCreator class to the container
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}Copy the code
The registration process
public abstract class AopConfigUtils {
/** * Stores the auto proxy creator classes in escalation order. */
private static finalList<Class<? >> APC_PRIORITY_LIST =newArrayList<Class<? > > ();/** * Setup the escalation List. * In Spring, there are three proxy generation classes by default. Priorities are sorted from top to bottom, with higher priorities */
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // Highest priority
}
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())) {/ / if the container is currently registered agent generator class, then compares them with the AnnotationAwareAspectJAutoProxyCreator priority. Take the one with the highest priority and register it in the container as the agent generator.
/ / AnnotationAwareAspectJAutoProxyCreator obviously been registered into the container
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);
returnbeanDefinition; }}Copy the code
Summary of registration Process
- @ EnableAspectJAutoProxy AspectJAutoProxyRegistrar annotations to import the configuration class
- The configuration class generator AnnotationAwareAspectJAutoProxyCreator to invoke the AopConfigUtils AspectJAutoProxyRegistrar registration agency
AnnotationAwareAspectJAutoProxyCreator how to automatically generate the agent
Class structure
Main process
Source code analysis
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());
returnproxy; }}return null;
}
// Generate a proxy object
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean ! =null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if(beanName ! =null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// Skip the infrastructure class
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// Skip the infrastructure class
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// Get the section 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 {
// Parse the target object interfaceevaluateProxyInterfaces(beanClass, proxyFactory); }}/ / generated Advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Generate the proxy
returnproxyFactory.getProxy(getProxyClassLoader()); }}Copy the code
conclusion
- Describes how Spring AOP in the form of @AspectJ is used
- Spring AOP can use @AspectJ identifiers such as execution, within, this, target, @annotation, and so on
- Describes how spring uses the @EnableAspectJAutoProxy annotation internally to enable Spring AOP functionality, And how the internal process of agent generator AnnotationAwareAspectJAutoProxyCreator according to @ Aspect class, to automatically generate the agent