什么是Spring
Spring是一个开源的轻量级框架,它的目标是使J2EE开发变得更容易。该框架提供了大约20个模块(Spring5.x版本中portlet组件已经被废弃):
core组件是spring所有组件的核心;bean组件和context组件是实现IOC和依赖注入的基础;AOP组件用来实现面向切面编程;web组件包括springmvc是web服务的控制层实现。
第一个Spring项目
以idea2019为例
(1)创建一个新项目Spring,勾选下方的"Create empty spring-config.xml"。(若忘记勾选也可以等新建完项目后自己新建配置文件spring-config.xml)
(2)新建后的项目:
使用示例
新建一个User类:
public class User {
private String uname;
private String password;
public User() {
System.out.println("User constructor");
}
public String getPassword() { return password; }
public void setPassword(String password) {
this.password = password;
}
public String getUname() {
System.out.println("getUname()");
return uname;
}
public void setUname(String uname) {
System.out.println("setUname()");
this.uname = uname;
}
//省略password的get/set方法
public void login() {
System.out.println(uname + " is logining");
}
}
按照以往的思路,我们需要创建User类的实例对象,设置好相关字段后,调用方法。而在Spring中,我们在spring-config.xml中添加相关配置:
<bean id="User" class="bean.User">
<property name="uname" value="C2y"/>
<property name="password" value="root"/>
</bean>
然后测试:
public static void main(String[] args) {
//生成工厂对象,
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User)ac.getBean("User");
user.login();
}
输出为:
所有的类实例的创建都不需要我们自己创建,而是交给Spring容器来创建和管理。
ApplicationContext 容器
ApplicationContext是 BeanFactory 的子接口,它被称为Spring上下文。它可以加载配置文件中中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。
最常被使用的 ApplicationContext 接口实现:
(1)FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。我们需要提供给构造器 XML 文件的完整路径。
ApplicationContext ac = new
FileSystemXmlApplicationContext("F:/javaCode/SpringWeb/src/spring-config.xml");
(2)ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。我们不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为容器会从 CLASSPATH 中搜索 bean 配置文件。
(3)WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
控制反转IOC
在传统的Java SE程序设计中,我们主动在类内部创建依赖对象,这导致类与类之间高耦合,难于测试。
以设计一辆汽车为例,我们需要先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这种设计方式出现了一个依赖关系:
从代码的角度来看:
每一个类的构造函数直接调用了底层类构造函数,如果我们将来需要调整轮胎Tire的尺寸为动态尺寸,而不是固定的30:
那么上层的类都需要调整。对于实际工程项目,则我们都需要修改以它作为依赖的类,这样的维护成本非常大。
控制反转IOC
控制反转IOC是面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。这种设计原则强调应该由上层控制下层。
前面我们提到,传统的Java设计中是直接在类内部创建一个对象,是我们主动控制去获取依赖对象;而IOC有一个容器负责创建这些对象,即IOC容器控制对象的创建,这也是反转的含义:依赖对象的获取被反转了,我们被动的接受依赖对象。
有了这个容器,我们只需要维护一个配置文件(例如xml),而不需要每次初始化一个类还要写一堆的初始化代码:
我们不需要了解所需类的具体创建实例的细节,就好比我们只需要向一个工程请求一个Car实例,然后它就会按照配置来创建。
依赖注入DI
依赖注入DI是控制反转的一种实现方式:它把底层类作为参数传入到上层类,实习上层类对下层类的控制。
这样如果我们想修改底层类的代码,则只需修改底层类即可
此处我们采用的是构造函数传入的方式进行依赖注入,此外还有set传递和接口传递。
代码示例
(1)基于构造方法的依赖注入
public class Student{
private String studentID;
private PersonMessage pm;
public Student(String studentID, PersonMessage pm) {
System.out.println("Student类构造方法");
this.studentID = studentID;
this.pm = pm;
}
public Student() {
System.out.println("Student类的无参构造函数");
}
//省略get/set,toString方法
}
public class PersonMessage {
private String name;
private int age;
public PersonMessage(String name, int age) {
System.out.println("PersonMessage类有参构造方法");
this.name = name;
this.age = age;
}
public PersonMessage() {
System.out.println("PersonMessage类无参构造方法");
}
//省略get/set,toString方法
}
spring-config.xml
<bean id="PersonMessage" class="bean.PersonMessage">
<!-- index属性表示构造函数的索引-->
<constructor-arg index="0" value="on1"/>
<constructor-arg index="1" value="18"/>
</bean>
<bean id="Student" class="bean.Student" >
<!--若要向一个对象传递一个引用,需要使用标签ref属性;-->
<!--若是直接值传递,则使用value属性-->
<constructor-arg ref="PersonMessage"/>
<constructor-arg type="java.lang.String" value="201902151918"/>
</bean>
输出:
(2)基于set方法的依赖注入
public class Student {
private String studentID;
private PersonMessage pm;
public Student() {
System.out.println("Student类的无参构造函数");
}
public Student(String studentID, PersonMessage personMessage) {
System.out.println("Student类的有参构造函数");
this.studentID = studentID;
this.personMessage = personMessage;
}
//省略 get/set,toString方法
}
public class PersonMessage {
private String name;
private int age;
public PersonMessage(String name, int age) {
System.out.println("PersonMessage类有参构造方法");
this.name = name;
this.age = age;
}
public PersonMessage() {
System.out.println("PersonMessage类无参构造方法");
}
//省略 get/set方法
}
spring-config.xml
<bean id="PersonMessage" class="bean.PersonMessage">
<property name="name" value="on1"/>
<property name="age" value="18"/>
</bean>
<bean id="Student" class="bean.Student" >
<property name="pm" ref="PersonMessage"/>
<property name="studentID" value="201902151918"/>
</bean>
输出:
Bean
构成应用程序的支柱并由Spring IoC容器管理的对象称为bean,所有的bean统一放在context的上下文中管理。
Bean 与 Spring 容器的关系
bean的属性
Bean 的作用域
Spring 框架支持以下五个作用域,分别为singleton、prototype、request、session和global session
singleton 是默认的作用域,这样IOC容器只会存在一个共享的bean示例。
<bean id="User" class="bean.User" scope="singleton">
<property name="uname" value="C2y"/>
<property name="password" value="root"/>
</bean>
Bean 生命周期
当一个 bean 被实例化时,它可能需要执行一些工作使它转换成可用状态;当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。为了实现这些 工作,我们可以在声明init-method 和 destroy-method 参数。
此处以 XML 的配置元数据为例:我们在User类添加两个方法:
public void init() {
System.out.println("初始化后的一些列工作");
}
public void destroy() {
System.out.println("清除User对象前的一些列工作");
}
修改xml文件:
<bean id="User" class="bean.User" init-method="init" destroy-method="destroy">
<property name="uname" value="C2y"/>
<property name="password" value="root"/>
</bean>
测试一下:
public static void main(String[] args) {
AbstractApplicationContext aac = new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User)aac.getBean("User");
user.login();
aac.registerShutdownHook();
}
输出:
如果你的所有bean对象的初始化方法和销毁方法都是一样的,则可以设置为默认的初始化和销毁方法:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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"
default-init-method="init"
default-destroy-method="destroy">
<bean id="User" class="bean.User" >
<property name="uname" value="C2y"/>
<property name="password" value="root"/>
</bean>
</beans>
bean继承
bean 定义包含了很多的配置信息,如构造函数的参数,属性值,容器的具体信息例如初始化方法,静态工厂方法名等。而子bean的定义可以继承父定义的配置数据。
Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。
示例
添加类VIPUser
public class VIPUser {
private int level;
private String AnoName;
private String uname;
private String password;
//省略get/set方法
@Override
public String toString() {
return "VIPUser{" +
"level=" + level +
", AnoName='" + AnoName + '\'' +
", uname='" + uname + '\'' +
", password='" + password + '\'' +
'}';
}
}
xml文件:
<bean id="User" class="bean.User" >
<property name="uname" value="C2y"/>
<property name="password" value="root"/>
</bean>
<bean id="VIPUser" class="bean.VIPUser" >
<property name="level" value="3"/>
<property name="AnoName" value="C2y-3"/>
</bean>
测试一下:
public static void main(String[] args) {
ApplicationContext aac = new ClassPathXmlApplicationContext("spring-config.xml");
VIPUser vipUser = (VIPUser)aac.getBean("VIPUser");
System.out.println(vipUser);
}
输出;