【Spring----对象的读取和存储】

一、存储Bean对象

1.修改配置文件

传统的存储Bean对象的方式:

更简单的存储Bean对象的方式:

此处我们看到,我们配置了存储对象的扫描包路径,只有被配置包下的所有类,添加了注解才能被正确的识别并保存到Spring中。

2.添加注解存储对象

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

1、类注解:@Controller、@Service、@Repository、@Component、@Configration

2.方法注解:@Bean

我们先了解类注解各自的含义:

1.@Controller:业务逻辑层,前端传过来的一系列请求在此层进行解析;

2.@Service:服务层,供业务逻辑层调用,主要负责对数据的一系列操作;

3.@Repository:数据持久层,供服务层调用,例如数据库的交互表的CRUD操作;

4.@Configration:系统配置信息。

5.@Component:组件(公共组件/class)

不同功能的代码放在不同的层,使用不同的注解,方便后期程序的维护和调试。

下来对这些注解的使用进行演示:

(1)、@Controller

 此处我们可以看到,controller包存放在com.zyp这个路径下,后续Spring启动的时候,就会对这个路径下的所有的类进行扫描,并且将加注解的类注册到Spring框架中。在controller这个包下面,存放了诸多关于业务逻辑代码,此时我们在UserController这个类前面加上@Controller这个注释,那么当Spring启动扫描路径下的包时,就会将这个类注册到Spring框架中供后续的调用。其他注释也是类似,后续不再进行描述~

(2)、@Service

 (3)、@Repository

 注意:数据持久层我们一般写作dao包,而不会写作repository包~

(4)、@Configration

 (5)、@Component

 (6)、@Bean(方法注解)

 使用方法注解时要特别注意:在使用时还需要搭配类注解来使用,因为Spring在启动的时候,默认的扫描类级别的注解,如果当前方法使用了方法注解@Bean,而其所属的类没有使用类注解,Spring是扫描不到这个方法注解的,更不会将当前方法对象存储到Spring框架中,因此,使用@Bean注解时,我们常见的做法是在其所属类前加上@Component配合使用,这样Spring在扫描时才会扫描到这个方法注解。

 面试问题:@Controller、@Service、@Repository、@Configration、@Component之间有什么关系?

 

 

 

我们从源码中可以看出这四种注解都使用了@Component注解,什么意思?我们可以理解为,这四组注解都是@Component的子类。 

二、获取Bean对象

我们之前所了解的存储对象到Spring框架中,所使用的方法是在配置文件中添加,类似于这样的操作:

 我们这里不再使用这种方式,而是使用注解的方式存储,那么这个id是如何指定的呢?

当我们获取bean对象的时候,使用id+类型来获取,使用注解来存储对象的方式,默认的id是类名,但是对于这个类名又有新的规则,那么我们先看下一版块,看看默认的beanName如何生成?

关于Bean对象的获取,我们首先来了解Spring是如何生成类注解的beanName:

 

 图二为具体的生成默认beanName的方法,这个方法生成beanName的方法并非是Spring jar包中的方法,而是JDK方法库里面的方法。这个方法的具体实现:如果传过来的name为null或者为空串,那么直接返回name,如果是非空串,则判断,判断这个name的长度是否大于1,如果是两个及以上的字符,判断第二个字符是否为大写以及第一个字符是否为大写,如果这两个条件成立,那么则直接返回name,不进行更多操作,否则,执行下面的步骤,将name串的首字符转换为小写返回。

 我们了解完生成beanName的规则之后,我们来进行验证:

 我们将UComponent这个类存储到Spring框架中,我们再去获取:

 此时我们成功拿到了bean对象,并执行了bean对象里面的方法。

 特殊的注释---@Bean,方法注解:

首先,我们使用@Bean将方法存入到Spring中的时候,获取时的类型,是返回值的类名.class,而非所属类的类名.class;

在使用@Bean时,我们可以手动的指定获取对象的id,@Bean重命名可以有多个别名,但是当指定别名之后,使用方法名时就会不起作用~

假设我们此时有两个返回值相同的方法Bean对象,那么我们在获取时如何进行区分呢?

 可以看到我们分别在注解后对Bean对象进行了重命名,获取的时候就根据这个重命名来取,此时上述所说的默认beanName将不再生效。

 此时要想通过原来所说的方式取到u1对象,我们的做法是重命名为多个名字,此时就可以通过多个名字来获取bean,如图:

 测试结果:

 注意:倘若方法1和方法2使用了相同的别名,此时去通过这个相同别名去获取bean对象的时候,那么默认的,就会返回两个方法1的值。避免方式是通过对一个方法添加另外一个别名,使用另外一个别名来获取。

我们知道,Spring是一个包含了众多方法工具的IoC容器,IoC是其设计思想,DI是其具体实现,那么我们如何实现依赖注入(获取对象)?

下面我们进入正题,介绍三种从Spring中获取对象的手段:

1、属性注入(属性注册)

我们在UserController中调用UserService:

业务逻辑层代码:

注意事项:参数类型使用包装类,因为是Controller层,接收到null值,不会出现500服务器错误。

服务层代码:

使用注解@Autowired

我们先不对@Autowired这个注释进行描述,在演示完成后我们详细描述这个注释的具体作用~

此处业务逻辑层的属性类型是一个自定义的用户类类型,此时就可以从Spring框架中获取到UserService对象,这样的过程就是依赖注入。

