An overview of the

Aspect Oriented programming (AOP) complements object Oriented programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, while in AOP, the modular unit is the aspect. Facets support modularity across pointcuts (such as transaction management) across multiple types and objects. Spring AOP is one of the key components of the Spring framework. The Spring IOC container does not depend on the AOP component, and we can not load this module if we do not need the AOP functionality in our project. AOP complements Spring IOC to provide a very powerful middleware solution.

Spring Aop

Spring Aop core concepts

  1. Aspect: An Aspect is one that contains multiple pointcuts. Transaction Management is a good example, usually declared in Spring AOP via @aspect
  2. Join point: a point that represents a specific way of execution, exception handling or method execution.
  3. Advice: Advice that operates at specific join points, such as before, after, and during the notification adding its own logic.
  4. Pointcut: the point at which we want a specific method, which is specific or matches a class of methods that starts with “select”. EL expressions are supported for matching.
  5. Introduction: AOP can introduce an existing class into a new interface during the proxy process.
  6. Target object: The current object, which is also the original object, is always returned when an AOP proxy occurs.
  7. AOP proxy: A proxy object created by AOP.
  8. Weaving: Section insertion timing. This can be run time, pre run time, post run time.

Spring AOP includes the following types of Advice: Pre-advice: Advice that runs before the join point, but does not prevent the execution process from advancing to the join point (unless it throws an exception). Post-notification: Notification to run after the join point completes normally (for example, if the method returns without throwing an exception). Exception notification: Run notification if the method exits because an exception is thrown. After recommendation (final) : Notice should be implemented regardless of how the join point exits (normal or special benefit). Surround advice: Advice around join points, such as method calls. This is the most powerful announcement. Wrap notifications can perform custom behavior before and after a method call. It is also responsible for choosing whether to return the join point or to end the method’s execution by returning its own return value or by throwing an exception

Surround advice is the most common kind of advice. Because Spring AOP, like AspectJ, provides a variety of advice types, it is recommended to use the least functional advice type to achieve the desired behavior. For example, if you only need to update the cache with the return value of a method, it’s better to use post-notification rather than surround notification, even though surround notification can do the same thing. Using the most specific types of advice provides a simpler programming model and reduces the likelihood of errors. For example, you do not need to call JoinPoint, the proceed() method for around advice, so the main business will not fail.

All suggestion arguments are concrete types, so you can use the appropriate type (for example, the type of the return value of the method execution) instead of the suggestion argument of the Object array.

The concept of join points for pointcut matching is key to AOP and differs from older technologies that provide only interception. Pointcuts make the proposed target independent of the object-oriented hierarchy. For example, surround recommendations that provide declarative transaction management can be applied to a set of methods that span multiple objects, such as all business operations in the service layer.

An overview of Spring Aop implementation

Spring AOP uses standard JDK dynamic proxies for AOP proxies by default. This allows any interface (or set of interfaces) to be proxied.

Spring AOP can also use CGLIB proxies. This is required for proxy classes rather than interfaces. By default, CGLIB is used if the business object does not implement an interface. Because it is best to program against interfaces rather than classes, business classes typically implement one or more business interfaces. In some cases (probably very few), you can force CGLIB when you need to recommend methods that are not declared on an interface or when you need to pass proxy objects to methods as concrete types.

Summary: Spring AOP uses JDK dynamic proxy by default, and generally uses GCLIB proxy if a class does not implement an interface

Spring Aop often uses annotations

1. Enable @AspectJ support

To use the @AspectJ aspects in your Spring configuration, you need to enable Spring support to configure Spring AOP based on the @AspectJ aspects and to automatically broker beans based on whether those aspects trigger notifications. By automatic proxy, we mean that if Spring determines that one or more aspects advise using a bean, it will automatically generate a proxy for that bean to intercept method calls and ensure that the recommendation runs as needed.

You can enable @AspectJ support using XML or Java-style configuration. In either case, you need to make sure that AspectJ’s AspectJweaver.jar library is on your application’s classpath (version 1.8 or later).

Example code:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}Copy the code

Declare a section

With @AspectJ support enabled, @Aspect Spring automatically detects any beans defined in the application context using classes with @AspectJ aspects (with annotations) and is used to configure Spring AOP. Here is an example of declaring a facet

Example code:

@Aspect
public class LogAspect {}Copy the code

3. Declare a pointcut

Pointcuts identify join points for interception, allowing us to control the timing of running recommendations. Spring AOP only supports method execution join points for Spring beans, so you can think of pointcuts as matching method execution on Spring beans. A pointcut declaration consists of two parts: a signature that contains a name and any parameters, and a pointcut expression that identifies exactly which method execution we are interested in. In AOP’s @AspectJ annotation style, the general method definition provides pointcut signatures. And define Pointcut expressions by using the @pointCut annotation (methods used as Pointcut signatures must have a void return type).

