
Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。
在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。
切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所要执行的逻辑。
Spring AOP提供了以下几种类型的增强:
Spring AOP通过使用动态代理技术,在目标对象方法执行时将切面的逻辑织入到目标对象的方法中。这样,我们可以在不修改原始业务代码的情况下,实现横切关注点的统一处理。
总而言之,Spring AOP是一种通过切面将横切关注点模块化的技术,它提供了一种简洁的方式来管理和重用跨越多个对象的关注点逻辑。
使用Spring AOP的主要原因是它可以帮助我们更好地管理各种横切关注点,例如日志记录、事务管理、安全性检查等。以下是一些使用Spring AOP的优点:
总之,使用Spring AOP可以帮助我们更好地管理和重用横切关注点逻辑,使得代码更易于维护和理解,并且可以提高代码的可重用性和灵活性。
Spring AOP(Aspect-Oriented Programming)是一种编程范式,它通过切面(Aspect)的概念来提供对横切关注点的支持。在传统的面向对象编程中,我们将功能逻辑以对象的形式组织起来,而在面对特定需求时,常常需要在多个对象或方法中添加相同的功能代码,例如日志记录、事务管理等。这样的代码重复不仅增加了代码量,也使得代码难以维护和理解。
Spring AOP的目标是通过将横切关注点与主业务逻辑进行解耦,实现关注点的模块化和可重用性。横切关注点指的是与业务逻辑无关但又必须在多个地方进行处理的功能,如日志记录、事务管理、异常处理等。
在Spring AOP中,我们可以定义切面(Aspect),切面由切点(Pointcut)、通知(Advice)和连接点(Joinpoint)组成。切点定义了哪些连接点会被切面所影响,通知定义了在切点处执行的逻辑,而连接点则表示程序执行过程中的某个特定点。
Spring AOP的工作原理是通过动态代理的方式,在运行时将切面逻辑织入到目标对象的方法中,从而实现对横切关注点的处理。
使用Spring AOP的好处包括:
总结而言,Spring AOP通过切面的概念,实现了对横切关注点的解耦和重用,提高了代码的组织性、可维护性和可扩展性。
是的,Spring AOP 的核心概念包括切面、连接点、通知、切点以及织入。下面我将对这些概念做一些简要的解释:
除此之外,Spring AOP 还有其他常用的概念,如目标对象(Target)、代理对象(Proxy)等。目标对象是含有连接点的对象,而代理对象是 Spring AOP 创建的一个包含切面代码的对象。
以上就是 Spring AOP 的核心概念,它们共同构成了切面编程的基础。
Spring AOP 支持两种类型的动态代理:JDK 动态代理和 CGLIB 动态代理。它们之间有以下区别:
综上所述,选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的需求和场景。如果目标对象实现了接口并且对性能要求较高,可以选择 JDK 动态代理;如果目标对象没有实现接口或者对性能要求不那么苛刻,可以选择 CGLIB 动态代理。默认情况下,Spring AOP 使用 JDK 动态代理,但在某些情况下会自动切换到 CGLIB 动态代理。
AOP (Aspect-Oriented Programming) 和 OOP (Object-Oriented Programming) 都是面向对象编程的范式,但它们关注的角度不同。下面是它们之间的区别:
综上所述,OOP 是一种面向对象的编程技术,它关注如何将数据和操作组织在一起。而 AOP 通过提取和组合通用功能以及横跨多个层次的共性代码来实现程序逻辑的复用和可维护性的增强。
下面是一个简单的 Spring AOP 的代码示例:
public class UserService{
   
   public void addUser(String username){
   
   
        System.out.println("Adding user: "+ username);}}@Aspect 
public class LoggingAspect{
   
   @Before("execution(* com.example.UserService.addUser(String)) && args(username)")
   public void beforeAddUser(String username){
   
   
        System.out.println("Before adding user: "+ username);
   }
   @AfterReturning("execution(* com.example.UserService.addUser(String)) && args(username)")
   public void afterAddUser(String username){
   
   
        System.out.println("After adding user: "+ username);
   }
}<beanid="userService" class="com.example.UserService"/>
<beanid="loggingAspect"class="com.example.LoggingAspect"/>
<aop:config>
<aop:aspectref="loggingAspect">
<aop:beforemethod="beforeAddUser"  pointcut="execution(* com.example.UserService.addUser(String))"/>
<aop:after-returningmethod="afterAddUser" pointcut="execution(* com.example.UserService.addUser(String))"/>
</aop:aspect>
</aop:config>这个示例代码中,UserService 类是我们的目标类,LoggingAspect 类是切面类。在切面类中,@Before 注解用于在 addUser 方法执行前打印日志,@AfterReturning 注解用于在 addUser 方法成功执行后打印日志。
配置文件中定义了目标类和切面类的实例,并使用 标签来进行配置。通过 和 标签分别将 beforeAddUser 和 afterAddUser 方法与切点关联起来。
当调用 UserService 的 addUser 方法时,Spring AOP 会根据配置自动触发切面逻辑,从而实现日志打印的功能。
这只是一个简单的示例,实际使用中可以根据需求定义更复杂的切面和通知逻辑。
下面是一个基于 Spring Boot 的 AOP 示例代码:
@Service
public class UserService{
   
