Spring 框架(Spring Framework)之 AOP(面向切面编程)详解

简介: Spring 框架(Spring Framework)之 AOP(面向切面编程)详解

AOP(面向切面编程)

概念

AOP((Aspect Oriented Programming)面向切面编程

  • 是一种思想,目的是在不修改源代码的基础上,对原有功能进行增强
  • 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • AOP是一种编程范式,是OOP的延续,在OOP基础之上进行横向开发。
  • AOP研究的不是每层内部如何开发,而是同一层面上各个模块之间的共性功能。比如:事务、日志、统计


Spring AOP 是对 AOP 思想的一种实现,将那些与业务无关,却为业务模块所共用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP还能解决一些系统层面上的问题,比如日志、事务、权限等。

Spring 底层是通过动态代理的方式实现的 AOP。同时支持 jdk 和 cglib 动态代理,Spring会根据被代理的类是否有接口自动选择代理方式:

  • 如果有接口,就采用 JDK 动态代理(也可以强制使用 CGLIB 动态代理)
  • 没有接口就采用用 CGLIB 动态代理的方式


Spring AOP工作流程

开发阶段分别开发,运行阶段组装运行

  • 开发阶段(开发者完成)

    • 开发共性功能,制作成增强
    • 开发非共性功能,制作成切点
    • 在配置文件中,声明切点与增强之间的关系,即切面
  • 运行阶段/容器启动阶段(AOP完成)

    1. Spring读取配置文件中的切面信息,根据切面中的描述,将 增强功能 增加在 目标对象 的 切点方法 上,动态创建代理对象
    2. 最后将经过代理之后对象放入容器中(注意:存入容器的是动态代理对象!)


术语及说明

  • 目标对象(target ):需要被增强的对象,即切入点方法所在对象
  • 连接点(jointPoint):被代理对象中的方法
  • 切点(pointCut):按照 AOP 的规则去切(匹配)连接点,匹配出来的就叫切点,即 需要被增强的方法

    切点表达式的:定义一组规则,用于在连接点(所有方法)中挑选切入点(被增强方法)

    常用方式切点表达式:execution(方法的修饰符 返回值类型 包名.类名.方法名(参数))

  • 增强(通知)(advice):一个具体的增强功能。增强方法在切点方法的什么位置上执行

    Spring AOP 通知(增强)分为5种类型:

    • 前置通知(before):在切点运行之前执行
    • 后置通知(after-returning):在切点正常运行结束之后执行
    • 异常通知(after-throwing):在切点发生异常的时候执行
    • 最终通知(after):在切点的最终执行
    • 环绕通知(around):一种特殊的通知,在一个方法中定义多个增强逻辑(和手动定义动态代理类似)

      环绕通知的自定义方法:

      1. 参数 ProceedingJoinPoint:被代理对象的方法
      2. 返回值:被增强方法的返回值
  • 代理对象(proxy ):目标对象被增强后成为代理对象
  • 切面(aspect):是一个设计概念,包含了Advice 和 Pointcut。切面 = 切入点 + 增强

    Advice定义了Aspect的任务和什么时候执行,Pointcut定义在哪里切入

    即 Aspect定义了一个什么样的增强功能,切入到哪个核心方法的哪个位置

  • 织入(Weaving):一个动作。将增强代码加入到核心代码的过程就叫织入


切点标志符(表达式)

在定义 SpringAOP 的切点时候,比如使用 @Pointcut 注解标记切点时,需要填写切入通知的连接点的特征,即连接点的匹配规则或表达式,这些表达式是通过被称之为切点指示符的符号进行编写的。

通配符

在使用切点指示符进行匹配表达式编写时,几乎都需要使用到通配符进行模糊匹配,常用的通配符有三个:..+*

  • ..:表示匹配方法中的任意数量和类型的参数,或者匹配类的任意包路径
  • +:表示匹配给定类的任意子类
  • *:表示匹配一个或多个数量的字符

注:

  • 方法修饰符可以省略
  • 方法返回值可以通过 * 标识任意返回值类型
  • 包名可以通过 * 标识任意包,一般会指定到一个具体的包路径
  • 类名可以通过 * 标识任意类
  • 方法名可以通过 * 标识任意方法
  • 参数可以通过 .. 标识任意参数


类型指示符(within)

within 指示符用于匹配类型(包括:接口、类和包),其语法格式如下:

within(<type name>)

示例:

// 匹配 com.example.dao 包下的所有类的所有方法,但不包括子包的类
@Pointcut("within(com.example.dao.*)")

// 匹配 com.example.dao 包及其子包中所有类中的所有方法
@Pointcut("within(com.example.dao..*)")

// 匹配 包路径前缀为com.example + 任意包路径 + dao 包及其子包中所有类中的所有方法
@Pointcut("within(com.example..dao..*)")

// 匹配 com.example.dao 包下的 UserDaoImpl 类的所有方法
@Pointcut("within(com.example.dao.UserDaoImpl)")

// 匹配当前包下的 UserDaoImpl 类的所有方法
@Pointcut("within(UserDaoImpl)")

// 匹配所有实现 com.example.dao 包下的 UserDao 接口的类的所有方法
@Pointcut("within(com.example.dao.UserDao+)")

注: 示例中的 UserDao 为接口,UserDaoImpl 为其一个实现类。


方法指示符(execution)

execution 指示符根据方法签名进行匹配,其语法格式如下:

execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters))

