Spring官网阅读(二)(依赖注入及方法注入)(2)

简介: Spring官网阅读(二)(依赖注入及方法注入)(2)

方法注入:


我们不完全按照官网顺序进行学习,先看这一小节,对应官网上的位置如下图

微信图片_20221112150416.jpg


为什么需要方法注入:


首先我们思考一个问题,在有了依赖注入的情况下,为什么还需要方法注入这种方式呢?换而言之,方法注入解决了什么问题?

我们来看下面这种场景:

@Component
public class MyService {
  @Autowired
  private LuBanService luBanService;
  public void test(int a){
    luBanService.addAndPrint(a);
  }
}
@Component
// 原型对象
@Scope("prototype")
public class LuBanService {
  int i;
  LuBanService() {
    System.out.println("luBan create ");
  }
  // 每次将当前对象的属性i+a然后打印
  public void addAndPrint(int a) {
    i+=a;
    System.out.println(i);
  }
}
public class Main02 {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
    MyService service = (MyService) ac.getBean("myService");
    service.test(1);
    service.test(2);
    service.test(3);
  }
}

在上面的代码中,我们有两个Bean,MyService为单例的Bean,LuBanService为原型的Bean。我们的本意可能是希望每次都能获取到不同的LuBanService,预期的结果应该打印出:


1,2,3


实际输出:


1

3

6


这个结果说明我们每次调用到的LuBanService是同一个对象。当然,这也很好理解,因为在依赖注入阶段我们就完成了LuBanService的注入,之后我们在调用测试方法时,不会再去进行注入,所以我们一直使用的是同一个对象。


我们可以这么说,原型对象在这种情况下,失去了原型的意义,因为每次都使用的是同一个对象。那么如何解决这个问题呢?只要我每次在使用这个Bean的时候都去重新获取就可以了,那么这个时候我们可以通过方法注入来解决。


通过注入上下文(applicationContext对象)


又分为以下两种方式:


实现org.springframework.context.ApplicationContextAware接口

@Component
public class MyService implements ApplicationContextAware {
  private ApplicationContext applicationContext;
  public void test(int a) {
    LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));
    luBanService.addAndPrint(a);
  }
  @Override
  public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}

直接注入上下文

@Component
public class MyService{
  @Autowired
  private ApplicationContext applicationContext;
  public void test(int a) {
    LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));
    luBanService.addAndPrint(a);
  }
}

通过@LookUp的方式(也分为注解跟XML两种方式,这里只演示注解的)

@Component
public class MyService{
  public void test(int a) {
    LuBanService luBanService = lookUp();
    luBanService.addAndPrint(a);
  }
  // 
  @Lookup
  public LuBanService lookUp(){
    return null;
  }
}

方法注入 之 replace-method


方法注入还有一种方式,即通过replace-method这种形式,没有找到对应的注解,所以这里我们也就用XML的方式测试一下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="myService" class="com.dmz.official.service.MyService">
    <replaced-method replacer="replacer" name="test"/>
  </bean>
  <bean id="replacer" class="com.dmz.official.service.MyReplacer"/>
</beans>
public class MyReplacer implements MethodReplacer {
    @Override
   public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("替代"+obj+"中的方法,方法名称:"+method.getName());
        System.out.println("执行新方法中的逻辑");
        return null;
    }
}
public class MyService{
    public void test(int a) {
        System.out.println(a);
    }
}
public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cc =
            new ClassPathXmlApplicationContext("application.xml");
        MyService myService = ((MyService) cc.getBean("myService"));
        myService.test(1);
    }
}

执行结果:

替代com.dmz.official.service.MyService$$EnhancerBySpringCGLIB$$61c14242@63e31ee中的方法,方法名称:test
执行新方法中的逻辑

这里需要注意一点:

我在测试replace-method这种方法注入的方式时,受动态代理的影响,一直想将执行我们被替代的方法。用代码体现如下:

public class MyReplacer implements MethodReplacer {
  @Override
  public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
//    System.out.println("替代"+obj+"中的方法,方法名称:"+method.getName());
//    System.out.println("执行新方法中的逻辑");
    method.invoke(obj,args);
    return null;
  }
}

但是,这段代码是无法执行的,会报栈内存溢出。因为obj是我们的代理对象,method.invoke(obj,args)执行时会进入方法调用的死循环。最终我也没有找到一种合适的方式来执行被替代的方法。目前看来这可能也是Spring的设计,所以我们使用replace-method的场景应该是想完全替代某种方法的执行逻辑,而不是像AOP那样更多的用于在方法的执行前后等时机完成某些逻辑。


依赖注入跟方法注入的总结:


  • 我们首先要明确一点,什么是依赖(Dependencies)?来看官网中的一段话:

微信图片_20221112151143.jpg

可以说,一个对象的依赖就是它自身的属性,Spring中的依赖注入就是属性注入。


  • 我们知道一个对象由两部分组成:属性+行为(方法),可以说Spring通过属性注入+方法注入的方式掌控的整个bean。
  • 属性注入跟方法注入都是Spring提供给我们用来处理Bean之间协作关系的手段
  • 属性注入有两种方式:构造函数,Setter方法。
  • 方法注入(LookUp Method跟Replace Method)需要依赖动态代理完成
  • 方法注入对属性注入进行了一定程度上的补充,因为属性注入的情况下,原型对象可能会失去原型的意义,见:为什么需要方法注入

画图如下:

微信图片_20221112151228.jpg


相关文章
|
12天前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
|
16天前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
17 1
|
2月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
40 0
|
2月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
51 0
|
1月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
10天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
55 2
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
10天前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
22 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
3月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
3月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
109 0