   public void addUser(String username){
   
   
        System.out.println("Adding user: "+ username);
   }
}@Aspect
@Component
public class LoggingAspect{
   
   @Before("execution(* com.example.UserService.addUser(String)) && args(username)")
   public voidb eforeAddUser(String username){
   
   
        System.out.println("Before adding user: "+ username);
   }
   @AfterReturning("execution(* com.example.UserService.addUser(String)) && args(username)")
   public void afterAddUser(String username){
        System.out.println("After adding user: "+ username);
   }
}在启动类上添加 @EnableAspectJAutoProxy 注解,开启 Spring Boot 的 AOP 功能。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   publicstaticvoidmain(String[] args){
        SpringApplication.run(Application.class, args);
  }
}这个示例代码中,UserService 类是我们的目标类,LoggingAspect 类是切面类。在切面类中,@Before 注解用于在 addUser 方法执行前打印日志,@AfterReturning 注解用于在 addUser 方法成功执行后打印日志。
启动类上添加了 @EnableAspectJAutoProxy 注解,开启了 Spring Boot 的 AOP 功能。
当调用 UserService 的 addUser 方法时,Spring AOP 会根据配置自动触发切面逻辑,从而实现日志打印的功能。
这只是一个简单的示例,实际使用中可以根据需求定义更复杂的切面和通知逻辑。
以下是使用 Spring Cloud 中 AOP 的一个简单示例:
假设我们有一个名为 HelloService 的服务,该服务需要记录日志。我们可以通过 AOP 来实现此功能。
LogAspect 切面类,用于记录日志:@Aspect
@Component
public class LogAspect{
   
   private static Logger logger = LoggerFactory.getLogger(LogAspect.class);
   @Pointcut("execution(public * com.example.service.HelloService.*(..))")
   public void log(){
   
   }
   
   @Before("log()")
   public void doBefore(JoinPoint joinPoint){
        logger.info("method {} start", joinPoint.getSignature().getName());
   }
  
   @AfterReturning(returning ="result", pointcut ="log()")
   public void doAfterReturning(Object result){
        logger.info("method return value: {}", result);
        logger.info("method end");
   }
  
   @AfterThrowing(throwing ="ex", pointcut ="log()")
   public void doAfterThrowing(Throwable ex){
        logger.error("method throw exception: {}", ex.getMessage());
   }
 }HelloService 中添加业务方法:@Service
public class HelloService{
   
   public String sayHello(String name){
   
      return"Hello, "+ name;
   }
}@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass =true)
public class Application{
   
   public static void main(String[] args){
   
        SpringApplication.run(Application.class, args);
   }
   @Bean
   public LogAspect logAspect(){
   
      return new LogAspect();
   }
}HelloService 的 sayHello 方法,观察控制台日志输出:@RestController
public class HelloController{
   
   @Autowired 
   private HelloService helloService;
   
   @GetMapping("/hello")
   public String sayHello(@RequestParam String name){
   
      return helloService.sayHello(name);
   }
}通过以上步骤,我们就成功地使用 Spring Cloud AOP 实现了一个简单的日志记录功能。在业务逻辑中引入切面可以很方便地实现各类横切关注点。
接下来用spring boot 方式去实现代码
在 Spring Boot 中使用 AOP 实现事务管理的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>application.properties 文件中配置数据源信息。spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.DriverUserService,实现添加用户和查询用户列表的方法,并添加事务注解 @Transactional。@Service
public class UserService{
   
   @Autowired
   private JdbcTemplate jdbcTemplate;
   
   @Transactional
   public void addUser(String name, Integer age){
   
        jdbcTemplate.update("insert into user(name,age) values (?,?)", name, age);
   }
   
   public List<User> findUsers(){
   
      return jdbcTemplate.query("select * from user",newBeanPropertyRowMapper<>(User.class));
   }
}TransactionAspect,实现事务管理的切面逻辑,并添加事务注解 @Transactional。@Aspect
@Component
public class TransactionAspect{
   
   @Autowired
   private PlatformTransactionManager transactionManager;
   
