likes
comments
collection
share

Spring 中用注解来存储和注入 Bean 对象

作者站长头像
站长
· 阅读数 25

1.存储 Bean 对象

1.1 配置环境

   在上一篇中👆介绍了如何创建一个Spring项目,并且涉及到如何通过<bean>标签来存储Bean对象,但是通过这种方式来存入Spring是非常麻烦的,假如我有100个对象需要存入Spring中,那我岂不是要写100bean标签?

Spring 中用注解来存储和注入 Bean 对象

  然后再创建于一个启动类:

Spring 中用注解来存储和注入 Bean 对象

  在包下面创建一个Bean对象:

Spring 中用注解来存储和注入 Bean 对象

  好,怎么用注解来存取Bean对象呢?

第一步,需要在配置文件spring-config.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"
       xmlns:content="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <content:component-scan base-package="com.spring.demo1"></content:component-scan>
</beans>

  (如果你看了上一篇文章,你会发现只是多了这两行代码:

xmlns:content="http://www.springframework.org/schema/context……"

<content:component-scan base-package="com.spring.demo1"></content:component-scan>

  这里的</content:component-scan>标签就是用来指定需要扫描哪一路径的,也就是说,Spring会扫描该路径下有哪些类是加了注解的,把加了注解的类存入Spring容器中,这个路径是你自己定的。

1.2 添加注解存储 Bean 对象

  想要将对象存储在Spring中,有两种注解类型可以实现:

(1)类注解:@Controller@Service@Repository@Component@Configuration

(2)⽅法注解:@Bean

1.2.1 @Controller

  它的格式如下:

@Controller //讲对象存储到 Spring 中
public class StudentController {
    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
    }
}
Spring 中用注解来存储和注入 Bean 对象

  获取它:

public class Main {
    public static void main(String[] args) {
        //创建一个上下文(Spring容器)
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //获取 Bean 对象
        StudentController student = context.getBean(StudentController.class);
        student.hi();
    }
}
Spring 中用注解来存储和注入 Bean 对象

1.2.2 @Service

  再创建一个Bean对象,加上@Service注解。

Spring 中用注解来存储和注入 Bean 对象

  获取:

Spring 中用注解来存储和注入 Bean 对象

1.2.2 @Repository、@Component、@Configuration

  @Repository@Component@Configuration,这些注解与上面的用法是一样的,这里就不再赘述。

1.2.3 这 5 种注解有什么不同?

  上面的5种注解既然都能将Bean对象存入容器中,那它们有什么区别呢?

  当我们想要组装一个机器人时,需要使用不同的零件和工具,例如电机、齿轮、螺丝等。这些零件和工具各自有不同的作用,电机负责提供动力,齿轮用于传递动力,螺丝用于连接零件等。

  类比到 Spring 中的注解,@Controller@Service@Repository@Component@Configuration 就像这些零件和工具一样,它们各自有不同的作用:

  1. @Controller:用于标识一个类是Spring MVC框架中的控制器(Controller),处理HTTP请求并返回响应给客户端。
  2. @Service:用于标识一个类是业务逻辑层(Service),包含了应用程序中的业务逻辑处理代码。
  3. @Repository:用于标识一个类是数据访问层(DAO,Data Access Object),用于访问数据库和其他数据存储介质。
  4. @Component:用于标识一个类是Spring框架中的组件,一般用于定义那些没有明确角色的类。
  5. @Configuration:用于标识一个类是Spring的配置类,它可以包含多个bean的定义。

  它们就像一个标签,用来表示当前这个类是用来干嘛的,它们是一种规范、约定。

Spring 中用注解来存储和注入 Bean 对象

1.2.4 Bean 命名规则

  我们已经了解了类注解的用法,那这里再来讲一讲关于取Bean对象的一些知识。上面我们取Bean对象是用了下面的方式:

//通过类型来接受
StudentService student1 = context.getBean(StudentService.class);
student1.hi();

  还有其它方法,就是用类名的方式来取Bean对象

//方法一:用类的名字 + 类的类型
StudentService student1 = context.getBean("studentService",StudentService.class);
student1.hi();

//方法二:用类的名字
StudentService student1 = (StudentService)context.getBean("studentService");
student1.hi();

  问题来了,这里的名字"studentService"是正确的吗?在上一篇文章中,介绍的是用<bean id="" class="">的方式来存Bean对象的,这里的id就是容器中对象的名称。但是在本篇中是用</content:component-scan>标签来指定待存储对象的路径,并没有指定其名称,那怎么取对象呢?先不管,我们来试一试。

