Location>code7788 >text

Spring AOP Basics, Quick Start

Popularity:626 ℃/2024-12-10 19:37:06

present (sb for a job etc)

AOP, cutter-oriented programming, as a supplement to object-oriented, encapsulates public logic (transaction management, logging, caching, permission control, flow restriction, etc.) into a cutter and separates it from the business code, which can beReducing duplicate code in the systemrespond in singingReduced coupling between modules. Cutaways are the public logic that has nothing to do with business, but is called by all business modules.

Let's start with an example of how to add logging of incoming methods to all methods in the following UserServiceImpl.

public class UserServiceImpl implements IUserService {

    @Override
    public List<User> findUserList() {
        ("execute method: findUserList");
        return (new User("seven", 18));
    }

    @Override
    public void addUser() {
        ("execute method: addUser");
        // do something
    }

}

The logging function is decoupled into a logging cutout, and its goal is decoupling. This leads to the idea of AOP: that is, the same code dispersed in each business logic code through theHorizontal cuttingway extracted into a separate module!

OOP object-oriented programming, for the business process of the entity and its attributes and behavior of the abstract encapsulation, in order to obtain a clearer and more efficient division of logical units.
AOP, on the other hand, is aimed at the extraction of cuts in the business process, which faces a step or phase of the process, in order to obtain the isolation effect of low coupling between the parts of the logical process. These two design ideas have essential differences in the goal.

AOP Related Terms

The first thing to know is that aop is not spring-specific, and likewise, these terms are not spring-specific. They are defined by the AOP Alliance

  1. Aspect: Aspects arereinforcerespond in singingcontact (math.)The combination of augmentation and tangent points together define what a tangent is all about.
    How to control the execution order between multiple facets? First of all, it should be clear that in the case of "entering" a join, the highest priority enhancement will be executed first; in the case of "exiting" a join, the highest priority enhancement will be executed last.

    1. The @Order annotation is usually used to directly define the cutout order.
    2. Implementing the Ordered interface overrides the getOrder method. The one with the lower return value (or annotation value) of the () method has higher priority.
  2. Join point: Generalfinger methodIn Spring AOP, a connection point always represents the execution of a method. A connection point is a point during the execution of an application where a cutout can be inserted. This point can be when a method is called, when an exception is thrown, or even when a field is modified. The faceted code can use these points to insert itself into the normal flow of the application and add new behavior. Of course, the connection point could also beClass initialization, method execution, method calls, field calls, or handling of exceptions

  3. Enhancement (or notification) (Advice): in AOP terminology theThe work on the cut is known as enhancement. Know is actually a code segment to be triggered by the Spring AOP framework when the program runs.

    1. Before: calls the enhancement before the target method is called;
    2. After: the enhancement is called after the target method completes, when it doesn't care what the output of the method is;
    3. After-returning: Calling an enhancement after the successful execution of the target method;
    4. Exception enhancement (After-throwing): call the enhancement after the target method throws an exception;
    5. Around: The enhancement wraps around the enhanced method, executing custom logic before and after the enhanced method is called.
  4. Pointcut: The definition of a pointcut matches one or more of the connections that the enhancement will weave into.Explicit class and method names are usually usedOr the profits.Define matching class and method names with regular expressionsto specify these cutpoints. AspectJ, for example, can be understood as execution expressions

  5. Introduction: Introduction allows us to add new methods or properties to existing classes. In AOP, this is represented asWhat to do (what to introduce)

  6. Target Object: An object that is enhanced (advised) by one or more aspects. It is usually a proxy object.

  7. Weaving: Weaving is the process ofThe process of applying a cutout to a target object and creating a new proxy object. Represented in AOP asHow did it happen?Weaving is divided into compile-time weaving, class loading weaving, and run-time weaving; SpringAOP is weaving at run-time.

