Spring中更简单地读取和存储对象(使用注解)

之前学习的spring的存储和读取对象,借助<beans>来将类的对象注册到spring框架中去(如图),但是我们每注册一个对象还要加一个bean过于繁琐和浪费时间,因此在spring中加入了注解来简化注册对象到spring中的过程

 

1.存储 Bean 对象 

1.1 前置⼯作:配置扫描路径(重要)

 虽然通过注解能够方便存储对象到spring中,但是在存储之前要设置spring扫描的地方即包的路径,只有你的类对象在设置的相应的地方时候spring才能通过注解获取并存储对象,还是先在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.bit.service">
</content:component-scan>
</beans>

 

其中扫描的路径就是最后一行进行配置的路径,也就是所需存储的类的位置 。

也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的

1.2 添加注解存储 Bean 对象

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

1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。

2. ⽅法注解:@Bean。

1.2.1 @Controller(控制器存储)

@Controller // 将对象存储到 Spring 中
public class UserController {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

 此时我们先使⽤之前读取对象的⽅式来读取上⾯的 UserController 对象,如下代码所示

public class Application {
    public static void main(String[] args) {
        // 1.得到 spring 上下⽂
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        // 2.得到 bean
        UserController userController = (UserController)
context.getBean("userController");
        // 3.调⽤ bean ⽅法
        userController.sayHi("Bit");
   }
}

1.2.2 @Service(服务存储) 

@Service
public class UserService {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

 读取方式和controller相同这里和下方都省略。。。

1.2.3 @Repository(仓库存储)

@Repository
public class UserRepository {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

1.2.4 @Component(组件存储)

@Component
public class UserComponent {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

 1.2.5 @Configuration(配置存储)

@Configuration
public class UserConfiguration {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

1.3 为什么要这么多类注解? 

在我们写一个大型程序的时候,往往需要不同的人完成不同的模块,就是为了让程序员看到不同的注解明白当前的代码 具体完成的是哪一个模块,上述我们展示的看起来每个注解好像都可以,其实功能就是一样的,实际上就是为了区分模块,举一个例子如下:

这和为什么每个省/市都有⾃⼰的⻋牌号是⼀样的?⽐如陕⻄的⻋牌号就是:陕X:XXXXXX,北京的⻋ 牌号:京X:XXXXXX,⼀样。甚⾄⼀个省不同的县区也是不同的,⽐如⻄安就是,陕A:XXXXX,咸 阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样。这样做的好处除了可以节约号码之外,更重要的作 ⽤是可以直观的标识⼀辆⻋的归属地

@Controller:表示的是业务逻辑层;

不同的项目有不同的功能,不同的功能需要不同的实现,实现这些核心功能的代码就叫业务逻辑
比如让你实现一个功能,给你两个数,让你获取它的和,你所写的如何才能获得任意给定的两个数的和,这个程序实现过程即可成为业务逻辑处理。

@Servie:服务层;

服务层实际上不执行任何具体的工作,其功能在于组织各个业务对象、应用程序专有的服务、工作流以及其他任何出现在业务逻辑中的特殊组件。

@Repository:持久层;

可以理解成数据 保存在 数据库或者 硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上

@Configuration:配置层 

执行流程如图

 1.3.1 类注解之间的关系 

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

 其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”

1.3.2 注意 Bean 命名

通过上⾯示例,我们可以看出,通常我们 bean 使⽤的都是标准的⼤驼峰命名,⽽读取的时候⾸字⺟⼩ 写就可以获取到 bean 了,如下图所示

然⽽,当我们⾸字⺟和第⼆个字⺟都是⼤写时,就不能正常读取到 bean 了,如下图所示: 

 我们可以在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容:

它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下

public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
   }
    // 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
        Character.isUpperCase(name.charAt(0))){
        return name;
   }
    // 否则就将⾸字⺟⼩写
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

所以对于上⾯报错的代码,我们只要改为以下代码就可以正常运⾏了 

 

1.4 ⽅法注解 @Bean

类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的,如以下代码的实现 

public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
   }
}

 然⽽,当我们写完以上代码,尝试获取 bean 对象中的 user1 时却发现,根本获取不到

public class Application {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        User user = (User) context.getBean("user1");
        System.out.println(user.toString());
   }
}

 执行结果如图

 

1.4.1 ⽅法注解要配合类注解使⽤

在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,因为一个类可能有多个方法,spring扫描所有的方法过于繁琐,因此spring设计的是按照类进行扫描如 下代码所示: 

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

再次执⾏以上代码,运⾏结果如下

 