Spring 中用注解来存储和注入 Bean 对象

结果:

Spring 中用注解来存储和注入 Bean 对象

发现能运行成功,我们再改一下名字,这次改为大驼峰:

Spring 中用注解来存储和注入 Bean 对象

结果:

Spring 中用注解来存储和注入 Bean 对象

报错了,这就说明在取对象的时候,名字要用小驼峰,但事实真是这样的吗?我们再创建一个SController类:

Spring 中用注解来存储和注入 Bean 对象

再来取对象:

Spring 中用注解来存储和注入 Bean 对象

唉?报错了,我们换回大驼峰试一试:

Spring 中用注解来存储和注入 Bean 对象

成功了。

  总结:默认情况下,使用原类名首字母小写(小驼峰)可以读取到 Bean 对象;当原类名首字母与第二个字母为大写的情况下,就用原类名(大驼峰)来读取 Bean 对象。

  当然,有一种办法可以指定类的名称:

Spring 中用注解来存储和注入 Bean 对象

5个注解都可以这样指定,这时我们再来:

Spring 中用注解来存储和注入 Bean 对象

1.2.5 方法注解 @Bean

  方法注解就是用来存方法返回的对象的,它是加在方法上的,并且这个方法必须得有返回值。

  我们来创建一个实体类User并完善getset方法 和 一个操作实体类的类UserBeans

Spring 中用注解来存储和注入 Bean 对象

  在类UserBeans中有一个方法用来创建一个User对象,并对它加上Bean注解把返回值存入到Spring容器中:

Spring 中用注解来存储和注入 Bean 对象

然后我们再到Main中来看看它有没有存入成功。

Spring 中用注解来存储和注入 Bean 对象

结果:

Spring 中用注解来存储和注入 Bean 对象

发现报错了,为什么?造成这个错误的原因有两个:

  1. @Bean注解的命名规则与类注解的命名规则不一样,这里的命名规则是加了注解的那个方法的方法名:
//获取一个 User 对象,这里必须跟方法名相同: user1
User user = context.getBean("user1", User.class);
  1. @Bean 注解必须搭配 5 大类注解,也就是上面的5个注解,Spring只会在类注解下扫描有没有@Bean注解。这里随便写一个:

Spring 中用注解来存储和注入 Bean 对象

再来试一试:

Spring 中用注解来存储和注入 Bean 对象

1.2.6 @Bean 重命名

  在获取 @Bean注解存入的对象的时候,需要用方法名来获取,但是这样感觉不太方便,所以我们可以对它重命名:

@Bean(name = "user")//重命名,重命名后原来的名字不能用了。
public User user1(){
    User user = new User();
    user.setUid(1);
    user.setUsername("张三");
    user.setPassword("123456789");
    user.setAge(20);
    return user;
}

Spring 中用注解来存储和注入 Bean 对象

Spring 中用注解来存储和注入 Bean 对象

  可以对它起多个名字:

Spring 中用注解来存储和注入 Bean 对象

Spring 中用注解来存储和注入 Bean 对象

1.2.7 多个 @Bean 的场景

  当我有多个方法需要加@Bean注解的时候:

Spring 中用注解来存储和注入 Bean 对象 Spring 中用注解来存储和注入 Bean 对象

(补充一下:方法如果有形参是不能存入容器中的,所以不能重载。)

  如果是相同的名字呢?我们这里再创建一个类:

  这时候有两个名字相同的方法,那么取对象会报错吗?

Spring 中用注解来存储和注入 Bean 对象 Spring 中用注解来存储和注入 Bean 对象 Spring 中用注解来存储和注入 Bean 对象

  艾,怎么没有报错?为什么打印的是“老六”?因为当@Bean重复的时候,Spring容器存储的过程是:如果遇到相同名称的对象,后一个替换前一个,就如Java中的Hash一样。

  当然,有一个注解可以改变它们的优先级:@Order

Spring 中用注解来存储和注入 Bean 对象 Spring 中用注解来存储和注入 Bean 对象 Spring 中用注解来存储和注入 Bean 对象

  这时候就是打印张三了,因为张三最后被加载。

2. 注入 Bean 对象

  对象注入就是把容器中的对象放入到另一个类中,有三种注入方式:

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

2.1 属性注入

  有如下代码

