您现在的位置是:首页 >技术教程 >Spring基于注解读取和存储对象网站首页技术教程

Spring基于注解读取和存储对象

PlLI- 2023-06-09 20:00:03
简介Spring基于注解读取和存储对象

目录

一. 存储 Bean 对象 

1. 前置工作:配置扫描路径

2. 使用五大类注解存储Bean对象

@Controller 

 命名规则

@Service

@Repository

@Compoent

@Configuration

五大类注解之间的关系

 3. 使用方法注解存储Bean对象

 二. 获取 Bean 对象

1. 属性注入

优点分析

缺点分析 

2. Setter 注入 

优点分析

缺点分析 

3. 构造方法注入

优点分析


通过前面的学习,我们学习实现了基本的 Spring 读取和存储对象,但在日常的开发中,往往采用更简单的方式,也就是利用注解来进行读取和存储Bean对象。

一. 存储 Bean 对象 

1. 使用五大类注解:

  1. @Controller
  2. @Service
  3. @Repository
  4. @Component
  5. @Configuration

2. 使用方法注解 @Bean 

1. 前置工作:配置扫描路径

为了让对象成功的存储在Spring中,就必须配置一下存储对象的扫描路径,通过扫描路径来决定是要对哪个路径下的对象进行存储到Spring容器中。同时,并不是在扫描路径下的所有类都会被保存到Spring中。只有在扫描路径下,同时添加了注解,才能被正确的识别并保存到Spring中。   也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的。

配置扫描路径,就需要在spring配置文件中,添加语句:

<content:component-scan base-package=""></content:component-scan>

base-package字段就表示要扫描的路径。添加扫描路径后,扫描的范围不止该路径本身,还会包括该路径的所有子路径。

2. 使用五大类注解存储Bean对象

 添加注解后,扫描路径内的类就会作为Bean对象存储于Spring容器中了。

@Controller 

这个注解可以理解为是一个 "控制器",用来验证用户请求的数据正确性。

这个注解也可以传入参数,这个参数可以作为这个Bean对象的名称。

// @Controller("stu")
@Controller // 将当前类存储到 spring 中
public class StudentController {
    public static void say(){
        System.out.println(" do student controller ");
    }
}

 用代码进行演示:

 命名规则

当注解里没有带有参数的时候,那么会有默认的命名规则,此处的名称指的是 getBean()方法中的第一个参数id。这里先总结一下再进行分析:

Bean 命名规则:

1. 默认情况下是首字母变为小写。例如:Student 这个类就对应为 student,StudentName 就对应为 studentName;

2. 如果类名首字母和第二个字母都为大写的情况下,那么对应的 Bean名称就为原类名。例如:CStudent 就对应为 CStudent,SController 就对应为 SController;

 从源码分析:

 

也可以对这个方法进行运行一下:

@Service

 这个注解可以理解为是 "服务层",用于调度具体的执行方法。

@Repository

这个注解可以理解为是"数据持久层",用于与数据库进行交互。 

@Compoent

这个注解可以理解为是"组件层",用于存放一些工具类。

@Configuration

这个注解可以理解为是"配置层",用于存放项目中的一些配置。

五大类注解之间的关系

实际上,五大类注解的作用都是一样的,之所以需要分五大注解,是基于软件开发模型的,为了让程序员看到类注解后,就能直接了解到当前类的用途。这种作用就类似于广东省内的车牌分为粤A,粤B,粤C等,A,B,C...的加入可以让我们一看车牌就知道是广州,深圳,珠海的车。 

在 JavaEE 中,标准分层至少分为三层: 

 通过观察 @Controller,@Service,@Repository,@Configuration等注解的源码可以看出,他们都属于 @Component 的子类。

 3. 使用方法注解存储Bean对象

类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的。

1. 先新建一个User类:


public class User {
    public int id;
    public String name;
    public int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 2. 给方法添加注解

注意:

1. 此方法必须有返回值,方法的返回值就作为Bean对象存储到Spring容器中。

2. 添加方法注解 @Bean 必须是在五大类注解的基础上添加!!!

