[Spring framework 4] - Spring AOP annotation implementation and xml implementation

Series Article Directory

[Spring Framework 1] - Introduction to Spring Framework

[Spring framework 2] - what is Spring IOC, what is Spring dependency injection

[Spring Framework 3] - Implementation of Spirng IOC and DI



foreword

Spring AOP is one of the core contents of the Spring framework. This blog mainly explains how to use Spring Aop. Through this blog, you can have a clear step on what is Spring AOP and how to run Spring AOP to specific businesses. This blog mainly implements Spring AOP from two dimensions. One is to use pure xml files, and the other is to use pure annotations.


1. What is Spring AOP (Aspect-Oriented Programming)

Spring AOP is an aspect-oriented programming technology that can process cross-cutting concerns for programs , such as logging, security control, performance statistics, and transaction processing.
On the one hand, AOP can separate some public behaviors that have nothing to do with the business but have an important impact on the business (such as transaction management, logging, etc.) from the business logic, which can make the program more modular and convenient. Maintain and expand.

Spring AOP weaves the aspect logic into the target object through the dynamic proxy method at runtime without modifying the original code, so as to realize the enhancement of the target object. It mainly depends on proxy mode and reflection mechanism.

Remarks : If you want to know what an agent is, you can visit the blogger's other two blogs

Design Patterns - Proxy Patterns

Java JDK dynamic proxy

What are crosscutting concerns

Corss-cutting concern: refers to the common concerns that exist in a system and are used by multiple modules, and have nothing to do with core business logic. For example: logging, performance statistics, security controls.
In traditional object-oriented programming, these cross-cutting concerns will be scattered in various modules, resulting in complex code and difficult to maintain.
The purpose of AOP is to separate these cross-cutting concerns from the core business logic, making the system easier to maintain and expand.

2. Basic concepts in Spring AOP

Combined with the schematic diagram of the specific business: addUser is the realization of the original specific business, and now it is necessary to use Spring AOP to cut into a verification security that has nothing to do with the original business before and after it.
We use this business to understand the concept of Spring AOP
insert image description here

Cross Cutting Concern

Cross-cutting concerns: It is an independent service, in our case it is checking security. It will be spread throughout the processing flow of the system.

Aspect

Section: Modularization of cross-cutting concerns, SecurityHandler in the figure

Advice

Notification: The specific implementation of cross-cutting concerns, common notification types are (Before, After, AfterReturning, AfterThrowing, Around)

Pointcut

Cutting point: It defines the application of Advice to those JoinPoints. For Spring, it is the method call
Spring only supports the method of JoinPoint, but for AspectJ this point can also be attribute modification, such as.

Joinpoint

Connection point: A specific point in the program execution process, such as when a method is called or when an exception is handled. In Spring AOP, a join point always represents a method execution. By declaring a parameter of type org.aspectj.lang.JoinPoint, the main part of the advice (Advice) can obtain the join point information.

Weave

Weaving: The process of applying Advice to TargetObject is called weaving, and Spring supports dynamic weaving

TargetObject

Target Object: A target object is an object that is informed by one or more aspects.

Proxy

Proxy: AOP proxy is an object that communicates between target correspondence and aspects; Spring AOP uses JDK's dynamic proxy by default, and its proxy is created at runtime, and CGLIB proxy can also be used

Introduction

Introduction: Introduction allows new interfaces and implementations to be introduced into existing classes, thus adding additional behavior to the class.

These core concepts together form the basis of Spring AOP, through which the management and control of cross-cutting concerns can be realized, thereby providing reuse and modularization of cross-cutting concerns.

3. Implement Spring AOP by using xml files

The code structure of the original spring project

insert image description here

pom.xml

<dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>
    <!--spring aop -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
    </dependencies>

UserManager class

package com.wangwei.springaop.service;

public interface UserManager {
    
    

   public void addUser(String username, String password);
   
   public void delUser(int userId);
   
   public int findUserById(int userId);
   
   public void modifyUser(int userId, String username, String password);
}

UserManagerImpl

package com.wangwei.springaop.service.impl;

import com.wangwei.springaop.service.UserManager;

public class UserManagerImpl implements UserManager {
    
    

   public void addUser(String username, String password) {
    
    

      System.out.println("---------UserManagerImpl.add()--------");
   }

   public void delUser(int userId) {
    
    

      System.out.println("---------UserManagerImpl.delUser()--------");
   }

   public int findUserById(int userId) {
    
    

      System.out.println("---------UserManagerImpl.findUserById()--------");
      return userId;
   }

   public void modifyUser(int userId, String username, String password) {
    
    

      System.out.println("---------UserManagerImpl.modifyUser()--------");
   }

}

Client class

package com.wangwei.springaop.client;