其中:

  • scope 表示方法作用域(如 public、private)
  • return-type 表示返回值类型
  • fully-qualified-class-name 表示方法所在类的完全限定名称
  • parameter 表示方法参数

示例:

// 匹配 com.example.dao 包中所有类中的所有方法(常用)
@Pointcut("execution(* com.example.dao.*.*(..))")

// 匹配 UserDaoImpl 类中的所有方法
@Pointcut("execution(* com.example.dao.UserDaoImpl.*(..))")
 
// 匹配 UserDaoImpl 类中的所有公共方法
@Pointcut("execution(public * com.example.dao.UserDaoImpl.*(..))")
 
// 匹配 UserDaoImpl 类中的所有返回值为 int 类型的公共方法
@Pointcut("execution(public int com.example.dao.UserDaoImpl.*(..))")
 
// 匹配 UserDaoImpl 类中第一个参数为 int 类型的所有公共方法
@Pointcut("execution(public * com.example.dao.UserDaoImpl.*(int , ..))")


名称指示符(bean)

bean 指示符用于匹配特定名称的 Bean 对象的方法,是 SpringAOP 扩展的指示符,AspectJ 中,我有对应的指示符。

示例如下:

// 匹配名称中带有后缀 Service 的 Bean 的所有方法
@Pointcut("bean(*Service)")


对象指示符(this 、target)

对象指示符共有两个 this 和 target,两者的区别如下:

  • this:用于匹配当前 AOP 代理对象类型的方法;
  • target:用于匹配当前目标对象类型的方法。

示例如下:

// 匹配任意实现了 UserDao 接口的代理对象的方法
@Pointcut("this(com.example.springAop.dao.UserDao)")

// 匹配任意实现了 UserDao 接口的目标对象的方法
@Pointcut("target(com.example.springAop.dao.UserDao)")


注解指示符(@within、@annotation)

注解指示符用于匹配使用了特定注解的类或方法,包括@within和 @annotation:

  • @within:表示匹配使用了特定注解的类的所有方法(注意和 within 的区别)
  • @annotation:表示匹配使用了特定注解的方法

示例如下:

// 匹配使用了 MyAnnotation 注解的类(注意是类)的所有方法
@Pointcut("@within(com.example.annotation.MyAnnotation)")

// 匹配使用了 MyAnnotation 注解的方法(注意是方法)
@Pointcut("@annotation(com.example.annotation.MyAnnotation)")


切点指示符的组合使用

所有的切点指示符都可以通过逻辑运算符进行组合使用,比如 &&||!,示例如下:

// 匹配了任意实现了 UserDao 接口的目标对象的方法并且该对象不在 com.zejian.dao 包及其子包下
@Pointcut("target(com.example.dao.UserDao) !within(com.example.dao..*)")

