
SpringIoC容器创建容器创建一个配置类 怎么加载这些分散的配置类实例化AnnotationConfigApplicationContext 落后方式使用 XML 配置文件 关闭容器close()是主动、手动调用的方法。你必须在代码里明确地调用它容器才会关闭。registerShutdownHook()是被动、自动调用的机制。你不需要手动调用它它会注册一个“钩子”当 JVM (Java 虚拟机) 即将关闭时这个钩子会被自动触发从而关闭容器。使用注解配置IoC容器 每个Bean通过Component和Autowired注入SpringBootApplication注解内部就包含了一个ComponentScan它默认会扫描启动类所在的当前包及其所有子孙包将所有分散的配置类和组件一网打尽DI 依赖注入IoC思想的具体实现形式主要包含对象注入和值注入Autowired注解默认是按照引用类型进行自动装配的去IOC容器中找某个类型的对象然后完成注入操作Resource 默认按名称By Name注入隐式构造器注入如果一个类只有一个构造方法那么 Spring 默认就会使用这个构造方法进行依赖注入不需要你在上面写Autowired可用Autowired(requiredfalse)允许可选注入存在相同类型bean怎么办 结合Qualifier指定bean的ID bean的默认ID是将类名的首字母小写 或者可以起别名Bean的初始化和清理方法上标记PostConstruct和PreDestroy自动装配自动装配的目的就是让当前模块和包, 在极少量配置的情况下注入第三方组件和包的 Bean。加载配置类的三种思路通过componentScan 指定组件扫描范围默认的范围失效Import 导入普通类-导入配置类-导入选择器ImportSelector接口实现类ImportSelector是 Spring框架提供的一个接口。当一个Configuration类通过Import注解导入了一个ImportSelector的实现类时Spring 容器在初始化时会执行这个实现类并将其返回的类名数组所对应的类都加载为 Bean。第三方依赖自定义的 EnableXxxxx注解用第三方帮你封装的原理 它的逻辑非常复杂但核心思想是EnableAuto Configuration通过 Spring Factories 机制从所有第三方 JAR 包中发现候选配置类 → 一系列Conditional条件筛选 → 递归解析 Import/Bean 并注册 BeanDefinition→ 最终按依赖顺序实例化并注入所有单例 Bean属性绑定解决 外部配置数据 到 Bean 属性 的映射ConfigurationProperties是面向对象批量的绑定。 将配置文件如application.yml中的属性批量绑定到 Java 类的字段上替代难以复用和管理的Value注解PropertySource去哪找数据它的职责是把非默认的配置文件比如你自己建了一个custom-pay.properties加载到 Spring 的Environment环境变量池中。ConfigurationProperties怎么用数据它的职责是拿着一个前缀比如pay.wechat去Environment池子里把对应的数据捞出来塞进当前 Java 对象的字段里。Spring Boot会自动加载application.properties或application.yml文件。通常不需要显式使用PropertySource为什么Value不需要写 Setter 方法就能注入而ConfigurationProperties必须写 Setter 方法因为它们底层操作内存的方式完全不同Value的暴力美学Field 注入它底层直接拿到类的Field对象通过调用field.setAccessible(true)打破 Java 的封装性然后直接向内存地址里塞值。ConfigurationProperties的优雅规范JavaBeans 内省它遵循了标准的 JavaBeans 规范。绑定器底层使用了java.beans.Introspector来分析你的类。它会去寻找名为setMaxReplyLength(int value)的公开方法。重点如果你只写了属性没生成 Setter 方法或者没加 Lombok 的DataSpring Boot 的绑定器即使在application.yml里找到了200也会因为找不到 Setter 方法而默默放弃绑定。最终你的变量依然是硬编码的100。松散绑定和参数校验松散绑定智能命名适配核心点打通了 Java 开发者与运维人员的命名习惯差异。参数校验 (Validation)启动期的安全卫士核心点践行“快速失败 (Fail-Fast)”原则。与其让系统带着错误的配置带病运行、半夜崩溃不如在启动时就直接报错停机。条件装配beanConditionalBean管理Scope(scopeName)定义 Spring Bean 的作用域即 Bean 实例的生命周期和可见范围。常用的作用域包括singleton: IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例prototype: 每次获取都会创建一个新的 bean 实例。bean的生命周期默认singleton的bean在容器启动时被创建可以使用Lazy注解来延迟初始化PostConstruct和PreDestroy注意的是destroy只有在容器关闭的时候才会执行第三方的类/Configuration这些类都是在jar包中我们没有办法在类上面添加注解 怎么办Bean标记在方法上并且这个方法必须位于一个Configuration类中声明返回值就是我想要注册到容器中的一个 Bean 实例。另一个使用场景对象的创建过程非常复杂需要一些前置逻辑判断如果说Component/Service是让 Spring 自动去“抓取”你自己写的代码那么ConfigurationBean就是让你手动去“组装”那些你无法控制、或者极其复杂的对象然后再塞给 SpringAOPAOP 提供了底层运行机制自定义注解精准标记了目标通知类封装了规则而通知则是具体执行的动作AOP能够将那些与业务无关却为业务模块所共同调用的逻辑或责任例如事务处理、日志管理、权限控制等封装起来降低模块间的耦合度通知类(Aspect Class / 切面类) 和通知通知就是将共性功能抽取出来后形成的方法 是具体的拦截动作五大通知类型设置当前通知方法与切入点之间的绑定关系Before前置通知方法执行前干活例如检查参数、校验 TokenAround环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行其他通知不需要考虑目标方法执行JoinPoint 与 ProceedingJoinPoint 有区别JoinPoint抽象了连接点它代表了当前正在被拦截的那个方法及其所有的上下文信息。spring自动把joinPoint对象传给你的通知方法了你的通知代码就获得了当前被拦截方法的所有元数据如目标类名、方法名、方法参数等切入点表达式作用是筛选连接点切入点PointCut注解该注解的作用是将公共的切入点表达式抽取出来像一个变量名一样可以复用execution指示器 最常用基于方法的签名结构*匹配任意符号常用..匹配多个连续的任意符号常用匹配子类类型annotation基于自定义注解的方式来匹配切入点方法。灵活 适合做特定业务场景下的定制化拦截。通知的执行顺序不同的切面类当中默认情况下通知的执行顺序是与切面类的类名字母排序是有关系的可以在切面类上面加上Order注解开启代理在原生 Spring 中必须通过EnableAspectJAutoProxy开启 AOP 代理。但在 Spring Boot 中只要引入了spring-boot-starter-aop依赖自动装配机制会自动配置AopAutoConfiguration默认开启代理功能所以我们只需要在切面类上加上Aspect和Component即可生效。AOP原理SpringAOP的本质或者可以说底层实现是通过代理模式/动态代理 当一个 Bean 被 AOP 切入即它的某些方法匹配了某个切入点那么 Spring IoC 容器中最终存储和管理的确实是这个 Bean 的代理 (Proxy) 对象spring事务Transactional作用出现异常就会进行事务的回滚操作 位置业务层的方法、类、接口影响范围不同在Spring的事务管理中默认只有运行时异常 RuntimeException才会回滚。如果还需要回滚指定类型的异常可以通过rollbackFor属性来指定。propagation 控制事务传播行为常见值REQUIRED默认值加入或者创建如果当前已存在事务即调用它的外部方法已经开启了事务那么当前方法就会加入到这个已存在的事务中成为这个大事务的一部分。如果当前没有事务那么Spring就会为当前方法创建一个新的事务。默认的是“命运共同体”一损俱损REQUIRES_NEW 总是新的在配置类开启注解式事务驱动 EnableTransactionManagement spring中需要手动创建 TransactionManager Bean, 并关联 DataSource原理当你在一个方法上加上Transactional时Spring 会为这个类生成一个代理对象Proxy。当你通过代理对象调用这个方法时Spring 的事务拦截器TransactionInterceptor会介入偷偷帮你做以下几件事拦截请求拿到数据库连接。开启事务执行connection.setAutoCommit(false)。放行调用你写的实际业务代码目标对象的方法。提交/回滚如果业务代码正常执行完拦截器执行connection.commit()。如果业务代码抛出异常拦截器执行connection.rollback()。事务失效事务方法非 public 修饰 对于 CGLIB它无法代理非 public 方法自调用在同个类中调用事务方法原因自调用时this引用指向原始对象而非代理对象导致 AOP 增强逻辑被绕过在“同类内部调用”这个场景下失效原理和Async完全一模一样都是因为绕过了代理对象。异常类型不被捕获或者在 try-catch 中吞掉异常原因Spring 事务默认只在抛出未检查异常即RuntimeException和Error时回滚事务传播行为设置不当在非 Spring 管理的 Bean 中使用带事务的方法父 调用 异步方法子同一个方法同时加Transactional和Async事务可以生效但不推荐原因是在 Spring 默认的 AOP 优先级中Async的切面优先级高于Transactional。异步AsyncSpring 的Async注解是通过动态代理实现的。正常情况当你从外部类 A调用类 B 的异步方法时你持有的实际上是类 B 的代理对象Proxy。调用时代理对象会拦截请求将其提交给线程池执行然后立即返回。同类调用情况当你直接在类 B 的内部方法methodA()中调用this.methodB()异步方法时你是通过this引用调用的。this指向的是**目标对象Target**本身而不是 Spring 创建的代理对象。结论绕过了代理对象Spring 就无法拦截调用异步逻辑也就不会生效方法会在当前线程同步执行。SpringMVC控制层注解RequestMapping设置当前controller方法请求访问路径若使用method属性定义请求方式重复性太高 简化 RESTul请求动作映射 GetMapping响应类注解 RestController Controller ResponseBody参数接收接收json数据RequestBody修饰方法形参JSON数据的键名与方法形参对象的属性名相同 才能自动映射或者JsonProperty手动映射也可全局统一配置命名策略 读取请求体 转json为对象对于非json格式数据从 URL 的查询参数 (?keyvalue)中获取 RequestParam(“查询参数”)注解将请求参数绑定给方法形参如果请求参数名与形参变量名相同可以省略RequestParam 对于集合类型默认情况下不会自动绑定请求参数必须通RequestParam 显式声明对于日期类型 DateTimeFormat 可以统一格式对于路径参数 PathVariable/users/{id} 使用{参数名称}描述路径参数只能提取非 JSON 类型的简单数据全局异常处理器RestControllerAdvice //表示当前类为REST风格的异常处理器 ControllerAdvice ResponseBody处理返回结果给前端ExceptionHandler //方法上面加。value属性指定捕获哪种类型的异常进行处理tip 留个ex.printStackTrace 不然什么错误都看不到了原理所有从 Controller 抛出的异常都会被DispatcherServlet的内置try-catch机制捕获 无需throws抛出 RuntimeException非受检异常Java 语法不要求为其添加 throws 声明拦截器 Interceptor定义拦截器 实现HandlerInterceptor接口拦截器是Spring框架中提供的需要交给IOC容器管理。Component注册配置拦截器 创建一个配置类实现WebMvcConfigurer接口并重写addInterceptors方法返回个拦截器postHandle在 Controller 执行完、视图渲染之前被调用主要用于修改ModelAndView的场景。如果 Controller 里直接写回 JSON这个阶段一般没什么可做的afterCompletion在整个请求完成之后被调用且无论是否出现异常都会执行对于那些已经通过preHandle的拦截器适合做清理/记录/异常处理/释放 ThreadLocal 等收尾工作。拦截器参数HttpServletRequest request:请求对象HttpServletReponse response:响应对象handler:被调用的处理器对象本质上是一个方法对象对反射中的Method对象进行了再包装Mybatis封装了jdbc操作原理现在我们把这些底层零件全部拼装起来一窥 MyBatis 的全貌项目启动装载期MyBatis 读取 XML把每一个select标签封装成重型的MappedStatement对象并以接口方法全限定名为 Key安全地塞进Configuration.StrictMap这个改造过的 HashMap 里。代码调用拦截期你在 Service 里调用userMapper.getUserById(1)。动态代理类拦截到调用拼接出字符串com.example.mapper.UserMapper.getUserById。查字典寻址期代理类拿着这个字符串去StrictMap里get拿到了对应的MappedStatement。引擎点火执行期代理类把你的参数1喂给MappedStatement里的sqlSource动态生成了带?的原生 SQL。落地反馈映射期最后调用 JDBC 的PreparedStatement执行查询拿着结果交给resultMaps翻译成 Java 对象返回给你。双向映射的本质输入参数的映射 (ParameterMapping)核心是 OGNL通过反射读取 Java 对象属性设置到 PreparedStatement 的参数中。出参映射 (ResultMapping)核心是通过反射调用 Java 对象的setter方法或直接对字段赋值将 ResultSet 的数据填充进去。spring 整合mybatis导入整合需要的jar包 使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息方法中有一个参数为dataSource 自动注入了 使用MapperScannerConfigurer加载Dao接口创建代理对象保存到IOC容器中Mapper注解MyBatis 对DAO 的实现方式程序运行时MyBatis用动态代理技术为该接口创建一个实现类对象并交给IOC容器管理tips IDEA可以配置SQL提醒、Mybatis日志输出参数占位符号#{?}需要传递多个参数的时候可自动封装到一个对象中但#{…}必须和对象属性同名 可全局配置 下划线_到驼峰命名的转换 注意sql语句的别名resultType(自动结果映射忽略大小写)无法满足需求时就需要使用resultMap标签进行显式、手动的映射只要表里有主键或者能唯一标识一行数据的业务键在写resultMap时第一个属性一定要用id标签映射它剩下的普通字段再按顺序用result映射。${}字符串拼接底层相当于直接把变量的值拼接到 SQL 字符串中如Statement。通常只在无法使用占位符的场景下使用比如动态传入表名、列名或ORDER BY的排序字段使用时必须由后端严格校验数据来源防止注入风险。Options 主键返回前者表示使用数据库生成的主键 后者表示把主键复制给对象的哪个属性多参数精确匹配 ParamUserfindByUsernameAndPassword(Param(username)Stringusername,Param(password)Stringpassword);//使用 Param(xyz) 就是给每个参数起一个明确的名字这里是 username 和 password以便在 XML 文件中通过 #{xyz} 来引用它们。springboot官方骨架可以省略Param注解 //其实就是这种情况下编译后字节码文件保留了形参名为什么表面上看觉得#{}里面写上形参名就行了”那为什么还需要Param呢 A在 Java 里方法参数名在编译之后可能会丢失(企业级替代方案) 传入实体类或 DTO 此时不需要 ParamMyBatis 会自动通过 getter 方法读取对象的属性匹配 XML 中的 test 条件。XML映射配置通用配置结构网上搜索即可复杂的SQL功能使用XML来配置映射语句namespace属性为mapper接口的全限定名复制引用id与Mapper接口中的方法名一致并保持resultType一致单条记录所封装的类型XML映射文件位置在src下同路同名或者手动指定位置动态SQLiftrue则拼接SQL。where根据查询条件来生成where关键字并会自动去除条件前面多余的and或or。 给每一个if条件后面都加上and就完事了e.namelikeconcat(%,#{name},%)预编译机制PreparedStatement定下了一个死规矩SQL指令的结构骨架和数据是两回事。骨架在第一步编译后就固定了第二步传入的数据永远只能是数据绝对不能改变SQL的结构和含义。LIKE %?%中%失去通配符含义正是这个安全机制的直接体现。而concat()函数之所以能工作是因为它本身就是SQL“骨架”的一部分数据库在第一步编译时就知道要去执行一个拼接函数这是计划内的工作。foreach遍历集合set标签用来替换set关键字同时可以自动去除掉更新字段后面多余的逗号PageHelperPageHelper实现分页查询时SQL语句的结尾一定一定一定不要加分号( 因为PageHelper要帮你在sql语句上拼接上limit啊PageHelper只会对紧跟在其后的第一条SQL语句进行分页处理。 页码输入负数怎么办 在PageHelper中我们可以通过合理化参数配置来解决这个问题。直接在application.yml中引入reasonable配置Mybatis-plus手动引入依赖然后dao层的mapper接口extends baseMapper注意需要配置mp主键auto 使用数据库的主键 不然就是雪花算法mp条件查询LambdaQueryWrapper 是 MyBatis-Plus 提供的一种查询构造器用于构建 SQL 查询条件。专门通过 Lambda 表达式 的方式来引用实体类的字段从而避免硬编码 字段名提高代码的可读性和类型安全性ServiceImplM, T是 MyBatis-Plus 提供的一个实现了IServiceT接口的通用 Service 实现类 理解为一个“开箱即用”的服务层模板ServiceImpl 源码 通过依赖注入调用 BaseMapper 的方法ServiceImpl 提供业务层面的封装BaseMapper 负责底层 SQL 执行Maven仓库 本地 中央 私人 依赖管理 pom.xml 中dependencyexclusionslifecycle clean package 等 Maven 插件可用于扩展 Maven 的功能 Maven Wrapper 是一个用于管理和使用 Maven 的工具它允许在没有预先安装 Maven 的情况下运行和构建 Maven 项目多模块管理继承和聚合统一管理依赖版本在maven中可以在父工程的pom文件中通过dependencyManagement来统一管理依赖版本。只是管理不引入 也可以通过自定义属性及属性引用的形式Maven打包方式jar普通模块打包springboot项目基本都是jar包内嵌tomcat运行war普通web程序打包需要部署在外部的tomcat服务器中运行pom父工程或聚合工程该模块不写代码仅进行依赖管理聚合将多个依赖关系复杂的模块组织成整体同时进行项目的构建。 聚合工程一个不具有业务功能的“空”工程有且仅有一个pom文件 【PS一般来说继承关系中的父工程与聚合关系中的聚合工程是同一个】 作用快速构建项目原文地址SSMMaven本文持续更新最新内容及相关资料请访问原文。