首页 > 其他分享 >Spring最全的依赖注入方式

Spring最全的依赖注入方式

时间:2023-09-05 12:22:20浏览次数:44  
标签:applicationContext 依赖 Spring 最全 Bean 注解 public 注入

前言

Spring 正如其名字,给开发者带来了春天,Spring 是为解决企业级应用开发的复杂性而设计的一款框架,其设计理念就是:简化开发。

Spring 框架中最核心思想就是:

  • IOC(控制反转):即转移创建对象的控制权,将创建对象的控制权从开发者转移到了 Spring 框架。
  • AOP(切面编程):将公共行为(如记录日志,权限校验等)封装到可重用的模块中,而使原本的模块内只需关注自身的个性化行为。

本文,将主要介绍 SpringIOC 的依赖注入,

控制反转 IOC

IOC 本身而言,其并不是什么新技术,只是一种思想理念。IOC 的核心就是原先创建一个对象,我们需要自己直接通过 new 来创建,而 IOC 就相当于有人帮们创建好了对象,需要使用的时候直接去拿就行,IOC 主要有两种实现方式:

  • DL(Dependency Lookup):依赖查找。

这种就是说容器帮我们创建好了对象,我们需要使用的时候自己再主动去容器中查找,如:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/application-context.xml");
Object bean = applicationContext.getBean("object");
  • DI(Dependency Inject):依赖注入。

依赖注入相比较依赖查找又是一种优化,也就是我们不需要自己去查找,只需要告诉容器当前需要注入的对象,容器就会自动将创建好的对象进行注入(赋值)。

依赖注入 DI

通过 xml 的注入方式我们不做讨论,在这里主要讨论基于注解的注入方式,基于注解的常规注入方式通常有三种:

  • 基于属性注入
  • 基于 setter 方法注入
  • 基于构造器注入

三种常规注入方式

接下来就让我们分别介绍一下三种常规的注入方式。

属性注入

通过属性注入的方式非常常用,这个应该是大家比较熟悉的一种方式:

@Service
public class UserService {
    @Autowired
    private Wolf1Bean wolf1Bean;//通过属性注入
}
setter 方法注入

除了通过属性注入,通过 setter 方法也可以实现注入:

@Service
public class UserService {
    private Wolf3Bean wolf3Bean;
    
    @Autowired  //通过setter方法实现注入
    public void setWolf3Bean(Wolf3Bean wolf3Bean) {
        this.wolf3Bean = wolf3Bean;
    }
}
构造器注入

当两个类属于强关联时,我们也可以通过构造器的方式来实现注入:

@Service
public class UserService {
     private Wolf2Bean wolf2Bean;
    
     @Autowired //通过构造器注入
    public UserService(Wolf2Bean wolf2Bean) {
        this.wolf2Bean = wolf2Bean;
    }
}
接口注入

在上面的三种常规注入方式中,假如我们想要注入一个接口,而当前接口又有多个实现类,那么这时候就会报错,因为 Spring 无法知道到底应该注入哪一个实现类。比如我们上面的三个类全部实现同一个接口 IWolf,那么这时候直接使用常规的,不带任何注解元数据的注入方式来注入接口 IWolf

@Autowired
private IWolf iWolf;

此时启动服务就会报错:

img

这个就是说本来应该注入一个类,但是 Spring 找到了三个,所以没法确认到底应该用哪一个。这个问题如何解决呢?

解决思路主要有以下 5 种:

1. 通过配置文件和 @ConditionalOnProperty 注解实现

通过 @ConditionalOnProperty 注解可以结合配置文件来实现唯一注入。下面示例就是说如果配置文件中配置了 lonely.wolf=test1,那么就会将 Wolf1Bean 初始化到容器,此时因为其他实现类不满足条件,所以不会被初始化到 IOC 容器,所以就可以正常注入接口:

@Component
@ConditionalOnProperty(name = "lonely.wolf",havingValue = "test1")
public class Wolf1Bean implements IWolf{
}