// 匹配名字以 Service 结尾, 并且在 com.example.service 包中的 bean
@Pointcut("bean(*Service) && within(com.example.service.*)")

注: 在组合指示符中,第一个指示符为主匹配表达式,而第二个指示符只是对第一个指示符的匹配结果进行过滤。


配置 Spring AOP(xml)

xml文件 配置切入点

  • <aop:pointcut

    • id:当前切点的唯一标志
    • expression:切入点表达式

xml文件 配置切面

  • <aop:aspect :配置一个切面

    • id:当前切面的唯一标志
    • ref:指定当前切面使用哪个通知

xml文件 配置通知类型

  • <aop:before :指定通知在切入点方法中执行的位置

    • method : 切面类中的增强方法名
    • pointcut-ref:切入点的id

xml文件 配置AOP示例

    <!--声明AOP配置-->
    <aop:config>
        <!-- 配置切入点(被增强的方法) -->
        <aop:pointcut id="pt" expression="execution(* cn.test.dao.impl.*.*(..))"/>

        <!--配置切面-->
        <aop:aspect ref="logger">
            <!-- 配置通知类型 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="pt"></aop:before>
            <!-- 后置通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
            <!-- 最终通知 -->
            <aop:after method="after" pointcut-ref="pt"></aop:after>-->
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"></aop:around>
        </aop:aspect>
    </aop:config>
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 切面类:此类中具有所有的增强代码逻辑
 */
public class Logger {

    /**
     * 前置通知:执行被代理对象方法之前执行
     * 方法:无参数,无返回值
     */
    public void before() {
        System.out.println("执行前置通知");
    }

    /**
     * 后置后置:正常执行被代理对象方法获取返回值之后执行
     */
    public void afterReturning() {
        System.out.println("执行后置通知");
    }

    /**
     * 异常通知:执行代码抛出异常的时候执行
     */
    public void afterThrowing() {
        System.out.println("执行异常通知");
    }


    /**
     * 最终通知:finally代码块中执行的逻辑
     */
    public void after() {
        System.out.println("执行最终通知");
    }

    /**
     * 环绕通知:在一个方法中定义多个增强逻辑
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object obj = null;

        try {
            System.out.println("执行前置通知");
            //执行被代理对象方法
            obj = pjp.proceed();
            System.out.println("执行后置通知");
        }catch (Exception e){
            System.out.println("执行异常通知");
        }finally {
            System.out.println("执行最终通知");
        }
        return obj;
    }


配置 Spring AOP(注解)

AOP注解版有两种:

  • 基于 XML 结合注解的配置方式
  • 基于纯注解的配置方式


开启 AOP 注解支持(xml 方式)

xml配置文件

  • 开启IOC注解的支持,包扫描

    • 自定义的对象,通过IOC注解进行对象创建和依赖注入
    • 第三方的对象,通过XML配置对象创建和依赖注入
  • 开启AOP注解的支持,自动代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/aop
                http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启包扫描-->
    <context:component-scan base-package="cn.test"></context:component-scan>

    <!--开启对AOP注解的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!--在切面类中通过注解完成AOP配置-->
</beans>


开启 AOP 注解支持(配置类方式)

  • @EnableAspectJAutoProxy:标注在配置类上,开启 aop 注解支持
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 配置类
 */
@Configuration
@ComponentScan(basePackages = "cn.test")
@EnableAspectJAutoProxy
public class SpringConfig {
}


切面类(注解)

  • @Aspect:标注在自定义类上,声明切面类

    注意:该切面类同时需要标注 IOC注解(@Component),交给 Spring 容器管理

  • 在切面类中的通知(增强)方法上通过注解配置通知类型:

    • @Before:前置通知

      • @AfterReturning: 后置通知
      • @AfterThrowing:异常通知
      • @After:最终通知
      • @Around:环绕通知

    通知注解的属性:

    • value / argNames 属性:切入点表达式 或 被 @Pointcut 标注的方法名()