An example:

@Pointcut("execution(* cn.edu.xxx.service.*.*(..) )"
public void serviceOperation(a) {}Copy the code

Spring AOP supports the following AspectJ pointcut indicators used in pointcut expressions:

  • Execution: The join point used to match method execution. This is the main pointcut indicator to use when using Spring AOP.
  • Within: Limits matches to join points within certain types (the execution of methods declared within a match type when using Spring AOP).
  • This: restricts matches to join points (the execution of methods when Spring AOP is used) where the bean reference (Spring AOP proxy) is an instance of a given type.
  • Target: Limits matching to join points (the execution of methods when Spring AOP is used) when the target object (the proxy application object) is an instance of a given type.
  • Args: Limits matches to join points (the execution of methods when using Spring AOP) where the arguments are instances of a given type.
  • @target: Restricts matching to join points (method execution when using Spring AOP) in cases where the class of the executing object has annotations of a given type.
  • Args: Restrict the join point of the match (execution of the method when using Spring AOP) where the runtime type of the actual parameter passed has an annotation of the given type.
  • @within: Limits the match to join points within a type with a given annotation (using Spring AOP, the execution of a method declared in the type of a given annotation).
  • @annotation: Topics that restrict match points to join points (methods that run in Spring AOP) have join points for a given annotation.

4. Define notifications

Arguments are associated with pointcut expressions and run before, after, or around the execution of the method that the pointcut matches. Pointcut expressions can be simple references to named pointcuts or pointcut expressions declared in place.

Classes with the same defined notification method @Aspect need to run at the same join point based on their notification types in the following order of assignment priority: @around, @before, @After, @AfterRETURNING, @AfterThrowing. Note, however, that due to Spring’s implementation style, the @Afteradvice method will be effectively invoked after AspectJAfterAdvice after any or advice methods in the same aspect. @ AfterReturning and @ AfterThrowing

When two suggestions of the same type defined by @After in the same class (for example, two suggested methods) @aspects need to be run at the same join point, the order is uncertain (because the source code declaration order cannot be retrieved in the following way) reflection of Javac compiled classes. Consider folding such suggestion methods into one suggestion method for each join point in each @aspect class, or refactoring the suggestions into separate @Aspect classes, which you can Order via Ordered or at the Aspect level.

Examples of defining notifications:

@Before("cn.edu.cqvie.aspect.LogAspect.serviceOperation()")
public void doServiceCheck(a) {
    System.out.println("doServiceCheck .....");
}

@After("cn.edu.cqvie.aspect.LogAspect.serviceOperation()")
public void doReleaseLock(a) {
    System.out.println("doReleaseLock .....");
}

@AfterReturning( pointcut="cn.edu.cqvie.aspect.LogAspect.serviceOperation()", returning="retVal")
public void doServiceCheck(Object retVal) {
    System.out.println("doServiceCheck ....." + retVal);
}
Copy the code

5. Introduce

An introduction (called an inter-type declaration in AspectJ) enables an aspect to declare that a proposed object implements a given interface and to provide an implementation of that interface on behalf of those objects. You can use @declareParents to import. This annotation is used to declare that the match type has a new parent class (and therefore a name).

Spring Aop core principles

After analyzing how Spring AOP is used and the basic concepts, let’s continue to analyze the principles of Spring AOP.

Rear AnnotationAwareAspectJAutoProxyCreator AOP processor

Spring again in the process of initialization of the bean, finish after the initialization is executed AnnotationAwareAspectJAutoProxyCreator# postProcessAfterInitialization to perform agent, so, The purpose of the proxy is to convert the Bean’s original object, BeanWapper, into a proxy object after Spring has initialized and before putting the Bean’s full object into the Spring Bean container. The execution process is as follows:

  1. Determine if the current bean needs AOP. For example, if the current bean type is Pointcut, Advice, Advisor, etc., AOP is not required.
  2. If the match to Advisors is not null, proxy is performed and the proxy object is returned.
  3. Determine the proxy implementation if the implementation is not set to GCLIB and implements the interface, then use the JDK proxy, otherwise use GCLIB.
  4. The code to execute the agent.

ProxyFactory agent factory

ProxyFactory is a ProxyFactory class. We need to obtain an instance of AopProxy from the ProxyFactory before we can use the AOP proxy. Spring source code is as follows:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // config is the ProxyFactory object
    // Optimize is true, or proxyTargetClass is true, or the user did not interface the ProxyFactory object
    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.");
        }
        // targetClass is the interface that uses the Jdk dynamic proxy directly
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        / / to use additional
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // Use the Jdk dynamic proxy
        return newJdkDynamicAopProxy(config); }}Copy the code

There are two classes we need to focus on here: JdkDynamicAopProxy and ObjenesisCglibAopProxy

JdkDynamicAopProxy

Here is the code to generate the proxy object:

// Get the proxy object
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // Find all the interfaces to implement
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // Create a proxy object
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code

Let’s see that the jdKDynamicAOpInvoke #invoke method is invoked when we invoke the target object method. The core principle is to generate a proxy class, find the associated MethodInterceptor r, and string these together in the proxy method to implement the proxy. Let’s look at the implementation of Inovke:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            // The goal does not implement its own equals method
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            // The target does not implement its own hashCode method
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            // Invoke the service based on the configuration of the proxy object, or directly if it is an implementation class of the Advised interface
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        // May be null. Minimize the time it takes to own the target object, in which case the object comes from the object pooltarget = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);

        // Get the interception chain for this method.
        // Get the interceptor chain for this method
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        // If there is no interceptor chain, the target object is called directly
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            // Construct a method call
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // Call the join point's interceptor chain
            retVal = invocation.proceed();
        }

        // Massage return value if necessary.Class<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if(target ! =null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            // Must come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            // Save the old proxy objectAopContext.setCurrentProxy(oldProxy); }}}Copy the code

To summarize, the process can be divided into three steps:

  1. Get the original object and the type of the original object, the original object thentargetSourceGet to thetarget“And then throughtargetTo obtaintargetClass
  2. Through the current objectProxyFactoryAdded and matchedAdvisorEncapsulated intoMethodInterceptorInterceptor chainchain
  3. If the chain is empty, the current method corresponding to target is executed directly
  4. If the chain is not empty, the MethodInterceptor in the chain is executed in turn. The order of execution is based on the set of notifications, and if there are notifications of the same type, they are executed in the order.

ObjenesisCglibAopProxy

The gCLIb proxy process is very similar to the JDK proxy process. I won’t go into the details here. Here is the gCLIb proxy object creation process:

  1. Create Enhancer
  2. Set the superClass of Enhancer to the class of the object set by proxyfactory.settarget ()
  3. Set Enhancer interfaces by ProxyFactory. AddInterface () to add the interface, and SpringProxy, Advised interface
  4. Set up Callbacks for DynamicAdvisedInterceptor Enhancer
  5. Finally, create a proxy object with Enhancer

View the generated proxy class code

// This setting is used to output the classes generated by the CGLIb dynamic proxy
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
// This setting is used to output JDK dynamic proxy generated classes in the com/sun/proxy directory by default
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
Copy the code

How to use Spring Aop

By implementing the PointcutAdvisor interface

  1. BeanNameAutoProxyCreator
@Bean
public BeanNameAutoProxyCreator creator(a){
    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
    beanNameAutoProxyCreator.setBeanNames("userService");
    beanNameAutoProxyCreator.setInterceptorNames("myAdvisor");
    return beanNameAutoProxyCreator;
}
Copy the code

Define the bean, the equivalent of a “automatic agent”, had the bean, can automatically in setBeanNames corresponding bean agent, agent logic for the interceptorNames

  1. DefaultAdvisorAutoProxyCreator
/ / defines the Advisor DefaultAdvisorAutoProxyCreator afterwards
// Go back to the BeanPostProcessor automatically
@Bean
public DefaultAdvisorAutoProxyCreator creator1(a){
    DefaultAdvisorAutoProxyCreator creator
            = new DefaultAdvisorAutoProxyCreator();
    return creator;
}
Copy the code

Define the Advisor

@Component
public class MyAdvisor implements PointcutAdvisor {
	@Override
	public Pointcut getPointcut(a) {
		NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();
		methodPointcut.setMappedName("test");
		return methodPointcut;
	}

	@Override
	public Advice getAdvice(a) {
		return new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("Before method execution."); }}; }@Override
	public boolean isPerInstance(a) {
		return false; }}Copy the code

In AbstractAutoProxyCreator# rear postProcessAfterInitialization processor method execution time.

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}

// wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // This Bean is not currently proxied
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    //1. If the current bean type is Pointcut, Advice, Advisor, etc., AOP is not required
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    //2. If the Advisors match is not null, then proxy and return the proxy object
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if(specificInterceptors ! = DO_NOT_PROXY) {// Dynamic proxy is required
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //3. Create a proxy object based on the bean object and advice
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // Store the type of the proxy object
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}
Copy the code

Later transferred to the AbstractAdvisorAutoProxyCreator# getAdvicesAndAdvisorsForBean then findEligibleAdvisors way to find all the Advisor. The Bean and Advisor are bound later.

Using @aspect, @pointcut, @before annotations

The annotated way of implementation, and the previous concept description has been mentioned in this place is not detailed description.

Spring source parsing

  • Spring Startup Process
  • The life cycle of Spring Beans
  • Spring Attribute Injection
  • Spring loop dependencies
  • Spring Aop
  • Spring transactions
  • Spring integration MyBatis
  • Spring FAQ

Reference documentation

spring doc