Java 21-Spring知识
1.Spring的体系结构
此前mybatis学习中,我们知道基于maven工程的开发,因此spring的集成也是可以利用maven坐标来完成相应的jar引入,在spring官网的spring-framework中即可看到下面坐标
spring-framework的源码内容分类
在spring-framework-5.0.2的docs的spring-framework-reference里面image中可以看到图片-spring-framework的体系结构
核心容器究竟干了什么?,这就关系到解耦和耦合的概念
这并不是运行期异常,而是在运行期前的编译器异常-idea左下角会出现complication cmpleted with 1 error ;我们未来实际开发中,会遇到很多次这样的编译器异常,这也证明了我们无法在没有依赖的约束情况下进行完美的编译代码;---这一特性也就是耦合:可以简单理解为程序间的依赖关系。分为以下:
1.类之间的依赖
2.方法间的依赖
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。它有如下分类:
(1)内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
(2)公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
(3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
(4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
(5)标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间存在一个标记耦合。
(6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
(7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。总结:耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚与耦合内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。
解耦意思是--当然我们无法完全消除类之间的依赖关系,我们需要某些对象完成某些事,因此我们在未来也是努力降低依赖关系。
我们未来要实现的是:编译期不依赖,而运行期才依赖的目标
也就是说在注册驱动环节中,我们尽量减少new对象,降低类对象的耦合性,读取字符串则显得反射的优秀。如此将会出现在编译器不依赖,运行期才依赖。运行期报错。
曾经代码中的问题分析:
解决曾经代码问题之编写工厂类和配置文件
因为在resources目录下得bean.properties文件最终编译后会在根目录下,因此不用给路径,直接给名字即可,这个地方需要进行try/catch,因为一旦没有properties文件,则无法完成初始化
此时删除掉其中一个impl后,编译无异常,运行当然错误。
单例对象,这也就是为何不建议在servlet中创建类成员变量,容易被多个调用处理,造成数据不准确,不安全。而多例对象就不会出现问题。多例对象对单例对象而已,效率没有单例高。
其实仔细回想,此前我们在service和dao层中没有定义类变量进行处理,都是定义在方法里面的局部变量进行处理,这也就避免了单例风险。
那么如何将多例解决为单例呢,思考下,我们进行初始化操作,这样只加载一次,自然也就多例变为单例。
1.1 IOC 控制反转
有时候,我们会将其理解为降低依赖,但其实我们应该这么想:new的有独立自主权,而工厂由配置文件决定,由工厂控制。我们将这个选择权交接称之为控制反转。带来好处是降低削弱耦合
1.2 spring的IOC正式登场
spring的ioc既不能实现dao层的增删改查,也不能实现表现层的请求参数封装。甚至无法接受请求,它目的就是降低耦合关系,前面的工厂模式降低耦合。而后面IOC将以配置方式来实现之前的编码操作,降低耦合关系。
spring集成知识
可以说pom里面的spring-framework显示的并不是那么简单,依赖自下向上,而对应的Spring的体系结构就知道核心容器的四个类似于放在map中
接下来我们在resources中,建立一个xml文件,名字暂且是bean.xml,里面放置约束,我们可以在spring文档的core下面搜索xmlns
假设放到桌面上,bean.xml,当然一般我们用classPathXmlApplicationContext,这样最为保险,否则用第二个,虽然路径可以随便,但是容易被人误删除
1.2.2 核心容器的两个接口引发的问题
- applicationContext 在构建核心容器时,创建对象采取的策略是立即加载-一读取配置文件,就以反射方式---立即创建配置文件的对象--单例对象适用
- BeanFactory---它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式了,也就是说什么时候根据id获取对象,什么时候才真正创建对象。---多例对象适用
那么什么时候适合用立即加载,什么时候适合用延迟加载呢:
在工厂模式解耦时,我们提到了在service或dao中也好,由于没有类成员,因此没有线程安全问题,可以考虑用单例模式创建service和dao对象,那么service和dao对象在什么时候创建,都是唯一的,自然就可以用立即创建,后面不用变,适用applicationContext,而beanFactory适合多例模式,但是开发中一般用applicationContext多些,因为beanFactory毕竟是父接口,方法较少。 而spring也比较厉害,可以自动识别出来。
当然 把bean交给spring来管理,要解以下几个基本知识点
1)spring对bean管理细节之一:创建bean的三种方式
2)spring对bean管理细节之二:bean对象的作用范围
3)spring对bean管理细节之三:bean对象的生命周期
1)三种方式
a:使用默认构造函数创建-报错:failed to instantiate
b:别人的jar包中类:是字节码文件,无法修改,也不确定它有无无参构造函数,那么如何使用它呢----
这个方式取得是instanceFactory,而不是其实现返回的instanceFactoryservice,得用第二种方法:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器),那个new当成jar包,不是真new
第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器),那个new当成jar包,不是真new
jar包中都是class文件,不是java文件,修改不了;如果我们将某个方法的返回值存入spring容器中,如何做?后面揭晓
============================
bean的作用范围调整:一般来说,我们的bean都是单例bean,
改成多例,用的是bean标签的scope属性,用于指定bean的作用范围
如果不是集群 则global-session就是session
3)bean对象的生命周期
当main方法结束后,线程中所占据的栈内存自动释放,也就是还没来及调用销毁方法,就自动消失了,如果我们要尝试调用销毁方法,需要将classPathApplicationContext对象不进行多态转换(如果看成父类,只能调用父类方法,调用不了子类方法)。
但是当我们进行后,发现多例关容器居然不出现了
1.4 spring 的依赖注入 dependency Injection
2 spring基于注解的IOC和IOC的案例
2.1 spring中的常用注解
引入后,有context空间和约束
扫描itheima 下面的所有类上的注解,进而找到serviceImpl的@conponent
此时只要加上@Component注解的所有类都可以被创建对象,而如果我们想某个独立,则可以给某个类注解加上value,例如此处value="accountService",则调用处不能通过,除非去掉impl
@Controller:一般用在表现层,放其他层也可以
@Service:一般用在业务层,放其他层也可以
@Repository:一般用于持久层,放其他层也可以
这三个注解他们的作用和属性与Component是一模一样的,他们三个是spring框架为我们提供明确的三层使用的注释,是我们的三层对象更加清晰。其实原理简单来理解也就是继承。一般来说,当我们需要创建各个层对象时,可以用对应各个层的注解,例如下:
用于注入数据的注解:
他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的。
Autowired作用是自动按照类型注入,只要容器有唯一的bean对象类型和要注入的变量类型匹配,就可以注入成功,出现位置:可以在变量上,也可以在方法上,此时大家注意到一个现象,就是set方法似乎没用到
此时需要第二种用于注入数据的注解:Qualifier:作用是在按照类中注入的基础上,按照名称注入。它在给类成员注入时,不能单独使用。但在给方法参数注入时是可以单独使用,属性 value:用于指定注入的bean的id
2.2 案例使用xml方式和注解方式实现单表的crud操作
2.3 改造基于注解的IOC案例,使用纯注解的方式实现,当然包括新注解的使用
2.4. spring和junit整合