【Spring】之基于注解存储和获取Bean对象

发布于:2024-04-28 ⋅ 阅读:(33) ⋅ 点赞:(0)

🏀🏀🏀来都来了,不妨点个关注!
🎧🎧🎧博客主页:欢迎各位大佬!
在这里插入图片描述

前言

在上一篇Spring的基础概念和使用中,我们讲解了如何存储Bean对象和获取Bean对象,大致流程就是在配置好的扫描路径文件.xml文件中添加一行Bean注册才行,如下:
在这里插入图片描述
但设想一下如果我有很多的对象需要存储到Spring容器中,那我是不是得添加多个Bean注册,这样写起来是不是就太麻烦了,每次我需要存储一个Bean就需要添加一个到扫描路径文件中,今天我们就讲解一种更简单的存储和获取Bean对象的方法——通过注解存储和获取Bean

前置工作

在我们使用注解存储和获取Bean对象之前,我们需要配置一下扫描路径,具体配置如下:

<?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.java.demo"></content:component-scan>
</beans>

注意事项:
在这里插入图片描述

1. 基于注解存储Bean对象

1.1 什么是注解

在Spring框架中,注解(Annotation)是一种元数据的形式,它允许开发者将额外的信息添加到代码中,而无需使用传统的XML配置文件。Spring框架中的注解大大简化了配置,使得开发者能够更直接地在Java代码中定义Spring的组件、依赖注入、切面、事务管理等。
而我们存储Bean对象就有两种方式,一种是通过五大类注解,一种是通过方法注解,如下:
类注解:

  • @Controller【控制器】校验参数的合法性(安检系统)
  • @Service【服务】业务组装(客服中心)
  • @Repository【数据持久层】实际业务处理(实际办理的业务)
  • @Configuration【配置层】配置
  • @Component【组件】工具类层(基础的工具)

方法注解:@Bean

对于以上五个类注解,其他四个都是@Component的子类,五个类注解的功能是一样的,命名不同是为了作区分,下面是Java项目开发时的规范,就是以不同的注解作区分:
在这里插入图片描述

1.2 通过类注解存储Bean对象

  • 通过@Controller存储
package com.java.demo.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    public void sayHi() {
        System.out.println("我是UserController");
    }

}

  • 通过@Service存储
package com.java.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void sayHi() {
        System.out.println("我是UserService");
    }

}

  • 通过@Repository
package com.java.demo.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {

    public void sayHi() {
        System.out.println("我是UserRepository");
    }
}

  • 通过@Configuration
package com.java.demo.configuration;

import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfiguration {

    public void sayHi() {
        System.out.println("我是UserConfiguration");
    }
}
  • 通过@Component
package com.java.demo.component;

import org.springframework.stereotype.Component;

@Component
public class UserComponent {
    public void sayHi() {
        System.out.println(" 我是UserComponent");
    }
}

1.2.1 读取Bean对象

以读取UserController为例,默认情况下我们获取Bean对象的名称为类名首字母小写,即userController,如下:

import com.java.demo.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        //1.创建上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        //2.获取Bean对象
        UserController userController = context.getBean("userController",UserController.class);

        //3.使用Bean对象
        userController.sayHi();

    }
}

1.2.2 读取Bean对象时命名规则的问题(默认命名)

通过上面UserController例子,我们知道当我们的Bean对象使用大驼峰命名时,我们可以通过首字母小写的方式进行读取Bean对象,
在这里插入图片描述
但当我们使用下面这个例子将IService存储到Spring容器中时,通过首字母小写的格式进行读取Bean对象的时候就会发生报错:

package com.java.demo.service;

import org.springframework.stereotype.Service;

@Service
public class IService {

    public void sayHi() {
        System.out.println("我是IService");
    }

}

在这里插入图片描述
而上述例子我们需要直接使用类名读取Bean对象,读取如下:
在这里插入图片描述

在这里插入图片描述

通过上面两个例子,我们就可以知道Bean的命名规则:当第一个字母大写,第二个字母小写的情况,即大驼峰命名时,我们通过第一个字母小写读取Bean对象,当第一个字母和第二个字母都大写的情况,我们通过原类名读取Bean对象。
这个我们可以通过Bean注解的源码进行解释:
在idea中双击shift:
在这里插入图片描述
最终找到以下方法:
在这里插入图片描述

1.3 通过方法注解存储Bean对象

值得注意的是,当我们使用方法注解@Bean存储对象时需要搭配着五大类注解使用,如下:
ArticleInfo定义如下:

package com.java.demo;


public class ArticleInfo {
       String id;
       String content;