    这样是为了提高效率,只有被五大类注解作用的时候,并且是在扫描路径范围内,才会去将对应被修饰 @Bean 的方法进行扫描,从而将返回的Bean对象存储至Spring容器中。

3. 通过方法注解 @Bean 存储到Spring中的Bean对象默认名称为方法名。

@Component
public class UserBeans {
    @Bean
    public User func(){
        User user = new User();
        user.setAge(20);
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

此外,还可以在 @Bean 注解当中指定参数 name 或者 value,来作为存储Bean对象的名称。可以指定一个名称,也可以指定多个。

@Bean(name = {"hello","helloUser"})

注意: 当 @Bean 使用了重命名之后,那么默认的使用方法名获取对象的方式就不能再使用了,也就是说 getBean() 参数中的 id 就不能为方法名了,只能为重命名后的名称。

 3. 使用getBean获取对象

getBean() 方法的名称,在不加参数 name 的情况下,默认为方法名。加了参数 name 之后,就以参数 name 的指定名称为准。 

补充:在方法注入的时候,还可以通过注解 @order(int i) 来控制注入的顺序,i 的值越小,权重就越高,就越早注入。如果是Bean对象名称相同的情况,就有可能会发生覆盖的问题。

 二. 获取 Bean 对象

在上述我们讲解了如何去利用 五大类注解 方法注解 的方式来更加简单的存储Bean对象,那么接下来就讲解如何来更加简单的从 Spring 容器中来获取 Bean 对象。主要有三种方式:

  1. 属性注入
  2. Setter 注入
  3. 构造方法注入

1. 属性注入

通过 @Autowired 注解进行实现:

通过 @Autowired 对一个属性进行注解,就会从 Spring 中去查找是否有跟这个属性相对应的Bean对象,如果有,就将这个Bean对象赋值给对应的属性。 

例如:先在Spring容器中存储一个Bean对象:       

@Service
public class UserService {
    public int age = 10;
    public void sayHi() {
        System.out.println("do userService sayHi() -> " + age);
    }
}

 然后再在 UserController 类中,对 UserService 进行注入,并存储在Spring容器中:

@Controller
public class UserController {
    // 1. 属性注入
    @Autowired
    private UserService userService;

    public void sayHi(){
        System.out.println(userService.age);
    }
}

最后在main方法中运行得到结果:

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        UserController userController = context.getBean("userController",UserController.class);
        userController.sayHi();
    }

可以得出 在 UserController 对象中已经成功注入了 UserService 对象。 

优点分析

属性注入使用起来简单方便,只需要给对应的属性添加注解 @Autowired ,就可以注入从Spring获得的Bean对象。 

缺点分析 

1. 属性注解存在一个问题:使用 @Autowired 注解给属性注入对象的时候, 要求 Spring 容器中有且仅有一个和 @Autowired 注入的属性相同的 Bean 对象。

例如,给Spring容器中存储两个 User对象,此时用属性注入来获取对象,就会报错。

2. 属性注入无法注入一个不可变对象,也就是被 final 修饰过的对象。而这也跟Java的语法知识是相关联的,Java中的 final 对象,要么直接赋值,要么通过构造方法赋值,所以当使用属性注入 final 对象时,它不符合 Java 中 final 的使用规范,所以就不能注入成功了。

3. 兼容性问题:使用属性注入的方式只适用于 IoC 框架。

4. 由于使用方法的简单,所以可能会违背了单一设计原则。

2. Setter 注入 

@Controller
public class UserController {
    // 2. Setter注入
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void sayHi(){
        System.out.println(userService.age);
    }
}

优点分析

通过Setter注入的方式,相比于属性注入,它更符合于单一设计原则,因为每一个Setter只针对一个对象。

缺点分析 

1. 依旧是无法注入一个被 final 修饰的对象;

2. 由于Setter注入是通过set方法实现的,那么就有可能 set 方法被执行多次从而改变注入的对象。

3. 构造方法注入

构造方法注入是官方最推荐的一种使用方式。 将 @Autowired注解放于构造方法上。

@Controller
public class UserController {
    // 3. 构造方法注入
    private UserService userService;

    @Autowired
    public UserController(UserService userService){
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println(userService.age);
    }

}

当只有一个构造方法的时候,作用在构造方法上的 @Autowired 注解是可以省略的,但是如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法,否则程序会报错。

优点分析

1. 可注入不可变对象:使用构造方法注入,就可以注入一个被 final 修饰的对象了; 

2. 注入对象不会被修改:与 Setter注入相比,构造方法只会执行一次,也就不会再存在像 Setter注入中 set方法被调用多次从而改变注入对象;

3. 完全初始化:由于注入对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,就会被完全初始化。

4. 通用性更好:构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

补充说明:静态类的加载顺序是高于Spring的,因此在启动类 main 方法中获取对象注入,就还是采用老方法,而不采用注解。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。