当然,这种配置方式,编译器可能还是会提示有多个 Bean,但是只要我们确保每个实现类的条件不一致,就可以正常使用。

2. 通过其他 @Condition 条件注解

除了上面的配置文件条件,还可以通过其他类似的条件注解,如:

  • @ConditionalOnBean:当存在某一个 Bean 时,初始化此类到容器。
  • @ConditionalOnClass:当存在某一个类时,初始化此类的容器。
  • @ConditionalOnMissingBean:当不存在某一个 Bean 时,初始化此类到容器。
  • @ConditionalOnMissingClass:当不存在某一个类时,初始化此类到容器。
  • ...

类似这种实现方式也可以非常灵活的实现动态化配置。

不过上面介绍的这些方法似乎每次都只能固定注入一个实现类,那么如果我们就是想多个类同时注入,不同的场景可以动态切换而又不需要重启或者修改配置文件,又该如何实现呢?

3.通过 @Resource 注解动态获取

如果不想手动获取,我们也可以通过 @Resource 注解的形式动态指定 BeanName 来获取:

@Component
public class InterfaceInject {
    @Resource(name = "wolf1Bean")
    private IWolf iWolf;
}

如上所示则只会注入 BeanNamewolf1Bean 的实现类。

4.通过集合注入

除了指定 Bean 的方式注入,我们也可以通过集合的方式一次性注入接口的所有实现类:

@Component
public class InterfaceInject {
    @Autowired
    List<IWolf> list;

    @Autowired
    private Map<String,IWolf> map;
}

上面的两种形式都会将 IWolf 中所有的实现类注入集合中。如果使用的是 List 集合,那么我们可以取出来再通过 instanceof 关键字来判定类型;而通过 Map 集合注入的话,Spring 会将 Bean 的名称(默认类名首字母小写)作为 key 来存储,这样我们就可以在需要的时候动态获取自己想要的实现类。

5. @Primary 注解实现默认注入

除了上面的几种方式,我们还可以在其中某一个实现类上加上 @Primary 注解来表示当有多个 Bean 满足条件时,优先注入当前带有 @Primary 注解的 Bean

@Component
@Primary
public class Wolf1Bean implements IWolf{
}

通过这种方式,Spring 就会默认注入 wolf1Bean,而同时我们仍然可以通过上下文手动获取其他实现类,因为其他实现类也存在容器中。

手动获取 Bean 的几种方式

Spring 项目中,手动获取 Bean 需要通过 ApplicationContext 对象,这时候可以通过以下 5 种方式进行获取:

1.直接注入

最简单的一种方法就是通过直接注入的方式获取 ApplicationContext 对象,然后就可以通过 ApplicationContext 对象获取 Bean

@Component
public class InterfaceInject {
    @Autowired
    private ApplicationContext applicationContext;//注入

    public Object getBean(){
        return applicationContext.getBean("wolf1Bean");//获取bean
    }
}
2.通过 ApplicationContextAware 接口获取

通过实现 ApplicationContextAware 接口来获取 ApplicationContext 对象,从而获取 Bean。需要注意的是,实现 ApplicationContextAware 接口的类也需要加上注解,以便交给 Spring 统一管理(这种方式也是项目中使用比较多的一种方式):

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 通过名称获取bean
     */
    public static <T>T getBeanByName(String beanName){
        return (T) applicationContext.getBean(beanName);
    }

    /**
     * 通过类型获取bean
     */
    public static <T>T getBeanByType(Class<T> clazz){
        return (T) applicationContext.getBean(clazz);
    }
}

封装之后,我们就可以直接调用对应的方法获取 Bean 了:

Wolf2Bean wolf2Bean = SpringContextUtil.getBeanByName("wolf2Bean");
Wolf3Bean wolf3Bean = SpringContextUtil.getBeanByType(Wolf3Bean.class);
3. 通过 ApplicationObjectSupport 和 WebApplicationObjectSupport 获取

这两个对象中,WebApplicationObjectSupport 继承了 ApplicationObjectSupport,所以并无实质的区别。