execution expression format:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
  • ret-type-pattern The return-type pattern, name-pattern, and param-pattern are mandatory; all other parts are optional. A return-type pattern determines that the return type of a method must match a conjunction in sequence. The most frequently used return type patterns are*It represents a match for any return type
  • declaring-type-pattern, a fully-qualified type name will only match methods that return the given type.
  • name-pattern The name pattern matches method names. You can use the*wildcard as all or part of the naming pattern.
  • The param-pattern parameter pattern is slightly more complex: () matches a method that takes no parameters, while (...) matches a method that takes any number of arguments (zero or more). The pattern (*) matches a method that accepts one argument of any type. The pattern (*,String) matches a method that takes two arguments, the first of which can be of any type, and the second of which must be of type String.

Example:

execution(* .*.*(..))

Relationship between Spring AOP and AspectJ

AspectJ is a java implementation of the AOP framework , it is able to java code for AOP compilation ( generally in the compilation period ) , so that java code with AspectJ AOP functionality ( of course, the need for special compilers ) . It can be said that AspectJ is currently the most mature implementation of the AOP framework , the most feature-rich language , more fortunate is that AspectJ and java programs are fully compatible with the almost seamless association , so for engineers with a basic foundation in java programming , on the hands and use are very easy.

  1. AspectJ is the stronger AOP framework that makes practical sense for theAOP standard
  2. Spring why not write a framework similar to AspectJ? Spring AOP uses a pure Java implementation, it does not require a specialized compilation process, it aThe important principle is non-invasiveness.The Spring team is fully capable of writing similar frameworks, it's just that Spring AOP was never intended to compete with AspectJ by providing a full-fledged AOP solution.Spring's development team believes that both proxy-based frameworks such as Spring AOP or mature frameworks such as AspectJ are valuable, and that between them there should beComplementary rather than competitive relationships
  3. The Spring team likes the @AspectJ annotation style better than the Spring XML configuration; so theIn Spring 2.0 uses the same annotations as AspectJ 5 and uses AspectJ to do entry point parsing and matchingHowever, AOP remains pure Spring AOP at runtime and does not depend on AspectJ's compiler or weaver (weaver)
  4. Spring 2.5 support for AspectJ: In some environments, adds support for AspectJ weaving on load, as well as providing a new bean entry point.

The following table summarizes the key differences between Spring AOP and AspectJ.

Spring AOP AspectJ
Implemented in pure Java Extended implementations using the Java programming language
No separate compilation process required AspectJ compiler (ajc) is required unless LTW is set.
You can only use runtime weaving Runtime weaving is not available. Compile-time, post-compile and load-time weaving are supported.
Not very powerful - only method-level weaving is supported More powerful - can weave fields, methods, constructors, static initial value set items, final classes/methods, etc .......
Can only be implemented on beans managed by the Spring container Can be implemented on all domain objects
Only method execution entry points are supported Supports all entry points
Proxies are created by the target object, and the cutover is applied to these proxies. Before executing the application (at runtime), aspects are woven directly into the code.
Much slower than AspectJ Better performance
Easy to learn and apply More complex than Spring AOP

Principles of AOP implementation

There are two implementations of AOP: static proxies and dynamic proxies.

static proxy

Static proxies are divided into: compile-time weaving (implemented by special compilers), class load-time weaving (implemented by special class loaders).

Proxy classes are generated during the compilation phase and enhancements are woven into the Java bytecode during the compilation phase, also known as compile-time enhancements.AspectJ uses static proxies.

Disadvantages: The proxy object needs to implement the same interface as the target object and implement the methods of the interface, there will be redundant code. Also, once the interface adds methods, both the target object and the proxy object have to be maintained.

dynamic agent

Dynamic Proxy: Proxy classes are created at runtime, the AOP framework does not modify the bytecode, but temporarily generates a proxy object in memory, which enhances the business methods during runtime and does not generate new classes.

Spring's AOP Implementation Principles

And Spring's implementation of AOP is through thedynamic agentRealized.

