有哪些依赖注入方式?

有哪些依赖注入方式? 构造方法注入通过调用类的构造方法推荐用于强依赖没有依赖对象就没法工作。这也是官方推荐的注入方式。好处依赖不可变final修饰、更安全。12345678910ComponentpublicclassUserService {privatefinalUserDao userDao;// Spring 会自动注入 UserDaoAutowiredpublicUserService(UserDao userDao) {this.userDao userDao;}}Setter注入适合可选依赖有无都能运行123456789ComponentpublicclassUserService {privateUserDao userDao;AutowiredpublicvoidsetUserDao(UserDao userDao) {this.userDao userDao;}}字段注入直接在字段上加Autowired。简单但不利于单元测试mock因为会依赖Spring框架二、Spring有哪些自动装配的方式1、什么是自动装配装配把bean之间的依赖关系配置清楚自动装配让Spring容器自己根据规则把依赖对象诸如进去而不是开发者手动写。这样可以减少配置提升开发效率。2、Spring提供了哪几种自动装配类型no默认值不自动装配必须显式依赖XML里面写property)byName根据属性名找到和Bean的id一样的Bean注入byType根据属性的类型找到容器里唯一匹配的Bean注入constructor根据构造方法参数的类型去容器里找匹配的 Bean 注入。【注意】在 Spring Boot 和注解驱动里主要用Autowired默认 byType可结合 Qualifier 指定名字ResourceJDK自带默认 byName找不到再 byTypeInjectJSR-330 标准注解行为类似 Autowired12345678910111213141516171819202122// 接口publicinterfaceUserDao {voidsave();}// 第一个实现Component(mysqlUserDao)publicclassUserDaoMysqlimplementsUserDao {Overridepublicvoidsave() {System.out.println(保存到 MySQL 数据库);}}// 第二个实现Component(oracleUserDao)publicclassUserDaoOracleimplementsUserDao {Overridepublicvoidsave() {System.out.println(保存到 Oracle 数据库);}}三、Spring中Bean的作用域有哪些singleton默认在一个Spring 容器ApplicationContext中只创建一个 Bean 实例。每次getBean()拿到的都是同一个对象。prototype每次调用getBean()都会新建一个对象。request仅 Web 应用中有效每个 HTTP 请求都会创建一个新的 Bean。Bean 生命周期和一次请求绑定请求结束后 Bean 被销毁。session仅 Web 应用中有效每个 HTTP 会话Session只对应一个 Bean。同一个 Session 里的请求共享 Bean不同 Session 用不同 Bean。123ComponentScope(prototype)// 或 request / sessionpublicclassUserService {}Session会话在 Web 里Session 表示用户从打开浏览器访问网站到关闭浏览器/超时退出的这一段交互过程。四、Spring中的单例Bean会存在线程安全问题吗Spring的单例Bean不是天然线程安全的。是否有问题取决于Bean是否有状态。无状态Bean只做方法调用不保存共享数据线程安全有状态Bean持有成员变量并且会被多个线程同时读写线程不安全解决办法改为多例prototype每次请求都新建一个实例--不共享--没有线程安全问题。但这样会失去单例的优势容器管理和性能都会受影响。避免在Bean中设计为无状态方法里只用局部变量使用ThreadLocal 保存状态推荐 ✅给每个线程准备一份独立副本避免了线程之间的数据覆盖。12345678910111213141516ServicepublicclassUserContextService {privateThreadLocalString currentUser newThreadLocal();publicvoidsetUser(String user) {currentUser.set(user);// 每个线程有自己独立的副本}publicString getUser() {returncurrentUser.get();// 取的就是当前线程的值}publicvoidclear() {currentUser.remove();// 防止内存泄漏重要}}五、说说循环依赖1、什么是循环依赖两个或多个Bean互相依赖形成“死循环”只在单例下会出现。如果是prototype的话会无限套娃。那Spring能解决哪些情况两边都是构造器注入不支持无法提前暴露“半成品”对象会直接报错。两边都是setter/字段注入支持Spring创建A会调用构造方法得到一个空对象把A的工厂放到三级缓存。给A注入依赖时发现需要B就去创建B。--创建B的时候发现需要A就去一级、二级缓存找找不到就调用三级缓存的ObjectFactory得到一个“早期A”把这个早期A放进二级缓存然后注入到B。B创建完后返回给A。完成A的初始化。这时候A、B都是成品Bean放进一级缓存。一边构造器、一边setter注入-看情况A构造器、Bsetter若Spring先创建A构造器立刻需要B但B还没创建--报错❌。若Spring先创建BB可以先用setter注入半成品A--✅ 能成功。六、Spring怎么解决循环依赖1️⃣ 一级缓存singletonObjects存放完全创建好的单例 Bean成品。以后再来getBean()直接从这里拿。2️⃣ 二级缓存earlySingletonObjects存放提前曝光的单例 Bean早期引用。这里的 Bean 已经实例化但可能还没注入属性、没初始化。如果别的 Bean 需要可以先用它占个坑后面再补全。3️⃣ 三级缓存singletonFactories存放ObjectFactory对象工厂可以生成早期 Bean。为什么要三级因为有些 Bean 可能需要 AOP 代理必须等真正用到的时候才生成代理对象 → 所以放工厂。 三级缓存的目的保证 Bean能被提前曝光同时还能支持代理增强AOP。七、为什么要三级缓存二级不行吗