Spring:全功能栈的应用程序框架
各模块介绍
Test
对应 spring-test.jar。 Spring 提供的测试工具,可以整合 Junit 测试,简化测试环节。
Core Container
Spring 的核心组件,包含了 Spring 框架最基本的支撑。
Beans,对应 spring-beans.jar,Spring 进行对象管理时依赖的jar包。
Core,对应 spring-core.jar,Spring 核心 jar 包。
Context,对应 spring-context.jar,Spring 容器上下文对象。
SpEL,对应 spring-expression.jar,Spring 表达式语言。
AOP
面向切面编程,对应 spring-aop.jar。
Aspects
AspectJ 的具体实现,面向切面编程的另一种实现。对应 spring-aspects.jar。
Instrumentation
服务器代理接口的具体实现。对应 spring-instrument.jar。
Messaging
集成 messaging api 和消息协议提供支持。对应 spring-messaging.jar。
Data Access/Integration
Spring 对数据访问层的封装。
JDBC,对应 spring-jdbc.jar。Spring 对 jdbc 的封装,当需要使用 spring 连接数据库时使用。 spring-jdbc.jar 需要依赖 spring-tx.jar。
Transactions,对应 spring-tx.jar。事务管理。
ORM,对应 spring-orm.jar。spring 整合第三方 orm 框架需要使用的 jar 包,例如 Hibernate 框架。
Web
Spring 对 javax 下的接口或类做的扩展功能。
spring-web.jar,对Servlet,filter,Listener等做的增强。
spring-webmvc.jar,实际上就是 SpringMVC 框架。需要依赖 spring 环境和 spring-web.jar。
Spring IoC/DI
简介
IoC(Inversion of Control)中文名称:控制反转。也被称为DI(dependency injection)依赖注入。
属于同一件事情的两个名称。
IoC/DI 是指一个过程:对象的创建仅仅通过 Spring 容器负责,Spring 容器可以通过对象的构造方法或工厂方法进行实例化对象。在创建对象过程中,如果对象需要依赖其他对象,也可以直接在 Spring 容器中注入到当前对象。
整个过程中对象本身在容器中控制自己的实例化(所以叫做控制反转),通过构造方法或 setter 方法把依赖对象注入到自己(所以又叫做依赖注入)。
相关术语
容器(Container):放置所有管理对象的对象。其本质是在容器对象里面有一个全局 Map 对象,map 对象中放置所有被管理的对象。Spring 中容器是指 ApplicationContext 接口及子接口或实现类。
beans:容器中所有被管理的对象称为 beans。如果单说其中一个对象可称为 bean。
创建项目
创建普通 Maven 项目,并命名为 MySpring。
添加依赖
在项目的 pom.xml 中添加 Spring 项目的最基本依赖。
Spring 项目想要运行起来必须包含:
- spring-context.jar。spring 上下文依赖,它依赖了下面的四个 jar。
- spring-core.jar。Spring 核心 jar 包。它依赖了 spring-jcl.jar
- spring-aop.jar。Spring AOP 基本支持。
- spring-expression.jar。Spring 的表达式语言支持。
- spring-beans.jar。Spring 容器的 bean 管理。
- spring-jcl.jar。Spring 4 版本时是 common-logging.jar。从 5 开始 Spring 自己对日志进行了封装。
所以在 Maven 中想要使用 Spring 框架只需要在项目中导入 spring-context 就可以了,其他的 jar 包根据 Maven 依赖传递性都可以导入进来:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.20</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> </dependencies>
|
创建 Spring 配置文件
在 src/main/resources 下新建 applicationContext.xml 文件。
文件名称没有强制要求。官方示例中配置文件名称叫做 applicationContext.xml,所以我们也把 Spring 配置文件叫做 applicationContext.xml
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
|
创建类
在 src/main/java 下新建 pojo.Student.java 文件。
package pojo;
import java.io.Serializable; import java.util.*;
public class Student implements Serializable { private Integer id; private String name; private int age; private List<String> books; private String[] hobbies; private Map<String, Object> friends; private Set<String> games; private Phone phone; private Properties info;
public Student() { System.out.println("创建一个Student对象,使用无参构造方法创建对象。"); id = 1; name = "张三"; age = 20; }
public Student(Integer id, String name, int age) { this.id = id; this.name = name; this.age = age; System.out.println("有三个参数(Integer id, String name, int age)的构造方法运行"); }
public Properties getInfo() { return info; }
public void setInfo(Properties info) { this.info = info; }
public Phone getPhone() { return phone; }
public void setPhone(Phone phone) { System.out.println("setPhone方法运行,参数是:" + phone); this.phone = phone; }
@Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", books=" + books + ", hobbies=" + Arrays.toString(hobbies) + ", friends=" + friends + ", games=" + games + '}'; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(id, student.id) && Objects.equals(name, student.name) && Objects.equals(books, student.books) && Arrays.equals(hobbies, student.hobbies) && Objects.equals(friends, student.friends) && Objects.equals(games, student.games); }
@Override public int hashCode() { int result = Objects.hash(id, name, age, books, friends, games); result = 31 * result + Arrays.hashCode(hobbies); return result; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; System.out.println("setId()"); }
public String getName() { return name; }
public void setName(String name) { this.name = name; System.out.println("setName()"); }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; System.out.println("setAge()"); }
public List<String> getBooks() { return books; }
public void setBooks(List<String> books) { this.books = books; }
public String[] getHobbies() { return hobbies; }
public void setHobbies(String[] hobbies) { this.hobbies = hobbies; }
public Map<String, Object> getFriends() { return friends; }
public void setFriends(Map<String, Object> friends) { this.friends = friends; }
public Set<String> getGames() { return games; }
public void setGames(Set<String> games) { this.games = games; } }
|
同理创建 Phone.java
package pojo;
import java.io.Serializable; import java.util.Objects;
public class Phone implements Serializable { private Integer id; private String phoneNo;
public Phone() { }
@Override public String toString() { return "Phone{" + "id=" + id + ", phoneNo='" + phoneNo + '\'' + '}'; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Phone phone = (Phone) o; return Objects.equals(id, phone.id) && Objects.equals(phoneNo, phone.phoneNo); }
@Override public int hashCode() { return Objects.hash(id, phoneNo); }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getPhoneNo() { return phoneNo; }
public void setPhoneNo(String phoneNo) { this.phoneNo = phoneNo; } }
|
Bean 实例化的两种方式
在 Spring 中实例化 Bean 有两种方式:
- 通过构造方法进行实例化。默认使用无参构造。这种方式和以前 new 的方式是等效的。
- 通过工厂进行实例化。可以通过静态工厂和实例工厂进行实例化。这种方式完全是根据设计模式中工厂模式的思想而研发出的。Spring 考虑到如果需要频繁实例化某个类的对象,工厂模式无疑是一个好选择。
构造方法实例化
在 applicationContext.xml 中配置:
<bean id="student1" name="stu1, stu2,stu3" class="pojo.Student"></bean> <bean id="student2" name="stu4, stu5,stu6" class="pojo.Student"></bean>
|
在 src/test/java 中创建 TestSpring.java 从而进行测试:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testContainer(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("========== 容器创建完毕 =========="); Student s1 = (Student) context.getBean("student1");
Student s2 = context.getBean("stu1", Student.class);
Student s3 = context.getBean("stu2", Student.class);
Student s4 = context.getBean("student2", Student.class);
System.out.println("s1 : " + s1); System.out.println("s2 : " + s2); System.out.println("s3 : " + s3); System.out.println("s4 : " + s4); System.out.println(s1 == s4);
} }
|
输出:
创建一个Student对象,使用无参构造方法创建对象。 创建一个Student对象,使用无参构造方法创建对象。 ========== 容器创建完毕 ========== s1 : Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null} s2 : Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null} s3 : Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null} s4 : Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null} false
|
为什么返回 false ?
容器中创建若干 bean 对象,容器管理这些 bean 对象时,会检查是否同类型,如果同类型,尝试调用 equals。
如果对象等价(equals 返回 true)。那么 getBean 会返回同一个对象。当使用时,如果有冲突,则底层切换具体对象。没有冲突直接使用。
工厂实例化
实例工厂
创建工厂类
在 src/main/java 下创建 factory 软件包,创建 StudentInstanceFactory 类:
package factory;
import pojo.Student;
public class StudentInstanceFactory { private Student student = new Student();
public StudentInstanceFactory(){ System.out.println("创建了学生实例工厂对象。"); }
public Student getInstance(){ return student; } }
|
配置 bean
在 applicationContext.xml 中配置:
<bean id="stuInstanceFactory" class="factory.StudentInstanceFactory"></bean>
<bean id="instanceFactoryStudent" factory-bean="stuInstanceFactory" factory-method="getInstance"></bean>
|
在测试类中测试效果
在 TestSpring.java 中添加:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testInstanceFactory(){ ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Student student = context.getBean("instanceFactoryStudent", Student.class); System.out.println(student); } }
|
输出:
创建一个Student对象,使用无参构造方法创建对象。 创建了学生实例工厂对象。 Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null}
|
静态工厂
创建工厂类
factory 软件包创建 StudentStaticFactory 类:
package factory;
import pojo.Student;
public class StudentStaticFactory { private static Student student = new Student(); public StudentStaticFactory(){ System.out.println("学生静态工厂创建对象"); } public static Student newInstance(){ return student; } }
|
配置 bean
在 applicationContext.xml 中配置:
<bean id="staticFactoryStudent" class="factory.StudentStaticFactory" factory-method="newInstance"></bean>
|
在测试类中测试效果
在 TestSpring.java 中添加:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testStaticFactory(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("staticFactoryStudent", Student.class); System.out.println(student); } }
|
输出:
创建一个Student对象,使用无参构造方法创建对象。 Student{id=1, name='张三', age=20, books=null, hobbies=null, friends=null, games=null}
|
属性注入
在上面演示的都是如何实例化 Bean,下面演示的是如果给 Bean 的属性进行赋值。
Spring 中给 Bean 属性赋值有两种方式:
- 构造注入(Constructor-based Dependency Injection):通过构造方法给 bean 的属性赋值。所以要求 bean 的类中必须提供对应参数的构造方法(有参构造方法)。相当于以前创建对象时
new People(1,"张三");
- 设值注入,又称 setter 注入(Setter-based Dependency Injection):通过 Bean 的 setter 方法赋值。所以要求 Bean 中属性必须提供 setter 方法。相当于以前的
People peo = new People(); peo.setId(1); peo.setName("张三");
Spring 配置文件中的属性赋值方式
- 简单类型, 8 种基本类型,对应包装类型, String 类型。直接赋值,使用 value。
- 引用类型,对象。使用其他的 bean 对象赋值。 ref。
- 数组类型。 使用标签
<array><value></value><ref></ref></array>
赋值。一个 array 代表一个数组。内部的子标签 value 或 ref 代表一个元素。
- List 集合。 使用标签
<list><value></value><ref></ref></list>
或 <array>
赋值。一个 list 是一个集合。
- Set 集合。 使用标签
<set><value></value><ref></ref></set>
赋值。一个 set 是一个集合。
- Map 集合。 使用标签
<map><entry key="" value="" key-ref="" value-ref=""/></map>
赋值。一个 map 是一个集合。一个 entry 是一个键值对。
key 是键,value 是简单数据值。ref 是引用对象 bean 的 id。
- 特殊的 Map 集合 Properties。使用
<props><prop key="">value</prop></props>
或者 <map>
。一个 props 是一个 Properties 对象,
一个 prop 是一个键值对。
- 为任意的引用类型属性,赋值 null。
<null/>
。
构造注入
配置 bean
在配置文件 applicationContext.xml 中可以通过 <bean>
的子标签 <constructor-arg>
设置构造方法中一个参数的值。
constructor-arg 里面有5个属性,这5个属性分为2类。
用来确定给哪个属性进行赋值
name:参数名称
index:参数索引。从0开始算起。
type:参数类型。8大基本数据类型可以直接写关键字。其他类型需要写类型的全限定路径。
这三个属性如果只需要用到一个就能精确的告诉 Spring,要设置的构造方法参数是哪个可以使用一个。
如果无法精确到某个构造方法参数,可以多个一起结合使用。
设置属性的值
value:简单数据类型直接设置。Spring 会自动进行类型转换。
ref:需要引用另一个 bean 的 id。也就是说这个参数是一个类类型,且这个类的对象也被 Spring 容器管理。
<bean id="student" class="pojo.Student"> <constructor-arg name="id" value="10"></constructor-arg> <constructor-arg type="java.lang.String" value="李四"></constructor-arg> <constructor-arg index="2" value="30"></constructor-arg> </bean>
|
在测试类中测试效果
在 TestSpring.java 中添加:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testConstructor(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("student", Student.class); System.out.println(student); } }
|
输出:
有三个参数(Integer id, String name, int age)的构造方法运行 Student{id=10, name='李四', age=30, books=null, hobbies=null, friends=null, games=null}
|
设值注入
配置 bean
spring 容器使用设置注入时,是基于 property 实现的。必须有 getter 方法。且属性命名是 property 名。
property 代表调用一个 setter 方法。
属性:
name 属性名。
value 要赋予的属性值。
ref 要赋予的属性值,引用其他 bean 的 id。
<bean id="student" class="pojo.Student"> <property name="id" value="100"></property> <property name="name" value="王五"></property> <property name="age" value="25"></property> </bean>
|
在测试类中测试效果
在 TestSpring.java 中添加:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testSetter(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("student", Student.class); System.out.println(student); } }
|
输出:
创建一个Student对象,使用无参构造方法创建对象。 setId() setName() setAge() Student{id=100, name='王五', age=25, books=null, hobbies=null, friends=null, games=null}
|
不同属性类型对应的写法
无论是构造注入还是设值注入都提供了 value 和 ref 进行设置值。
这两个属性只能给属性赋予简单数据类型或其他 bean 的引用。如果类的属性是数组、集合等类型需要通过下面方式进行设置。
这些标签都是 <property>
或 <constructor-args>
的子标签。
一旦使用了下面子标签方式,就不能对 <property>
或 <constructor-args>
设置 value 属性或 ref 属性。且需要在 People 类中提供对应名称,对应类型的属性。
Set 类型
<bean id="stu" class="pojo.Student"> <property name="games"> <set> <value>game1</value> <value>game2</value> </set> </property> </bean>
|
List 类型
<bean id="stu" class="pojo.Student"> <property name="books"> <list> <value>book1</value> <value>book2</value> </list> </property> </bean>
|
Array 类型
<bean id="stu" class="pojo.Student"> <property name="hobbies"> <array> <value>吃饭</value> <value>睡觉</value> </array> </property> </bean>
|
Map 类型
<bean id="stu" class="pojo.Student"> <property name="friends"> <map> <entry key="基友" value="基友"></entry> <entry key="闺蜜" value="闺蜜"></entry> </map> </property> </bean>
|
Null 值类型
<bean id="stu" class="pojo.Student"> <property name="phone"> <null></null> </property> </bean>
|
引用其他 Bean
如果需要引用其他 Bean,直接在 property 标签中使用 ref 引用就可以。使用子标签 ref 也可以,但是没有直接用 ref 属性的方式简单。
<bean id="phone" class="pojo.Phone"> <property name="id" value="1"/> <property name="phoneNo" value="114514"/> </bean>
|
然后
<bean id="stu" class="pojo.Student"> <property name="phone"> <null></null> </property> </bean>
|
Properties 类型
<bean id="stu" class="pojo.Student"> <property name="info"> <props> <prop key="npy">无</prop> </props> </property> </bean>
|
综合:
<bean id="stu" class="pojo.Student"> <property name="books"> <list> <value>book1</value> <value>book2</value> </list> </property> <property name="hobbies"> <array> <value>吃饭</value> <value>睡觉</value> </array> </property> <property name="friends"> <map> <entry key="基友" value="基友"></entry> <entry key="闺蜜" value="闺蜜"></entry> </map> </property> <property name="games"> <set> <value>game1</value> <value>game2</value> </set> </property> <property name="phone"> <null/> </property> <property name="info"> <props> <prop key="npy">无</prop> </props> </property> </bean> <bean id="phone" class="pojo.Phone"> <property name="id" value="1"/> <property name="phoneNo" value="114514"/> </bean>
|
在测试类中测试效果
在 TestSpring.java 中添加:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Student;
public class TestSpring { @Test public void testDI(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("stu", Student.class); System.out.println(student); System.out.println(student.getInfo()); System.out.println(student.getPhone()); } }
|
输出:
创建一个Student对象,使用无参构造方法创建对象。 setPhone方法运行,参数是:null Student{id=1, name='张三', age=20, books=[book1, book2], hobbies=[吃饭, 睡觉], friends={基友=基友, 闺蜜=闺蜜}, games=[game1, game2]} {npy=无} null
|