If you configure a bean for Spring cutover, then Spring in the creation of the bean, in fact, the creation of the bean is a proxy object, the subsequent call to the bean method, in fact, call the proxy class rewrite the proxy method. Spring's AOP uses two kinds of dynamic proxies, respectively, the JDK's dynamic proxy, and CGLib's dynamic proxy.

  • If the target class implements an interface, Spring AOP will choose to use the JDK dynamic proxy target class. The proxy class is dynamically generated according to the interface implemented by the target class, and does not need to be written, the generated dynamic proxy class and the target class all implement the same interface.The core of the JDK dynamic proxy is theInvocationHandlerinterfaces andProxyClass.
  • If the target class does not implement an interface, then Spring AOP chooses to dynamically proxy the target class using CGLIB.CGLIB (Code Generation Library) can dynamically generate the bytecode of a class at runtime, dynamically create subclass objects of the target class, and augment the target class in the subclass objects.CGLIB isBy way of inheritancedo with dynamic proxies, so CGLIB exists as a bundle:Classes are finalOr the way to do it isfinalor the method isprivateorstatic method, that is, methods that cannot be implemented by subclasses cannot be proxied using CGLIB.

So which dynamic agent is used when?

  1. If the target object implements an interface, AOP is implemented by default using the JDK's dynamic proxies
  2. If the target object implements an interface, you can force the use of CGLIB to implement AOP
  3. If the target object does not implement the interface, the CGLIB library must be used.

How AOP is configured

XML-based

Spring provides the ability to use the "aop" namespace to define a facet, so let's take a look at it.(for) instance

  • Define the target class
public class AopDemoServiceImpl {

    public void doMethod1() {
        ("AopDemoServiceImpl.doMethod1()");
    }

    public String doMethod2() {
        ("AopDemoServiceImpl.doMethod2()");
        return "hello world";
    }