import com.wangwei.springaop.service.UserManager;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    
    

   public static void main(String[] args) {
    
    
       //读取xml配置文件,申明工厂
      BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
      //获取beanid为userManager的bean
      UserManager userManager = (UserManager)factory.getBean("userManager");
      userManager.addUser("wangwei", "123");
   }
}


applicationContext.xml

<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 https://www.springframework.org/schema/aop/spring-aop.xsd">

   <bean id="userManager" class="com.wangwei.springaop.service.impl.UserManagerImpl"/>


</beans>

The results of the current run

insert image description here

XML way to implement Spring AOP

At present, the business needs to cut into a security checking service through Spring AOP technology before the program executes the addUser() method.

Our Cross Cutting Concern is this security checking service .
Now we modularize this cross-cutting concern

Create the SecurityHandler class , which represents Aspect and is a modularization of cross-cutting concerns .

package com.wangwei.springaop.service.impl;

public class SecurityHandler {
    
    
   private void checkSecurity() {
    
    
      System.out.println("-------checkSecurity-------");
   }
}

Configure in the XML configuration file

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <bean id="userManager" class="com.wangwei.springaop.service.impl.UserManagerImpl"/>

   <bean id ="securityHandlerAspect" class="com.wangwei.springaop.service.impl.SecurityHandler"/>

<!--   配置切面通知和切点-->
   <aop:config>
      <aop:aspect ref="securityHandlerAspect">
         <aop:before method="checkSecurity" pointcut="execution(* com.wangwei.springaop.service.impl.UserManagerImpl.addUser(..))"/>
      </aop:aspect>

   </aop:config>

</beans>

The above advice (advice) is configured as Before ,
and it defines the Advice applied to those JoinPoints. Here, the expression execution is used to locate the joinPoint (joinPoint)
. The joinPoint represents the execution of a method, which refers to addUser() implementation of this method.

Realize the effect:
insert image description here

Supplementary expressions about Spring AOP pointcuts

A pointcut expression has two parts: the signature and the pointer

The signature section specifies the method signature of the join point to match.
For example: "execution(public * com.example.service...(...))" means to match all public methods in the com.example.service package.
The indicator section is used to further refine the matching join points. Common indicators include:

execution : The matching method execution join point.
within : Matches method execution join points within the specified type.
args : Matches method execution join points whose argument types match the specified type.
annotation : Match method execution joinpoints with the specified annotation.
Here are some examples of pointcut expressions:

  1. execution(public * com.example.service...(...)): Matches all public methods in the com.example.service package.
  2. within(com.example.service.*): Matches all methods in the com.example.service package.
  3. execution(* com.example.service.UserService.addUser(…)): Matches the addUser method of the com.example.service.UserService class.
  4. args(String, *): Matches methods whose first parameter is of type String.
    annotation(org.springframework.transaction.annotation.Transactional): Matches methods annotated with @Transactional.

The syntax of pointcut expressions is very flexible and can be combined and customized as needed to select exactly the join point to which the aspect is applied.

Examples of Common Expressions

Generally, the format of the execution execution expression is as follows:

execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)

Among them, the meaning of each part is as follows:

  1. modifiers-pattern (optional): Modifiers for matching methods, such as
    public, protected, etc. The wildcard * can be used to match any modifier.
  2. return-type-pattern: used to match the return type of the method, which can be a specific type or a wildcard * to represent any type.
  3. declaring-type-pattern (optional): used to match the type of the class where the method is located, which can be a specific type or a wildcard * to represent any type.
  4. method-name-pattern: The name used to match the method, which can be a specific method name or use the wildcard
    * to match any method name.
  5. param-pattern: The parameter type used to match the method, which can be a specific type, a wildcard for any type, ... for any number of parameters, ( ) for one parameter, and (*, *) for two parameters, etc.
  6. throws-pattern (optional): Used to match the exception types that may be thrown by the method, which can be a specific exception type or a wildcard * to indicate any exception.

Here are some examples:
execution(public * com.example.service . . (…)) : matches all public methods in the com.example.service package.
execution(* com.example.service.UserService.addUser(String, )): Matches the addUser method of the com.example.service.UserService class. The first parameter is of type String, and the second parameter is of any type.
execution(
com.example.service. . (…) throws java.io.IOException): matches all methods in the com.example.service package, and may throw java.io.IOException.

Fourth, implement Spring AOP through annotations

Next, we use pure annotations to achieve the above business requirements. Before the program executes the addUser() method, it cuts into a security checking service through Spring AOP technology.

  1. Still the same as before, first clarify who is the cross-cutting concern. According to the above requirements, we know that the service that checks security is a cross-cutting concern.
  2. Modularize the cross-cutting concerns and define a SecurityHandler, which is an aspect (Aspect)
    insert image description here
    code example:
  3. Add @Aspect annotation, table name This class is an aspect, and add @Component annotation table name to hand it over to spring ioc for management
