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

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


1.存储 Bean 对象 1.1 前置?作:配置扫描路径(重要) 虽然通过注解能够方便存储对象到spring中,但是在存储之前要设置spring扫描的地方即包的路径,只有你的类对象在设置的相应的地方时候spring才能通过注解获取并存储对象,还是先在spring-config.xml中配置以下文件

spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

其中扫描的路径就是最后一行进行配置的路径,也就是所需存储的类的位置 。
也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的
1.2 添加注解存储 Bean 对象 想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
【spring|Spring中更简单地读取和存储对象(使用注解)】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:配置层
执行流程如图
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

1.3.1 类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

其实这些注解??都有?个注解 @Component,说明它们本身就是属于 @Component 的“?类”
1.3.2 注意 Bean 命名
通过上?示例,我们可以看出,通常我们 bean 使?的都是标准的?驼峰命名,?读取的时候?字?? 写就可以获取到 bean 了,如下图所示
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

然?,当我们?字?和第?个字?都是?写时,就不能正常读取到 bean 了,如下图所示:
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

我们可以在 Idea 中使?搜索关键字“beanName”可以看到以下内容:spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

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

所以对于上?报错的代码,我们只要改为以下代码就可以正常运?了
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

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

执行结果如图spring|Spring中更简单地读取和存储对象(使用注解)
文章图片


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

再次执?以上代码,运?结果如下
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

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

最终结果如下:
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

属性注?的核?实现如下:
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

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

以上程序的执?结果如下:
spring|Spring中更简单地读取和存储对象(使用注解)
文章图片

同?类型多个 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 = "https://www.it610.com/article/user2") private User user; public User getUser() { return user; } }


    推荐阅读