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

简介: Spring-core 更简单的读取和存储对象;相关注解的用法

 目录

1.存储 Bean 对象

1.1 前置⼯作:配置扫描路径

1.2 添加注解存储 Bean 对象

1.2.1 @Controller(控制器存储)

1.2.2 @Service(服务存储)

1.2.3 @Repository(仓库存储)

1.2.4 @Component(组件存储)

1.2.5 @Configuration(配置存储)

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

1.3.1 类注解之间的关系

1.3.2 Bean 命名规则

1.4 方法注解 @Bean

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

1.4.2 重命名 Bean

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

2.1 属性注入

2.2 构造方法注入

2.3 Setter 注入

2.4 三种注入优缺点分析

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

@Autowired 和 @Resource 的区别

2.6 同⼀类型多个 @Bean 报错

总结


在 Spring 中想要更简单的存储和读取对象的核⼼是使⽤注解,也就是我们接下来要学习 Spring 中的相关注解,来存储和读取 Bean 对象。

1.存储 Bean 对象

之前我们存储 Bean 时,需要在 spring-config 中添加⼀⾏ bean 注册内容才⾏,如下图所示:

image.gif编辑

⽽现在我们只需要⼀个注解就可以替代之前要写⼀⾏配置的尴尬了,不过在开始存储对象之前,我们先要来点准备⼯作。

1.1 前置⼯作:配置扫描路径

注意:想要将对象成功的存储到 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="org.example"></content:component-scan>
</beans>

image.gif

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

