(b)Spring注解式开发,@Value,@Autowired,@Resource注解属性自动装配时的原理和区别

@Value简单类型的注入

属性注入的三种方式

为了简便开发Spring给我们提供了多样化的注入: @Value注解可以出现在属性上,setter方法上以及构造方法的形参上

简单类型的属性上使用@Value注解完成属性值的注入(可以不提供属性的setter方法)

@Component
public class User {
    @Value(value = "zhangsan")
    private String name;
    @Value("20")
    private int age;
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

简单类型属性对应的setter方法上添加@Value注解完成注入

@Component
public class User {
    private String name;
    private int age;
    @Value("李四")
    public void setName(String name) {
        this.name = name;
    }

    @Value("30")
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

简单类型属性所在的构造方法的形参上添加@Value注解完成注入

@Component
public class User {
    private String name;
    private int age;
    public User(@Value("张三") String name, @Value("33") int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试程序简单类型的注入

@Test
public void testAutowired(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-di-annotation.xml");
    UserService userService = applicationContext.getBean("user", UserService.class);
    System.out.println(user);
}

@Autowired非简单类型的注入

@Autowired注解的应用

@Autowired自动装配注解可以用来注入非简单类型, 单独使用时默认是根据类型进行注入的,若要根据名称注入的话需要配合@Qualifier注解一起使用

  • @Autowired注解可以标注在属性上、setter方法上、构造方法上、构造方法的参数上, 一切具有参数的方法上(可以不提供属性的setter方法上)
  • @Autowired注解可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean自动装配到数组上
  • @Autowired注解可以应用在指定了泛型的集合属性上, 此时Spring会读取集合的泛型信息然后自动装配所有同类型的bean
  • @Autowired注解用在指定了泛型的Map属性上(Map的键值为String), 那么Spring将自动装配所有泛型类型的bean作为value,以bean的id值作为key
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	boolean required() default true;
}

@Autowired注解的required属性

属性值 说明
required=true(默认) 自动注入的时候要求被注入的Bean必须是存在的,如果不存在则报错
required=false 允许某一属性不被设置 , 自动注入的时候Bean存在或者不存在都没关系,存在的话就注入不存在的话也不报错

属性注入的几种方式

Dao接口及其实现类

//UserDao接口
public interface UserDao {
    void insert();
}

//UserDao实现类
@Repository 
public class UserDaoForMySQL implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向mysql数据库插入User数据");
    }
}

//UserDao接口的另一个实现类
@Repository 
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

非简单类型的属性上使用@Autowired注解完成属性值的注入(可以不提供属性的setter方法)

  • @Autowired注解默认是根据类型注入的,如果UserDao接口有两个实现类,给UserService的自定义属性UserDao时自动注入时会报错(提示Bean的数量大于1)
@Service 
public class UserService {
    // 自动注入属性
    @Autowired 
    private UserDao userDao;
    
    // 没有提供构造方法和setter方法
    public void save(){
        userDao.insert();
    }
}

非简单类型属性对应的setter方法上使用@Autowired注解完成属性的自动注入

@Service
public class UserService {
    private UserDao userDao;
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

非简单类型属性所在的构造方法上使用@Autowired注解完成属性的自动注入

@Service
public class UserService {
    private UserDao userDao;
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

非简单类型对应的构造方法的形参上使用@Autowired注解完成属性的自动注入

@Service
public class UserService {
    private UserDao userDao;
    public UserService(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }
    public void save(){
        userDao.insert();
    }
}

测试程序

@Test
public void testAutowired(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml");
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
}

省略@Autowired注解

构造方法只有一个并且构造方法的形参名和对象的属性名能够对应上时@Autowired注解可以省略,如果有多个构造方法就不能省略

@Service
public class UserService {
    private UserDao userDao;
    // 构造方法的参数只有一个时,自动注入时@Autowired注解可以省略
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

属性/方法形参注入的原理

@Autowired注解是根据类型进行自动装配的,所有可能找到多个或者一个

  • 如果是非集合类型的自定义属性找到多个组件,Spring会继续以属性名/形参名作为bean的id值匹配到具体的一个bean,然后为对象的属性赋值
  • 如果是集合类型的属性找到多个组件不会报错,Spring会把容器中的所有集合泛型类型的bean封装到一个集合中然后为这个属性赋值

属性注入: Spring从容器查找组件时先按照属性的类型(可能找到一个或多个bean,也可能没找到bean),若找到多个会将属性名作为id继续匹配到某个具体的bean

  • 根据类型找到且只有一个组件,直接为对象的属性赋值
  • 根据类型没有找到组件则报NoSuchBeanDefinitionException异常
  • 找到了多个bean则会按照属性名作为id继续匹配到某个具体的bean , 找到则会为该属性赋值 ,如果根据id还是找不到则会报错
  • 默认情况下所有使用@Autowired注解的属性默认一定要被赋值,当Spring找不到匹配的bean去装配属性时会抛出异常

方法形参的注入: Spring从容器中找到对应的组件为方法上的每一个形参赋值,注入原理和属性注入原理一样

  • 先按照形参的类型匹配bean,如果匹配多个bean再将形参的变量名作为bean的id继续匹配到某个具体的bean,如果还匹配不到则报错

@Autowired和@Qualifier联合使用

属性注入的几种方式

@Autowired注解和@Qualifier注解联合起来才可以根据名称进行装配,在@Qualifier注解中指定要查找的Bean名称(SpringMVC常用)

