Spring介绍以及IOC和AOP的实现

Spring

spring是由interface21框架为基础经过重新设计,并不断丰富其内涵在2004年3月24日发布了1.0版本。

spring的创作理念就在于使现有的技术更加容易使用

优点
  • Spring是一个开源的免费框架(容器)

  • Spring是一个轻量级非入侵式的框架

  • Spring的核心技术控制反转(IOC)和面向切面编程(AOP)

  • 支持事务的处理,支持框架的整合。

总结:Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

Spring的组成

  1. Core Container(核心容器)

    • 功能:IoC(控制反转)和 DI(依赖注入)实现,管理 Bean 的生命周期。

    • 核心类ApplicationContextBeanFactory

  2. AOP(面向切面编程)

    • 功能:通过动态代理实现日志、事务等横切逻辑。

    • 注解@Aspect@Transactional

  3. Data Access(数据访问)

    • 功能 :简化 JDBC(JdbcTemplate)、集成 ORM(Hibernate/JPA)。

    • 事务管理 :声明式事务(@Transactional

  4. Web MVC

    • 功能:基于 Servlet 的 MVC 框架,支持 REST。

    • 核心类DispatcherServlet@Controller

  5. Context(上下文)

    • 功能 :扩展 ApplicationContext,提供国际化、事件机制等。
  6. WebFlux(响应式 Web)

    • 功能:非阻塞响应式编程(基于 Reactor),替代传统 Web MVC。
  7. Testing(测试)

    • 功能 :集成 JUnit,支持 Mock 测试(@SpringBootTest)。

IOC和AOP

Spring Ioc:控制反转的意思,它指的是一种思想,早些时候我们需要通过new关键字来创造对象,这就存在了一个弊端,当用户需要其它功能的时候,就会出现问题,我们就需要实时的根据需求进行修改,当我们通过IOC容器来帮我们进行实例化对象的时候,就大大的降低了对象之间的耦合度。依赖注入DI就是实现这个技术的一种方式。

AOP(面向切面编程) 的核心思想是:将与业务无关的通用功能(如日志、事务、权限)从业务代码中剥离 ,以切面(Aspect) 的形式统一管理,避免代码重复和耦合。Spring AOP 的代理实现分为 JDK 动态代理CGLIB 代理,具体选择取决于目标对象是否实现接口。

当我们使用Spring AOP的时候:

1.先添加依赖:

XML 复制代码
<!-- Maven 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.定义切面类

java 复制代码
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
@Aspect
@Component
public class LogAspect {
​
    // 定义切入点:拦截 service 包下所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}
​
    // 前置通知:方法执行前调用
    @Before("serviceMethods()")
    public void beforeAdvice() {
        System.out.println("【前置通知】准备执行方法...");
    }
​
    // 后置通知:方法正常返回后调用
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void afterReturningAdvice(Object result) {
        System.out.println("【后置通知】方法执行成功,返回值:" + result);
    }
​
    // 异常通知:方法抛出异常时调用
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void afterThrowingAdvice(Exception ex) {
        System.out.println("【异常通知】方法执行异常:" + ex.getMessage());
    }
​
    // 环绕通知:控制方法整个执行流程
    @Around("serviceMethods()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("【环绕通知】方法开始执行...");
        Object result = pjp.proceed(); // 执行目标方法
        System.out.println("【环绕通知】方法执行结束");
        return result;
    }
}

3.切入点表达式

Spring AOP使用AspectJ切入点表达式语言定义切入点:

  • execution([修饰符] 返回类型 [类名].方法名(参数) [异常])

    • * 代表任意

    • .. 代表任意参数或包路径

    • 示例:

      • execution(public * *(..)) - 所有public方法

      • execution(* set*(..)) - 所有以set开头的方法

      • execution(* com.xyz.service.*.*(..)) - service包下所有类的所有方法

      • execution(* com.xyz.service..*.*(..)) - service包及其子包下所有类的所有方法

4.通知执行顺序

  1. 环绕通知的前半部分(@Around)

  2. 前置通知(@Before)

  3. 目标方法执行

  4. 后置通知(@AfterReturning)或异常通知(@AfterThrowing)

  5. 最终通知(@After)

  6. 环绕通知的后半部分(@Around)

可以通过@Order注解指定切面执行顺序:

java 复制代码
@Aspect
@Component
@Order(1)  // 数字越小优先级越高
public class LoggingAspect {
    // ...
}

Spring AOP的高级特性

1.获取方法的信息

在通知方法中可以通过JoinPointProceedingJoinPoint获取方法信息:

java 复制代码
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
    // 获取方法签名
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    
    // 获取方法参数名和值
    String[] paramNames = signature.getParameterNames();
    Object[] paramValues = joinPoint.getArgs();
    
    // 获取目标类
    Object target = joinPoint.getTarget();
    Class<?> targetClass = target.getClass();
}