1.2 添加注解存储 Bean 对象

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

    1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
    2. ⽅法注解:@Bean。

    接下来我们分别来看。

    1.2.1 @Controller(控制器存储)

    使⽤ @Controller 存储 bean 的代码如下所示:

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

    image.gif

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

    public class App {
        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("zhangsan");
        }
    }

    image.gif

    1.2.2 @Service(服务存储)

    使⽤ @Service 存储 bean 的代码如下所示:

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

    image.gif

    读取 bean 的代码:

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

    image.gif

    1.2.3 @Repository(仓库存储)

    使⽤ @Repository 存储 bean 的代码如下所示:

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

    image.gif

    读取 bean 的代码:

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

    image.gif

    1.2.4 @Component(组件存储)

    使⽤ @Component 存储 bean 的代码如下所示:

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

    image.gif

    读取 bean 的代码:

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

    image.gif

    1.2.5 @Configuration(配置存储)

    使⽤ @Configuration 存储 bean 的代码如下所示:

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

    image.gif

    读取 bean 的代码:

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

    image.gif

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

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

    那么为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类的⽤途,⽐如:

      • @Controller:表示的是业务逻辑层;
      • @Servie:服务层;
      • @Repository:持久层;
      • @Configuration:配置层。

      程序的⼯程分层,调⽤流程如下:

      image.gif编辑

      1.3.1 类注解之间的关系

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

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

      1.3.2 Bean 命名规则

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

      image.gif编辑

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

      image.gif编辑

      这个时候,我们就要查询 Spring 关于 bean 存储时⽣成的命名规则了。

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

      image.gif编辑

      顺藤摸⽠,我们最后找到了 bean 对象的命名规则的⽅法:

      image.gif编辑

      它使⽤的是 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);
      }

      image.gif

      1.4 方法注解 @Bean

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

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

      image.gif

      获取 bean 对象中的 user1

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

      image.gif

      然⽽,当获取 bean 对象中的 user1 时却发现,根本获取不到:

      image.gif编辑

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

      在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:

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

      image.gif

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

      image.gif编辑

      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;
       }
      }

      image.gif

      此时我们使⽤ 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);
       }
      }

      image.gif

      重命名的 name 其实是⼀个数组,⼀个 bean 可以有多个名字;

      并且 name={} 可以省略。

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

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

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

        1. 属性注⼊
        2. 构造⽅法注⼊
        3. Setter 注⼊

        下⾯我们按照实际开发中的模式,将 Service 类注⼊到 Controller 类中。

        2.1 属性注入

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

        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;
         }
        }

        image.gif

        Controller 类的实现代码如下:

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

        image.gif

        获取 Controller 中的 getUser ⽅法:

        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());
         }
        }

        image.gif

        最终结果如下:

        image.gif编辑

        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);
         }
        }

        image.gif

        如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略

        但是如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法,否则程序会报错

        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);
         }
        }

        image.gif

        2.4 三种注入优缺点分析

        属性注⼊

          • 优点是简洁,使⽤⽅便;
          • 缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)。不能注入一个final修饰的属性。

          构造⽅法注⼊

            • 优点是可以注入final修饰的属性,注入的对象不会被修改,依赖对象在使用前一定会被完全初始化,通用性好
            • 缺点是如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了

            Setter 注入

              • 优点是方便再类实例之后,重新对该对象进行配置或注入
              • 不能注入一个final修饰的属性;注入对象可能会被改变,因为setter方法可能会被多次调用,就有被修改的风险

              补充:

              final修饰的属性,需要具备下列两个条件之一:

              image.gif编辑

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

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

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

              image.gif

              @Autowired 和 @Resource 的区别

                • 出身不同:@Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
                • 使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name设置,根据名称获取 Bean。
                • @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊。

                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;
                 }
                }

                image.gif

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

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

                image.gif

                image.gif编辑

                报错的原因是,⾮唯⼀的 Bean 对象。

                同⼀类型多个 Bean 报错处理

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

                  • 使⽤ @Resource(name="user1") 定义。
                  • 使⽤ @Qualifier 注解定义名称。

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

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

                  image.gif

                  ② 使⽤ @Qualifier

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

                  image.gif

                  总结

                  1. 将对象存储到 Spring 中:

                         a. 使⽤类注解:@Controller、@Service、@Repository、@Configuration、@Component【它们之间的关系】

                         b. 使⽤⽅法注解:@Bean【注意事项:必须配合类注解⼀起使⽤】

                  2. Bean 的命名规则:⾸字⺟和第⼆个字⺟都⾮⼤写,⾸字⺟⼩写来获取 Bean,如果⾸字⺟和第⼆个字⺟都是⼤写,那么直接使⽤原 Bean 名来获取 Bean。

                  3. 从 Spring 中获取对象:

                         a. 属性注⼊

                         b. Setter 注⼊

                         c. 构造函数注⼊(推荐)

                  4. 注⼊的关键字有:

                         a. @Autowired

                         b. @Resource

                  5. @Autowired 和 @Resource 区别:出身不同; 使⽤时设置参数不同 @Resource ⽀持更多的参

                  数,⽐如 name。

                  6. 解决同⼀类型多个 Bean 的报错:

                         a. 使⽤ @Resource(name="")

                         b. 使⽤ @Qualifier(value="")

                  相关文章
                  |
                  4月前
                  |
                  XML Java 测试技术
                  Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
                  这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
                  Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
                  |
                  2月前
                  |
                  Java Spring
                  获取spring工厂中bean对象的两种方式
                  获取spring工厂中bean对象的两种方式
                  45 1
                  |
                  2月前
                  |
                  前端开发 Java Spring
                  【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
                  【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
                  121 2
                  |
                  2月前
                  |
                  存储 Java 程序员
                  SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解
                  本文详细讲解了Spring框架中IOC容器如何存储和取出Bean对象,包括五大类注解(@Controller、@Service、@Repository、@Component、@Configuration)和方法注解@Bean的用法,以及DI(依赖注入)的三种注入方式:属性注入、构造方法注入和Setter注入,并分析了它们的优缺点。
                  33 0
                  SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解
                  |
                  4月前
                  |
                  安全 Java C#
                  Spring创建的单例对象,存在线程安全问题吗?
                  Spring框架提供了多种Bean作用域,包括单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)、全局会话(GlobalSession)等。单例是默认作用域,保证每个Spring容器中只有一个Bean实例;原型作用域则每次请求都会创建一个新的Bean实例;请求和会话作用域分别与HTTP请求和会话绑定,在Web应用中有效。 单例Bean在多线程环境中可能面临线程安全问题,Spring容器虽然确保Bean的创建过程是线程安全的,但Bean的使用安全性需开发者自行保证。保持Bean无状态是最简单的线程安全策略;
                  |
                  5月前
                  |
                  缓存 安全 Java
                  Spring高手之路21——深入剖析Spring AOP代理对象的创建
                  本文详细介绍了Spring AOP代理对象的创建过程,分为三个核心步骤:判断是否增强、匹配增强器和创建代理对象。通过源码分析和时序图展示,深入剖析了Spring AOP的工作原理,帮助读者全面理解Spring AOP代理对象的生成机制及其实现细节。
                  53 0
                  Spring高手之路21——深入剖析Spring AOP代理对象的创建
                  |
                  4月前
                  |
                  存储 Java API
                  |
                  5月前
                  |
                  存储 Java 分布式数据库
                  使用Spring Boot和HBase实现大数据存储
                  使用Spring Boot和HBase实现大数据存储
                  |
                  6月前
                  |
                  NoSQL Java Spring
                  教程:Spring Boot与ETCD键值存储的整合
                  教程:Spring Boot与ETCD键值存储的整合
                  |
                  6月前
                  |
                  存储 XML Java
                  在 Java 中,Spring 框架提供了一种更加简单的方式来读取和存储对象
                  【6月更文挑战第18天】Java Spring 框架利用注解简化对象管理:@Component(及衍生注解@Service等)标注Bean类,自动注册到容器;@Autowired用于字段或方法,实现依赖注入,提升灵活性,减少XML配置。
                  34 2