   @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
   public void tx(){
   
   }
   @Around("tx()")
   public Object around(ProceedingJoinPoint point)throws Throwable {
        TransactionStatus status = transactionManager.getTransaction(newDefaultTransactionDefinition());
        Object result;
        try{
            result = point.proceed();
            transactionManager.commit(status);
         }catch(Throwable e){
            transactionManager.rollback(status);
            throw e;
         }
         return result;
    }
}@SpringBootApplication
@EnableTransactionManagement
public class Application{
   
   public static void main(String[] args){
   
   
        SpringApplication.run(Application.class, args);
   }
   
   @Bean
   public TransactionAspect transactionAspect(){
   
        return new TransactionAspect();
   }
   
   @Bean
   public DataSource dataSource(){
   
        return new DruidDataSource();
   }
   
   @Bean
   public JdbcTemplate jdbcTemplate(){
   
        return new JdbcTemplate(dataSource());
   }
   @Bean
   public PlatformTransactionManager txManager(){
   
        return new DataSourceTransactionManager(dataSource());
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的事务管理。在 Service 层添加 @Transactional 注解,即可自动开启事务,无需手动操作数据库连接和事务绑定。
在 Spring Boot 中使用 AOP 实现日志记录管理的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>LogAspect,实现日志记录的切面逻辑。在方法执行前输出日志记录开始的信息,在方法执行后输出日志记录结束的信息及方法的返回值。@Aspect
@Component
public class LogAspect{
   
   private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
   
   @Pointcut("execution(* com.example.service..*.*(..))")
   public voidl og(){
   
   }
   
   @Before("log()")
   public void before(JoinPoint point){
 
        logger.info("start execute method: {}.{}", point.getTarget().getClass().getName(), point.getSignature().getName());
   }
   
   @AfterReturning(pointcut ="log()", returning ="returnValue")
   public void afterReturning(JoinPoint point, Object returnValue){
        logger.info("end execute method: {}.{}. return value is {}", point.getTarget().getClass().getName(), point.getSignature().getName(), returnValue);
   }
}application.properties 文件中配置以下属性:logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.level.com.example=DEBUG这里指定了控制台日志输出格式为带时间戳、线程号、日志级别、类名以及消息。并设置了 com.example 包下的日志级别为 DEBUG。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   public static void main(String[] args){
   
        SpringApplication.run(Application.class, args);
   }
   
   @Bean
   public LogAspect logAspect(){
   
      return new LogAspect();
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的日志记录管理。在业务逻辑中引入切面可以很方便地实现各类横切关注点。
在 Spring Boot 中使用 AOP 实现安全控制管理的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>SecurityAspect,实现安全控制的切面逻辑。在方法执行前检查用户是否具有相应的权限,如果没有权限,则抛出异常。@Aspect
@Component
public class SecurityAspect{
   
   @Autowired
   private HttpServletRequest request;
   
   @Pointcut("execution(* com.example.service..*.*(..))")
   public void checkPermission(){
   
   } 
   
   @Before("checkPermission()")
   public void before(JoinPoint point){
   
        // 获取当前登录用户的权限
        Set<String> permissions = getCurrentUserPermissions();// 获取方法上的注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RequiredPermission permissionAnnotation = method.getAnnotation(RequiredPermission.class);
        // 检查用户是否具有相应权限
        if(permissionAnnotation != null &&!permissions.contains(permissionAnnotation.value())){
   
             throw new AccessDeniedException("Access Denied");
        }
   }
   private Set<String> getCurrentUserPermissions(){
   
   // 从请求中获取当前登录用户的权限,可以根据实际情况进行实现
   // ...return Collections.emptySet();
   }
}RequiredPermission,用于标注需要进行权限检查的方法。@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission{
    String value();
}@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   public static void main(String[] args){
        SpringApplication.run(Application.class, args);
   }
   
   @Bean
   public SecurityAspect securityAspect(){
   
        return new SecurityAspect();
   }
}@RequiredPermission 注解标注需要进行权限检查的方法,并传入相应的权限值。@Service
public class UserService{
   
   @RequiredPermission("user:add")
   public void addUser(String name, Integer age){
   
      // 添加用户的逻辑
   }
   @RequiredPermission("user:list")
   public List<User> findUsers(){
   
      // 查询用户列表的逻辑
      return Collections.emptyList();
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Spring Security 实现了一个简单的安全控制管理。在 Service 层的方法上使用 @RequiredPermission 注解标注需要进行权限检查的方法,在切面中进行权限验证。如果当前登录用户没有相应的权限,则抛出访问拒绝异常。
在 Spring Boot 中使用 AOP 实现参数校验管理的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>ValidationAspect,实现参数校验的切面逻辑。在方法执行前进行参数校验,如果校验失败,则抛出异常。@Aspect
@Component
public class ValidationAspect{
   
   @Pointcut("execution(* com.example.service..*.*(..))")
   public void validate(){
   
   }
   
   @Before("validate()")
   public void before(JoinPoint point){
        Object[] args = point.getArgs();
        for(Object arg : args){
   
             validateObject(arg);
        }
   }
   private void validateObject(Object obj){
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<Object>> violations = validator.validate(obj);
        if(!violations.isEmpty()){
             throw new ConstraintViolationException(violations);
        }
   }
}@NotNull、@Min、@Max 等注解。@Service
public class UserService{
   
   public void addUser(@NotNull String name,@Min(18)@Max(99) Integer age){
   
   // 添加用户的逻辑
   }
}@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   public static void main(String[] args){
        SpringApplication.run(Application.class, args);
   }
   
   @Bean
   public ValidationAspect validationAspect(){
   
      returnnewValidationAspect();
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Hibernate Validator 实现了一个简单的参数校验管理。在 Service 层的方法参数上使用标准的 Java Bean Validation 注解进行参数校验,在切面中进行参数校验。如果校验失败,则抛出约束违规异常 ConstraintViolationException。
在 Spring Boot 中使用 AOP 实现性能监控的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>PerformanceAspect,实现性能监控的切面逻辑。在方法执行前记录开始时间,在方法执行后计算执行时间,并输出到日志中。@Aspect
@Component
public class PerformanceAspect{
   
   private static final Logger LOGGER = LoggerFactory.getLogger(PerformanceAspect.class);
   
   @Around("execution(* com.example.service..*.*(..))")
   public Object measurePerformance(ProceedingJoinPoint joinPoint)throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long startTime = System.currentTimeMillis();
        LOGGER.info("Start executing method: {}", methodName);
        Object result = joinPoint.proceed();long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        LOGGER.info("Finish executing method: {}. Execution time: {} ms", methodName, executionTime);
        return result;
   }
}@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   public static void main(String[] args){
        SpringApplication.run(Application.class, args);
   }
   
   @Bean
   public PerformanceAspect performanceAspect(){
   
      return new PerformanceAspect();
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的性能监控。在切面中使用 @Around 注解拦截所有 Service 层的方法,并记录方法的开始时间和结束时间,计算执行时间,并输出到日志中。这样我们就可以在日志中查看每个方法的执行时间,以进行性能监控和优化。
在 Spring Boot 中使用 AOP 实现数据缓存的示例:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>application.properties 或 application.yml 文件中添加以下配置:spring.redis.host=127.0.0.1
spring.redis.port=6379CachingAspect,实现数据缓存的切面逻辑。在方法执行前尝试从缓存中获取数据,如果找到了,则直接返回缓存数据;如果没有找到,则执行方法逻辑并将结果存入缓存。@Aspect
@Component
public class CachingAspect{
   
   private static final Logger LOGGER = LoggerFactory.getLogger(CachingAspect.class);
   
   @Autowired
   private RedisTemplate<String, Object> redisTemplate;
   
   @Around("execution(* com.example.service..*.*(..))")
   public Object cacheData(ProceedingJoinPoint joinPoint)throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        String cacheKey = methodName + Arrays.toString(joinPoint.getArgs());// 尝试从缓存中获取数据
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        Object cachedData = valueOperations.get(cacheKey);
        if(cachedData != null){
            LOGGER.info("Retrieve data from cache for method: {}", methodName);
            return cachedData;
        }// 从数据库等数据源获取数据
        Object fetchedData = joinPoint.proceed();// 将数据存入缓存
        valueOperations.set(cacheKey, fetchedData);
        LOGGER.info("Cache data for method: {}", methodName);
        return fetchedData;
   }
}@SpringBootApplication
@EnableAspectJAutoProxy
public class Application{
   
   public static void main(String[] args){
        SpringApplication.run(Application.class, args);
   }
   @Bean
   public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
   }
   
   @Bean
   public CachingAspect cachingAspect(){
   
        return new CachingAspect();
   }
}通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Redis 实现了一个简单的数据缓存。在切面中使用 @Around 注解拦截所有 Service 层的方法,将方法名和参数作为缓存的键,尝试从 Redis 缓存中获取数据。如果找到了缓存数据,则直接返回;如果没有找到,则执行方法逻辑并将结果存入缓存。这样可以提高数据读取的性能,减少对数据源的访问。需要注意的是,缓存的键需要保证唯一性,不同的方法和参数应该使用不同的缓存键。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。