2.处理通知中的异常

在环绕通知当中可以捕获并处理异常

java 复制代码
@Around("serviceLayer()")
public Object handleException(ProceedingJoinPoint joinPoint) {
    try {
        return joinPoint.proceed();
    } catch (BusinessException ex) {
        // 处理业务异常
        return fallbackResult();
    } catch (Throwable ex) {
        // 处理其他异常
        throw new RuntimeException("系统异常", ex);
    }
}

Spring AOP 的代理实现分为 JDK 动态代理CGLIB 代理,具体选择取决于目标对象是否实现接口。

JDK动态代理

需要目标至少实现一个接口,实现原理:JDK动态代理通过反射在运行时动态生成实现指定接口的代理类,将方法调用转发到InvocationHandler处理,在调用真实方法前后可插入自定义逻辑。

我们进行示例:

1.定义业务接口和实现

java 复制代码
public interface OrderService {
    void createOrder(String orderId);
    void cancelOrder(String orderId);
}
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderId) {
        System.out.println("创建订单: " + orderId);
    }
    
    @Override
    public void cancelOrder(String orderId) {
        System.out.println("取消订单: " + orderId);
    }
}

2.实现InvocationHandler

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
​
public class OrderServiceHandler implements InvocationHandler {
    private final Object target;  // 目标对象
    
    public OrderServiceHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置处理
        System.out.println("[日志] 开始执行 " + method.getName() + " 方法");
        System.out.println("[参数] " + (args != null ? Arrays.toString(args) : "无"));
        
        // 调用目标方法
        Object result = method.invoke(target, args);
        
        // 后置处理
        System.out.println("[日志] " + method.getName() + " 方法执行完成");
        return result;
    }
}

3.创建并使用

java 复制代码
import java.lang.reflect.Proxy;
​
public class JdkProxyDemo {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderServiceImpl();
        
        // 2. 创建调用处理器
        OrderServiceHandler handler = new OrderServiceHandler(target);
        
        // 3. 创建代理对象
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
            OrderService.class.getClassLoader(),  // 类加载器
            new Class[]{OrderService.class},     // 代理接口数组
            handler                             // 调用处理器
        );
        
        // 4. 使用代理对象
        proxy.createOrder("ORD123456");
        System.out.println("----------------");
        proxy.cancelOrder("ORD123456");
    }
}

执行结果

java 复制代码
[日志] 开始执行 createOrder 方法
[参数] [ORD123456]
创建订单: ORD123456
[日志] createOrder 方法执行完成
----------------
[日志] 开始执行 cancelOrder 方法
[参数] [ORD123456]
取消订单: ORD123456
[日志] cancelOrder 方法执行完成

CGLIB动态代理

CGLIB动态代理通过继承目标类生成子类,重写方法实现代理,无需接口即可拦截方法调用(无法代理final类/方法)。

示例:

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
public class CglibDemo {
    // 目标类(无需接口)
    static class UserService {
        public void saveUser(String name) {
            System.out.println("保存用户: " + name);
        }
    }
​
    public static void main(String[] args) {
        // 1. 创建 Enhancer
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class); // 设置父类(目标类)
        
        // 2. 设置回调(拦截逻辑)
        enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
            System.out.println("[CGLIB] 方法调用前: " + method.getName());
            Object result = proxy.invokeSuper(obj, args); // 调用父类方法
            System.out.println("[CGLIB] 方法调用后");
            return result;
        });
​
        // 3. 生成代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 4. 调用代理方法
        proxy.saveUser("张三");
    }
}

Spring AOP 是高层抽象,底层默认通过 JDK动态代理(基于接口)或 CGLIB(基于子类)实现动态代理

相关推荐
ahauedu27 分钟前
jar命令提取 JAR 文件
java·jar
青岛少儿编程-王老师44 分钟前
CCF编程能力等级认证GESP—C++1级—20250628
java·开发语言·c++
linmoo19861 小时前
Spring AI 系列之十四 - RAG-ETL之一
人工智能·spring·etl·rag·springai·documentreader
linmoo19862 小时前
Spring AI 系列之十五 - RAG-ETL之二
人工智能·spring·etl·transformers·rag·springai
lifallen2 小时前
KRaft 角色状态设计模式:从状态理解 Raft
java·数据结构·算法·设计模式·kafka·共识算法
LittleLoveBoy2 小时前
Java HashMap key为Integer时,遍历是有序还是无序?
java·开发语言
经典19922 小时前
Java 设计模式及应用场景
java·单例模式·设计模式
IguoChan2 小时前
9. Redis Operator (2) —— Sentinel部署
后端
ansurfen2 小时前
耗时一周,我的编程语言 Hulo 新增 Bash 转译和包管理工具
后端·编程语言
库森学长2 小时前
索引失效的场景有哪些?
后端·mysql·面试