Spring Basics - Quick Interview Notes (Crash Course)

中文 | English

1. Overview of Spring

The primary goal of the Spring framework is to simplify enterprise application development in Java EE.

Spring offers two core features:

  • Inversion of Control (IoC): Also known as Dependency Injection (DI)
  • Aspect-Oriented Programming (AOP)

2. Inversion of Control (IoC)

Inversion of Control (IoC) refers to delegating the control and management of objects to the Spring framework instead of handling it manually. In practice, this means that when you need an object (known as a Bean, such as a business logic handler or a connection pool), you don’t need to create it yourself using new. Instead, you retrieve it directly from the Spring container. How these objects are initialized can either be defined by the developer or left to Spring’s default mechanisms. Dependency Injection (DI) is another name for the IoC feature. It allows Spring to inject the required objects directly into your code, so you don’t need to explicitly acquire them. In practice, this is achieved by annotating the variable with @Autowired.

2.1 Different Ways to Declare Bean Obejcts in Spring

To perform dependency injection of a bean object, you first need to declare the bean, which tells Spring how to initialize it.

There are several ways to declare beans in Spring:

(1) XML Configuration:Declaring objects through the <bean> element in the XML configuration file. (This method is mostly deprecated after the advent of Spring Boot)

```xml
<bean id="myBean" class="com.example.MyBean"/>
```

(2) Annotation-Based Configuration:Using annotations to mark classes, instructing Spring to manage them as beans at runtime. Common annotations include @Component@Service@Repository and @Controller.

```java
@Component
public class MyBean { ... }
```
  • @Component:The most generic annotation used to mark any class as a Spring bean, such as utility classes or classes that don't have a clear category.
  • @Repository:Typically used to mark classes in the data access layer (DAO).
  • @Service:Typically used to mark classes in the business logic layer (Service).
  • @Controller:Typically used to mark controller classes in the controller layer (e.g., in Spring MVC).

The reason for these different annotation names: ① (Main reason) Semantic distinction; ② Spring performs special handling for some annotations. For example, @Repository translates underlying data access exceptions into Spring's DataAccessException

(3) JavaConfig Configuration:Configuring Spring beans through Java classes. You can mark a configuration class with the @Configuration annotation and declare beans in methods using the @Bean annotation.

```java
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    // Multiple beans can be configured. Typically, related beans are grouped under one config class.
}
```

For the @Bean configuration method, you need to enable scanning using the @ComponentScan annotation so that Spring can manage the beans. For example: Adding @ComponentScan("com.example") to the main class.

2.2 Spring's Bean Container: BeanFactory

After Spring initializes the Beans, a container is needed to store these Beans. This container is the BeanFactory.

BeanFactory is an interface that defines several important getBean(...) methods, allowing users to easily retrieve Bean objects using different methods, such as by name or class type.

In Spring Boot web applications, the default implementation of BeanFactory used is AnnotationConfigServletWebServerApplicationContext. Its inheritance hierarchy is shown in the diagram below:


在这里插入图片描述

From top to bottom, the most important classes/interfaces are as follows:

  • BeanFactory:The most basic container interface, providing the fundamental Inversion of Control (IoC) functionality.
  • ApplicationContext:A subinterface of BeanFactory, which extends its functionality and offers a more complete set of features, including internationalization, event propagation, resource access, AOP support, and more.
  • WebApplicationContext:A specific interface for web applications, built on top of ApplicationContext. It adds features tailored for web development, such as managing the lifecycle of Servlet contexts, accessing Servlet properties, and registering Servlets and Filters in web applications.
  • ConfigurableWebApplicationContext: A subinterface of WebApplicationContext, offering additional configuration and customization capabilities.
  • ServletWebServerApplicationContext:A specific implementation class of ApplicationContext for web applications. It integrates with the Servlet container (e.g., Tomcat), responsible for configuring and starting the Servlet container, and provides Servlet-container-specific functionalities like setting context paths, configuring SSL, and handling error pages.
  • AnnotationConfigServletWebServerApplicationContext:A subclass of ServletWebServerApplicationContext, which uses annotation-based configuration to manage the Beans in a web application. It scans specified packages (using the @ComponentScan annotation) to find and register Beans with specific annotations, then assembles them into the container.