1.4.2 重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所示

@Component
public class Users {
    @Bean(name = {"u1"})
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
   }
}

 此时我们使⽤ u1 就可以获取到 User 对象了,如下代码所示

class App {
    public static void main(String[] args) {
        // 1.得到 spring 上下⽂
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        // 2.得到某个 bean
        User user = (User) context.getBean("u1");
        // 3.调⽤ bean ⽅法
        System.out.println(user);
   }
}

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

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊

对象装配(对象注⼊)的实现⽅法以下 3 种:

1. 属性注⼊

2. 构造⽅法注⼊

3. Setter 注⼊

2.1 属性注⼊

属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中

Service 类的实现代码如下:

import org.springframework.stereotype.Service;
@Service
public class UserService {
    /**
     * 根据 ID 获取⽤户数据
     *
     * @param id
     * @return
     */
    public User getUser(Integer id) {
        // 伪代码,不连接数据库
        User user = new User();
        user.setId(id);
        user.setName("Java-" + id);
        return user;
   }
}

 Controller 类的实现代码如下

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
    // 注⼊⽅法1:属性注⼊
    @Autowired
    private UserService userService;
    public User getUser(Integer id) {
        return userService.getUser(id);
   }
}

 获取 Controller 中的 getUser ⽅法:

import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserControllerTest {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        UserController userController =
context.getBean(UserController.class);
        System.out.println(userController.getUser(1).toString());
   }
}

最终结果如下:

 

 属性注⼊的核⼼实现如下:

2.2 构造⽅法注⼊

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示

@Controller
public class UserController2 {
    // 注⼊⽅法2:构造⽅法注⼊
    private UserService userService;
 @Autowired
    public UserController2(UserService userService) {
        this.userService = userService;
   }
    public User getUser(Integer id) {
        return userService.getUser(id);
   }
}

 2.3 Setter 注⼊

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注 解,如下代码所示:

@Controller
public class UserController3 {
    // 注⼊⽅法3:Setter注⼊
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
   }
    public User getUser(Integer id) {
        return userService.getUser(id);
   }
}

2.4 三种注⼊优缺点分析

属性注⼊的优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只 有在使⽤的时候才会出现 NPE(空指针异常),但是大多数情况下我们为了偷懒,并且多数使用也就是在spring中,因此这种方法应用较多

构造⽅法注⼊是 Spring 推荐的注⼊⽅式,它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这种 情况应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点是通⽤性,就是每种语言的set方法可能不尽相同,但是构造方法基本一致,还要在使⽤之 前⼀定能把保证注⼊的类不为空。

Setter ⽅式是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法,所有 Spring 现版本已经 推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了。

2.5 @Resource:另⼀种注⼊关键字

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如 下代码所示:

@Controller
public class UserController {
    // 注⼊
    @Resource
    private UserService userService;
    public User getUser(Integer id) {
        return userService.getUser(id);
   }
}

@Autowired 和 @Resource 的区别:

1.其出身不一样,前者是来自于spring框架,后者来自于jdk中自带的jar包

2.使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean,当我们spring中有一个类的多个对象时候,并且在树形注入时候并没有按照对象的名称进行注入,那么当我们获取对象时候Spring会按照先根据名称查找,差找不到,因为我们没有使用对应的名称,后按照类查找的方式进行但是其查找结果,发现结果不止一个,spring 不知道获取哪一个就会报错

2.6 同⼀类型多个 @Bean 报错

当出现以下多个 Bean,返回同⼀对象类型时程序会报错,如下代码所示

@Component
public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
   }
    @Bean
    public User user2() {
        User user = new User();
        user.setId(2);
        user.setName("MySQL");
        return user;
   }
}

 在另⼀个类中获取 User 对象,如下代码如下

@Controller
public class UserController4 {
    // 注⼊
    @Resource
    private User user;
    public User getUser() {
        return user;
   }
}

以上程序的执⾏结果如下:

 同⼀类型多个 Bean 报错处理

解决同⼀个类型,多个 bean 的解决⽅案有以下两个

使⽤ @Resource(name="user1") 定义

使⽤ @Qualifier 注解定义名称

① 使⽤ @Resource(name="XXX")

@Controller
class UserController4 {
    // 注⼊
    @Resource(name = "user1")
    private User user;
    public User getUser() {
        return user;
   }
}

② 使⽤ @Qualifier

@Controller
public class UserController5 {
    // 注⼊
    @Autowired
    @Qualifier(value = "user2")
    private User user;
    public User getUser() {
        return user;
   }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值