package com.wangwei.springaop.service.impl;

import org.aspectj.lang.annotation.Aspect;

@Aspect
@Component
public class SecurityHandler {
    
    


   private void checkSecurity() {
    
    
      System.out.println("-------checkSecurity-------");
   }
}

  1. To implement cross-cutting concerns, that is, advice, we need to set its settings as pre-advice and add @Before annotation
package com.wangwei.springaop.service.impl;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
@Component
public class SecurityHandler {
    
    

   @Before("")
   private void checkSecurity() {
    
    
      System.out.println("-------checkSecurity-------");
   }
}

  1. Define the cut point, and define the advice (Advice) to apply to those JoinPoints. Our connection point here is the addUser() method.
package com.wangwei.springaop.service.impl;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class SecurityHandler {
    
    
//	@Pointcut("execution(* com.wangwei.springaop.service.impl.UserManagerImpl.addUser(..))")
//	public void myServiceMethods() {}
//
//	@Before("myServiceMethods()")
//	private void checkSecurity(JoinPoint joinPoint) {
    
    
//		System.out.println("-------checkSecurity-------");
//	}

	@Before("execution(public * com.wangwei.springaop.service.impl.UserManagerImpl.addUser(..))")
	private void checkSecurity(JoinPoint joinPoint) {
    
    
		//可以通过在advice中添加JoinPoint类型的参与来获取客户端调用的方法名或者参数等等...
		for (int i = 0; i < joinPoint.getArgs().length; i++) {
    
    
			//打印参数
			System.out.println(joinPoint.getArgs()[i]);
		}
		//获取方法名
		System.out.println(joinPoint.getSignature().getName());
		
		System.out.println("-------checkSecurity-------");
	}

}

  1. In addition, its UserManagerImpl class is also included in Spring container management through @Service
package com.wangwei.springaop.service.impl;

import com.wangwei.springaop.service.UserManager;
import org.springframework.stereotype.Service;

@Service
public class UserManagerImpl implements UserManager {
    
    

	public void addUser(String username, String password) {
    
    

		System.out.println("---------UserManagerImpl.add()--------");
	}

	public void delUser(int userId) {
    
    

		System.out.println("---------UserManagerImpl.delUser()--------");
	}

	public int findUserById(int userId) {
    
    

		System.out.println("---------UserManagerImpl.findUserById()--------");
		return userId;
	}

	public void modifyUser(int userId, String username, String password) {
    
    

		System.out.println("---------UserManagerImpl.modifyUser()--------");
	}

}

  1. The client makes the call
package com.wangwei.springaop.client;

import com.wangwei.springaop.service.UserManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.wangwei.springaop")
//启动Aspect的自动代理,Spring默认不会自动启用AspectJ的自动代理功能
@EnableAspectJAutoProxy
public class Client {
    
    

	public static void main(String[] args) {
    
    
//		BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
//		UserManager userManager = (UserManager)factory.getBean("userManager");
//		userManager.addUser("wangwei", "123");
		//我们使用AnnotationConfigApplicationContext来加载配置类,使用MyAppApplication.class作为配置类,即告诉Spring使用MyAppApplication这个类中的注解来构建应用程序上下文
		ApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
		/*
		使用context对象来获取我们的bean并进行依赖注入和使用。注意由于使用Spring Aop时将其目标类进行了代理,所以在Spring IOC容器中
		UserManagerImpl的类型变为了代理类
		下面是打印容器中的对象和对象的类型,可以看到UserManagerImpl的类型为Proxy类型
		 */

		String[] beanNames = context.getBeanDefinitionNames();
		for (String beanName : beanNames) {
    
    
			Class<?> beanType = context.getType(beanName);
			System.out.println("Bean Name: " + beanName);
			System.out.println("Bean Type: " + beanType);
			System.out.println("-----------------------");
		}
		//由于UserManagerImpl的类型变为了代理类,所以这里通过默认的bean的名称进行获取
		UserManager userManager = (UserManager) context.getBean("userManagerImpl");
		userManager.addUser("wangwei","123");

	}
}

  1. running result
    insert image description here

Five, Spring's support for AOP

Spring implements AOP through JDK's dynamic proxy , and can implement AOP by using CGLIB proxy .

  1. If the target object implements the interface, by default, JDK's dynamic proxy will be used to implement AOP
  2. It is also possible to force the use of CGLIB to generate proxies that implement AOP if the target object implements the interface
  3. If the target object does not implement the interface, then CGLIB must be introduced, and spring will switch between JDK's dynamic proxy and CGLIB proxy

How to force the use of CGLIB

In Spring, you can force the use of CGLIB proxies through configuration files or annotations.

xml file method

