Spring自动装配之方法、构造器位置的自动注入操作

作者:金一白 时间:2021-11-30 23:28:40 

Spring自动装配之方法、构造器位置的自动注入

1. 注解定义

@Autowired的定义信息如下:


@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

/**
 * Declares whether the annotated dependency is required.
 * <p>Defaults to {@code true}.
 */
boolean required() default true;

}

可以看出,@Autowired注解可以定义在构造器上,方法上,方法参数上,字段上,还有自定义注解上;

2. 注解使用

2.1 定义在造器上


@Autowired //定义在构造器方法上
public UserService(UserDao userDao) {
this.userDao = userDao;
}

或者


// 定义在构造器参数上
public UserService(@Autowired UserDao userDao) {
this.userDao = userDao;
}

特别注意,当一个类只有一个有参构造器,且该构造器不一定需要是public修饰的, 组件注入的时候不需要指定在构造器方法上或者构造器参数上指定@Autowired,只需要声明构造器即可;

2.2 定义在方法和参数上

定义在Set方法上


@Autowired //定义在set方法上
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

定义在配置Bean的方法上


package com.yibai.spring.annotation.main.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import com.yibai.spring.annotation.service.UserDao;
import com.yibai.spring.annotation.service.UserService;

//@ComponentScan("com.yibai.spring.annotation.service")
@Service
public class MainConfigForAutowired {

// Spring自动从IOC容器中找出UserDao作为方法参数传入,这里@Autowired可有可无
@Bean
public UserService userService(@Autowired UserDao userDao) {
 UserService userService = new UserService();
 userService.setUserDao(userDao);
 return userService;
}

@Bean
public UserDao userDao() {
 return new UserDao();
}
}

2.3 定义在字段上


package com.yibai.spring.annotation.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import lombok.Getter;

@Getter
@Service
public class UserService {

@Qualifier(value = "userDao")
@Autowired(required = true)
private UserDao userDao;

public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
}

}

3. 注入位置推荐

一般推荐注入位置放在构造器上,因为不管字段还是方法的方式注入,都是先创建组件,再注入依赖的组件,如果在构造方法上就需要使用依赖的组件,那么只有在构造器上注入才是可以实现的,因为执行顺序的问题;


package com.yibai.spring.annotation.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import lombok.Getter;

@Getter
@Service
public class UserService {

@Qualifier(value = "userDao")
@Autowired(required = true)
private UserDao userDao;

public UserService() {
 //@Autowired定义在字段或者set方式上,在构造器上无法获取到依赖的组件
 System.out.println(userDao); // null
}

public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
}
}

Spring 自动注入的几种方式

Spring 中实现自动装配的注解有以下几个:

  • @Autowired

  • @Qualifier

  • @Primary

  • @Resource

  • @Inject

一、@Autowired

Spring 中最常用的一个注解,当一个组件需要另一个组件作为属性的时候,我们可以通过两种方式对属性进行赋值,一种是通过构造方法,一种是通过 set 方法(类比),而这个注解使用的方法就是后者。

下面介绍该注解的特点:

首先是 按照类型 自动注入,适用于容器中只有一种该类型的组件;

如果存在多个相同类型的组件,则将属性名作为 id 查询容器中的组件并注入;

默认属性对应的组件在容器中必须是存在的,如果想无论存在与否都注入可以令属性 required = false;

可以在该注解的基础之上使用 @Qualifier("bookDao") 注解指定要注入组件的 id,这时属性名的 id 已失效;

如果不使用上述注解指定 id ,存在多个相同类型的组件时也可以使用 @Primary 注解设置 Bean 的优先级为最优。


@Autowired(required = false)
@Qualifier("bookDao2")
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao2;

二、@Resource

与 @Autowired 不同,@Resource 注解是 按照属性名 自动注入,它属于 JSR250 规范;

该注解不支持 @Qualifier、@Primary 的使用,但是可以使用它的 name 属性指定要注入组件的 id 值。


@Resource(name = "bookDao3")
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao3;

三、@Inject

要使用 @Inject 注解必须要先导包,它属于 JSR330 规范 :


<!--    inject 注解    -->
<dependency>
   <groupId>javax.inject</groupId>
   <artifactId>javax.inject</artifactId>
   <version>1</version>
</dependency>

导包之后就可以使用了,该注解的效果和 @Autowired 的效果一样,只不过没有任何属性,所以功能有些欠缺,但是可以和另外两个注解配合使用。


@Inject
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao;

四、@Autowired 的使用方式

该注解主要是通过 BeanPostProcessor 的实现类 AutowiredAnnotationBeanPostProcessor 实现的。

该类及其父类重写了 postProcessBeforeInitialization 方法,在初始化 Bean 之前,先对属性进行赋值,从而实现自动注入。