  • 默认情况下当IoC容器里存在多个类型相同的bean时,Spring会尝试将属性名/形参名作为bean的Id去匹配到一个具体的bean然后为对象的属性自动装配
  • 若将属性名/形参名作为bean的Id还是匹配不到具体的bean,此时自动装配将无法工作即报错
  • 此时可以添加一个@Qualifier注解指定一个字符串作为bean的Id去容器中匹配对应名称的bean,找到则为对象的属性自动装配,再找不到还报错

Dao层的UserDao接口及其实现类

public interface UserDao {
    void insert();
}
@Repository 
public class UserDaoForMySQL implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向mysql数据库插入User数据");
    }
}
@Repository 
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

非简单类型的属性上额外添加@Qualifier注解指定bean的Id(默认按照属性名)去容器中匹配对应名称的bean完成对象属性的自动装配

@Service 
public class UserService {
    // 自动注入属性
    @Autowired 
    @Qualifier("userDaoForOracle") 
    private UserDao userDao;
    
    // 没有提供构造方法和setter方法
    public void save(){
        userDao.insert();
    }
}

非简单类型属性对应的setter方法上额外添加@Qualifier注解指定bean的Id(默认按方法形参的变量名)去容器中匹配对应名称的bean完成对象属性的自动装配

@Service
public class UserService {
    private UserDao userDao;
    @Autowired
    // 指定要查找的bean的名称
    @Qualifier("userDaoForOracle") 
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void save(){
        userDao.insert();
    }
} 

非简单类型属性所在方法的形参中额外添加@Qualifiter注解指定bean的Id(默认按照形参的变量名)去容器中匹配对应名称的bean完成对象属性的自动装配

@Autowired
public void setUserDao(@Qualifier("userDaoForOracle") UserDao userDao ) {
    // 容器启动时候bean对象就会被创建,那么这个方法也会自动运行 
    System.out.println(bookDao);
    System.out.println(bookService);
}

@Resource非简单类型注入(通用)

@Resource和@Autowired的区别

来源的区别

  • @Resource注解是JDK扩展包属于JDK的一部分,所以该注解是标准注解更加具有通用性(JSR-250标准中制定的注解类型, JSR是Java规范提案)
  • @Autowired注解是Spring框架自己的

注入方式的区别

  • @Resource注解根据名称装配: 默认使用标注处的属性名/形参名作为bean的Id去匹配bean,如果找不到还会通过类型装配但要求这种类型的Bean只能有一个
  • @Autowired注解根据类型装配: 默认根据标注处的属性名/形参名的类型去匹配bean,如果想根据名称装配需要配合@Qualifier注解一起用

注解的作用范围不同

  • @Resource注解可以标注在属性上、setter方法上
  • @Autowired注解可以标注在属性上、setter方法上、构造方法上、构造方法的参数上

@Resource注解属于JDK扩展包所以不在JDK当中需要额外引入jakarta.annotation-api依赖(高于JDK11或低于JDK8需要引入以下依赖)

  • Spring6不再支持JavaEE而是JakartaEE9 (Oracle把JavaEE贡献给Apache了, Apache把JavaEE的名字改成JakartaEE了,之前的包名javax.*统一修改为jakarta.*)

<dependency>
  <groupId>jakarta.annotationgroupId>
  <artifactId>jakarta.annotation-apiartifactId>
  <version>2.1.1version>
dependency>

<dependency>
  <groupId>javax.annotationgroupId>
  <artifactId>javax.annotation-apiartifactId>
  <version>1.3.2version>
dependency>

属性注入的几种方式

在属性上使用Resource注解根据指定的bean的Id去容器中匹配对应的bean为属性自动装配

//给UserDao接口的实现类UserDaoForOracle起名xyz
@Repository("xyz")
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}
@Service
public class UserService {
    @Resource(name = "xyz")
    private UserDao userDao;

    public void save(){
        userDao.insert();
    }
}

在属性上使用Resource注解根据默认的bean的Id(属性名/形参名)去容器中匹配对应的bean为属性自动装配

//指定UserDaoForOracle的名字为userDao,让这个Bean的名字和UserService类中的UserDao属性名一致
@Repository("userDao")
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

@Service
public class UserService {
    //当使用@Resource注解时没有指定name属性的时候,会把属性名当作bean的Id去容器中匹配对应的bean为属性自动装配
    @Resource
    private UserDao userDao;

    public void save(){
        userDao.insert();
    }
}

属性上使用Resource注解根据类型注入属性值,当通过bean的Id找不到对应bean的时候也会按照属性的类型进行注入(要求该类型的bean只要一个否则报错)

@Service
public class UserService {
    //UserService的属性名修改为userDao2
    @Resource
    private UserDao userDao2;

    public void save(){
        userDao2.insert();
    }
}

属性对应的setter方法上使用@Resource注解根据默认的bean的Id(属性名/形参名)属性自动装配(setter方法去掉set之后首字母变小写就是bean的Id)

//指定UserDaoForOracle的名字为userDao
@Repository("userDao")
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

@Service
public class UserService {
    private UserDao userDao;
    @Resource
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

属性对应的setter方法上使用@Resource注解指定bean的Id去容器中匹配对应的bean为属性自动装配

@Repository 
public class UserDaoForMySQL implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向mysql数据库插入User数据");
    }
}

@Service
public class UserService {
    private UserDao userDao;
    @Resource(name = "userDaoForMySQL")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void save(){
        userDao.insert();
    }
}

你可能感兴趣的:(Spring,spring,数据库,java)