    public String getId() {
        return id;
    }

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

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "ArticleInfo{" +
                "id='" + id + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

Articles定义如下:

package com.java.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class Articles {

    @Bean      //将当前方法的返回值存储到IoC容器中
    public ArticleInfo articleInfo() {
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setId("1");
        articleInfo.setContent("斗破苍穹");
        return articleInfo;
    }
}

这里的读取Bean对象和上面一样这里就不赘述了。
在这里插入图片描述
获取Bean对象时的注意事项:@Bean的默认命名=方法名。
在这里插入图片描述
重命名Bean对象的几种方式:

@Bean("art1") 
 @Bean(name = "art2") 
@Bean(value = "art3") 

同时Bean支持指定多个名称

  @Bean(value = {"art4","art5"}) 
  @Bean(name = {"art4","art5"}) 

2. 获取Bean对象(对象装配)

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:

  1. 属性注⼊
  2. 构造⽅法注⼊
  3. Setter 注入
    在我们项目开发中,如我们上面提到的项目开发规范,需要在Controller层中调用Service层中的类,此时就需要用到我们上述提到的对象装配, 比如现在我们需要在UserController中调用UserSerivice类中的sayHi()方法。

2.1 属性注入

属性注⼊是使⽤ @Autowired 实现的,下面我们以将UserService注入到UserController中并调用其sayHi()方法。
UserController定义如下:

package com.java.demo.controller;

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void sayHi() {
        userService.sayHi();
        System.out.println("我是UserController");
    }


}
package com.java.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void sayHi() {
        System.out.println("我是UserService");
    }

}

属性注入的优缺点:
优点:使用简单
缺点:

  1. 无法注入final修饰的变量
  2. 通用性问题:只适用于IoC容器
  3. 更容易违背单一设计原则,因为使用起来比较简单

2.1.1 同一类型多个@Bean报错

当出现以下多个 Bean,返回同⼀对象类型时程序可能会报错,如下代码所示:
在UserService中我们注入了两个User对象到Spring容器中,然后在UserController中打印User的信息,代码实现如下:
UserService定义如下:

package com.java.demo.service;

import com.java.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

@Service
public class UserService {


    @Bean
    public User user1() {
        User user1 = new User();
        user1.setName("张三");
        return user1;
    }

    @Bean
    public User user2() {
        User user2 = new User();
        user2.setName("张三");
        return user2;
    }

}

UserController定义如下:

package com.java.demo.controller;

import com.java.demo.User;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    @Autowired
    private User user;

    @Autowired
    private UserService userService;

    public void sayHi() {
        System.out.println(user);

    }


}

报错如下:

在这里插入图片描述
解决方案如下:
1. 将属性的名字和Bean的名字对应上

在这里插入图片描述
在这里插入图片描述
2. @Autowired和@Qualifier配合使用

2.2 Setter方法注入

Setter方法注入即在setter方法上加上@Autowired注解,下面我们用代码进行演示:
UserController定义如下:

package com.java.demo.controller;

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
    //setter方法注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void sayHi() {
        userService.sayHi();
    }

}

UserService定义和属性注入时一样,这里就不赘述了,
Setter方法注入优缺点:
优点:通常Setter只set一个属性,因此更符合单一设计原则
缺点:

  1. 无法修饰final变量
  2. setter注入的方法可以被修改(这是因为setter本身就是一个方法,因此可以在多个地方被调用和修改)

2.3 构造方法注入(Spring4.x之后推荐的注入方法)

构造方法注入即在类的构造方法中注入,需要注意的是,当类中只有这一个构造方法的时候, @Autowired是可以省略的。

package com.java.demo.controller;

import com.java.demo.User;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    private UserService userService;
     //构造方法注入
     @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void sayHi() {
        userService.sayHi();
    }

}

构造方法注入优缺点:
优点:

  1. 可以修饰final变量(这是因为构造方法有两种初始化方式,一种是直接赋值,一种则是构造方法初始化)
  2. 注入的对象不会被修改,因为构造方法只会被加载一次
  3. 构造方法注入可以保证对象完全初始化
  4. 构造方法注入通用性更好

缺点:

  1. 写法比属性注入复杂
  2. 使用构造方法注入,无法解决循环依赖的问题

2.4 @Autowired和@Resouce 的区别

  • 出身不同 @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
  • 依赖查找顺序不同:** @Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找 @Resource 是先根据名称查找(byName),如果(根据名称)查找不到,再根据类型(byType)进行查找.
  • 支持的参数不同: @Autowired 和 @Resource 在使用时都可以设置参数,比如给 @Resource 注解设置 name 和 type 参数,实现代码如下:
@Resource(name = "userinfo", type = UserInfo.class)
private UserInfo user;

但二者支持的参数以及参数的个数完全不同,其中 @Autowired 只支持设置一个 required 的参数,而 @Resource 支持 7 个参数。

  • 依赖注入的支持不同: @Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。

今天的分享到这里结束了,感谢支持!