          @Around("pt()")
          public Object around(ProceedingJoinPoint pjp)
  • @Pointcut:标注在切面类中的空的方法上,抽取公共的切入点表达式

    • value / argNames 属性:切入点表达式
    • 通知注解配置 方法名() 即可引入公共的切入点表达式

          @Pointcut(value="execution(* cn.test.service.impl.*.*(..))")
          public void pt() {}

切面类示例

/**
 * 切面类:此类中具有所有的增强代码逻辑
 */
@Component
@Aspect
public class Logger {

    /**
     * 前置通知:执行被代理对象方法之前执行
     * 方法:无参数,无返回值
     */
    //@Before(value="execution( * cn.test.dao.impl.*.*(..) )")
    public void before() {
        System.out.println("执行前置通知");
    }

    /**
     * 后置通知:正常执行被代理对象方法获取返回值之后执行
     */
    //@AfterReturning(value="execution( * cn.test.dao.impl.*.*(..) )")
    public void afterReturning() {
        System.out.println("执行后置通知");
    }

    /**
     * 异常通知:执行代码抛出异常的时候执行
     */
   // @AfterThrowing("execution( * cn.test.dao.impl.*.*(..) )")
    public void afterThrowing() {
        System.out.println("执行异常通知");
    }

    /**
     * 最终通知:finally代码块中执行的逻辑
     */
    //@After("execution( * cn.test.dao.impl.*.*(..) )")
    public void after() {
        System.out.println("执行最终通知");
    }
    
    // @Pointcut:抽取公共的切入点表达式
    @Pointcut(value="execution(* cn.test.service.impl.*.*(..))")
    public void pt() {}

    /**
     * 环绕通知:在一个方法中定义多个增强逻辑
     */
    //@Around("execution( * cn.test.dao.impl.*.*(..) )")
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object obj = null;
        try {
            System.out.println("1执行前置通知");
            //执行被代理对象方法
            obj = pjp.proceed();
            System.out.println("2执行后置通知");
        }catch (Exception e){
            System.out.println("3执行异常通知");
        }finally {
            System.out.println("4执行最终通知");
        }
        return obj;
    }
}


四大通知的执行顺序问题

参考:https://blog.csdn.net/qq_45193304/article/details/109430545

SpringAOP四大通知(前置、后置、异常、最终通知)的执行顺序非常复杂,跟Spring版本、配置先后顺序、配置方式(xml | 注解)都有关,故建议使用环绕通知

xml方式正确顺序配置aop的执行顺序:

try {
    // 前置通知(before) : 在切点运行之前执行
   
    // 切点执行,被代理对象方法调用
       
    // 后置通知(after-returning): 在切点正常运行结束之后执行
}catch (Exception e){
    // 异常通知(after-throwing): 在切点发生异常的时候执行
}finally {
    // 最终通知(after): 在切点的最终执行
}

注解方式正确顺序配置aop的执行顺序:

try{
    try{
        //@Before  -- 首先执行前置通知
        method.invoke(..); -- 然后执行切入点方法
    }finally{
        //@After  -- 再而肯定会执行最终通知 --- 注解配置的注意点
    }
    //@AfterReturning  -- 如果没有异常,则继续执行后置通知
    return;  -- 返回结果
}catch(){
    //@AfterThrowing  -- 如果有异常,则执行异常通知
}


切面方法的可传参数

JoinPoint 对象

JoinPoint 对象封装了 SpringAop 中切面方法的信息,在切面方法中添加 JoinPoint 参数,就可以获取到封装了该方法信息的JoinPoint 对象。

常用 API:

  • Signature getSignature() :获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的 Class 等信息

    joinPoint.getSignature().getName()) :目标方法名

    joinPoint.getSignature().getDeclaringType().getSimpleName()) :目标方法所属类的简单类名

    joinPoint.getSignature().getDeclaringTypeName()) :目标方法所属类的类名

    Modifier.toString(joinPoint.getSignature().getModifiers())) :目标方法声明类型

  • Object[] getArgs() :获取传入目标方法的参数对象
  • Object getTarget() :获取被代理的对象
  • Object getThis() :获取代理对象