1、Set 方法

该注解除了可以放在属性上面,还可以放在方法上面:


@Component
public class Boss {
   private Car car;
   public Car getCar() {
       return car;
   }
   @Autowired
   public void setCar(Car car) {
       this.car = car;
   }
}

可以放在 set 方法上面,他会自动的去 IOC 容器中找方法中的参数,这里的参数是 car ,所以他会去容器中找 car 这个类,然后创建一个对象完成赋值。

官方 3.X 建议使用该方式注入。

2、构造器

对于加在 IOC 容器中的组件,容器启动后会调用 无参构造器 创建对象进行初始化赋值操作。

我们也可以不使用默认的,我们提供一个有参构造器:


@Autowired
public Boss(Car car) {
   this.car = car;
}

构造器要使用的组件,也都是从容器中获取。

所以也可以这么写:


public Boss(@Autowired Car car) {
   this.car = car;
}

同时如果该类只有一个有参构造器,那么 @Autowired 注解 可以省略。

官方 4.X 开始建议使用该方式注入。

3、@Bean + 方法参数

我们可以不改变 Boss 这个类,即不在 Boss 中注入 Car,而是在将 Boss 放入容器的时候注入它需要的参数 Car。


@Bean
public Boss boss(@Autowired Car car) {
   return new Boss();
}

这里的 @Autowired 可以省略,也是用的最多的一种方式。

五、使用 Spring 底层的组件

如果自己写的组件想要使用 Spring 底层的组件可以使用另一种方式 :比如想要使用 Spring 的 ApplicationContext。

Spring 为我们提供了相关的接口,他们都是 xxxAware,比如 ApplicationContextAware。

每一个接口都对应一个方法,我们可以在方法中获取 Spring 底层的组件,然后给成员变量赋值以获取相关组件。


public class Red implements ApplicationContextAware {
   private ApplicationContext context;
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
       this.context = applicationContext;
   }
}

关于 Aware 的原理:

其实它还是使用了我们之前说过的 BeanPostProcessor ,每一个 Aware 都对应一个 AwareProcessor,这个 processor 正是BeanPostProcessor 的实现类,所以肯定会有一个 postProcessBeforeInitialization 方法,我们重点来看一下这个方法。


@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null && (bean instanceof ApplicationContextAware)) {
 acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
 AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  invokeAwareInterfaces(bean);
  return null;
 }, acc);
}
else {
 invokeAwareInterfaces(bean);
}
return bean;
}

在 invokeAwareInterfaces(bean); 方法中主要是下面的逻辑:


if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}

其实就是先判断是不是那几个 Aware 中的一个,如果是就赋值,我们能看到的就是在 初始化 的时候利用 后置处理器 完成赋值。

来源:https://blog.csdn.net/luojinbai/article/details/86586512

标签:Spring,装配,构造器,注入
0
投稿

猜你喜欢

  • 详解idea maven nexus 常见命令配置

    2021-06-07 18:29:03
  • SpringBoot过滤器与拦截 器深入分析实现方法

    2023-11-28 23:04:15
  • JAVA NIO实现简单聊天室功能

    2023-05-01 10:32:49
  • SpringMVC解析JSON请求数据问题解析

    2023-06-02 21:41:32
  • 利用java实现二叉搜索树

    2023-10-25 13:25:07
  • Java新特性之Nashorn_动力节点Java学院整理

    2022-07-31 17:18:13
  • Java中SpringSecurity密码错误5次锁定用户的实现方法

    2021-07-10 10:38:17
  • java弹幕小游戏1.0版本

    2021-12-06 04:42:48
  • JavaWeb项目部署到服务器详细步骤详解

    2023-11-29 11:15:20
  • Spring Boot缓存实战 EhCache示例

    2023-08-30 12:23:35
  • iOS应用中使用Toolbar工具栏方式切换视图的方法详解

    2023-06-21 09:24:48
  • 详解Android的.aar文件生成方法以及使用技巧

    2023-08-06 06:43:20
  • Java中的泛型方法详解及简单实例

    2023-09-11 13:28:05
  • 详解Maven安装教程及是否安装成功

    2021-07-14 00:00:21
  • OpenCV和C++实现图像的翻转(镜像)、平移、旋转、仿射与透视变换

    2023-07-14 23:47:22
  • Maven打包没有指定主类问题(xxx.jar中没有主清单属性)

    2023-11-26 21:34:44
  • 使用spring注入枚举类型作为参数

    2023-11-23 13:25:57
  • Spring Cloud 的 Hystrix.功能及实践详解

    2023-11-19 06:40:46
  • Springboot启动后执行方法小结

    2022-09-26 22:12:02
  • Java实现Http工具类的封装操作示例

    2021-08-14 10:27:57
  • asp之家 软件编程 m.aspxhome.com