This article is a sequel to Detailed Spring AOP Aspect Programming (PART 1).

In the last article, we went from writing dead code to using proxies; From programmatic Spring AOP to declarative Spring AOP. Everything is moving in the direction of simple pragmatism. Rod Johnson has put a lot of effort in the direction of Spring AOP to make it easy to use the Spring framework, but that’s not the case. So, what improvements did Luo make to Spring AOP?

Now continue!

Spring AOP: cutting side

The AOP framework discussed earlier can be thought of as an interceptor framework, but the interceptor seems very arbitrary. For example, if it intercepts a class, it intercepts all methods in that class. Similarly, we run into this problem when we use dynamic proxies. It’s not very elegant to think that you need to filter out the methods we need to block by judging the method name in your code. In a large number of real projects, it seems that we only need to intercept certain methods, not all of them. Therefore, Lao Luo resorted to a very important TOOL of AOP, Advisor (section), to solve this problem. It is also the heart of AOP! Is the focus of our attention!

That is, we can combine enhanced classes with intercepting matching criteria through a section, and then configure this section into the ProxyFactory to generate proxies.

The “intercept match condition” mentioned here is called a Pointcut in AOP, which is simply an express-based intercept condition.

To summarize, advisors encapsulate Advice and pointcuts. When you understand this sentence, read on.

I deliberately added two methods to the GreetingImpl class, both beginning with “good”. All you need to do is block the two new methods, but not the sayHello() method.

@Component
public class GreetingImpl implements Greeting {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello! " + name);
    }
    public void goodMorning(String name) {
        System.out.println("Good Morning! " + name);
    }
    public void goodNight(String name) {
        System.out.println("Good Night! "+ name); }}Copy the code

In Spring AOP, Luo has provided us with a number of aspect classes, which I personally feel the most useful is the regular expression based aspect class. Just look at you:

<?xml version="1.0" encoding="UTF-8"? >
<beans .">
    <context:component-scan base-package="aop.demo"/>
    <! -- Configure a section -->
    <bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="greetingAroundAdvice"/>            <! - enhance - >
        <property name="pattern" value="aop.demo.GreetingImpl.good.*"/> <! -- Cut point (regular expression) -->
    </bean>
    <! -- Configure a proxy -->
    <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="greetingImpl"/>                <! -- Target class -->
        <property name="interceptorNames" value="greetingAdvisor"/> <! - section - >
        <property name="proxyTargetClass" value="true"/>            <! -- Proxy target class -->
    </bean>
</beans>Copy the code

Note that interceptorNames in the configuration of the proxy object above is no longer an enhancement, but an aspect, since the enhancement is already encapsulated in that aspect. In addition, the section defines a pointcut (regular expression), which is intended to intercept only methods that meet the pointcut matching criteria.

It is important to note that the pointcut expressions here are based on regular expressions. Example, the “aop. Demo. GreetingImpl. Good. *” behind the expression “. * “means to match all the characters, the translation is” match aop. Demo. GreetingImpl classes begin with good method “.

Except RegexpMethodPointcutAdvisor in Spring AOP also provides several aspect, such as:

  • DefaultPointcutAdvisor: Default section (extensible to customize the section)

  • The cut surface of the NameMatchMethodPointcutAdvisor: according to the method name match

  • The cut surface of the StaticMethodMatcherPointcutAdvisor: used to match a static method

In general, it seems acceptable to let the user configure one or a few agents, but as the project expands, there will be more and more agent configurations, which will become more and more repetitive work, not to mention trouble, but also prone to error. Can we have the Spring framework automatically generate proxies for us?

10. Spring AOP: Automatic proxy (scan Bean name)

Spring AOP provides a tool that automatically generates proxies based on Bean names, BeanNameAutoProxyCreator. It is configured like this:

<?xml version="1.0" encoding="UTF-8"? >
<beans .>.<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Impl"/>                       <! Generate proxy only for beans with suffix "Impl" -->
        <property name="interceptorNames" value="greetingAroundAdvice"/> <! - enhance - >
        <property name="optimize" value="true"/>                         <! -- Whether to optimize the proxy generation strategy -->
    </bean>
</beans>Copy the code

The above use of BeanNameAutoProxyCreator only generates proxies for beans with the suffix “Impl”. It is important to note that we cannot define proxy interfaces, or interfaces, because we do not know how many interfaces these beans implement. You cannot proxy interfaces at this point, only classes. So a new configuration item is provided, which is optimize. When true, the proxy generation strategy can be optimized (the default is false). That is, if the class has an interface, proxy interfaces (using JDK dynamic proxies); If there is no interface, proxy classes are used (using CGLib dynamic proxies). Instead of forcing the proxy class regardless of the proxy interface, as was previously done with the proxyTargetClass attribute. Spring AOP does provide a lot of good services for us!