Use the proxy-target-class="true" attribute to force the use of CGLIB proxies.

<aop:config>
    <aop:proxy proxy-target-class="true"/>
    <!-- 其他的切面和通知配置 -->
</aop:config>

Annotation method

Set proxyTargetClass = true in the @EnableAspectJAutoProxy annotation to force the use of CGLIB proxy.

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
    
    
    // 配置其他的Bean和切面
}

An example of forcing the use of CGLIB through annotations:

client code

package com.wangwei.springaop.client;

import com.wangwei.springaop.service.UserManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.wangwei.springaop")
//启动Aspect的自动代理,Spring默认不会自动启用AspectJ的自动代理功能
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Client {
    
    

	public static void main(String[] args) {
    
    
//		BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
//		UserManager userManager = (UserManager)factory.getBean("userManager");
//		userManager.addUser("wangwei", "123");
		//我们使用AnnotationConfigApplicationContext来加载配置类,使用MyAppApplication.class作为配置类,即告诉Spring使用MyAppApplication这个类中的注解来构建应用程序上下文
		ApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
		/*
		使用context对象来获取我们的bean并进行依赖注入和使用。注意由于使用Spring Aop时将其目标类进行了代理,所以在Spring IOC容器中
		UserManagerImpl的类型变为了代理类
		下面是打印容器中的对象和对象的类型,可以看到UserManagerImpl的类型为Proxy类型
		 */

		String[] beanNames = context.getBeanDefinitionNames();
		for (String beanName : beanNames) {
    
    
			Class<?> beanType = context.getType(beanName);
			System.out.println("Bean Name: " + beanName);
			System.out.println("Bean Type: " + beanType);
			System.out.println("-----------------------");
		}
		//由于UserManagerImpl的类型变为了代理类,所以这里通过默认的bean的名称进行获取
		UserManager userManager = (UserManager) context.getBean("userManagerImpl");
		userManager.addUser("wangwei","123");

	}
}

Run the screenshot :
You can see that the type of the target class has changed from the previous proxy to CGLIB
insert image description here

Features of JDK dynamic proxy

Advantage:

  1. Interface-based proxies are suitable for proxies of interface implementation classes.
  2. Use Java's built-in reflection mechanism to implement proxy without additional dependencies.
    Disadvantages:
  3. The proxy's target object must implement at least one interface.
  4. The performance of dynamic proxies is relatively low, because every call to a proxy method involves a reflection call.
  5. For classes without interfaces, JDK dynamic proxies cannot be used directly.

Features of CGLIB dynamic proxy

Advantage:

  1. Inheritance-based proxies are suitable for classes that have no interfaces or cannot be proxied through interfaces.
  2. Ordinary classes can be directly proxied without the target object implementing the interface.
  3. CGLIB implements proxy by generating subclasses, so its performance is relatively high.
    pros and cons:
  4. The generated proxy class is a subclass of the target class. If the target class is declared final, CGLIB proxy cannot be used.
  5. CGLIB requires additional dependencies.

the difference

  1. JDK dynamic proxy is based on the interface and is used to implement the proxy through the interface, which is suitable for the proxy of the interface implementation class; while CGLIB is based on inheritance, and the proxy is generated by generating subclasses, which is suitable for the class that has no interface or cannot be proxied through the interface.
  2. JDK dynamic proxy is based on the interface and is used to implement the proxy through the interface, which is suitable for the proxy of the interface implementation class; while CGLIB is based on inheritance, and the proxy is generated by generating subclasses, which is suitable for the class that has no interface or cannot be proxied through the interface.
  3. The performance of DK dynamic proxy is relatively low, because every call to proxy method will involve reflection call; while CGLIB implements proxy by generating subclasses, and its performance is relatively high.
  4. JDK dynamic proxy cannot be used directly for classes without interfaces, while CGLIB can directly proxy ordinary classes.

6. Summary

Steps to run Spring AOP into specific business:

  1. abstracts away crosscutting concerns
  2. Modularize cross-cutting concerns, and the result of modularization is the aspect (Aspect)
  3. Concretely implement the cross-cutting concerns, the result of the implementation is advice (advice) and define the type of advice
  4. Define the cut point, that is, apply the advice advice to those connection points (joinpoint)

This process involves agents, weave, and imports.

If you choose JDK dynamic proxy or CGLIB proxy:

Choosing to use JDK dynamic proxy or CGLIB depends on specific needs.

  1. If the target class implements the interface and has low performance requirements, you can choose JDK dynamic proxy;
  2. .If the target class has no interface or cannot be proxied through the interface, or has high performance requirements, you can choose CGLIB proxy.
  3. The spring framework will automatically switch between dynamic proxy and CGLIB proxy

Guess you like

Origin blog.csdn.net/wangwei021933/article/details/130758819
Recommended