Spring 中用注解来存储和注入 Bean 对象

  我想将StudentService存到StudentController中的studentService属性上:用@Autowired注解

@Controller //将对象存储到 Spring 中
public class StudentController {

    @Autowired
    public StudentService studentService;

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        studentService.hi();
    }
}
Spring 中用注解来存储和注入 Bean 对象

  这里要注意属性的命名规范:studentService,加了@Autowired注解后,会先在容器中寻找是否有当前的类型(StudentService),如果只有一个,那么不管你的名称是什么,直接把这一个注入到属性中;要是有多个相同的类型,就会通过属性名称来寻找对象。

public class Main {
    public static void main(String[] args) {

        //创建一个上下文(Spring容器)
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //获取 StudentController 对象
        StudentController studentController = context.getBean("studentController",StudentController.class);
        studentController.hi();
    }
}

结果:

Spring 中用注解来存储和注入 Bean 对象

注入成功。

  但是,它不能对实现final修饰的变量进行注入:

Spring 中用注解来存储和注入 Bean 对象

这是因为final对象要么直接赋值,要么在构造方法中赋值,这里违背了java语法的规范,所以不能赋值。

2.2 Setter 注入

  在属性的set方法上加一个@Autowired注解:

@Controller //将对象存储到 Spring 中
public class StudentController {


    private StudentService studentService;

    //Setter注入,必须写一个set方法。
    @Autowired 
    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        studentService.hi();
    }
}

结果: Spring 中用注解来存储和注入 Bean 对象

  这种方法也是不能注入 final 属性的:

Spring 中用注解来存储和注入 Bean 对象

2.3 构造方法注入

@Controller //讲对象存储到 Spring 中
public class StudentController {
    
    private StudentService studentService;

    //构造方法注入
    @Autowired // 当只有一个构造方法的时候,这个注解可以省略!
    public StudentController(StudentService studentService){
        this.studentService = studentService;
    }
    
    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        studentService.hi();
    }
}
Spring 中用注解来存储和注入 Bean 对象

成功。

  这时候就可以对 final 属性进行注入了:

@Controller //讲对象存储到 Spring 中
public class StudentController {

    // final 属性
    private final StudentService studentService;
    //构造方法注入
    @Autowired
    public StudentController(StudentService studentService){
        this.studentService = studentService;
    }

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        studentService.hi();
    }
}
Spring 中用注解来存储和注入 Bean 对象

2.4 @Resource 注解

  @Resource注解@Autowired注解是一模一样的用法,它们的区别是:

  1. @Autowired 来⾃于Spring,⽽ @Resource 来⾃于 JDK 的注解。
  2. @Resource不能用于构造方法注入。
  3. @Resource可以设置name参数,根据名称来获取Bean对象。

2.4.1 @Resource 的 name 属性

  这里着重讲一下第三个点:

Spring 中用注解来存储和注入 Bean 对象

Spring 中用注解来存储和注入 Bean 对象

  这里存入了3个User对象到Spring容器中,我们分别用@Autowired@Resource注解来注入对象:

@Controller
public class UserController {

    @Autowired
    private User user;

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        System.out.println(user.toString());
    }
}
public class Main {
    public static void main(String[] args) {

        //创建一个上下文(Spring容器)
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        UserController userController = context.getBean("userController",UserController.class);
        userController.hi();
    }
}

结果: Spring 中用注解来存储和注入 Bean 对象

@Controller
public class UserController {

    @Resource
    private User user;

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        System.out.println(user.toString());
    }
}

结果:

都是会报错的,为什么?因为这里查询用的名称是user(默认用属性名来查找),容器里没有这个对象,但是我又不想改名称怎么办?答案就是用@Resourcename属性:

@Controller
public class UserController {

    @Resource(name = "user1")//指定名称来取对象
    private User user;

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        System.out.println(user.toString());
    }
}

结果:

Spring 中用注解来存储和注入 Bean 对象

2.4.1 @Autowired + @Qualifier

  @Autowired注解也能解决上面的问题,但是要借助@Qualifier

@Controller
public class UserController {

    @Autowired
    @Qualifier(value = "user1")//指定获取某个对象
    private User user;

    public void hi(){
        System.out.println("你好,我是 StudentController 的 hi 方法!");
        //来验证是否注入成功
        System.out.println(user.toString());
    }
}

结果:

Spring 中用注解来存储和注入 Bean 对象

(如有错误,请在评论区指出,谢谢🌹)