Spring 更简单的读取和存储对象

存储 Bean 对象

通过注解,来实现更简单的存储和读取对象。但必须要配置扫描路径,就是在 配置文件当作添加 content:component-scan:

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

意思就是,当前要存储的对象是在 com.beans 包下的对象。

使用五大类注解实现

五个注解如下:

  1. Controller 控制器
  2. Service 服务
  3. Repository 仓库
  4. Configuration 配置
  5. Component 组件

Controller

就像之前存储的话,是要在 配置文件 当中来实现注入,现在直接通过 Controller 就可以了:

@Controller
public class blog {

    public void sayHi() {
        System.out.println("hello Controller");
    }
}

这样就完成了对象的注入,使用原始的读取方式读取对象:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        Blog blog = context.getBean("blog", Blog.class);
        //使用 bean
        blog.sayHi();
    }
}

运行结果如下:
在这里插入图片描述
但要注意的是,对象所在的包,只能是在 配置文件 的包下面。

Service

使用 Service 注解:

@Service
public class UserService {
    public void sayHi() {
        System.out.println("hello Service");
    }
}

代码:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserService userService = context.getBean(UserService.class);
        //使用 bean
        userService.sayHi();
    }
}

运行结果如下:
在这里插入图片描述

Repository

使用 Repository 注解:

@Repository
public class UserRepository {
    public void sayHi() {
        System.out.println("hello Repository");
    }
}

代码如下:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserRepository userRepository = context.getBean(UserRepository.class);
        //使用 bean
        userRepository.sayHi();
    }
}

运行结果如下:
在这里插入图片描述

Configuration

使用 Configuration 注解,要注意的是,千万不要把 Configuration 写成 Configurable:

@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("hello Configuration");
    }
}

代码如下:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
        //使用 bean
        userConfiguration.sayHi();
    }
}

运行结果如下:

在这里插入图片描述

Component

通过 Component 注解来注入对象:

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

代码如下:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserComponent userComponent = context.getBean(UserComponent.class);
        //使用 bean
        userComponent.sayHi();
    }
}

运行结果如下:
在这里插入图片描述

为什么需要五大类注解

明明每个注解都能实现类的注入,那为什么还需要五个呢?因为这就涉及到软件工程方面的知识了,一个软件至少会分为一下四层:
在这里插入图片描述

  1. Configuration 就是配置层:关于当前项目中的所有配置,都会写在这个文件夹里。
  2. Controller 就是控制层:就是前端参数校验,主要就是校验前端数据的合理性和正确性。
  3. Service 就是服务层,负责数据的组装和接口调用。更直白的说,服务器九四 “牵线搭桥” 让程序去调用对应的功能/接口/服务。
  4. Repository 叫做仓库,但其实是 数据持久层,也叫做 Dao 层,就是对数据进行持久化操作(对数据库进行增删改查操作)。

一个软件至少需要这四层,那么这五个类注解,可以更直观的让程序员知道当前类的用途。可以让代码的可读性更高。

五大类中间的关系

查看 Configuration 的源码,发现父类是 Component :
在这里插入图片描述
查看 Controller 的源码,发现仍然是依靠 Component 来实现的:
在这里插入图片描述
查看 Repository 源码,还是依靠 Component 来实现的:
在这里插入图片描述
查看 Service 的源码,发现还是一样:
在这里插入图片描述
也就是说,这些注解的作用是一样的,但是对于开发人员的不一样的。Component 是所有类的父类。

Spring 使用五大类注解,生成 bean name:通过查看 AnnotationBeanNameGenerator.class 文件:
在这里插入图片描述
当然,字节码文件看起来是很累的,通过 gitee 里面可以找到源文件。在全站搜索 spring frame 相关项目就可以找到了:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后就是源码了:
在这里插入图片描述
在 IDEA 里面查看 class 文件,找到名字相关的方法:
在这里插入图片描述
然后发现又调用了这个代码:
在这里插入图片描述
然后再次进入:
在这里插入图片描述
然后找到文件位置:
在这里插入图片描述
在这里插入图片描述
再往上找,发现是 JDK 里面的:
在这里插入图片描述
所以,只要看这段代码就知道为什么了:
在这里插入图片描述

也就是只有第一个字母是大写的,类名才是小写,当前两个字母都是大写的时候,返回的就是原类名了。

使用 Bean 注解来实现

听过 Bean 来注入,就是 Bean 方法注解。Bean 注解,是针对方法实现的,只能添加在 方法上面:

  1. 先创建 User 对象:

    public class User {
        private int id;
        private String name;
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    
        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;
        }
    }
    
  2. 然后添加方法

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

    这样就完成了对象的注入。

  3. 获取对象

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        User user = context.getBean("user1", User.class);
        //使用 bean
        System.out.println(user);
    }
}

运行结果如下:
在这里插入图片描述