同样的,下面这个工具类也需要增加注解,以便交由 Spring 进行统一管理:

@Component
public class SpringUtil extends /*WebApplicationObjectSupport*/ ApplicationObjectSupport {
    private static ApplicationContext applicationContext = null;

    public static <T>T getBean(String beanName){
        return (T) applicationContext.getBean(beanName);
    }

    @PostConstruct
    public void init(){
        applicationContext = super.getApplicationContext();
    }
}

有了工具类,在方法中就可以直接调用了:

@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {
    @GetMapping("/bean3")
    public Object getBean3(){
        Wolf1Bean wolf1Bean = SpringUtil.getBean("wolf1Bean");
        return wolf1Bean.toString();
    }
}
4.通过 HttpServletRequest 获取

通过 HttpServletRequest 对象,再结合 Spring 自身提供的工具类 WebApplicationContextUtils 也可以获取到 ApplicationContext 对象,而 HttpServletRequest 对象可以主动获取(如下 getBean2 方法),也可以被动获取(如下 getBean1 方法):

@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {

    @GetMapping("/bean1")
    public Object getBean1(HttpServletRequest request){
        //直接通过方法中的HttpServletRequest对象
        ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        Wolf1Bean wolf1Bean = (Wolf1Bean)applicationContext.getBean("wolf1Bean");

        return wolf1Bean.toString();
    }

    @GetMapping("/bean2")
    public Object getBean2(){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//手动获取request对象
        ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());

        Wolf2Bean wolf2Bean = (Wolf2Bean)applicationContext.getBean("wolf2Bean");
        return wolf2Bean.toString();
    }
}
5. 其他方式获取

当然,除了上面提到的方法,我们也可以使用最开始提到的 DL 中代码示例去手动 new 一个 ApplicationContext 对象,但是这样就意味着重新初始化了一次,所以是不建议这么去做,但是在写单元测试的时候这种方式是比较适合的。

@Autowrite 和 @Resource 区别

上面我们看到了,注入一个 Bean 可以通过 @Autowrite,也可以通过 @Resource 注解来注入,这两个注解有什么区别呢?

  • @Autowrite:通过类型去注入,可以用于构造器和参数注入。当我们注入接口时,其所有的实现类都属于同一个类型,所以就没办法知道选择哪一个实现类来注入。
  • @Resource:默认通过名字注入,不能用于构造器和参数注入。如果通过名字找不到唯一的 Bean,则会通过类型去查找。如下可以通过指定 name 或者 type 来确定唯一的实现:
@Resource(name = "wolf2Bean",type = Wolf2Bean.class)
 private IWolf iWolf;

@Qualifier 注解是用来标识合格者,当 @Autowrite@Qualifier 一起使用时,就相当于是通过名字来确定唯一:

@Qualifier("wolf1Bean")
@Autowired
private IWolf iWolf;

那可能有人就会说,我直接用 @Resource 就好了,何必用两个注解结合那么麻烦,这么一说似乎显得 @Qualifier 注解有点多余?

@Qualifier 注解是多余的吗

我们先看下面声明 Bean 的场景,这里通过一个方法来声明一个 Bean (MyElement),而且方法中的参数又有 Wolf1Bean 对象,那么这时候 Spring 会帮我们自动注入 Wolf1Bean

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(Wolf1Bean wolf1Bean){
        return new MyElement();
    }
}

然而如果说我们把上面的代码稍微改一下,把参数改成一个接口,而接口又有多个实现类,这时候就会报错了:

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(IWolf iWolf){//此时因为IWolf接口有多个实现类,会报错
        return new MyElement();
    }
}

@Resource 注解又是不能用在参数中,所以这时候就需要使用 @Qualifier 注解来确认唯一实现了(比如在配置多数据源的时候就经常使用 @Qualifier 注解来实现):

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(@Qualifier("wolf1Bean") IWolf iWolf){
        return new MyElement();
    }
}

总结