Why use the JDK’s dynamic proxy when CGLib can proxy any class? I’m sure you ask.

Based on years of actual project experience, CGLib is slow to create agents and fast to run once they are created, while JDK dynamic agents are the opposite. It is recommended that you use CGLib to create proxies during system initialization and place them in Spring’s ApplicationContext for later use.

This example matches only the target class, not the specified methods. To match methods, consider using cuts and pointcuts. Spring AOP based on section also provides an automatic proxy generators: DefaultAdvisorAutoProxyCreator.

Spring AOP: Automatic proxy (scan section configuration)

To match the specified methods in the target class, we still need to configure the aspects and pointcuts in Spring:

<?xml version="1.0" encoding="UTF-8"? >
<beans .>.<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="pattern" value="aop.demo.GreetingImpl.good.*"/>
        <property name="advice" ref="greetingAroundAdvice"/>
    </bean>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <property name="optimize" value="true"/>
    </bean>
</beans>Copy the code

Here no need to configure the agent, because the agent will be automatically generated by the DefaultAdvisorAutoProxyCreator. That is, this class can scan all aspect classes and automatically generate proxies for them.

It seems that no matter how simplified, Lao Luo has always been unable to solve the configuration of the section, this heavy manual labor. There will still be a lot of faceted configuration in the Spring configuration file. However, there are many cases where Spring AOP’s aspect classes are really inadequate. For example, if we want to intercept methods that specify annotations, we must extend DefaultPointcutAdvisor, define a custom aspect class, and then configure the aspect in the Spring configuration file. Do not do not know, do you know quite troublesome.

Luo’s solution seems to have fallen into the abyss of section-oriented programming, which is really called “section-oriented programming”. The most important aspect is the section-oriented programming, and the most troublesome aspect is the section-oriented programming.

You have to simplify the aspect configuration for Spring to break new ground!

God like Luo finally realized this, accepted the netizens’ suggestion, integrated AspectJ, while keeping the above mentioned aspect and proxy configuration (for the sake of compatibility with older projects, but also to save face). Integrating Spring with AspectJ is different from using AspectJ directly. We don’t need to define AspectJ classes (which are a new language that extends Java syntax and requires a specific compiler), Just use AspectJ pointcut expressions (which are a much friendlier representation than regular expressions).

12. Spring + AspectJ (annotated: intercepting methods through AspectJ Execution expressions)

Here is a simple example to implement the surround enhancement mentioned earlier. Define an Aspect section class:

@Aspect
@Component
public class GreetingAspect {
    @Around("execution(* aop.demo.GreetingImpl.*(..) )")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        before();
        Object result = pjp.proceed();
        after();
        return result;
    }
    private void before(a) {
        System.out.println("Before");
    }
    private void after(a) {
        System.out.println("After"); }}Copy the code

Note the @aspect annotation above the class, which indicates that the class is an Aspect (actually the Advisor). This class doesn’t need to implement any interface, just define a method (it doesn’t matter what it’s called), annotate the method with the @around annotation, which uses AspectJ pointcut expressions. The parameters to the method include a ProceedingJoinPoint object, called Joinpoint in AOP, through which you can retrieve any information about the method, such as method names, parameters, and so on.

The following focuses on analyzing the pointcut expression:

execution(* aop.demo.GreetingImpl.*(..) )

  • Execution () : Represents the interception method, with parentheses defining the rules to be matched.

  • The first “*” : indicates that the return value of the method is arbitrary.

  • The second “*” : matches all methods in the class.

  • (..) : indicates that method parameters are arbitrary.

Is it more readable than regular expressions? If you want to match the specified method, simply change the second “*” to the specified method name.

How do you configure it? See how easy it is:

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="aop.demo"/>
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>Copy the code

Two lines of configuration, no need to configure a lot of agents, and no need to configure a lot of aspects, which is great! Note the proxy-target-class=”true” attribute, which defaults to false. By default, only interfaces can be propped (using JDK dynamic proxies), and only target classes can be propped (using CGLib dynamic proxies) when true.

The power of Spring in combination with AspectJ goes beyond that. Let’s get a little more stylish. How about intercepting methods for specifying annotations?

13. Spring + AspectJ (annotation-based: intercepting methods via aspectJ@annotation expression)

To intercept the specified annotation method, we first need to customize an annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tag {
}Copy the code

This defines an @tag annotation that can be annotated on a method at runtime.

Just change the Aspect class’s pointcut expression slightly:

@Aspect
@Component
public class GreetingAspect {
    @Around("@annotation(aop.demo.Tag)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {... }... }Copy the code

This time using the @annotation() expression, you simply define the name of the annotation you want to intercept in parentheses.

Define the @tag annotation directly on the method you want to intercept, as simple as that:

@Component
public class GreetingImpl implements Greeting {
    @Tag
    @Override
    public void sayHello(String name) {
        System.out.println("Hello! "+ name); }}Copy the code

With only one method in the example above, this solution is even more valuable if there are multiple methods and we want to intercept only some of them.

In addition to the @around annotation, there are several related annotations, to summarize a little:

  • @before: Pre-enhanced

  • @after: Rear enhancement

  • @around: Surround enhancement

  • AfterThrowing: Throw enhancement

  • DeclareParents: Introduce enhancements

There is also an @afterreturning enhancement, also known as Finally enhancement and equivalent to the Finally statement, which is executed After the method ends, which is, by definition, a little later than @After.

The last one @declareparents is actually introducing enhancements! Why not call it @introduction? I don’t know why, but what it does is introduce enhancements.

14. Spring + AspectJ (introducing enhancements)

To implement aspectJ-based introduction enhancements, we also need to define an Aspect class:

@Aspect
@Component
public class GreetingAspect {
    @DeclareParents(value = "aop.demo.GreetingImpl", defaultImpl = ApologyImpl.class)
    private Apology apology;
}Copy the code

Just define an interface in the Aspect class that needs to be enhanced, which is the interface that needs to be implemented dynamically at runtime. This interface is annotated with the @declareParents annotation, which has two properties:

  • Value: indicates the target class

  • DefaultImpl: The default implementation class to introduce the interface

We only need to provide a default implementation class for the introduced interface to complete the introduction of enhancements:

public class ApologyImpl implements Apology {
    @Override
    public void saySorry(String name) {
        System.out.println("Sorry! "+ name); }}Copy the code

The implementation is automatically enhanced to the GreetingImpl class at runtime, which means we don’t need to modify the GreetingImpl code to implement the ApologyImpl interface. We provide an enablement class for the ApologyImpl interface. To do what GreetingImpl wants to do.

Try it with a client:

public class Client {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
        Greeting greeting = (Greeting) context.getBean("greetingImpl");
        greeting.sayHello("Jack");
        Apology apology = (Apology) greeting; // Force a transition to an Apology interface
        apology.saySorry("Jack"); }}Copy the code

Get the greetingImpl object (which is actually a proxy object) from the Spring ApplicationContext and turn it into your interface Greeting, which you implement statically, or your interface Apology, which you implement dynamically.

The introduction of AspectJ is much more convenient than the original introduction of Spring AOP, and is also interface oriented (previously only implementation class oriented), which is a big break.

It’s all very powerful and flexible! But there are still users who can’t try these features out because they’re still using JDK 1.4 (there’s no such thing as annotations). I didn’t expect Spring AOP to consider those legacy systems as well.

15. Spring + AspectJ (based on configuration)

In addition to using the @aspect annotation to define Aspect classes, Spring AOP also provides a configuration-based way to define Aspect classes:

<?xml version="1.0" encoding="UTF-8"? >
<beans .">
    <bean id="greetingImpl" class="aop.demo.GreetingImpl"/>
    <bean id="greetingAspect" class="aop.demo.GreetingAspect"/>
    <aop:config>
        <aop:aspect ref="greetingAspect">
            <aop:around method="around" pointcut="execution(* aop.demo.GreetingImpl.*(..) )"/>
        </aop:aspect>
    </aop:config>
</beans>Copy the code

The < AOP :config> element is used for AOP configuration, configuring aspects in its children, including information about enhancement types, target methods, pointcuts, and so on.

Whether you can’t use annotations or don’t want to use annotations, Spring AOP has a full range of services for you.

Well, all of the more practical AOP techniques THAT I know of are here, as well as some of the more advanced features that I won’t go into here due to my lack of energy.

As usual, here’s an awesome hd codeless mind map to summarize the points above:

Here’s another table summarizing the solutions for each enhancement type:

Strengthen the type Based on AOP interface Based on the @ Aspect Based on the < aop: config >
Before Advice

MethodBeforeAdvice

@Before

<aop:before>

AfterAdvice (Post-enhanced)

AfterReturningAdvice

@After

<aop:after>

AroundAdvice (Surround enhancement)

MethodInterceptor

@Around

<aop:around>

ThrowsAdvice (Throw enhancement

ThrowsAdvice

@AfterThrowing

<aop:after-throwing>

IntroductionAdvice

DelegatingIntroductionInterceptor

@DeclareParents

<aop:declare-parents>

Finally, a UML class diagram describes the overall architecture of Spring AOP:



Follow wechat official account [Programmer’s Dream, focusing on Java, SpringBoot, SpringCloud, microservices, Docker, and full-stack technologies such as backend separation.