    public String doMethod3() throws Exception {
        ("AopDemoServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • Define the cutout class
public class LogAspect {

    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        ("-----------------------");
        ("Surrounding Notice: Access Methods");
        Object o = ();
        ("Surrounding Notice: Withdrawal methods");
        return o;
    }

    public void doBefore() {
        ("preemptive notice");
    }

    public void doAfterReturning(String result) {
        ("post notification, return value: " + result);
    }

    public void doAfterThrowing(Exception e) {
        ("Exception Notification, exceptions: " + ());
    }

    public void doAfter() {
        ("Final notification");
    }

}
  • XML Configuration AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
       xmlns:xsi="http:///2001/XMLSchema-instance"
       xmlns:aop="/schema/aop"
       xmlns:context="/schema/context"
       xsi:schemaLocation="/schema/beans
 /schema/beans/
 /schema/aop
 /schema/aop/
 /schema/context
 /schema/context/
">

    <context:component-scan base-package="" />

    <aop:aspectj-autoproxy/>

    <!-- target class -->
    <bean class="">
        <!-- configure properties of bean here as normal -->
    </bean>

    <!-- tangent plane (math.) -->
    <bean class="">
        <!-- configure properties of aspect here as normal -->
    </bean>

    <aop:config>
        <!-- 配置tangent plane (math.) -->
        <aop:aspect ref="logAspect">
            <!-- Configuring entry points -->
            <aop:pointcut expression="execution(* .*.*(..))"/>
            <!-- Surrounding Notice -->
            <aop:around method="doAround" pointcut-ref="pointCutMethod"/>
            <!-- preemptive notice -->
            <aop:before method="doBefore" pointcut-ref="pointCutMethod"/>
            <!-- post notification;returningcausality:用于设置post notification的第二个参数的名称,The type isObject -->
            <aop:after-returning method="doAfterReturning" pointcut-ref="pointCutMethod" returning="result"/>
            <!-- Exception Notification:If there are no exceptions,Enhancements will not be implemented;throwingcausality:Name of the second parameter for setting the notification、typology-->
            <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointCutMethod" throwing="e"/>
            <!-- Final notification -->
            <aop:after method="doAfter" pointcut-ref="pointCutMethod"/>
        </aop:aspect>
    </aop:config>

</beans>
  • test category
public static void main(String[] args) {
    // create and configure beans
    ApplicationContext context = new ClassPathXmlApplicationContext("");

    // retrieve configured instance
    AopDemoServiceImpl service = ("demoService", );

    // use configured instance
    service.doMethod1();
    service.doMethod2();
    try {
        service.doMethod3();
    } catch (Exception e) {
        // ();
    }
}

Based on AspectJ annotations (write expressions directly)

XML-based declarative AspectJ has some shortcomings , you need to configure a lot of code information in the Spring configuration file , in order to solve this problem , Spring uses the @AspectJ framework provides a set of annotations for the implementation of AOP .

Annotation name account for
@Aspect Used to define a cutout.
@pointcut Used to define an entry point expression. It is also necessary to define a method signature containing a name and arbitrary parameters to represent the entry point name when used, which is a normal method with a return value of void and an empty method body.
@Before Used to define a pre-notification, equivalent to BeforeAdvice. when used, it is usually necessary to specify a value attribute value which is used to specify a entry point expression (either an existing entry point or a directly defined entry point expression).
@AfterReturning Used to define a post notification, equivalent to AfterReturningAdvice. pointcut / value and returning attributes can be specified when used, where pointcut / value attributes serve the same purpose, both are used to specify the entry point expression.
@Around Used to define a wrap-around notification, equivalent to a MethodInterceptor, which is used by specifying a value attribute that specifies the entry point at which the notification will be planted.
@After-Throwing Used to define exception notifications to handle unhandled exceptions in a program, equivalent to ThrowAdvice, where the pointcut / value and throwing attributes can be specified. The pointcut/value is used to specify the entry point expression, and the throwing attribute value is used to specify a formal parameter name to indicate that a formal parameter of the same name can be defined in the Advice method, which can be used to access the exception thrown by the target method.
@After Used to define a final notification that will be executed regardless of an exception. When used, you need to specify a value attribute that specifies the entry point at which the notification will be planted.
@DeclareParents Used to define an introduction notification, equivalent to IntroductionInterceptor (mastery not required).

Based on JDK dynamic agent

Based on the JDK dynamic proxy example source code click here

  • Define the interface
public interface IJdkProxyService {

    void doMethod1();

    String doMethod2();

    String doMethod3() throws Exception;
}
  • implementation class
@Service
public class JdkProxyDemoServiceImpl implements IJdkProxyService {

    @Override
    public void doMethod1() {
        ("JdkProxyServiceImpl.doMethod1()");
    }

    @Override
    public String doMethod2() {
        ("JdkProxyServiceImpl.doMethod2()");
        return "hello world";
    }

    @Override
    public String doMethod3() throws Exception {
        ("JdkProxyServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • Defining a cutout
@EnableAspectJAutoProxy
@Component
@Aspect
public class LogAspect {

    /**
     * define point cut.
     */
    @Pointcut("execution(* .*.*(..))")
    private void pointCutMethod() {
    }


    /**
     * Surrounding Notice.
     *
     * @param pjp pjp
     * @return obj
     * @throws Throwable exception
     */
    @Around("pointCutMethod()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        ("-----------------------");
        ("Surrounding Notice: Access Methods");
        Object o = ();
        ("Surrounding Notice: Withdrawal methods");
        return o;
    }

    /**
     * preemptive notice.
     */
    @Before("pointCutMethod()")
    public void doBefore() {
        ("preemptive notice");
    }


    /**
     * post notification.
     *
     * @param result return val
     */
    @AfterReturning(pointcut = "pointCutMethod()", returning = "result")
    public void doAfterReturning(String result) {
        ("post notification, return value: " + result);
    }

    /**
     * Exception Notification.
     *
     * @param e exception
     */
    @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
    public void doAfterThrowing(Exception e) {
        ("Exception Notification, exceptions: " + ());
    }

    /**
     * Final notification.
     */
    @After("pointCutMethod()")
    public void doAfter() {
        ("Final notification");
    }

}
  • APP Launch
public class App {
    public static void main(String[] args) {
        // create and configure beans
        ApplicationContext context = new AnnotationConfigApplicationContext("");

        // retrieve configured instance
        IJdkProxyService service = ();

        // use configured instance
        service.doMethod1();
        service.doMethod2();
        try {
            service.doMethod3();
        } catch (Exception e) {
            // ();
        }
    }
}

Non-Interface Use of Cglib Proxy

Cglib-based proxy example source code here

  • class definition
@Service
public class CglibProxyDemoServiceImpl {

    public void doMethod1() {
        ("CglibProxyDemoServiceImpl.doMethod1()");
    }

    public String doMethod2() {
        ("CglibProxyDemoServiceImpl.doMethod2()");
        return "hello world";
    }

    public String doMethod3() throws Exception {
        ("CglibProxyDemoServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • Cutting Definition

Same as above

  • APP Launch
public class App {
    public static void main(String[] args) {
        // create and configure beans
        ApplicationContext context = new AnnotationConfigApplicationContext("");

        // cglib proxy demo
        CglibProxyDemoServiceImpl service = ();
        service.doMethod1();
        service.doMethod2();
        try {
            service.doMethod3();
        } catch (Exception e) {
            // ();
        }
    }
}

Assembling AOP using annotations

The above uses AspectJ annotations in conjunction with a complexexecution(* .*.*(..)) The syntax defines how AOP should be assembled, and there is another way to assemble AOP using annotations, which generally exist in different application scenarios:

  • For business development, it is common to use annotations to assemble AOP, because if you want to use AOP for enhancement, business development needs to configure annotations, and the business is able to perceive that this method (this class) has been enhanced. If you use expressions to assemble AOP, when subsequent new beans are added, if you are not clear about the existing AOP assembly rules, it is easy to be forced to assemble them, but not perceived during development, resulting in online failures. For example, Spring provides the@Transactionalis a very good example. If you write your own bean that you want to be called in a database transaction, mark it with the@Transactional

  • For infrastructure development, where the business does not need to be aware of what methods have been enhanced, AOP can be assembled using expressions. the time-consuming duration of all interfaces needs to be documented, and expressions can be written directly with no intrusion into the business

  • Defining Annotations

@Target()
@Retention()
public @interface LogAspectAnno {

}
  • Modify the cutover class to use annotations to define the
@EnableAspectJAutoProxy
@Component
@Aspect
public class LogAspect {

    @Around("@annotation(logaspectanno)") //take note of,Parentheses arelogaspectanno,rather thanLogAspectAnno
    public Object doAround(ProceedingJoinPoint pjp, LogAspectAnno logaspectanno) throws Throwable {
        ("-----------------------");
        ("Surrounding Notice: Access Methods");
        Object o = ();
        ("Surrounding Notice: Withdrawal methods");
        return o;
    }
    
}
  • Modify the implementation class, here only the doMethod1 method is assembled with AOP.
@Service
public class CglibProxyDemoServiceImpl {

    @LogAspectAnno()
    public void doMethod1() {
        ("CglibProxyDemoServiceImpl.doMethod1()");
    }

    public String doMethod2() {
        ("CglibProxyDemoServiceImpl.doMethod2()");
        return "hello world";
    }
}

@Service
public class JdkProxyDemoServiceImpl implements IJdkProxyService {

    @LogAspectAnno
    @Override
    public void doMethod1() {
        ("JdkProxyServiceImpl.doMethod1()");
    }

    @Override
    public String doMethod2() {
        ("JdkProxyServiceImpl.doMethod2()");
        return "hello world";
    }
}
  • APP Class
// create and configure beans
ApplicationContext context = new AnnotationConfigApplicationContext("");

// cglib proxy demo
CglibProxyDemoServiceImpl service1 = ();
service1.doMethod1();
service1.doMethod2();

IJdkProxyService service2 = ();
service2.doMethod1();
service2.doMethod2();
  • Output:
-----------------------
Wrap-around notification: enter method
CglibProxyDemoServiceImpl.doMethod1()
Wrap-around notification: exit method
CglibProxyDemoServiceImpl.doMethod2()
-----------------------
Wrap-around notification: entry method
JdkProxyServiceImpl.doMethod1()
Wrap-around notification: exit method
JdkProxyServiceImpl.doMethod2()

As you can see, only the doMethod1 method is enhanced, doMethod2 is not enhanced, that is because @LogAspectAnno only annotates the doMethod1() method, so as to achieve a more fine-grained control, it is the business perception that this method is enhanced.

application scenario

We know that AO can encapsulate logic or responsibilities that are not related to the business, but are common to business modules (e.g., transaction processing, log management, permission control, etc.) for easyReducing duplicate code in the systemReduced coupling between modulesImprove system scalability and maintainability

  1. Unified log management based on AOP.
  2. Based on Redisson + AOP to realize the interface anti-scrubbing , an annotation can limit the interface within a specified period of time the number of times a single user can request .
  3. Based on the Spring Security-provided@PreAuthorize The underlying implementation of permission control is also based on AOP.

Logging

To log using AOP, you only need to add the following to theController to use customized@Log The log annotation makes it possible to log user actions to the database.

@Log(description = "new subscriber")
@PostMapping(value = "/users")
public ResponseEntity create(@Validated @RequestBody User resources){
    checkLevel(resources);
    return new ResponseEntity((resources),);
}

AOP Cutting ClassesLogAspectis used to intercept messages with@Log annotated methods and processed:

@Aspect
@Component
public class LogAspect {

    private static final Logger logger = ();

    // Define a cutpoint to intercept methods with the @Log annotation.
    @Pointcut("@annotation()") // This needs to be modified according to your actual package name.
    public void logPointcut() {
    }

    // Wrap-around notification for logging
    @Around("logPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
      //...
    }
}

current limit

To limit the flow on an interface using the AOP method, you only need to add a new flow to theController method using a customized@RateLimit A flow-limiting annotation is sufficient.

/**
 * This interface can only be accessed a maximum of 10 times in 60 seconds, and is saved to redis with the key name limit_test.
 */
@RateLimit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit")
public int test() {
     return ATOMIC_INTEGER.incrementAndGet();
}

AOP Cutting ClassesRateLimitAspectis used to intercept messages with@RateLimit annotated methods and processed:

@Slf4j
@Aspect
public class RateLimitAspect {
      // Intercepts all data with @RateLimit annotated method
      @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
      //...
    }
}

One more thing about the stream-limiting implementation, we didn't write our own Redis Lua stream-limiting script here, but instead utilized Redisson'sRRateLimiter The underlying implementation is based on Lua code + token bucket algorithm.

privilege control

Spring Security uses AOP for method interception. Before actually calling the update method, Spring checks the permissions of the current user and executes it only if the user's permissions meet the corresponding conditions.

@Log(description = "Modifying the menu")
@PutMapping(value = "/menus")
// The `update` method can be accessed if the user has either of the `admin`, `menu:edit` permissions
@PreAuthorize("hasAnyRole('admin','menu:edit')")
public ResponseEntity update(@Validated @RequestBody Menu resources){
    //...
}

Interview questions column

Java interview questions columnIt's online, so feel free to visit.

  • If you don't know how to write a resume, resume projects don't know how to package them;
  • If there's something on your resume that you're not sure if you should put on it or not;
  • If there are some comprehensive questions you don't know how to answer;

Then feel free to private message me and I will help you in any way I can.