文章目录
Spring
spring概述
1 Spring定义
Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦合度的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架的能力。
Spring自诞生以来备受青睐,一直被广大开发人员作为Java企业级应用程序开发的首选。时至今日,Spring俨然成为了Java EE的代名词,成为了构建Java EE 应用的事实标准。
自2004年4月,Spring1.0 版正式发布以来,Spring已经步入到了第6个大版本,即 Spring6,本课程采用Spring5.3.24正式版本。
2 Spring核心
Spring指的是Spring Framework,通常我们称之为Spring框架。Spring框架是一个分层的面向切面的Java应用程序的一站式解决框架,它是Spring技术栈的核心和基础,是为了解决企业级引用开发的复杂性而创建的。
Spring有两个核心模块:IoC和AOP。
Ioc:Inverse of Control的简写,为 控制反转,指把创建对象交给Spring进行管理。
AOP:Aspect Oriented Programming 的简写,为 面向对象编程。AOP用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP还解决一些系统层面上的问题,比如日志、事务、权限等。
3 Spring Framework的特点
- 控制反转:IoC,反转资源获取方向;把自己创建的资源、向环境索取资源变为环境将资源准备好,我们享受资源注入。
- 面向切面编程:AOP,在不修改源代码的基础上增强代码功能。
- 容器:Spring IoC是一个容器,因为它包含并管理组件对象的生命周期;组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大降低了使用门槛,大幅度提高了开发效率。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方库,而且在Spring旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在Spring Framework 的基础上全部使用Spring来实现。
入门案例
1 环境要求
- JDK:Java8-15
- Spring:5.3.24
2 构建工程
2.1 构建子工程first-spring
在jsd2303-spring中创建子工程 spring-first
- New - Module
- 点击 Next
- Name为spring-first,点击 Finish
2.2 入门案例
① 在spring-first/pom.xml中引入相关依赖,并 刷新maven
<dependencies><!-- spring context依赖
当引入此依赖后,表示将Spring的基础依赖引入了
--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.24</version></dependency></dependencies>
② 在工程中创建包 cn.tedu.spring.begin
③ 创建类 User
④ User类中定义方法
publicclassUser{publicvoidadd(){System.out.println("添加方法...");}}
⑤ 创建spring配置文件:resources目录下创建bean.xml
⑥ 在bean.xml中用标签完成对象创建
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--完成user对象创建
id属性:唯一标识
class属性:要创建的对象所在类的绝对路径
--><beanid="user"class="cn.tedu.spring.User"></bean></beans>
⑦ 创建测试类cn.tedu.spring.TestUser进行测试
publicclassTestUser{publicstaticvoidmain(String[] args){// 1.加载spring配置文件,进行对象创建ApplicationContext context =newClassPathXmlApplicationContext("bean.xml");// 2.获取spring创建好的对象User user =(User) context.getBean("user");// 3.使用对象调用方法测试System.out.println("user = "+ user);
user.add();}}
2.3 对象存储
存放到容器中,查看源码,DefaultListableBeanFactory.java,第164行
key:唯一标识
value:类的定义(描述信息)
可以查看 BeanDefinition 的源码,有类的描述信息,是否初始化的状态等等
IoC容器
IoC 是 Inversion of Control 的简写,译为 控制反转。
Spring通过IoC容器来管理所有的Java对象的实例化和初始化,控制着对象与对象之间的依赖关系。我们将由IoC容器管理的Java对象成为 Spring Bean,它与使用关键字 new 创建的Java对象没有任何区别。
IoC容器是Spring框架中最重要的核心组件之一,它贯穿了Spring从诞生到成长的整个过程。
1 控制反转IoC
- 控制反转是一种思想- 将对象的创建权利交出去,交给第三方容器负责- 将对象和对象之间的关系维护权交出去,交给第三方容器负责
- 如何实现通过依赖注入DI的方式实现
2 依赖注入DI
DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。
依赖注入常见的实现方式有两种:
- set注入
- 构造注入
所以 IoC 是一种控制反转的思想,而 DI 是对IoC的一种具体实现。
Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值(或Bean对象之间关系的维护)
3 IoC容器实现
Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式
① BeanFactory
这是IoC容器的基本实现,是Spring内部使用的接口。面向Spring本身,不提供给开发人员使用。
② ApplicationContext
BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不是底层的BeanFactory
源码说明:
③ ApplicationContext的主要实现类
类型说明ClassPathXmlApplicationContext通过读取类路径下的xml格式配置文件创建IoC容器对象FileSystemApplicationContext通过文件系统路径读取xml格式配置文件创建IoC容器对象
4 基于XML管理bean
4.1 环境准备
① 创建子工程 spring-ioc-xml
② pom.xml中引入spring依赖,并刷新maven
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.24</version></dependency></dependencies>
③ 创建spring配置文件:resources/bean.xml
④ 在工程目录java下创建类 cn.tedu.spring.iocxml.User
publicclassUser{privateString username;privateString password;publicvoiduserMethod(){System.out.println("userMethod执行~~");}}
4.2 获取bean方式
- 根据id获取id属性是bean的唯一标识,所以根据bean标签的id属性可以精确获取到一个组件对象。① bean.xml
<beanid="user"class="cn.tedu.spring.iocxml.User"></bean>
② 创建测试类UserTestpublicclassUserTest{publicstaticvoidmain(String[] args){// 1.加载配置文件ApplicationContext context =newClassPathXmlApplicationContext("bean.xml");// 2.根据id获取beanUser user1 =(User) context.getBean("user");System.out.println("1-根据id获取对象:"+ user1); user1.userMethod();}}
- 根据类型获取
User user2 = context.getBean(User.class);System.out.println("2-根据类型获取bean:"+ user2);user2.userMethod();
- 根据id和类型获取
User user3 = context.getBean("user",User.class);System.out.println("3-根据id和类型获取bean:"+ user3);user3.userMethod();
- 注意当根据类型获取bean时,要求IoC容器中指定类型的bean只能有一个,当配置两个时会抛出异常
<beanid="user"class="cn.tedu.spring.iocxml.User"></bean><beanid="user2"class="cn.tedu.spring.iocxml.User"></bean>
4.3 基于setter依赖注入
类有属性,创建对象过程中,向属性注入具体的值
方式1:使用set方法完成(使用xml中的标签实现)
方式2:基于构造器完成
案例
① 创建Package名为dibase,创建Book类
packagecn.tedu.spring.DI;publicclassBook{privateString bookName;privateString bookAuthor;// 无参构造函数publicBook(){}// 全参构造函数publicBook(String bookName,String bookAuthor){this.bookName = bookName;this.bookAuthor = bookAuthor;}publicStringgetBookName(){return bookName;}publicvoidsetBookName(String bookName){this.bookName = bookName;}publicStringgetBookAuthor(){return bookAuthor;}publicvoidsetBookAuthor(String bookAuthor){this.bookAuthor = bookAuthor;}@OverridepublicStringtoString(){return"Book{"+"bookName='"+ bookName +'\''+", bookAuthor='"+ bookAuthor +'\''+'}';}}
② 创建spring配置文件:resources目录下创建 bean-di.xml
<!-- set方法注入 --><beanid="book"class="cn.tedu.spring.DI.Book"><!--2.使用property标签注入--><propertyname="bookName"value="java"></property><propertyname="bookAuthor"value="tedu"></property></bean>
③ 创建测试类TestBook进行测试
publicclassBookTest{// spring的set方法注入@TestpublicvoidspringSetTest(){ApplicationContext context =newClassPathXmlApplicationContext("bean-di.xml");Book book = context.getBean("book",Book.class);System.out.println("book = "+ book);}}
4.4 基于构造器依赖注入
- 说明通过构造器方式实现依赖注入
- 操作步骤说明1. 创建类,定义属性,生成有参数构造方法2. 进行xml配置3. 创建测试类测试
① 创建电影信息类Film,定义属性并生成全参构造方法
publicclassFilm{// 电影名称、主演privateString title;privateString actor;// 全参构造publicFilm(String title,String actor){System.out.println("Film的有参构造已经执行~~");this.title = title;this.actor = actor;}publicStringgetTitle(){return title;}publicvoidsetTitle(String title){this.title = title;}publicStringgetActor(){return actor;}publicvoidsetActor(String actor){this.actor = actor;}@OverridepublicStringtoString(){return"Film{"+"title='"+ title +'\''+", actor='"+ actor +'\''+'}';}}
② 在bean-di.xml中进行注入配置
<!-- 构造器注入演示:Film类 --><beanid="film"class="cn.tedu.spring.DI.Film"><constructor-argname="title"value="霸王别姬"></constructor-arg><constructor-argname="actor"value="张国荣"></constructor-arg></bean>
③ 创建测试类TestFilm测试
publicclassFilmTest{@TestpublicvoidFilmConsDITest(){// 1.加载配置文件ApplicationContext context =newClassPathXmlApplicationContext("bean-di.xml");// 2.获取指定beanFilm film = context.getBean("film",Film.class);// 3.输出测试System.out.println("film = "+ film);}}
4.5 特殊值处理注入
4.5.1 字面量赋值
string number = 10;
声明一个变量number,初始化为 10,此时number就不代表字符number了,而是作为一个变量的名字。当引用number时,实际拿到的结果是 10。
而如果number是带引号的 “number” ,则它不是一个变量,而代表 number 本身这个字符串。
这就是字面量,所以字面量没有引申含义,就是我们看到的这个数据本身。
<!-- 使用value属性给bean的属性赋值时,spring会把value的属性值看作是字面量 --><propertyname="number"value="1016"></property>
4.5.2 null值
使用 标签,或者 标签 实现注入。
① Film类中增加电影描述属性
// 1.电影描述privateString description;// 2.生成对应的 set() get() 方法,重新生成toString()方法// 3.重新生成全参构造方法
② bean-di.xml配置文件调整
<!-- 构造器注入演示:Film类 --><beanid="film"class="cn.tedu.spring.DI.Film"><constructor-argname="title"value="霸王别姬"></constructor-arg><constructor-argname="actor"value="张国荣"></constructor-arg><!-- 电影描述注入空值null --><constructor-argname="description"><null></null></constructor-arg></bean>
③ 执行测试类进行测试
- 课堂练习cn.tedu.spring下创建包exercise,在包下创建商品表 Product,类属性如下:商品标题:title商品库存:num商品销量:sales商品描述:comment1. 实现 商品Product类的创建,setter() getter() toString(),2. 通过配置文件bean-product.xml通过set方式注入一组数据(商品描述为null值);通过构造参数方式注入一组数据(商品描述为null值);3. 创建测试类TestProduct进行测试。
- 练习答案
① Product类
publicclassProduct{privateString title;privateInteger num;privateInteger sales;privateString comment;// 无参构造函数、有参构造函数 setter() getter() toString() }
② bean-product.xml
<!-- set方法注入 --><beanid="product"class="cn.tedu.spring.exercise.Product"><propertyname="title"value="手机"></property><propertyname="num"value="100"></property><propertyname="sales"value="1000"></property><propertyname="comment"><null></null></property></bean><!-- 构造参数方法注入 --><beanid="productCons"class="cn.tedu.spring.exercise.Product"><constructor-argname="title"value="电脑"/><constructor-argname="num"value="2"/><constructor-argname="sales"value="3"/><constructor-argname="comment"><null></null></constructor-arg></bean>
③ ProductTest测试类
@TestpublicvoidtestProduct(){ApplicationContext context =newClassPathXmlApplicationContext("bean-product.xml");Product product = context.getBean("product",Product.class);System.out.println("product = "+ product);}
4.5.3 xml实体
- 说明< > 小于号、大于号在XML文档中用来定义标签的开始,具有特殊含义,在注入属性值时不能够随便使用,可以使用XML实体
< >
来代替 - 表示方式普通字符xml实体<<>>
查看示例 bean-di.xml:
<!-- xml实体 --><beanid="filmEntity"class="cn.tedu.spring.DI.Film"><constructor-argname="title"value="霸王别姬"></constructor-arg><constructor-argname="actor"value="张国荣"></constructor-arg><!--xml实体表示--><constructor-argname="description"value="<真好看啊电影>"></constructor-arg></bean>
4.5.4 CDATA区
CDATA区,是xml中一种特有的写法,在CDATA区中可以包含特殊符号
表示方式:
<![CDATA[内容]]>
,在内容区域可以存放普通字符和特殊符号
CDATA区存放特殊符号演示
<!-- xml实体-CDATA区 --><beanid="filmCdata"class="cn.tedu.spring.DI.Film"><constructor-argname="title"value="霸王别姬"></constructor-arg><constructor-argname="actor"value="张国荣"></constructor-arg><!--xml实体表示--><constructor-argname="description"><!-- CDATA区存放数据,可通过 CD + Tab键自动补全格式 --><value><![CDATA[<真好看啊>]]></value></constructor-arg></bean>
4.6 对象类型属性注入
需要注入的数据类型为对象,而不是一个字面量。
- 环境准备准备一个一对多案例,比如部门Dept和员工Emp是一对多的关系。
① 创建包diobj,并创建部门类Dept
publicclassDept{// 部门名称privateString dName;// 定义方法,用于测试输出publicvoiddeptFunc(){System.out.println("Dept部门名称:"+ dName);}publicvoidsetdName(String dName){this.dName = dName;}publicStringgetdName(){return dName;}@OverridepublicStringtoString(){return"Dept{"+"dName='"+ dName +'\''+'}';}}
② 创建员工类Emp,创建setter() getter() 和 toString()方法
publicclassEmp{// 员工所属部门的对象、姓名、工资privateDept dept;privateString eName;privateDouble salary;// 定义方法测试publicvoidwork(){System.out.println(eName +"薪资:"+ salary);
dept.deptFunc();}publicvoidsetDept(Dept dept){this.dept = dept;}publicvoidseteName(String eName){this.eName = eName;}publicvoidsetSalary(Double salary){this.salary = salary;}publicDeptgetDept(){return dept;}publicStringgeteName(){return eName;}publicDoublegetSalary(){return salary;}@OverridepublicStringtoString(){return"Emp{"+"dept="+ dept +", eName='"+ eName +'\''+", salary="+ salary +'}';}}
4.6.1 引用外部bean
说明:
可以通过在当前bean标签中通过 ref属性引用外部bean的方式实现。
示例:通过使用外部bean方式,在员工中注入部门对象
① 配置文件 bean-diobj.xml
<!--在Emp中注入Dept
方式1:引用外部bean
1.创建两个类对象:dept 和 emp
2.在emp的bean标签中,通过property标签注入dept的bean
--><beanid="dept1"class="cn.tedu.spring.diobj.Dept"><propertyname="dName"value="开发部"></property></bean><beanid="emp1"class="cn.tedu.spring.diobj.Emp"><!-- 普通属性注入 --><propertyname="eName"value="张三丰"></property><propertyname="salary"value="50000.0"></property><!-- 对象类型注入,使用ref属性 --><propertyname="dept"ref="dept1"></property></bean>
② 创建测试类测试 TestDept
publicclassTestDept{// 对象类型注入测试用例@TestpublicvoidtestObjDI(){// 1.加载xml配置文件ApplicationContext context =newClassPathXmlApplicationContext("bean-diobj.xml");// 2.获取bean对象Emp emp1 = context.getBean("emp1",Emp.class);// 3.测试(调用员工emp对象的方法)System.out.println("emp1 = "+ emp1);
emp1.work();}}
4.6.2 内部bean
在需要注入对象的bean标签中内嵌 对象类型属性的 bean标签即可。
内嵌bean
① bean-diobj.xml进行属性注入配置
<!--在Emp中注入Dept
方式2:引用内部bean
在emp的bean标签中,通过内嵌部门bean标签方式实现
--><beanid="emp2"class="cn.tedu.spring.diobj.Emp"><propertyname="eName"value="张无忌"/><propertyname="salary"value="8000.0"/><!--对象注入--><propertyname="dept"><beanid="dept2"class="cn.tedu.spring.diobj.Dept"><propertyname="dName"value="销售部"/></bean></property></bean>
② 使用测试类测试
// 对象类型注入:内嵌bean@TestpublicvoidtestObjDi2(){ApplicationContext context =newClassPathXmlApplicationContext("bean-diobj.xml");Emp emp2 = context.getBean("emp2",Emp.class);System.out.println("emp2 = "+ emp2);
emp2.work();}
4.6.3 级联属性赋值(了解)
可以在标签中给需要注入对象的属性重新赋值!
① 配置文件编写
<!--方式3:级联属性(需要注入的属性)赋值--><beanid="dept3"class="cn.tedu.spring.diobj.Dept"><propertyname="dName"value="市场部"/></bean><beanid="emp3"class="cn.tedu.spring.diobj.Emp"><!-- 普通属性注入 --><propertyname="eName"value="赵敏"/><propertyname="salary"value="5000.0"/><!-- 对象类型注入 --><propertyname="dept"ref="dept3"/><!-- 级联属性(Dept)赋值 --><propertyname="dept.dName"value="客服部"></property></bean>
② 测试类测试
// 对象类型注入:级联属性赋值@TestpublicvoidtestObjDi3(){ApplicationContext context =newClassPathXmlApplicationContext("bean-diobj.xml");Emp emp3 = context.getBean("emp3",Emp.class);System.out.println("emp3 = "+ emp3);
emp3.work();}
4.7 数组类型属性注入
使用 标签和子标签实现。
说明:一个人除了姓名、年龄等属性外,还会有爱好,一个人的爱好可能有多个,可以把多个爱好存入数组中。
创建包:cn.tedu.spring.diarray
① 在diarray包中创建类:Person
publicclassPerson{// 姓名、年龄、爱好privateString name;privateString age;privateString[] hobby;// 定义测试方法publicvoidrun(){System.out.println("Persen is running...");// 打印数组测试System.out.println(Arrays.toString(hobby));}publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicStringgetAge(){return age;}publicvoidsetAge(String age){this.age = age;}publicString[]getHobby(){return hobby;}publicvoidsetHobby(String[] hobby){this.hobby = hobby;}@OverridepublicStringtoString(){return"Person{"+"name='"+ name +'\''+", age='"+ age +'\''+", hobby="+Arrays.toString(hobby)+'}';}}
② 新建配置文件:bean-diarray.xml 进行注入
<!-- 创建Person对象并注入属性 --><beanid="person"class="cn.tedu.spring.diarray.Person"><!-- 普通属性注入 --><propertyname="name"value="孙悟空"/><propertyname="age"value="36"/><!-- 数组属性注入,使用<array>标签 --><propertyname="hobby"><array><value>抽烟</value><value>喝酒</value><value>烫头</value></array></property></bean>
③ 编写测试类TestPerson测试
publicclassTestPerson{// 数组注入测试用例@TestpublicvoidtestArray(){ApplicationContext context =newClassPathXmlApplicationContext("bean-diarray.xml");Person person = context.getBean("person",Person.class);System.out.println("person = "+ person);
person.run();}}
4.8 集合类型属性注入
4.8.1 List集合属性注入
场景1:使用 标签下的 子标签和 子标签实现。
场景2:使用 标签下的 子标签和子标签实现。ref标识引用其他的bean
环境说明:创建老师类Student和学生类Student,一个老师可以有多个学生,在老师类中存入所教学生的对象,将其存入List集合中。
环境准备
① 创建包 dimap
② Teacher类
publicclassTeacher{// 老师姓名privateString tName;// 老师所教学生的对象,放到List集合中privateList<Student> studentList;publicStringgettName(){return tName;}publicvoidsettName(String tName){this.tName = tName;}publicList<Student>getStudentList(){return studentList;}publicvoidsetStudentList(List<Student> studentList){this.studentList = studentList;}@OverridepublicStringtoString(){return"Teacher{"+"tName='"+ tName +'\''+", studentList="+ studentList +'}';}}
③ Student类
publicclassStudent{// 学生姓名、年龄privateString sName;privateString age;publicStringgetsName(){return sName;}publicvoidsetsName(String sName){this.sName = sName;}publicIntegergetAge(){return age;}publicvoidsetAge(Integer age){this.age = age;}@OverridepublicStringtoString(){return"Student{"+"sName='"+ sName +'\''+", course='"+ course +'\''+'}';}}
④ 创建配置文件:bean-dilistmap.xml 进行注入
<!-- 创建2个Student对象,用于Teacher对象的注入 --><beanid="stu1"class="cn.tedu.spring.dimap.Student"><propertyname="sName"value="梁山伯"/><propertyname="age"value="43"/></bean><beanid="stu2"class="cn.tedu.spring.dimap.Student"><propertyname="sName"value="祝英台"/><propertyname="age"value="33"/></bean><!-- 创建Teacher类的bean对象,并注入属性 --><beanid="teacher"class="cn.tedu.spring.dimap.Teacher"><!-- 普通属性注入 --><propertyname="tName"value="沙师弟"/><!-- List集合属性注入 --><propertyname="studentList"><list><refbean="stu1"/><refbean="stu2"/></list></property></bean>
⑤ 测试类TestTeacher测试
publicclassTestTeacher{@TestpublicvoidtestListMap(){ApplicationContext context =newClassPathXmlApplicationContext("bean-dilistmap.xml");Teacher teacher = context.getBean("teacher",Teacher.class);System.out.println("teacher = "+ teacher);List<Student> list = teacher.getStudentList();for(Student student: list){System.out.println(student);}}}
4.8.2 Map集合属性注入
使用标签下的 子标签、子标签、子标签 子标签 子标签实现
<beanid="xxx"class="xxx"><propertyname="xxx"><map><!-- 第1条数据-字面量值演示 --><entry><key><value>xxx</value></key><value>xxx</value></entry><!-- 第2条数据-对象演示 --><entry><key><value>xxx</value></key><refbean="xxx"></ref></entry></map></property></bean>
说明:使用上述的老师类和学生类,一个学生也可以有多个老师,在学生类Student中添加老师的属性,放到Map集合中。
① 调整Student类
// 1.学生的老师:可以有多个,放到Map集合中privateMap<String,String> teacherMap;// 2.生成setter() getter()方法,重新生成toString()方法
② 创建配置文件:bean-dimap.xml
<!--Map集合属性注入--><beanid="stuMap"class="cn.tedu.spring.dilistmap.Student"><propertyname="sName"value="步惊云"/><propertyname="age"value="36"/><propertyname="teacherMap"><map><entry><key><value>1111</value></key><value>雄霸</value></entry><entry><key><value>2222</value></key><value>断浪</value></entry><entry><key><value>3333</value></key><value>大空翼</value></entry></map></property></bean>
③ 创建测试类进行测试 TestMap
@TestpublicvoidtestMap(){ApplicationContext context =newClassPathXmlApplicationContext("bean-dimap.xml");Student student = context.getBean("stuMap",Student.class);System.out.println("student = "+ student);}
4.8.3 引用集合类型bean注入
- 说明通过使用 标签实现
- 使用步骤- 在xml配置文件中引入util约束
<beansxmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation=" http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"></beans>
- 使用util标签进入注入<!-- Map集合util标签 --><util:mapid="xxx"></util:map><!-- List集合util标签 --><util:listid="xxx"></util:list>
环境准备及操作步骤
添加课程类,一个学生可以上多门课程
① 在Student类中添加List集合属性
// 1.一个学生可以上多门课程,把课程名称放到List集合中privateList<String> courseList;// 2.生成get和set方法// 3.重新生成toString()方法
② 创建spring配置文件:bean-diref.xml,引入util约束
<!-- 添加3行带有util的配置 --><?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
③ 配置xml文件完成注入
<!--引用集合类型bean注入--><beanid="stuUtil"class="cn.tedu.spring.dilistmap.Student"><propertyname="sName"value="孔慈"/><propertyname="age"value="36"/><propertyname="teacherMap"ref="teacherMap"></property><propertyname="courseList"ref="courseList"></property></bean><util:mapid="teacherMap"><entry><key><value>10000</value></key><value>小泽老师</value></entry><entry><key><value>10001</value></key><value>王老师</value></entry></util:map><util:listid="courseList"><value>Spring</value><value>SpringMVC</value><value>MyBatis</value></util:list>
④ 创建测试方法进行测试
// 引用集合类型bean注入(util)@TestpublicvoidtestRefBean(){ApplicationContext context =newClassPathXmlApplicationContext("bean-diref.xml");Student student = context.getBean("stuUtil",Student.class);System.out.println("student = "+ student);}
4.9 p命名空间
这也是一种注入方式,可以在xml中定义命名空间或者叫名称空间,可以简化xml代码。
在bean-diref.html中操作
① 在xml配置文件中定义命名空间
xmlns:p="http://www.springframework.org/schema/p"
② 在xml文件进行命名空间属性注入
<!-- p命名空间注入: 注入学生属性 --><beanid="studentp"class="cn.tedu.spring.iocxml.dimap.Student"p:sid="100"p:sname="铁锤妹妹"p:courseList-ref="courseList"p:teacherMap-ref="teacherMap">
③ 测试
// p命名空间注入测试用例@TestpublicvoidtestRefP(){ApplicationContext context =newClassPathXmlApplicationContext("bean-diref.xml");Student studentp = context.getBean("studentp",Student.class);System.out.println("studentp = "+ studentp);}
4.10 引入外部属性文件
- 说明 当前所有的配置和数据都在xml文件中,一个文件中有很多bean,修改和维护起来很不方便,生产环境中会把特定的固定值放到外部文件中,然后引入外部文件进行注入,比如数据库连接信息。
- 示例将外部文件中的数据引入xml配置文件进行注入
① pom.xml中引入数据库相关依赖,并刷新maven
<!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version></dependency><!-- 数据源,连接池依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version></dependency>
② resources目录下创建外部属性文件,一般为properties格式,定义数据库信息,比如:jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc://mysql://localhost:3306/spring
jdbc.driver=com.mysql.cj.jdbc.Driver
③ 创建spring配置文件bean-jdbc.xml,引入context的命名空间
使用context可以为XML外部实体注入定义,使得解析器在解析XML文档时可以正确地识别外部实体
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 引入外部属性文件 --><context:property-placeholderlocation="classpath:jdbc.properties"></context:property-placeholder><!-- 完成数据库信息注入 --><beanid="druidDataSource"class="com.alibaba.druid.pool.DruidDataSource"><propertyname="url"value="${jdbc.url}"></property><propertyname="username"value="${jdbc.user}"></property><propertyname="password"value="${jdbc.password}"></property><propertyname="driverClassName"value="${jdbc.driver}"></property></bean></beans>
④ 创建包jdbc,包中创建测试类 TestJdbc
publicclassTestJdbc{// 外部文件属性引入测试用例@Testpublicvoiddemo02(){ApplicationContext context =newClassPathXmlApplicationContext("bean-jdbc.xml");DruidDataSource druidDataSource = context.getBean("druidDataSource",DruidDataSource.class);System.out.println(druidDataSource.getUrl());}}
4.11 bean的作用域
- 说明 bean的作用域,是指在交给spring创建bean对象时,可以指定是单实例还是多实例,通过bean标签中的scope属性来指定,默认是单实例。
- 单实例和多实例- 单实例单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如配置类或数据库连接池等。- 多实例多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。
在Spring中可以通过配置bean标签的scope属性来之地那个bean的作用域范围,具体如下
取值含义创建对象时机singleton(默认)在IoC容器中,这个bean的对象为单实例IoC容器初始化时prototype这个bean在IoC容器中有多个实例获取bean时
案例演示
① 创建包scope,并在包下创建类Sku
publicclassSku{}
② 创建spring的配置文件:bean-scope.xml
<!-- singleton:单实例 --><!-- 之后改为prototype多实例测试 --><beanid="sku"class="cn.tedu.spring.scope.Sku"scope="singleton"></bean>
③ 创建测试类TestOrders
@TestpublicvoidtestOrders(){ApplicationContext context =newClassPathXmlApplicationContext("bean-scope.xml");Orders orders = context.getBean("orders",Orders.class);System.out.println("orders = "+ orders);Orders orders1 = context.getBean("orders",Orders.class);System.out.println("orders1 = "+ orders1);}// 单实例,sku1和sku2地址相同// 多实例,sku1和sku2地址不同
4.12 bean的生命周期
是指一个bean对象从创建到销毁的整个过程。
4.12.1 bean的完整生命周期
- 实例化阶段(bean对象创建)在这个阶段中,容器会创建一个Bean的实例,并为其分配空间。这个过程可以通过构造方法完成。
- 属性赋值阶段在实例化完Bean之后,容器会把Bean中的属性值注入到Bean中,这个过程可以通过set方法完成。
- 初始化阶段(bean对象初始化)在属性注入完成后,容器会对Bean进行一些初始化操作;- 初始化之前:bean的后置处理器可以接收到bean,此处可以对bean做相关操作。- 初始化之后:bean的后置处理器可以接收到bean,此处可以对bean做相关操作。
- 使用阶段初始化完成后,Bean就可以被容器使用了
- 销毁阶段容器在关闭时会对所有的Bean进行销毁操作,释放资源。
4.12.2 生命周期验证
① 创建包life,创建类User
packagecn.tedu.spring.life;importorg.springframework.beans.BeansException;publicclassUser{privateString username;// 1.无参数构造publicUser(){System.out.println("1-bean对象创建,调用无参数构造。");}// 3.初始化阶段publicvoidinitMethod(){System.out.println("3-bean对象初始化,调用指定的初始化方法");}// 5.销毁阶段publicvoiddestoryMethod(){System.out.println("5-bean对象销毁,调用指定的销毁方法");}publicStringgetUsername(){return username;}publicvoidsetUsername(String username){this.username = username;// 2.给bean对象属性赋值System.out.println("2-通过set方法给bean对象赋值。");}@OverridepublicStringtoString(){return"User{"+"username='"+ username +'\''+'}';}}
② 创建spring配置文件 bean-life.xml
<beanid="user"class="cn.tedu.spring.iocxml.life.User"scope="singleton"init-method="initMethod"destroy-method="destroyMethod"><propertyname="username"value="聂风"></property></bean>
③ 创建测试类TestUser测试
@TestpublicvoidtestUser(){ClassPathXmlApplicationContext context =newClassPathXmlApplicationContext("bean-life.xml");User user = context.getBean("user",User.class);// 4.bean对象初始化完成,可以使用System.out.println("4-bean对象初始化完成,开发者可以使用了。");// 销毁bean
context.close();}
④ 后置处理器处理演示,新建类MyBeanPost
publicclassMyBeanPostimplementsBeanPostProcessor{// BeanPostProcessor接口@OverridepublicObjectpostProcessBeforeInitialization(Object bean,String beanName)throwsBeansException{System.out.println("3之前:bean后置处理器,在初始化之前执行。"+ beanName +":"+ bean);return bean;}@OverridepublicObjectpostProcessAfterInitialization(Object bean,String beanName)throwsBeansException{System.out.println("3之后:bean后置处理器,在初始化之后执行。"+ beanName +":"+ bean);return bean;}}
⑤ 在spring的配置文件bean-life.xml中进行后置处理器配置
<!-- bean的后置处理器需要放到IoC容器中才能生效 --><beanid="myBeanPost"class="cn.tedu.spring.life.MyBeanPost"></bean>
⑥ 运行测试类测试
4.12.3 bean生命周期扩展
- bean的初始化和销毁应用场景- 初始化 - 创建连接池- 加载资源文件- 进行数据校验- 销毁 - 关闭连接池- 保存数据- 释放占用的资源
- 后置处理器实现自定义的Bean对象处理逻辑,比如在Bean实例化之前或者之后对Bean对象进行自定义的修改,可以方便地实现自定义逻辑和修改Bean对象的行为。
4.13 基于xml自动装配
自动装配说明:
根据指定的策略,在IoC容器中匹配某一个bean,自动为指定的bean中的所依赖的类类型或者接口类型属性赋值。
环境准备
① 创建包auto,创建部门和员工的两个java类
② 部门类 Dept
publicclassDept{privateString dName;@OverridepublicStringtoString(){return"Dept{"+"dName='"+ dName +'\''+'}';}publicStringgetdName(){return dName;}publicvoidsetdName(String dName){this.dName = dName;}}
③ 员工类 Emp
publicclassEmp{privateString eName;privateDept dept;@OverridepublicStringtoString(){return"Emp{"+"eName='"+ eName +'\''+", dept="+ dept +'}';}publicStringgeteName(){return eName;}publicvoidseteName(String eName){this.eName = eName;}publicDeptgetDept(){return dept;}publicvoidsetDept(Dept dept){this.dept = dept;}}
④ 创建spring配置文件bean-auto.xml
<!--通过byType和byName自动装配--><beanid="dept"class="cn.tedu.spring.iocxml.auto.Dept"><propertyname="dName"value="技术部"></property></bean><!--autowire="byType" 或者 autowire="byName"--><beanid="emp"class="cn.tedu.spring.iocxml.auto.Emp"autowire="byType"><propertyname="eName"value="步惊云"></property></bean>
⑤ 创建测试类测试TestAuto
@TestpublicvoidtestAuto(){ApplicationContext context =newClassPathXmlApplicationContext("bean-auto.xml");Emp emp = context.getBean("emp",Emp.class);System.out.println("emp = "+ emp);}
使用bean标签的autowire属性设置自动装配效果;
自动装配方式:byType
byType: 根据类型匹配IoC容器中的某个兼容类型的bean,为属性自动赋值;
1. 如果在IoC中,没有任何一个兼容类型的bean能够为属性赋值,则改属性不装配,默认值为null;
2. 如果在IoC中,有多个兼容类型的bean能够为属性赋值,则抛出异常 NoUniqueBeanDefinitionException
自动装配方式:byName
byName:将自动装配的属性名,作为bean的id在IoC容器中匹配相对应的bean进行赋值
5 基于注解管理bean
从Java5开始,Java增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
Spring从2.5版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化Spring的xml配置。
Spring通过注解实现自动装配:
- 引入依赖
- 开启组件扫描
- 使用注解定义Bean
- 依赖注入
5.1 创建子工程
子工程:spring-ioc-annotation
在pom.xml中添加springframework的依赖,刷新maven
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.24</version></dependency></dependencies>
5.2 开启组件扫描
Spring默认不使用注解装配Bean,因此需要在Spring的xml配置中,通过context:component-scan元素开启Spring Beans的自动扫描功能。开启此功能后,Spring会自动从扫描指定的包(base-package属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。
① 工程下创建包:cn.tedu.spring.bean
② resources目录下创建spring配置文件 bean.xml
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 2.开启组件扫描,让spring可以通过注解方式实现bean管理,包括创建对象、属性注入 --><!-- base-package:扫描哪个包中的注解,在cn.tedu的包或者子包中建了类,在
类上、属性上、方法上加了spring的@Component注解,这里就能扫描到--><context:component-scanbase-package="cn.tedu.spring"></context:component-scan></beans>
5.3 使用注解定义Bean
Spring提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义成Spring Bean。
注解说明@Component该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅标识容器中的一个组件(Bean),并且可以作用在任何层次,例如Service层、Dao层等,使用时只需将该注解标注在相应的类上即可。@Respository该注解用于数据访问层(Dao层)的类标识为Spring中的Bean,功能与@Component相同。@Service该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。@Controller该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
③ 创建User类,并添加注解
// value可以不写,默认为类名首字母小写//@Component(value = "user") // <bean id="user" class="xxx">//@Repository//@Service@ControllerpublicclassUser{}
④ 创建测试类测试TestUser
publicclassTestUser{@TestpublicvoidtestUser(){ApplicationContext context =newClassPathXmlApplicationContext("bean.xml");User user = context.getBean("user",User.class);System.out.println("user = "+ user);}}
5.4 @Autowired注入
单独使用@Autowired注解,默认根据类型装配(byType)
@Autowired注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须存在,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在就注入,不存在也不报错。
5.4.1 属性注入
① cn.tedu.spring下创建包autowired,并在autowired下创建两个包:controller包 和 service包
② 控制器层controller.UserController
publicclassUserController{privateUserService userService;publicvoidaddController(){System.out.println("controller is running...");
userService.addService();}}
③ 服务层service.UserService接口
publicinterfaceUserService{publicvoidaddService();}
④ 服务层service.UserServiceImpl接口的实现类
publicclassUserServiceImplimplementsUserService{@OverridepublicvoidaddService(){System.out.println("service is running...");}}
⑤ 在UserController和UserSerivceImpl中添加@Controller注解和@Service注解
⑥ 在UserController中注入UserServiceImpl
@ControllerpublicclassUserController{// 注入service// 第一种方式:属性注入@Autowired// 根据类型找到对象,完成注入privateUserService userService;}
⑦ 测试类测试autowired.TestUserController
publicclassTestUserController{@TestpublicvoidtestUserController(){ApplicationContext context =newClassPathXmlApplicationContext("bean.xml");UserController controller = context.getBean(UserController.class);
controller.addController();}}
5.4.2 set注入
① 修改UserController类
// 方式二:通过set方法注入privateUserService userService;@AutowiredpublicvoidsetUserService(UserService userService){this.userService = userService;}
② 测试
5.4.3 构造方法注入
① 修改UserController类
// 第三种方式:构造方法注入privateUserService userService;@AutowiredpublicUserController(UserService userService){this.userService = userService;}
② 测试
5.4.4 形参上注入
① 修改UserController类
// 第四种方式:形参注入privateUserService userService;publicUserController(@AutowiredUserService userService){this.userService = userService;}
② 测试
5.4.5 只有一个构造函数,无注解
① 修改UserController类
// 第五种方式:只有一个有参数构造函数,无注解privateUserService userService;publicUserController(UserService userService){this.userService = userService;}
② 测试
5.4.6 @Autowire注解和@Qualifier注解联合
① 再创建一个UserService接口的实现类service.UserServiceImpl2
@ServicepublicclassUserServiceImpl2implementsUserService{@OverridepublicvoidaddService(){System.out.println("service2 is running...");}}
② 测试发现报错
因为UserService有两个实现类,而@Autowired注解根据byType定位,所以找到了两个实现类
③ 解决:修改UserController (使用两个注解)
// 1.第六种方式:根据类型和名称一起注入@Autowired@Qualifier(value ="userServiceImpl2")// 类名首字母小写privateUserService userService;// 2.将构造函数注释
5.5 @Resource注入
@Resource注解也可以完成属性注入。它和@Autowired注解的区别如下
- @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该解释是标准注解,更加具有通用性,而@Autowired注解是Spring框架自己的。
- @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name,通过name找不到的话会自动启动通过类型byType装配。而@Autowired注解默认根据类型装配byType,如果想根据名称匹配,需要配合@Qualifier注解一起使用。
- @Resource注解用在属性上、setter方法上
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
案例演示
① 工程下创建包 resource,和之前一样,创建controller和service两个包,并创建UserController类和UserService接口以及该接口的实现类UserServiceImpl
② 修改UserController
@Controller("myUserController")publicclassUserController{// 根据名称进行注入@Resource(name="myUserService")privateUserService userService;publicvoidadd(){System.out.println("controller...");
userService.add();}}
③ 修改ServiceControllerImpl1
@Service("myUserService")publicclassUserServiceImplimplementsUserService{
⑤ 测试
- 指定@Resource中的name,则根据名称装配
- 未指定name时,则根据属性名装配
- 未指定name,属性名也不一致,则根据类型装配
5.6 Spring全注解开发
全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。
① 工程下创建包:config,创建类SpringConfig
// 配置类@Configuration// 开启组件扫描@ComponentScan("cn.tedu.spring")publicclassSpringConfig{}
② 在resource下创建测试类进行测试
publicclassTestUserControllerAnno{publicstaticvoidmain(String[] args){// 加载配置类ApplicationContext context =newAnnotationConfigApplicationContext(SpringConfig.class);UserController controller = context.getBean(UserController.class);
controller.add();}}
版权归原作者 字节探索者 所有, 如有侵权,请联系我们删除。