# SpringDemo202601 **Repository Path**: nieps/spring-demo202601 ## Basic Information - **Project Name**: SpringDemo202601 - **Description**: spring案例 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-23 - **Last Updated**: 2026-01-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring笔记 早期开发: * jsp ----本质servlet > jsp1.0---- java代码和html混合 > > ~~~jsp > > > <% > String name="张三"; > %> > 名称:<%= name %> > > > ~~~ > > > > jsp2.0 ----jstl > > ~~~jsp > > > > 名称:${name} > > > ~~~ jsp+javabean java类封装你的操作------调用其它类 ~~~java //A类方法 public void save(){ B b =new B(); b.method().... } //B类 默认操作mysql数据库 //新的需求 ---增加redis 做为缓存 -----第一想法修改B的实现 //有新的需求 不去修改 而是扩展 //假如接口 I---声明方法 -----B类的实现 //Redis---实现 ~~~ ![image-20260123102350915](assets/image-20260123102350915.png) 降低偶合度:依赖于抽象,不依赖于具体实现 抽象: * 抽象类 抽象类与子类是 is a的关系 有血缘关系 * 接口 功能的声明 ssh ## Hello示例 MVC M: model 模型层 业务层 ![image-20260123141420389](assets/image-20260123141420389.png) ### 工厂实现类 ![image-20260123142951684](assets/image-20260123142951684.png) ### 实现步骤 1. 新建maven工程 2. 引入依赖 ~~~xml org.springframework spring-context 6.2.15 compile ~~~ 3. 定义java类的实现 (定义业务类: 接口+实现类) 4. 定义配置文件\*.xml 文件 spring配置文件 ~~~xml ~~~ 5. 定义工厂类的实例(读取上面的配置文件 根据配置的 bean 通过反射创建对应类的实例,保存到内存中,使用的时候直接从内存获取) ~~~java public void testApp() { //创建工厂类实例 目的的获取目标类的实例 ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); //从工厂类中获取实现 CustomerService customerService=applicationContext.getBean("customerService",CustomerService.class); //调用方法 customerService.query(); } ~~~ ### spring中bean 的生命周期 (面试题) 1. bean的定义 2. bean的实例化 (创建实例)----调用构造方法 很少定义构造方法 3. 注入依赖 4. 调用初始化方法 只会实例化一次 5. bean的使用 6. 销毁 ### 关于bean 的作用域 (面试题 了解) * singleton 单例 所有bean 在整个应用中只有一个实例 (默认的) * prototype 原型 每次调用创建一个新的对象 (很少用) * request 每一次请求创建一个对象 * session 一个会话创建一个对象 * application 整个应用创建一个对象 ### 依赖注入 的方式 * **set注入** 要给注入的属性提供set方法 才能注入 (常用) * 构造注入 通过构造函数注入 ## 初化化实现 * 属性 init-method="方法名" * 实现接口 InitializingBean ,重写afterPropertiesSet方法 (代码侵入 ) * **注解(推荐使用的)@PostConstruct** (后面介绍) ## 销毁 (释放资源 一般不用) * 属性 destroy-method ="方法名" * 实现接口 DisposableBean ,重写destroy方法 * 注解(推荐使用) @PreDestroy (后面演示) ## autowire自动装配 bean之间的依赖关系 ,交给spring自动管理 自动装配的类型: * default 默认 需要手动注入 通过property 或构造注入 * **byType 根据类型自动装配** 对应注解:@Autowire 默认按类型自动状态 > 1. 当容器发现Bean中的依赖属性,会去容器中查找对应的实例,如果找到了注入 ,找不到默认为null > > 2. 如果同一类型有多个bean,在注入时就会报错:如 > > ~~~ > No qualifying bean of type 'com.by.dao.CustomerDao' available: expected single matching bean but found 2: customerDao,customerDao2 > ~~~ > > 解决办法:通过属性 primary="true", 作用:当有多个可用类型时,优先选择设置该属性的bean * **byName 按名称自动装配** 去查询容器中与要注入的属性名一致的bean ,找到就注入,找不到 不注入 @Resource * constructor 构造注入 必须提供要注入的属性的 构造方法 才可注入(且参数是按照类型注入) * no 不注入 补充: * 泛型 类泛型、方法泛型 类型参数化 * 反射 掌握基本应用 * 可以熟悉元注解 * 设计模式: 单例 工厂(简单工厂、抽象工厂) ## 作业 CRM原型 1. 把整个系统的表设计 (要求使用建模软件: pdmaner powerdesigner) 2. 根据原型 编写mapper ## 元注解 **元注解,本质是注解的注解。** 共有5个: - @Documented – 注解是否将包含在JavaDoc中 - **@Retention – 什么时候使用该注解** - **@Target – 注解用于什么地方** - @Inherited – 是否允许子类继承该注解 - @Repeatable 指定重复注解 (查看Mybatis定义插件时用的注解 ) ### 注解的作用域 @Retention * SOURCE 说明该注解 只存在于源码文件中 * CLASS 说明该注解在源码、字节码文件中都 有 * RUNTIME 说明该注解在源码、字节码文件及运行时都 有 ### @target 说明自定义 的注解能够使用在哪些地方 ## Spring 注解 MVC三层模型: * C 控制层 * M 业务层 * dao数据访问层 ![image-20260126161209478](assets/image-20260126161209478.png) 面试题: @Autowired和@Resource的区别? ## @Resource注解 `@Resource` 注解的包名取决于你使用的 JDK 和相关框架的版本。主要有以下三种情况: #### 📦 1. JDK 8 及更早版本 在这些版本中,`@Resource` 是 Java EE API 的一部分,直接包含在 JDK 中。 - **包名:** `javax.annotation.Resource` #### 📦 2. JDK 9 至 JDK 16 从 JDK 9 开始,Java 实行了模块化(Jigsaw),移除了内置的 Java EE API,因此 `@Resource` 不再包含在 JDK 中。 - **你需要:** 手动添加第三方依赖。 - **包名:** 通常仍然是 `javax.annotation.Resource`,通过引入 `javax.annotation:javax.annotation-api` 依赖来使用。 #### 📦 3. JDK 17 及更高版本 (Jakarta EE 9+) 从 Java EE 转移到 Eclipse 基金会后,项目更名为 Jakarta EE,并从版本 9 开始将所有包名从 `javax.*` 更改为 `jakarta.*`。 - **包名:** `jakarta.annotation.Resource` - **你需要:** 引入 `jakarta.annotation:jakarta.annotation-api` 依赖。 ------ ### 📌 总结 | 环境 | 包名 | 说明 | | :----------- | :---------------------------- | :------------------------------------------- | | **JDK 8** | `javax.annotation.Resource` | JDK 内置,无需额外依赖。 | | **JDK 9-16** | `javax.annotation.Resource` | 需要手动添加 `javax.annotation-api` 依赖。 | | **JDK 17+** | `jakarta.annotation.Resource` | 需要手动添加 `jakarta.annotation-api` 依赖。 | ### 💡 特别提示:Spring 框架版本的影响 如果你在使用 Spring 框架,还需要注意框架版本的兼容性: - **Spring 5.x**:主要支持 `javax.annotation` 包。 - **Spring 6.x**:最低要求 JDK 17,并全面迁移到了 `jakarta.annotation` 包。 ## Spring注解 ![Spring注解](assets/Spring%E6%B3%A8%E8%A7%A3.png) ## AOP 纵向抽取: 抽象类 是对类的 抽象 通过继承达到复用 横向抽取:把不相干的类 共同的业务或逻辑提取出来,在不改变原有代码的情况下,将提取的业务增加到目标类的执行上。 ### 静态代理 * 目标对象(目标类) 要代理 的对象 * 代理 对象(代理类) * 要求目标类与代理类实现相同的接口 ### 动态代理 jdk动态代理与cglib代理 | 对比维度 | JDK 动态代理 | CGLIB 动态代理 | | :----------- | :----------------------------------------------------------- | :----------------------------------------------------------- | | **实现原理** | 基于接口,在运行时动态生成一个实现指定接口的代理类。 | 基于继承,在运行时通过生成被代理类的子类来实现代理。 | | **依赖条件** | 被代理类**必须**实现至少一个接口。 | 被代理类**无需**实现接口,但不能是 `final` 类。 | | **方法限制** | 只能代理接口中定义的方法。 | 可代理被代理类的所有非 `final` 方法,但无法代理 `final` 或 `private` 方法。 | | **底层技术** | 使用 Java 原生的反射机制 (`java.lang.reflect.Proxy`)。 | 使用 ASM 字节码操作框架直接生成字节码。 | | **性能特点** | 初始化开销较低,方法调用通过反射,JDK8+ 后性能已非常接近 CGLIB。 | 初始化生成字节码开销较高,但方法调用性能略好,差距在 JDK8+ 后已很小。 | | **依赖要求** | JDK 原生支持,无额外依赖。 | 需引入 CGLIB/ASM 依赖(Spring-core 已包含)。 | ### 术语 * **切面** 包含**切点**(用于说明在哪些类的哪些方法上使用 增强)和**增强**(要在目标方法上增加的逻辑代码) (切面是一个类,里面定义了很多你要在目标方法上增加的代码) * 关于新增逻辑 三种说法 advice > 1. 增强 > 2. 横切关注点 > 3. 通知 ### 切点表达式 ~~~java execution(返回类型 方法名 (参数) ) ~~~ ## 定时任务 * Timer * 定时线程池 * Spring Task (单体结构) * XXL-JOB 分布式任务调度 ## 作业 定时给用户发邮件,生成一人验证码 ~~~java String code="a..ZA..Z0..9" ~~~ ## event 用户下单:OrderService * 保存订单 * 保存订单明细 * 更新库存... ( 通知库存系统 扣减库存) * 更新会员积分 ~~~java public class OrderSerice{ StockService stock; int save(){ //生成订单 //保存明细 stock.updateStock();//更新库存 } } ~~~ ~~~java public class StockService{ int updateStock(){ } } ~~~ 假如: 库存系统 StockService 核心组件: * 事件 就是个Java类,传递的数据 * 事件发布者 通知相关组件更新数据 * 事件监听器 @EventListener 观察者 ### 实现 #### 定义事件对象 就是组件通信要传递的参数,这里是普通javabean ~~~java public class OrderEvent { int goodsId; int num; } ~~~ #### 主题 (被观察者 生成订单--通知其它业务) ~~~java @Service public class OrderServiceImpl implements OrderService { @Autowired ApplicationContext context; @Override public void save() { System.out.println("保存订单"); System.out.println("保存订单明细 "); //发布事件 context.publishEvent(new OrderEvent(1,3)); System.out.println("------------"); } } ~~~ #### 监听者(观察者 当生成订单时要联动实现数据变化的) ~~~java @Service public class StockServiceImpl implements StockService { @EventListener(value = {OrderEvent.class})//监听OrderEvent事件 @Override public void updateStock(OrderEvent event) { System.out.println("更新库存,商品"+event.getGoodsId()+" 减少数量:"+event.getNum()); } } ~~~ ## 观察者设计模式(Observer Design Pattern) 观察者设计模式是一种**行为型设计模式**,核心思想是定义**对象间一对多的依赖关系**:当一个被观察对象(主题)的状态发生改变时,其所有依赖的观察对象(观察者)会被**自动通知并更新**,实现**状态变化的解耦传递**。 发布订阅 角色: * 观察者-----抽象 * 被观察者----抽象 ## 异步任务 实现条件: * 启用异步任务 @EnableAsync * 定义异步任务方法 @Async * 配置线程池 ~~~java @Bean public ThreadPoolTaskExecutor taskExecutor(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程数 executor.setCorePoolSize(5); //最大线程数 executor.setMaxPoolSize(10); //线程队列最大线程数 executor.setQueueCapacity(20); executor.initialize(); return executor; } ~~~ 应用场景: 通常是耗时操作: * 发送短信 * 发送邮件 * 执行数据库备份 * 导入导出excel文件 ## 登录逻辑 1. 输入用户名 密码 验证码 2. 业务逻辑 > 1. 判断验证码是否正确 正确进入2 > > 2. 根据用户名查询用户,如果没找到,账号不存在 ,找到了 进入3 > > 3. 把**用户提交过来的密码加密** ,然后与数据库加密过的密码比较 相等就是正确 ,否则用户名或密码不正确 > >