In non-Spring Boot or non-web applications, other implementations of BeanFactory are also used, such as XmlBeanFactory, FileSystemXmlApplicationContext, ClassPathXmlApplicationContext, etc.

2.3 The Bean Lifecycle in Spring

A Spring Bean goes through three main stages from creation to destruction:

  1. Instantiation:When the container starts, Spring will instantiate the Bean based on the configuration (via XML, annotations, etc.), which means invoking the Bean’s constructor.
  2. Dependency Injection:After instantiation, if the Bean has dependencies on other Beans, those Beans will be injected because they may be needed in the subsequent initialization process.
  3. Initialization:The user-defined initialization method is executed. There are several ways to define this:
    • Implement the InitializingBean interface and the afterPropertiesSet() method
    • Use the @PostConstruct annotation on the Bean's initialization method.
    • Specify an initialization method in the @Bean annotation.
  4. In Use:After initialization, the Bean is stored in the Bean container, and can be retrieved and used during runtime.
  5. Destruction:The destruction phase occurs when the Bean is no longer needed (usually when the program exits normally). Spring provides two ways to define a destruction method:
    • Implement the DisposableBean interface and the destroy() method.
    • Use the @PreDestroy annotation on the Bean's destruction method.

Example 1 (Using Annotations for the Bean Lifecycle with @Component):

```java
@Component
public class MyBean1 {

    @Autowired
    private RestTemplate restTemplate;

    public MyBean1() {
        System.out.println("Construct MyBean1. restTemplate:" + restTemplate);  // restTemplate is null
    }

    @PostConstruct  // post construct
    public void init() {
        System.out.println("Init MyBean1. restTemplate:" + restTemplate);  // restTemplate has been injected.
    }

    @PreDestroy  // pre destroy(在销毁之前)
    public void destroy() {
        System.out.println("Destroy MyBean1");
    }

}
```
```
// Start spring Boot application ...
Construct MyBean1. restTemplate:null
Init MyBean1. restTemplate:org.springframework.web.client.RestTemplate@5effc15d
// Shut down SpringBoot application...
Destroy MyBean1
```

Example 2 (Using Interfaces for the Bean Lifecycle with @Component):

```java
@Component
public class MyBean2 implements InitializingBean, DisposableBean {

    @Autowired
    private RestTemplate restTemplate;

    public MyBean2() {
        System.out.println("Construct MyBean2. restTemplate:" + restTemplate);  // restTemplate is null
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Init MyBean2. restTemplate:" + restTemplate);  // restTemplate has been injected.
    }

    @Override
    public void destroy() {
        System.out.println("Destroy MyBean2");
    }
}
```
```
// Start spring boot application ...
Construct MyBean2. restTemplate:null
Init MyBean2. restTemplate:org.springframework.web.client.RestTemplate@5effc15d
// Shut down spring boot application ...
Destroy MyBean2
```

Example 3(Use @Bean):

```java
class MyBean3 {

    @Autowired
    public RestTemplate restTemplate;

    public MyBean3() {
        System.out.println("Construct MyBean3. restTemplate:" + restTemplate);  // restTemplate为null
    }

    private void init() {
        System.out.println("Init MyBean3. restTemplate:" + restTemplate);  // restTemplate has been injected.
    }

    private void destroy() {
        System.out.println("Destroy MyBean3");
    }
}

@Configuration
public class MyBeanConfiguration {

    @Bean(initMethod="init", destroyMethod="destroy")
    public MyBean3 myBean() {
        return new MyBean3();
    }
}
```
```
// Start Spring boot application ...
Construct MyBean3. restTemplate:null
Init MyBean3. restTemplate:org.springframework.web.client.RestTemplate@5effc15d
// Shut down spring boot application...
Distroy MyBean3
```

When it comes to the Bean lifecycle, most people have probably seen this diagram online:


在这里插入图片描述

This diagram essentially combines the methods mentioned above and complicates the understanding. However, developers don't need to worry about the order in which these methods are executed, and typically, they won't use all of them together.

2.4 Bean Injection Methods in Spring

Once we have declared the Beans, we can inject them wherever they are needed. Spring provides the following injection methods:

Suppose we have a configuration class that defines the following Beans:

```java
@Configuration
public class TestConfiguration {

    @Bean(name = "restTemplate1")  // There are two RestTemplate Beans with different names
    public RestTemplate restTemplate1() {
        return new RestTemplate();
    }

    @Bean(name = "restTemplate2")
    public RestTemplate restTemplate2() {
        return new RestTemplate();
    }

    @Bean // This is a Random Bean.
    public Random random() {
        return new Random();
    }
}
```

(1) Injection using Annotations: This method allows for dependency injection using annotations in the code, such as @Autowired, @Resource, etc.

  • @Autowired:This annotation, provided by Spring, automatically injects dependencies based on the type. If you need to inject by name, you can use it in conjunction with the @Qualifier annotation.
  • @Resource:This annotation, provided by Java EE, allows you to specify whether to inject by name or by type through its parameters.

```java @Component public class MyBean1 {

@Autowired  // Find a unique Bean with the Random class.
private Random random1;

@Resource(type = Random.class)  // Injection by the class type.
private Random random2;

// @Autowired  // This injection will cause error because there are two Beans with the same class type "RestTemplate".
// private RestTemplate restTemplate;

@Autowired
@Qualifier("restTemplate1")  // Injection by the bean name.
private RestTemplate restTemplate1;

@Resource(name = "restTemplate2")  // Injection by the bean name.
private RestTemplate restTemplate2;

@PostConstruct
public void init() {
    System.out.println("random1:" + random1);
    System.out.println("random2:" + random2);
    System.out.println("restTemplate1:" + restTemplate1);
    System.out.println("restTemplate2:" + restTemplate2);
}

}

```

(2) **Constructor-based Injection**:You can directly specify the Beans that need to be injected in the constructor of the class.

```java
@Component
public class MyBean1 {

    private Random random1;
    private Random random2;
    private RestTemplate restTemplate1;
    private RestTemplate restTemplate2;

    private MyBean1(Random random1,
                    Random random2,
                    // Specify the name of the bean to be injected using @Qualifier
                    @Qualifier("restTemplate1") RestTemplate restTemplate1,  
                    // This can be injected normally and unexpectedly. I don't know why. My version is spring-beans5.3.9
                    RestTemplate restTemplate2  
    ) {
        this.random1 = random1;
        this.random2 = random2;
        this.restTemplate1 = restTemplate1;
        this.restTemplate2 = restTemplate2;
    }

    @PostConstruct
    public void init() {
        System.out.println("random1:" + random1);
        System.out.println("random2:" + random2);
        System.out.println("restTemplate1:" + restTemplate1);
        System.out.println("restTemplate2:" + restTemplate2);
    }
}
```
```
random1:java.util.Random@738a1324
random2:java.util.Random@738a1324  // Since there is only a Random bean, they are the same.
restTemplate1:org.springframework.web.client.RestTemplate@65a86de0  // restTemplate1
restTemplate2:org.springframework.web.client.RestTemplate@745e1fb7 // restTemplate2
```

(3) Injection using Aware Interfaces:Spring provides several XxxxxAware interfaces that allow you to inject system Beans like ApplicationContext or BeanFactory. (Although you can also use @Autowired for this purpose.)

```java
@Component
public class MyBean1 implements ApplicationContextAware, ResourceLoaderAware {

    private ApplicationContext applicationContext;
    private ResourceLoader resourceLoader;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Autowired  // You can use @Autowired to inject it.
    private ApplicationContext applicationContext2;

    @Autowired
    private ResourceLoader resourceLoader2;

    @PostConstruct
    public void init() {
        System.out.println("applicationContext: " + applicationContext);
        System.out.println("resourceLoader: " + resourceLoader);
        System.out.println("applicationContext2: " + applicationContext2);
        System.out.println("resourceLoader2: " + resourceLoader2);
    }
}
```
```
// These beans is the same, because they are the same implement class in Spring boot web application.
applicationContext: ... AnnotationConfigServletWebServerApplicationContext@1dc76fa1 ...
resourceLoader: ... AnnotationConfigServletWebServerApplicationContext@1dc76fa1 ...
applicationContext2: ... AnnotationConfigServletWebServerApplicationContext@1dc76fa1 ...
resourceLoader2: ... AnnotationConfigServletWebServerApplicationContext@1dc76fa1 ...
```

