一、Spring中,原始的存对象、取对象
1.0 存、取对象的必要性
在我之前的的博客浅谈Sping Framework(不知道怎么创建一个Spring项目的同学也可以移步到我的上一篇博文中进行查看学习)中提到了Spring框架的两个核心:ioc(控制反转)和DI(依赖注入),所以Spring的核心功能就是解耦合。
将Bean(对象)托管在Spring中,需要用的时候就将Bean取出来使用就好了。那么,我们应该怎样将Bean存入Spring,又怎样将Bean从Spring中取出来呢?(以下都使用Bean来表示对象)。
插播一下一点知识点(我们创建的项目中如果有pom.xml文件的话,说明这是一个Maven项目,如果在pom.xml文件中添加Spring依赖之后,这个Maven项目就变成了一个Spring项目;如果在pom.xml文件中添加Servlet依赖之后,这个Maven项目就变成了一个Servlet项目;从这里就能感觉到这有点像画画,同是一张白纸,使用铅笔去画,就会得到一副素描;使用水彩去画,就会得到一副水彩画;使用墨水去画,就会得到一副水墨画…)
1.1 将Bean存入Spring中
1.1.1 首先创建类
Bean其实就是类,类实例化之后就是Bean。所以想要将Bean存入Spring中,首先需要拥有Bean。你所创建的Bean根据你自己的需求来创建即可,下边创建的类只是我用以演示。
此处我创建了一个User类
public class User {
public void sayHi(){
System.out.println("执行了User构造方法!");
System.out.println("hello world!");
}
}
1.1.1将Bean配置到Spring当中
我们都知道蓝色的java文件是用来存储java源文件的,而resources文件是用来存储外部资源的,我们在将Bean注入到Spring里之前,需要在resources包下配置Spring配置文件。
在resources包下新建一个File,取名为什么都可以,但是必须以 .xml 结尾,当然了,一般来说编写代码的文件名最好是见名知意,所以我这里取名为 spring-configuratin.xml。
Spring配置文件:
<?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">
</beans>
将Spring配置文件配置好了之后,如果想要将创建的User对象存入Spring中,首先在Spring配置文件里写入标签< bean id = “所创建的类的别名(此别名不可重复)” class = “包名 + 类名(标注了一个扫描路径)”>< /bean> ,如果你创建的类就在蓝色Java文件下的话,你只需要在class中填入一个类名,如果你的类名是在包下,你的class中就需要填入包名+类名。因为我的User类就在Java文件下,所以我的class里直接填入User。
此时,就将Bean存入Spring中了。当Spring框架启动之后,这个Bean就会被注入到Spring当中,所以其实在Spring配置文件中书写< bean>< /bean>相当于声明。
1.2 将Bean从Spring中取出
1.2.1 获取Spring上下文对象(这个上下文对象其实就是Spring本身)
有两个类可以去获取Spring上下文对象:
1、类1:ApplicationContext
1、类2:BeanFactory
BeanFactory这个类出现得比较早,现在也算是比较古老的了,所以有些方法现在已经不再继续使用。它的使用方法和ApplicationContext一样。
1.2.2 使用getBean()方法获取Bean
其实getBean()是个重载方法,还有几种办法可以获取到Bean,但是那些办法或多或少会出现异常,所以上述介绍的方法是最好的办法,同学们只需要记住这种最好的办法就行了。
1.2.3 使用Bean(可选)
获取到Spring中的Bean之后,你既可以选择使用也可以选择不使用,但是一般来说,既然获取了Bean,都是为了要使用Bean才会想去获取。
1.3 经典面试题——>ApplicationContext和BeanFactory有什么区别??(必记)
1.3.1 相同点
1、都可以得到Spring对象,或者说都可以得到ioc容器。
2、它俩都是来自Spring的顶级接口。
1.3.1 不同点
1、继承关系和功能:
ApplicationContext是属于BeanFactory的子类,所以ApplicationContext继承BeanFactory的所有方法、属性。但儿子有的,父亲不一定有。所以ApplicationContext拥有BeanFactory没有的特性,例如:国际化支持、资源访问支持、事件传播…
2、性能:
ApplicationContext的加载方式是将Bean一次性加载完了,所以在使用了ApplicationContext进行Spring上下文对象的获取之后再使用getBean()方法去获取Bean的速度会比较快,因为类加载时加载所有资源。其牺牲了内存空间去提高速率。是“饿汉模式”。但是ApplicationContext会根据Bean的数量去衡量,此时是否值得去牺牲内存空间换取速度,如果不值得,它也不会强行去加载。ApplicationContext有自己的一套策略,可以在饿汉模式和懒汉模式之间智能切换。(我当前的开发环境下ApplicationContext是饿汉模式,不同的开发环境下,ApplicationContext模式不同。)
BeanFactory是懒汉模式,只有Bean是第一次使用时,才会加载。节省内存空间,但是运行速率较低。
二、在Spring中,实现更加简单的存、取对象(通过注解的方式)
2.1 改变Spring配置文件
Spring配置文件:
<?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=" "></content:component-scan>
</beans>
将以上的Spring配置文件复制粘贴到spring-configuration.xml文件中(效果如下):
2.2 使用注解的方式更便捷的存、取对象
2.2.1 五大类注解
注解分为五大类注解、方法注解。首先此处介绍五大类注解,那是哪五大类呢?:
1、@Controller:控制器
2、@Service:服务
3、@Repository(mapper):仓库
4、@Component:组件
5、@Configuration:配置文件
2.2.1.1 使用注解对Bean进行存、取
2.2.1.1.1 在配置文件中声明Bean的路径
1、在配置文件中的的 < content:component-scan base-package=" 类所在的路径(包名(使用注解注入Bean的话,一般类名就不需要写了,如果只想要存、取当前Bean,那么还是写类名吧。将类写在包下的好处就是一个包可以写许多类,此时路径只需要填写类所在的包就行了,这时Spring就会扫描到该包下的所有类,此时就能一次性存、取很多个Bean,节约了开发成本,提高了开发效率),如果类在源文件java文件下的话,路径是写通配符 ** ">< /content:component-scan>
2.2.1.1.2 在 所想注入进Spring的类 的顶部标上注解(五大类注解中的任一个注解都可以)
以我创建的ArticleController 类做演示:
为什么五大类注解中的任一个注解都可以呢??因为五大类注解每个注解的功能作用都是一样的,都是能便捷的将Bean注入Spring中。但是既然五大注解的功能作用都一样,五大类注解里设置五个不同的注解有啥意义呢?? 就像车牌,我们可以想一想,如果每张车牌都是用中xxxx标识,那么此时车牌的可读性变得很低哇,毫无意义了。就像有些城市会将车道分外省外车道、省内车道,疫情期间,走省外车道的车辆就需要出示48小时核算检查,而省内车牌的车辆就不需要。那么怎么辨认车辆是省内车牌还是省外车牌呢??依靠车牌,比如京A就是北京的车牌号,桂A是南宁的车牌。车牌是用来标识车辆的所属地的。所以即使5个注解的功能作用都相同,但是不同注解描述了当前类的不同业务类型。
2.2.1.1.3 获取Bean
跟上面一样,还是使用类ApplicationContext或类BeanFactory获取Spring上下文对象,然后再使用getBean()方法获取Bean。但是getBean里的参数有所改变。
由于使用注解时,是使用类名(将首字母字母小写)作为getBean()的第一个参数。但是如果说:
1、编写代码时,类名命名不规范,其首字母和第二个字母都是大写(例如类名BController),这时候将类名首字母小写后将类名作为getBean()第一个参数,还能获取到Bean吗??
创建类
public class BController {
public void print(){
System.out.println("BController 的方法!");
}
}
运行结果:
报错了。 所以编写代码时,类名命名不规范,其首字母和第二个字母都是大写(例如类名BController)这时候将类名首字母小写之后作为getBean()第一个参数,不能获取到Bean。
那么此时应该如何获取Bean对象呢??查看源码分析。
在IDEA中,连续按下shift两下,出现全局搜索,在搜索栏里输入beanName,点击Files,找到AnnotationBeanNameGenerator,点击AnnotationBeanNameGenerator进去,跳转到Spring提供的源码页,在AnnotationBeanNameGenerator里找到buildDefaultBeanName,将光标移到buildDefaultBeanName上停留,长按住ctrl,鼠标点击buildDefaultBeanName,跳转到buildDefaultBeanName方法里,再找到Introspector.decapitalize,点击decapitalize跳转到decapitalize里面,然后得到类命名规则的源码,我们来分析一下源码。
2、编写代码时,类名命名不规范,没有使用大驼峰的命名规则,类名其首字母是小写(例如类名aController),这时候直接作为getBean()第一个参数,还能获取到Bean吗??
可以获取到。和类名首字母是大写时一样,都是通过将类名的首字母变成小写的。原理参照上述的源码分析。
3、如果编写代码时,直接将类写在java源文件下,使用注解存、取Bean时,在配置文件中的路径该怎么写??使用原始存、取对象方法中的直接写(包名+)类名??此时还能获取到Bean吗??
无法获取到Bean。路径是使用通配符 **,但是这样写代码,会导致Spring扫描花费很长的时间,所以不建议将类直接写在java源代码下,建议将类写在包下。
2.2.2 方法注解
方法注解就是在方法的顶部标注——>@Bean,但是使用方法注解获取Bean时,必须搭配五大类注解使用,不然代码报错。
一般情况下,我们都是使用方法的方法名作为getBean()的第一个参数去获取Bean,但是在特殊情况下,会获取不到Bean,代码报错。
例如具有两个类 ,类1StudentBeans,其中含有一个student()方法,类2是StudentBeans2,其中也含有一个方法student()。类名很大概率上不会重名,但是方法名很大概率上会重名。类1StudentBeans和类2StudentBeans2中的方法重名,并且返回类型一样,都是Student类型。
此时获取Bean,测试类App中打印的信息是李四对象。
但是当我们将类StudentBeans2注释掉,只剩下类StudentBeans时,一样的获取Bean的代码,获取到的对象是张三。
原因是系统不知道该去获取哪个对象,因为使用的都是方法注解,返回的对象类型也一样,都是Student。针对这个问题,方法注解也给出解决办法。方法注解可以设置它的name属性,@Bean(name = {“合法标识名即可1”,“合法标识名即可2”,“合法标识名即可3”,…}),因为是一个{}花括号,所以可以设置多个name属性,使用name属性作为getBean()的第一个参数即可获取到对象,在具有多个相同方法名的情况下。
获取Bean,此时即使存在多个相同的方法名,由于使用了name属性标识,也能正确获取你想获取的Bean。
那么方法注解加上了name属性,此时不能再使用方法名作为getBean()的第一个参数去获取Bean,只能通过name属性里设置的值作为getBean()的第一个参数去获取Bean。这就有点像买了一辆新车,新车开到车管所进行上牌,在路上,交警见到新车是不会拦的,交警知道你是新车要到车管所进行上牌,新车上完牌以后(这里就相当于方法注解的name属性),不能说车开了一段时间,又把车牌揭下来,无牌驾驶是违反交通法规的。
2.3 五大类注解的区别
1、五大类注解中,虽然五个注解都具有相同的功能作用,但是不同的类注解描述不同的业务类型,通过类注解可以了解当前类的用途。就像看到不同的车牌号,就能知道车辆的归属地是哪里一样。
2、功能有细微差别。 (有什么差别,这里先不展开说,比较复杂,解释的话还需要一段篇幅,就不放在这讲了)。
2.4 五大类注解的用途(必记)
很重要!! 这是企业在用的东西,行业标准,以后开发的任何Spring系列项目都必须按照这个规范来。
1、@Controller(控制器):归属于业务逻辑层,用来控制用户的行为,检查用户参数的有效性。
什么叫做业务逻辑层呢??业务逻辑层是直接和用户打交道的,用户一进来之后,第一个访问的就是控制器。举个栗子,就像咱们的地铁站,乘客进入地铁站乘车第一个接触的就是安保系统,安保人员对乘客进行全身检查确保安全之后才会放行乘客进入候车区。
2、@Service(服务):归属于服务层,调用持久化类实现相应功能。
举个例子,通俗的解释@Service。@Service就像医院的就诊前台中心,如果一个不熟悉医院门诊的病人去就诊,病人就会找到就诊前台中心的护士询问,护士们就会给病人指路,此时护士起到了一个服务人员的作用,为那些对医院门诊、缴费业务具有疑问的病人答疑解惑,提出建议。
3、@Repository(仓库):归属于持久层,是直接和数据库进行交互。通常每个表都会对应一个@Repository。
在上面提到了@Service是调用持久化类实现相应功能,这句话是什么意思,在以下举个例子来理解。用户从就诊前台的护士处知道了门诊的具体路径之后,找到了@Repository,@Repository就相当于一个医生,医生听完用户的病状描述之后,会在自己所学的医学知识体系中(就相当于数据库,从数据库检索数据)进行数据检索,判断用户的病症,然后给用户开单检查,开单拿药。
4、@Configration(配置文件):归属于配置层,用来配置当前项目的一些信息。
比如登录功能,需要有拦截器,哪些URL需要拦截,哪些不需要拦截,需要在配置文件里去配置。
5、@Component(组件):归属于公共工具类。
在咱们的业务实现里,还有一些类比较特殊,它是凌驾于以上四种注解的。@Component专门提供某些公共方法。
2.5 更简单的获取Bean
从Spring中获取Bean叫做对象装配,有时也叫做对象注入。程序运行期间,动态地将Bean注入到当前类中。
2.5.1 属性注入
属性注入是使用 注解@AutoWired (Auto:自动,Wired :写)实现的,注解@AutoWired叫做自动写入。
当前我想要将Service类中的Bean注入到Controller类中,此时该怎么操作呢??
APP测试类里如果controller类能够获取到service类的Bean,说明通过属性注入获取Bean成功。
从上述图片知道,获取成功了。