开始时间:2021-12-14
课程地址
Spring概述
Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。 Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。
Spring 的主要作用就是为代码==“解耦”==,降低代码间的耦合度。 就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。
Spring优点
Spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器管理对象,容器是装东西的, Spring 容器不装文本,数字。装的是对象。 Spring 是存储对象的容器。
- 轻量
- 针对接口编程,解耦合
- AOP 编程的支持
- 方便集成各种优秀框架
IoC 控制反转
控制反转(IoC, Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理
- 控制: 创建对象,对象的属性赋值,对象之间的关系管理。
- 反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,
给属性赋值。 - 正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
当前比较流行的实现方式是依赖注入。
用IoC的作用:目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
Java中创建的对象的方法:
java中创建对象有哪些方式:
- 构造方法 , new Student()
- 反射
- 序列化
- 克隆
- 动态代理
- ioc :容器创建对象
ioc的体现: servlet 参考博客
1: 创建类继承HttpServelt
2: 在web.xml 注册servlet , 使用
<servlet-name> myservlet </servlet-name>
<servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class>
- 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()
- Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器
Tomcat作为容器:里面存放的有Servlet对象, Listener , Filter对象
IoC技术实现
1.加入依赖
2.创建类
3.创建Spring配置文件
4.使用容器中的对象
依赖: classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA
对 classB 有依赖。
DI 是ioc的技术实现,
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,
赋值,查找都由容器内部实现。
spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。
Spring 框架使用依赖注入(DI)实现 IoC。
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称
为 Bean。
创建的前期工作和之前用maven创建module一样
File-New-Project 建立一个空的就行
然后新建module 选择maven-create from archetype 然后选择maven-archetype-quickstart
新建好的module中的pom.xml添加Spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
以及添加插件
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
</plugins>
</build>
然后创建beans.xml配置文件
src-main-resources-右键new一个XML configuration File-Spring Config
如果没有Spring config这个选项,一种是没有在pom.xml配置上面说的这个插件
另一个是由于IDEA不是旗舰版
如果都没问题,配置完插件后要刷新一下,才能生效找到这个Spring Config,通过这个xml创建beans.xml
spring的配置文件中
1.beans :是根标签,spring把java对象称为bean。
2.spring-beans.xsd是约束文件,和mybatis指定dtd是一样的。
在beans.xml中声明bean
<!--声明bean-->
<bean id="someService" class="BUPT.service.SomeServiceImpl"/>
声明bean ,就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值。spring通过这个名称找到对象
class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
spring就完成 Someservice someservice = new SomeserviceImpl()
spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
springMap.put( id的值,对象);
例如 springMap.put(“someservice”, new SomeserviceImpl());
比较两种创建对象的方式
@Test
public void test01() {
//主动创建对象
SomeService service=new SomeServiceImpl();
service.doSome();
}
@Test
public void test02() {
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config = "beans.xml";
//2.创建表示spring容器的对象, ApplicationContext
// ApplicationContext就是表示Spring容器,通过容器获取对象了
// ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取某个对象, 你要调用对象的方法
//getBean("配置文件中的bean的id值")
//转了接口类型才能调用其方法
SomeService service = (SomeService) ac.getBean("someService");
//使用spring创建好的对象
service.doSome();
}
使用spring容器创建的对象
1.指定spring配置文件的名称
2.创建表示spring容器的对象, ApplicationContext
ApplicationContext就是表示Spring容器,通过容器获取对象了
ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
从容器中获取某个对象, 你要调用对象的方法
getBean(“配置文件中的bean的id值”)
相当于给key拿value了
spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有的对象。
spring创建对象:默认调用的是无参数构造方法
当有多个spring bean对象时,可以通过以下语句获取对象信息
spring默认调用无参构造
@Test
public void test03(){
String config="beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//使用spring提供的方法, 获取容器中定义的对象的数量
int nums = ac.getBeanDefinitionCount();
System.out.println("容器中定义的对象数量:"+nums);
//容器中每个定义的对象的名称
String names [] = ac.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
也可以获取非自定义类的对象
比如获得date对象
在bean配置文件中添加语句
<bean id="mydate" class="java.util.Date"/>
//获取一个非自定义的类对象
@Test
public void test04() {
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//使用getBean();
Date my = (Date) ac.getBean("mydate");
System.out.println("Date:" + my);
}
依赖注入
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。
初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类: set 注入、构造注入。
set注入
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、
直观,因而在 Spring 的依赖注入中大量使用。
简单类型的赋值
创建Student类对象
只需要加set方法,不需要加get方法
package BUPT.ba01;
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在resource里面配置
这里即使是int类型,也要加双引号
这是xml文件的要求
<bean id="myStudent" class="BUPT.ba01.Student">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
通过property来实现赋值,调用的是Student类的set方法
测试方法里面进行测试
package BUPT;
import BUPT.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
@Test
public void test02() {
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config = "ba01/applicationContext.xml";
//2.创建表示spring容器的对象, ApplicationContext
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student mystudent = (Student) ac.getBean("myStudent");
System.out.println(mystudent);
}
}
但也要注意,虽然明说是set+属性名这样进行赋值
其实他看的不是Student类里面定义的属性,而是找的对应的set方法
比如我没有定义一个属性叫email
但是我在Student类里面设置了一个方法叫 setEmail()
public void setEmail(String email) {
System.out.println("输出的是自定义set方法的email" + email);
}
配置文件也给一个
<property name="email" value="zhangsan@qq.com"/>
那他依然会自动调用这个方法
因为他找的是property后面这个name 再去Student类里找对应的setName方法
引用类型的赋值
在实体类新建一个school类
并设置school类为Student类的一个属性
school类
public class School {
private String address;
private String name;
}
Student类
public class Student {
private String name;
private int age;
public School school;
//setName setAge以及toString重写省略了,实际是有的
public void setSchool(School school) {
System.out.println("school:" + school);
this.school = school;
}
}
新增配置
<bean id="mySchool" class="BUPT.ba02.School" >
<property name="name" value="BUPT"/>
<property name="address" value="Beijing"/>
</bean>
此时再修改Student的xml配置
<bean id="myStudent" class="BUPT.ba02.Student">
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="school" ref="mySchool"/>
</bean>
注意引用类型通过ref="id"实现的
构造注入
就是调用Student里面的constructor来协助创建对象
Student类里面加一个构造函数
public Student(String name, int age, School school) {
this.name = name;
this.age = age;
this.school = school;
}
配置文件中使用constructor
<bean id="myStudent" class="BUPT.ba03.Student">
<constructor-arg name="age" value="25"/>
<constructor-arg name="name" value="王五"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="BUPT.ba03.School" >
<property name="name" value="BUPT"/>
<property name="address" value="Beijing"/>
</bean>
自动注入
只对引用类型有效
- byName:当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用
byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。 - byType:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(父子类,或是接口-实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
我们来看byName的一个示例
在配置bean id时,后面加一个 autowire “byName”
因为我们在Student类里面调用School这个类时是这样初始化的
public School school;
所以我们把这个school 作为School类的id名
让他俩匹配在一起,此时就可以进行自动注入了
<bean id="myStudent" class="BUPT.ba04.Student" autowire="byName">
<property name="age" value="25"/>
<property name="name" value="王五"/>
</bean>
<bean id="school" class="BUPT.ba04.School" >
<property name="name" value="NJUPT"/>
<property name="address" value="Beijing"/>
</bean>
再来看byType
将autowire后面的值改为 byType
<bean id="myStudent" class="BUPT.ba04.Student" autowire="byType">
<property name="age" value="25"/>
<property name="name" value="王五"/>
</bean>
<bean id="myschool" class="BUPT.ba04.School" >
<property name="name" value="NJUPT"/>
<property name="address" value="Beijing"/>
</bean>
这种的byType比较好理解,因为用school传school
那么我们再看一种情况
新建一个中学类extends学校类
然后配置文件写下面的内容
<bean id="myschool" class="BUPT.ba04.MiddleSchool" >
<property name="name" value="NCGZ"/>
<property name="address" value="Sichuan"/>
</bean>
可以看到该类属于school类的子类,仍然可以自动注入
但如果有多个子类或者父类子类都写在bean里面,会报错,不知道注入谁
多配置文件
- 每个文件的大小比一个文件要小很多。效率高
- 避免多人竞争带来的冲突。
如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。
学生考勤模块一个配置文件,张三
学生成绩一个配置文件,李四
多文件的分配方式:
- 按功能模块,一个模块一个配置文件
- 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等
将之前的一个拆分一下
school一个
<bean id="myschool" class="BUPT.ba04.MiddleSchool" >
<property name="name" value="NCGZ"/>
<property name="address" value="Sichuan"/>
</bean>
student一个
<bean id="myStudent" class="BUPT.ba04.Student" autowire="byType">
<property name="age" value="25"/>
<property name="name" value="王五"/>
</bean>
然后总的来一个,用来import子配置文件
<import resource="spring-school.xml"/>
<import resource="spring-student.xml"/>
也能正常使用
在total里面也可以使用通配符来import,放在同一级的目录下,才能读取
这样的话要注意total自身的一个命名,不能把自己也包含进去了,防止自己调用自己
<import resource="spring-*.xml"/>
结束时间:2021-12-16