本文主要讲述了如何在 Spring 中使用灵活的方式来实现各种场景的注入方式,并且着重介绍了当一个接口有多个实现类时应该如何注入的问题,最后也介绍了常用几个注入注解的区别,通过本文,相信大家对如何使用 Spring 中的依赖注入会更加的熟悉。

标签:applicationContext,依赖,Spring,最全,Bean,注解,public,注入
From: https://www.cnblogs.com/binbingg/p/17679285.html

相关文章

  • AspNetCore依赖注入在控制台的应用
    ①安装包:Microsoft.Extensions.DependencyInjection②应用主方法里面://接口IService,实现:Service,方法:Execute();varserviceProvider=newServiceCollection().AddSingleton<IService,Service>().BuildServiceProvider();var......
  • 软件测试|快速、可靠的JavaScript依赖管理工具——yarn
    简介Yarn是一个由Facebook于2016年推出的JavaScript软件包管理器。它的目标是解决npm(Node.js的默认软件包管理器)在性能和可靠性方面的一些问题。Yarn旨在提供更快、更安全、更稳定的依赖项安装过程,使JavaScript开发人员能够更轻松地管理和构建项目。本文将详细介绍Yarn的特点、优势......
  • springboot启动错误: 找不到或无法加载主类
    当在eclipse启动springboot项目时出现问题:springboot错误:找不到或无法加载主类解决办法:1,通过cmd命令行,进入项目目录进行,mvncleaninstall进行编译项目install后,再到eclipse上选中项目按F5刷新项目。再运行即可问题解决。2,在eclipse上选中项目 点击clean清理项目再运行问......
  • 基于springboot的美食系统
    系统使用技术:springboot前端技术:bootstrap、vue、css、js等开发工具:idea数据库:mysql5.7项目介绍:该系统框架后端采用springboot框架,前端使用bootstrap、css、js等,系统界面美观,功能全面。系统主要分为2个角色:用户和管理员,主要功能为:菜谱管理、公告管理、资讯管理,收藏菜谱并评论,趣味答......
  • 基于springboot+LayUI的校园信息发布平台
    系统使用技术:springboot前端技术:LayUI、css、js等开发工具:idea数据库:mysql5.7项目介绍:本系统是一个类似于论坛的信息发布平台,采用MVC三层架构和rest风格URL,后端使用SpringBoot框架,前端采用LayUI框架+freemarker动态模板生成,数据库使用mysql,开发工具采用idea,jdk采用1.8。系统实现了......
  • SpringBoot启动o.s.b.d.LoggingFailure… 错误解决方法
    把@SpringBootApplication改成@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})即:......
  • 基于springboot的简易聊天系统
    系统使用技术:springboot前端技术:thymeleaf、js、css、layui开发工具:idea数据库:mysql5.7项目介绍:该系统基于springboot,mysql数据库,页面美观,可以在此基础上进行二次开发。下面我们来看看部分相关功能。系统登录:聊天可以选择某个好友进行聊天,聊天内容可以是文字、表情、图片,或者带有格......
  • 基于springboot的多人聊天系统
    系统使用技术:springboot前端技术:css、js等开发工具:idea数据库:mysql5.7项目介绍:该系统基于SpringBoot,采用Mysql数据库,通信协议WebSocket,使用Maven管理项目,功能点满足毕设/课设需要,也可根据需要在原有基础上二次开发。主要功能有用户模块:用户注册、用户登录,好友模块:添加好友、好友申......
  • 基于springboot的通知反馈系统
    博主主页:猫头鹰源码博主简介:Java领域优质创作者、博客专家、公司架构师、全网粉丝5万+、专注Java技术领域和毕业设计项目实战主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询项目介绍: 该系统创作于2022年4月,包含详细数据库设计。基于springboot......
  • 基于springboot的在线心理测评系统
    项目介绍:本系统采用springboot框架,数据层采用mybatis,数据库使用mysql,功能包含前台和后台,比如心理文章查看、留言、测评、公告等,后台维护所有信息,具体功能实现可以建议看下方的演示视频,系统适合于基础一般的同学使用。项目技术:后端:springboot、mybatis前端:layui、js、css、ajax,thym......