具体过程: 先获取上下文对象,然后获取到UserController对象,调用UserController里面的findUserById 方法查询用户信息,方法内部需要注入UserService对象,在UserService这个属性前加上了@Autowired这个注释,意味着从Spring框架中将UserService对象注入到这个字段中,之后再通过这个对象调用UserService类中的findUserId方法~

2、Setter注入

同样的,我们在UserController2中调用UserService:

UserController2代码:

 UserService代码与上面相同,这里不再进行演示。

同样的,我们使用了@Autowired

区别在于,我们这次并没有在属性前加@Autowired,而是在其setter方法前加上这个注解,下面我们来验证这种方法的可行性:

 这个代码的执行过程和属性注入类似,唯一区别在于对依赖的注入方式不同。

3、构造方法注入

同样的,我们在UserController3中调用UserService:

UserController3代码:

 此方法我们的@Autowired注解加到了构造方法前面,下面我们对这种方式进行验证:

 同样的效果,区别也是对于@Autowired的使用方式不同~

注意事项:如果当前类里面只有一个构造方法,那么@Autowired注解是可以被省略的(这点和setter注入是不同的)如果当前类中存在一个以上的构造方法,那么@Autowired注解是不能被省略的。

 4、小结(三种注入优缺点分析)

  • 属性注入的优点是简洁,使用方便;缺点是只能用于IoC容器,如果是非IoC容器不可用,并且只有在使用的时候才会出现NPE(空指针异常)
  • 构造方法注入是Spring推荐的注入方式,它的缺点是如果有多个注入会显得比较臃肿,但出现这种情况应该考虑一下当前类是否符合程序的单一指责的设计模式了,它的优点是通用性,在使用之前一定能把保证注入的类不为空
  • Setter方式是Spring早期的推荐注入方式,但通用性不如构造方法,所有Spring版本已经推荐使用构造方法注入的方式来进行类注入了

 5、@Resource:另一种注入关键字

@Resource的用法和@Autowired类似,但又存在一些区别,它的作用也是从Spring框架中获取Bean对象注入到当前属性中。

(1)首先我们来看属性注入:

同样的,在UserController4中调用UserService层对象进行注入:

UserController4代码:

 我们可以在属性字段前加上了@Resource注解,并且我们可以看到,这个注解来自于JDK的方法库中,而非来自于Spring jar包,这点和@Autowired不同。

 下面我们测试这个注解的可行性:

 结果依然是可行的~和@Autowired并无差异~

 (2)在Setter方法前使用@Resource注解

我们在UserController6代码中调用UserService层对象进行注入:

 我们来看看这个注解的可行性:

 同样也是可行的~

 (3)在构造方法上使用@Resource注解进行对象注入

我们在UserController5代码中调用UserService层对象进行注入:

 可以看到,在构造方法前使用@Resource注解,代码会报错,因此,我们的结论是,不能再构造方法前使用@Resource注解进行对象注入,这是不可行的~换句话说,在使用构造方法进行对象注入的时候,只能使用@Autowired进行对象注入,而不能使用@Resource注解进行注入~

 (4)总结:@Autowired 和 @Resource有什么区别?

  • 出身不同,@Autowired 出自于 Spring 框架,而@Resource 出自于 JDK。
  • 作用范围不同,使用@Autowired可以进行属性注入、Setter注入、构造器注入;而@Resource只能进行属性注入和Setter注入。
  • 功能不同,@Resource可以搭配更多的属性进行使用,而@Autowired支持的属性较少,比如使用@Resource可以搭配name属性进行使用,从而完成对象的别名注入。

扩展(@Autowired 和 @Resource 关于注入对象名称的问题):

无论是@Autowired 还是 @Resource ,它们在进行对象注入的时候,它们都是做了两手准备的,它们会先从id或者类型.class 中的一个进行获取,比如先从id进行获取,如果根据id获取不到,此时才会根据另一种类型获取。

例如上面的例子:

 @Autowired先根据类型.class获取,后根据id获取

@Resource先根据id获取,后根据类型.class获取

问题:如果注入的id是不存在的,并且类型有多个,那么注入就会失败。参考上篇文章中说到的。下面我们来看一个案例:

在UserController7中调用UserBean代码中的 @Bean 对象,使其注入到UserController7中:

UserBean代码:

 UserController7代码:

 在UserController7中我们使用的是@Autowired注解,由于Spring框架中并没有userModel这样名称的bean对象,因此会使用类型.class的方式来从Spring框架中获取bean对象,我们测试可行性:

执行代码,系统会报出一下错误信息:

 修改代码进行测试:

 此时,由于我们在使用@Bean 方法注解的时候,将存入到Spring框架中的UserModel对象重命名为了u1,因此在此处使用u1这个名称的时候就可以从Spring框架中获取到这个bean对象。

接下来我们看看使用@Resource情况如何?

 可以看到,也是可以获取到的~

那么问题来了,如果我们非要使用userModel 这个名称呢?(即Spring框架中不存在这个名称的UserModel对象)

有两种解决方案:

1.@Resource(name="指定bean对象的名称")

 

 这个写法的用途在于,当使用@Resource注解来注入对象的时候,根据名称(id)来获取的时候,就不会使用userModel这个名称来查找获取注入的bean对象,而是使用u2这个名称来查找获取并注入到UserModel这个字段中。

2.@Autowired 搭配@Qualifier来解决

 @Qualifier这个注释的中文意思是限定符,用途也是类似的,当@Autowired在Spring框架中查找需要获取注入的bean对象时,先使用类型.class查找获取,当查询不到的时候,就使用名称(id)来查找获取,指定获取的bean对象的名称就不再使用userModel,而是使用@Qualifier中的value值来查找获取~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值