3. Event Listeners in Spring

The Spring framework provides a robust event-driven mechanism based on the Observer pattern, enabling event publishing and listening capabilities.

An event listener in Spring is composed of three main parts:

  • Event:This represents the message in the Observer pattern. Events are published by event publishers and listened to by event listeners.
  • Event Publisher:This corresponds to the subject (observable) in the Observer pattern. In Spring, events are typically published when there are changes in the application context's state. Developers can also define and publish custom events.
  • Event Listener:This is equivalent to the observer in the Observer pattern. It is usually implemented by the developer to listen for specific events, such as context state changes in Spring.

To create a custom event listener, simply implement the ApplicationListener<E extends ApplicationEvent> interface, where the generic type E specifies the type of event you want to handle. For example:

```java
@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("onApplicationEvent:" + event.getSource());
    }
}
```

3.1 Built-in Event Listeners in Spring

Spring includes many built-in event listeners that respond to various changes in the application context or other system states. If you register a listener for these events, you can hook into the framework’s lifecycle and execute custom logic.

Think of these listeners as Spring's equivalent of lifecycle hooks.

Here are some common built-in events provided by Spring (handled via the ApplicationListener class):

  • ContextRefreshedEvent:Triggered when the ApplicationContext is initialized or refreshed. This is often used for tasks like initializing the application or loading caches.
  • ContextStartedEvent:Triggered when the ApplicationContext starts. You can use this event to execute tasks that need to run when the application begins.
  • ContextStoppedEvent:Triggered when the ApplicationContext stops. It is useful for cleanup tasks or releasing resources.
  • ContextClosedEvent:Triggered when the ApplicationContext is closed. This event is typically used for final cleanup and resource release.
  • RequestHandledEvent:Triggered in Spring MVC after an HTTP request has been processed. It is often used to collect statistics or log request-handling details.

Additionally, other Spring frameworks (such as Spring Boot) offer other types of listeners, such as:

  • ServletContextListener:Monitors the lifecycle of the ServletContext.
  • HttpSessionListener:Monitors the lifecycle of HTTP sessions.
  • ServletRequestListener:Monitors the lifecycle of HTTP requests, such as request creation and destruction.
  • SpringApplicationRunListener:Observes lifecycle events during a Spring Boot application’s startup process.

Example 1(ApplicationListener):

```java
@Component
public class MyListener1 implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("ContextRefreshedEvent");
    }
}
```
```java
@Component
public class MyListener2 implements ApplicationListener<ContextClosedEvent> {

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.println("ContextClosedEvent");
    }
}
```
```java
@Component
public class MyListener3 implements ApplicationListener<ContextStartedEvent> {

    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("ContextStartedEvent");
    }
}
```

输出:

```
ContextRefreshedEvent
...
ContextRefreshedEvent
// After shutting down the application.
ContextClosedEvent
```

Different Spring containers might have different events. For example, in my Spring Boot application, ContextStartedEvent and ContextStoppedEvent will never triggered. You should use ApplicationStartedEvent and ApplicationStoppedEvent.

Example 2(SpringApplicationRunListener):

```java
public class MySpringBootListener implements SpringApplicationRunListener {

    // The constructor must be written this way.
    public MySpringBootListener(SpringApplication application, String[] args) {
        System.out.println("SpringApplicationRunListener constructor");
    }

    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        System.out.println("SpringApplicationRunListener starting:" + bootstrapContext);
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        System.out.println("SpringApplicationRunListener environmentPrepared:" + bootstrapContext);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener contextPrepared:" + context);
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener contextLoaded:" + context);
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener started:" + context);
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener running:" + context);
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener failed:" + context);
    }
}
```

Add a configuration in src/resource/META-INF/spring.factories to register the Listener:

```
org.springframework.boot.SpringApplicationRunListener=com.example.MySpringBootListener
```

Output:

```
SpringApplicationRunListener constructor
SpringApplicationRunListener starting:org.springframework.boot.DefaultBootstrapContext@2a898881
SpringApplicationRunListener environmentPrepared:org.springframework.boot.DefaultBootstrapContext@2a898881
SpringApplicationRunListener contextPrepared:org.springframework.context.annotation.AnnotationConfigApplicationContext@585811a4
SpringApplicationRunListener contextLoaded:org.springframework.context.annotation.AnnotationConfigApplicationContext@585811a4
SpringApplicationRunListener started:org.springframework.context.annotation.AnnotationConfigApplicationContext@585811a4
SpringApplicationRunListener running:org.springframework.context.annotation.AnnotationConfigApplicationContext@585811a4
```

3.2 Custom Event Listener

In addition to using built-in listeners, you can also define and handle your own custom events.

To create and use custom events, follow these three steps:

  1. Define an Event Class:By extending the ApplicationEvent class.
  2. Publish Events Where Needed:Use the ApplicationEventPublisher.publishEvent(...) method to trigger your event.
  3. Define a Listener for Your Event:Implement the ApplicationListener<YourEventClass> interface in your listener class.

For example:

(1) Defining the Event Class:

```java
public class CustomEvent extends ApplicationEvent {

    public String anyArg;

    public CustomEvent(Object source, String anyArg) {
        super(source);
        this.anyArg = anyArg;
    }
}
```

(2) Creating a Listener:

```java
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("CustomEvent:" + event.anyArg);
    }
}
```

(3) Publishing the Event: For this example, we’ll trigger the event through a Controller endpoint:

```java
@RestController
@RequestMapping("test")
public class TestController {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/eventTest")
    public void eventTest() {
        eventPublisher.publishEvent(new CustomEvent(this, "myEvent"));
    }
}
```

When the eventTest endpoint is called, the listener’s method will be triggered, and the console will output:

```
CustomEvent:myEvent
```

4. Aspect-Oriented Programming (AOP) in Spring

4.1 Introduction to AOP

In object-oriented programming, we often deal with various business modules (e.g., creating orders, querying orders, dispatching tasks). These modules frequently require shared functionality, such as logging or permission checks. If we manually add this logic before and after every method, it introduces a high degree of coupling between business logic and auxiliary functionality. This not only makes the code less maintainable but also complicates future enhancements.

Aspect-Oriented Programming (AOP) addresses this challenge by separating cross-cutting concerns (e.g., logging, security) from the core business logic. With AOP, these concerns are injected into specific points of an application dynamically, rather than being scattered throughout the code. This approach keeps the core logic clean and improves maintainability and reusability.

At its core, AOP is an implementation of the Proxy Design Pattern. By using a proxy class, AOP intercepts method calls and injects additional logic before or after the core functionality.


在这里插入图片描述

Note: In the proxy pattern, an actual proxy class is generated. For instance, if there’s a XxxxService.class class in your application, enabling AOP will generate a proxy class like XxxxService$Proxy.class. When you call a method, you’re actually invoking it through this proxy class.

4.2 Static Proxy vs. Dynamic Proxy

In Java, implementing AOP (or the proxy pattern) typically involves two approaches: static proxy and dynamic proxy.

  • Static Proxy:The proxy class and behavior are defined at compile-time. These classes can be handwritten or generated by tools/frameworks like AspectJ during the build process.
    • Advantages:High performance: Proxy classes are precompiled, so the JVM can directly load and execute them.
    • Disadvantages:Inflexibility. Once compiled, the proxy logic and interception points are fixed and cannot be adjusted at runtime.
  • Dynamic Proxy (Used by Spring AOP):A proxy class is generated at runtime using reflection and bytecode manipulation libraries such as CGLIB. The bytecode for the proxy class is created dynamically, enabling more flexible behavior.
    • Advantages:Flexibility. Proxies and interception logic can be created and modified dynamically at runtime.
    • Disadvantages:Lower performance(though not significant). Since the proxy classes are generated dynamically, this approach incurs additional overhead compared to static proxies. However, Spring mitigates this by pre-generating proxies during startup, reducing runtime overhead. This pre-generation is one reason why Spring applications can have slower startup times.

4.3 Implementing AOP in Spring