使用示例:

@Component
@Aspect
@Slf4j
public class Logger {

    @Before(value="execution( * cn.test.dao.impl.*.*(..) )")
    public void before(JoinPoint joinPoint) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        StringBuffer uri = request.getRequestURL();

        Object[] args = joinPoint.getArgs();
        if (Objects.nonNull(args) && args.length > 0 && args[0] instanceof WebDataBinder){
            // 不打印initBinder方法的请求入参,否则会报错
            return;
        }
        List<Object> logArgs = Arrays.stream(args)
            .filter(arg -> (!(arg instanceof ServletRequest)
                && !(arg instanceof ServletResponse)
                && !(arg instanceof MultipartFile)
                && !(arg instanceof HttpSession)))
            .collect(Collectors.toList());
        log.info("执行前置通知, uri: {}, args: {}", uri, logArgs);
    }
}


ProceedingJoinPoint 对象

ProceedingJoinPoint 对象是 JoinPoint 的子接口,该对象只用在 @Around 的切面方法中

添加了 两个方法

  • Object proceed() throws Throwable :执行目标方法
  • Object proceed(Object[] var1) throws Throwable :传入的新的参数去执行目标方法

使用示例详解案例 MethodAnnotationLogAspect


案例

HttpLogAspect

打印 controller 方法日志的请求和返回的切面类

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Aspect
@Component
public class HttpLogAspect {

    @Pointcut("execution(* com.test.ssmtest..controller..*(..))")
    public void pt() {}

    @Before(value = "pt()")
    public void testBefore(JoinPoint joinPoint) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        StringBuffer uri = request.getRequestURL();

        Object[] args = joinPoint.getArgs();
        if (Objects.nonNull(args) && args.length > 0 && args[0] instanceof WebDataBinder){
            // 不打印initBinder方法的请求入参,否则会报错
            return;
        }
        List<Object> logArgs = Arrays.stream(args)
            .filter(arg -> (!(arg instanceof ServletRequest)
                && !(arg instanceof ServletResponse)
                && !(arg instanceof MultipartFile)
                && !(arg instanceof HttpSession)))
            .collect(Collectors.toList());
        log.info("request, uri: {}, args: {}", uri, JSON.toJSONString(logArgs));
    }

    @AfterReturning(value = "pt()", returning = "rt")
    public void afterReturning(Object rt) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        StringBuffer uri = request.getRequestURL();
        log.info("response, uri: {}, return: {}", uri, JSON.toJSONString(rt));
    }
}


MethodAnnotationLogAspect

打印使用了自定义注解的方法的调用时间

自定义注解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAnnotation {
    String methodDesc();
}

切面类

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class MethodAnnotationLogAspect {

    @Around("@annotation(com.test.ssmtest.aspect.LogAnnotation)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法上的注解
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        String methodDesc = annotation == null ? "" : annotation.methodDesc();

        long startTime = System.currentTimeMillis();
        log.info("开始时间:{},进入方法:{}", DateFormatUtils.format(startTime, "yyyy-MM-dd HH:mm:ss:SSS"), methodDesc);
        // 执行方法
        Object proceedReturn = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        log.info("用时:{} ms,离开方法:{}", time, methodDesc);
        return proceedReturn;
    }
}
相关文章
|
24天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
11天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
2天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
23 9
|
14天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
619 7
|
12天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
12天前
|
存储 NoSQL Java
Spring Session框架
Spring Session 是一个用于在分布式环境中管理会话的框架,旨在解决传统基于 Servlet 容器的会话管理在集群和云环境中的局限性。它通过将用户会话数据存储在外部介质(如数据库或 Redis)中,实现了会话数据的跨服务器共享,提高了应用的可扩展性和性能。Spring Session 提供了无缝集成 Spring 框架的 API,支持会话过期策略、并发控制等功能,使开发者能够轻松实现高可用的会话管理。
Spring Session框架
|
19天前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
27 2
|
19天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
20天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
41 1
|
29天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
38 13
下一篇
无影云桌面