获取 Bean 对象

获取 bean 对象也叫做对象装配,是把对象从容器中取出来放到某个类中,有时候也叫对象注入。对象装配的实现方法有三种:

属性注入

通过 @Autowired 注入:

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void sayHi() {
        userService.sayHi();
    }
}

就是把 UserService 类注入,然后就可以使用类了:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserController userController = context.getBean(UserController.class);
        //使用 bean
        userController.sayHi();
    }
}

运行结果如下:
在这里插入图片描述

构造方法注入

通过一个构造方法来实现属性注入:

@Controller
public class UserController {
    private UserService userService;

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

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

运行结果如下:
在这里插入图片描述
如果类当中,只有一个构造方法,那么 @Autowird 可以省略。

Setter 注入

通过 Setter 方法注入:

@Controller
public class UserController {
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void sayHi() {
        userService.sayHi();
    }
}

运行结果如下:
在这里插入图片描述

属性注入 和 构造方法注入 和 Setter 注入的区别

  1. 属性注入特点是写法简单,但是通用性不好,只能运行在 IoC 容器下,如果是 非 IoC 容器的话,就会出现问题。
  2. 构造方法注入,通用性更好。因为构造方法的优先级是最高的。确保使用注入对象之前,对象一定初始化过了。当构造方法注入参数太多时,就需要检查自己的代码是否符合单一设计原则的规范了。构造方法注入,也是 Spring 推荐的注入方法。
  3. Setter 注入是早期 Spring 版本推荐的写法,但是通用性没有构造方法注入通用,因为只有一个参数。

@Resource 和 @Autowired 的区别

  1. 相同点:都可以完成对象的注入。

  2. 不同点:
    a)出身不同,@Resource 来自于 JDK,@Autowired 是由 Spring 框架提供的。

    b)@Autowired 支持属性注入,构造方法注入 和 Setter 注入,而 @Resource 不支持构造方法注入。

    c)支持的参数不同:@Resource 支持更多的参数设置,比如 name、type 设置。而 @Autowired 只支持 required 参数设置。

重命名 Bean

比如我们有多个 User 对象,那么获取 Bean 的时候。就要通过方法名获取了,但是如果重命名对象。也可以得到,就是在 Bean 后面加个括号,里面重命名就好了:

@Component
public class UserBeans {
    @Bean(name = "userInfo")
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

代码如下:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        User user = context.getBean("userInfo", User.class);
        //使用 bean
        System.out.println(user);
    }
}

运行结果如下:
在这里插入图片描述
如果是重命名多个名字的时候,就用大括号把名字括起来:

@Component
public class UserBeans {
    @Bean(name = {"userInfo", "user1"})
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

@Bean 的命名规则: 当没有设置 name 属性的时候,那么 bean 默认的名称就是方法名。当设置了 name 属性之后,只能通过重命名的那么属性对应的值类获取。也就是重命名之后,再使用方法名就获取不到 bean 对象了。

把 Bean 对象注入到其他类

我们把 User 对象(有多个对象)注入到 Controller 当中,看看能不能实现对象的使用:

@Controller
public class UserController {
    @Autowired
    private User user;
    public void sayHi() {
        System.out.println("User-> " + user);
    }
}

然后通过 UserController 来使用对象:

public class Test1 {

    public static void main(String[] args) {
        //得到上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean
        UserController userController = context.getBean(UserController.class);
        //使用 bean
        userController.sayHi();
    }
}

运行结果如下:
在这里插入图片描述
报错提示说,User 对象被注入了两个。一个是 userInfo,一个是 user2。因为 Autowired 在注入的时候,是先根据类型来查找,然后就会去找 User 对象,找 User 类型的对象在 Spring 当中有没有。然后就找到了两个,然后就报错。

一个对象注入多次的问题

就是上面这种情况,在扫描的时候,发现了多个对象。解决方案如下:

  1. 精确的描述 bean 的名称,就是将注入的名称写对。比如说我们注入 user1 :

    @Controller
    public class UserController {
        @Autowired
        private User user1;
        public void sayHi() {
            System.out.println("User-> " + user1);
        }
    }
    

    运行结果如下:
    在这里插入图片描述

  2. 使用 Resource 注解,Resource 是来自 JDK 的,Resource 有 name 属性,就是类似于重命名:

    @Controller
    public class UserController {
        @Resource(name = "user1")
        private User user;
        public void sayHi() {
            System.out.println("User-> " + user);
        }
    }
    

    运行结果如下:
    在这里插入图片描述

  3. 通过 Autowired 搭配 Qualifier :

    @Controller
    public class UserController {
        @Autowired
        @Qualifier("user1")
        private User user;
        public void sayHi() {
            System.out.println("User-> " + user);
        }
    }
    

    运行结果如下:
    在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lockey-s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值