Using AOP in Spring is straightforward and involves three key steps:

  1. Define an Aspect Class::Create a class to represent the aspect. This class will contain the cross-cutting logic (e.g., logging). Annotate the class with @Component and @Aspect.
  2. Define a Pointcut: Specify the methods to which the cross-cutting logic should apply. Use the @Pointcut annotation to declare these interception points.
  3. Define Advice:Implement the cross-cutting behavior. Use annotations such as @Before, @After, or @Around to mark methods as advice. These annotations determine when the logic should run (e.g., before or after the target method executes).

For example:

```java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect  // Marks this class as an Aspect, enabling Spring to recognize it as containing AOP logic.
@Component  // Tells Spring to manage this class as a Spring bean.
public class LogAspect {

    // Defining a Pointcut: This specifies which methods will trigger the Aspect.
    // The syntax for defining Pointcuts is flexible and can be customized. See the official documentation:
    // https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html
    // Alternatively, you can skip defining a separate Pointcut method and directly embed expressions in annotations like @Before or @After.
    @Pointcut("execution(* com.*.MyService.*(..))")
    public void logPointcut() {
    }

    // Before Advice: Executes before the target method is called.
    @Before("logPointcut()")  // Associates this advice with the logPointcut() Pointcut.
    public void beforeAdviceLog(JoinPoint joinPoint) {
        System.out.println("beforeAdviceLog");
    }

    // After Advice: Executes after the target method completes.
    @After("logPointcut()")
    public void afterAdviceLog(JoinPoint joinPoint) {
        System.out.println("afterAdviceLog");
    }

    // Around Advice: Allows custom logic before and after the target method is executed.
    @Around("logPointcut()")
    public Object aroundAdviceLog(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around beforeAdviceLog");
        Object result = null;
        try {
            result = joinPoint.proceed();  // Invokes the target method and stores its return value.
        } catch (Throwable throwable) {
            // Handles exceptions thrown by the target method.
            // Typically, you might log the error and rethrow it for further handling.
            System.out.println("Around Exception");
            throw throwable;
        }

        System.out.println("Around afterAdviceLog");
        return result;  // Returns the target method's return value.
    }

}
```

Business Class is as follows:

```java
@Service
public class MyService {

    public void doSomething() {
        System.out.println("do something...");
    }

}
```

The output is as follows when the doSomething is invoked:

```
Around beforeAdviceLog
beforeAdviceLog
do something...
afterAdviceLog
Around afterAdviceLog
```

4.4 Declaring Pointcuts

Pointcuts in Spring AOP can be declared using various types of expressions, designed to cover a wide range of use cases. These are the main types of pointcut expressions (refer to the official documentation):

  • execution: This type matches methods based on their signatures. The expression follows the format: execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)
    • Example 1:execution(public * com.example.service.*.*(..)). Matches all public methods in the com.example.service package.
    • Example 2:execution(* com.example.service.UserService.*(..)). Matches all methods in the UserService class.
    • Example 3:execution(* com.example.service.*.*(java.lang.String, int)). Matches methods in the com.example.service package that have parameters of type String and int.
    • Example 4:execution(* com.example.service.*.*() throws java.io.IOException). Matches methods in the com.example.service package that throw an IOException.
    • Example 5:execution(public static * com.example.service.*.*(..)). Matches all public static methods in the com.example.service package.
  • within: This type matches based on the class to which a method belongs. The format is: within(type-pattern). Note: Compared to execution, within provides broader matching. For more specific matching, prefer execution.
  • annotation:Matches methods annotated with a specific annotation. The format is: @annotation(annotation-type)
    • Example 1:@annotation(org.springframework.transaction.annotation.Transactional). Matches methods annotated with @Transactional.
  • bean: Matches all methods within a specific Spring Bean based on its name.
    • Example:bean(accountService*). Matches all methods in beans whose names start with accountService. For example, if there is a bean named AccountServiceImpl, all its methods will match.

Futhermore, Pointcuts can also be combined using && (AND), || (OR), and ! (NOT).

Examples:

```java
@Pointcut("execution(* com.example.service.*.*(..)) && within(com.example.controller.*)")
@Pointcut("execution(* com.example.service.*.*(..)) || execution(* com.example.dao.*.*(..))")
@Pointcut("execution(* com.example.service.*.*(..)) && !execution(* com.example.service.internal.*.*(..))")
```

References

Next Post Previous Post
No Comment
Add Comment
comment url