Spring框架的两大特性为IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programing,面向切面编程)。
Spring采用了非侵入式设计,也就是我们在使用Spring框架时,无虚继承框架提供的任何类,保证了在更换框架时,代码不需要进行很大程度的重构。
Spring是轻量级的、非侵入性的、所依赖的东西非常少、资源占用非常少、部署简单。
控制反转:将在程序中手动创建对象的控制权交由Spring管理,自己则无需关心对象的创建,需要的时候直接从Spring容器中取即可,也就是将创建对象的控制权交给了Spring,控制权反转。
1. 导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!--引入web会将所有的spring相关依赖全部导入-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
2. 定义一个符合JavaBean规范(符合规范的实体类对象可以交由Spring容器进行管理)的类
A. JavaBean:JavaBean 是一种JAVA语言写成的可重用组件,是符合一定规范编写的Java类,不是一种技术,而是一种规范。符合这种规范的类,可以被其它的程序员或者框架使用。它的方法命名,构造及行为必须符合特定的约定:
1、所有属性为private。
2、这个类必须有一个公共的无参构造函数。
3、访问这个类的属性要使用getter和setter。
4、实现serializable接口。
package com.zt.pojo;
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello(){
System.out.println("Hello," + name);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
B. 在学习MVC开发模式时,我们习惯将实体类称为POJO:(Plain Ordinary Java Object)简单的Java对象,不允许有业务方法,也不能携带connection之类的方法,实际就是普通JavaBeans,POJO的叫法是为了避免和EJB混淆所创造的简称。当然还是有细小的区别,JavaBean中是可以有一些简单方法的,而POJO中除了属性、构造、getter、setter之外,不允许有其他方法。
package com.zt.pojo;
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
C. Entity:一个实体类对应一个数据表,用于ORM映射,其中的属性对应数据表中的字段,在传递参数时,可以将参数存储在实体类中传递实体类,除了与数据库相关外,其他的,几乎和POJO没有区别。
3. 编写配置文件
<?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">
<bean id="user" class="com.zt.entity.User">
<property name="name" value="zzt"/>
</bean>
</beans>
xmlns是xml namespace std,用于引入一些约束性文件,保证该xml文件符合官方定义的规范。
xmlns:xsi则是加了前缀xsi的命名空间,用于在不同种类的约束下可能会有同名的标签存在,为了保证正确解析,对于重复的标签我们用标识符加以区分,<xsi:标签名>。不加表示符则表示默认的命名空间,Spring的默认命名空间是<bean:标签名>。
<bean>标签:id 表示这个对象在容器中的标识 class 这个对象的类型
<property>标签:用于对实体类的对象进行赋值,name是属性名,value是对应的属性值。读者应该反应过来,此处赋值采用的就是属性对应的setter。
4. 编写测试
@Test
public void testCreateBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
读取并解析配置文件后生成Spring容器applicationContext,通过容器的getBean方法,提供id和类型就可以得到容器帮我们创建的实体类。
ClassPathXmlApplicationContext:类路径的资源加载器,在Maven工程中,main目录下java和resources里的东西编译后会被放置在类路径下。
当然Spring也提供了加载其他地方资源的加载器:FileSystemXmlApplicationContext。但是并不常用,因为我们一般不会把项目的配置文件放在项目之外的地方。
控制反转 vs 依赖注入:控制反转是一种思想,将实体类的控制权从程序员移交给Spring容器,依赖注入是实现控制反转的主要方式;在程序中,往往实体类之间存在联系,相互耦合,传统的开发过程是由程序员自行维护的,一旦某个类出现了问题,另一个类在使用这个类的地方就需要进行修改,原因在于是我们手动绑定了他们的关联关系;而Spring容器则是通过让程序员直接配置哪些是关联的,由容器负责真正实现关联,当我们要替换使用的类的时候,直接修改一下配置文件就好了,而不需要把每一个使用的地方的代码都修改一遍。
我们举个例子,现在有两个学生,他们都有跑步的方法,但是行为方式完全不同,我们由老师发出run的命令,于是代码可以按下编写:
public interface Stu {
public void run();
}
public class StuA implements Stu{
public void run(){
System.out.println("A跑的很快");
}
}
public class StuB implements Stu{
public void run(){
System.out.println("B跑的很慢");
}
}
public class Teacher {
private Stu stu;
public Stu getStu() {
return stu;
}
public void setStu(Stu stu) {
this.stu = stu;
}
}
import com.zt.entity.Teacher;
import com.zt.entity.Stu;
import com.zt.entity.StuA;
public class TestNoDI {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Stu stuA = new StuA();
teacher.setStu(stuA);
teacher.getStu().run();
}
}
现在一切都好,但是如果教师转而对学生B发出命令了呢?我们就要修改参数的绑定:
import com.zt.entity.Teacher;
import com.zt.entity.Stu;
import com.zt.entity.StuB;
public class TestNoDI {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Stu stuB = new StuB();
teacher.setStu(stuB);
teacher.getStu().run();
}
}
而一旦我们的系统变得很庞大之后,所有这些用代码直接绑定的地方我们都需要一个一个修改!!!而Spring可以通过在配置文件里修改一次就可以一劳永逸。
<?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">
<bean id = "stuA" class = "com.zt.entity.StuA"/>
<bean id = "stuB" class = "com.zt.entity.StuB"/>
<bean id="teacher" class="com.zt.entity.Teacher">
<property name="stu" ref="stuA"/>
</bean>
</beans>
<property>标签的ref用于指向bean id,标识要将哪个实体类作为属性进行赋值。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zt.entity.Teacher;
public class TestHasDI {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans_experient.xml");
Teacher teacher = applicationContext.getBean("teacher", Teacher.class);
teacher.getStu().run();
}
}
如果我们想要修改使用的stu,直接在配置文件中修改ref即可,而不需要修改使用该属性的代码:
<bean id="teacher" class="com.zt.entity.Teacher">
<property name="stu" ref="stuB"/>
</bean>
1.构造器注入:调用构造方法进行注入(必须要有与参数属性、数量一致的构造方法才可以使用)
Java本身的限制就使得不可能存在两个构造方法存在相同的参数属性和参数数量,因此保证了各个构造方法的唯一。
package com.zt.entity;
public class Dog {
private String name;
private int age;
private String master;
private String home;
public Dog() {
}
public Dog(String name, String master) {
this.name = name;
this.master = master;
}
public Dog(String name, int age, String master, String home) {
this.name = name;
this.age = age;
this.master = master;
this.home = home;
}
}
调用无参构造注入:
<bean id="dog" class="com.zt.entity.Dog"/>
public class TestFirst {
@Test
public void testCreateBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Dog dog = applicationContext.getBean("dog", Dog.class);
System.out.println(dog);
}
}
调用全参构造注入:
<bean id="dog" class="com.zt.entity.Dog">
<constructor-arg name="name" value="汪汪"/>
<constructor-arg name="home" value="nanjing"/>
<constructor-arg name="master" value="zjm"/>
<constructor-arg name="age" value="5"/>
</bean>
可见,参数的顺序是无关紧要的。
尝试使用不存在的构造方法进行注入:
可以看到xml文件的约束帮助我们检查了文件的合法性,由于不存在只有一个String name的构造方法,因此无法通过检查。
为了防止注入错误,Spring严格检查了参数类型、参数数量以及参数名。
构造器注入也可以使用index的方式指定属性,从0开始,与对应构造方法的参数顺序相同:
但是极其不推荐使用index,因为这样很容易产生歧义:
显然我们设定的参数符合右边的两个方法,但是实际上Spring执行了第二个。为了避免这种情况,我们应该使用name进行参数与值的绑定。
2.[重要]调用setter注入时,不同类型的属性注入方法:
<property> + value : 用于基本类型的注入,除基本数据类型外还包括string。
<property> + ref : 用于引用类型的注入。
<property> + <array> + <value> : 用于数组类型的注入。
<property> + <list> + <value> : 用于list类型的注入。
<property> + <map> + <entry> : 用于map类型的注入。
<property> + <props> + <prop> : 用于property类型的注入。
还可以为引用类型注入null,使用<null>标签。
我们增加一个类:Adress用于实验
package com.zt.entity;
public class Adress {
}
package com.zt.entity;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Teacher {
private Stu stu;
private String name;
private Adress adress;
@Override
public String toString() {
return "Teacher{" +
"stu=" + stu +
", name='" + name + '\'' +
", adress=" + adress +
'}';
}
public Adress getAdress() {
return adress;
}
public void setAdress(Adress adress) {
this.adress = adress;
}
private String[] hobbies;
private List<String> duties;
private Map<String,String> family;
private Set<String> carts;
private Properties workExperience;
public Teacher() {
}
public Teacher(Stu stu, String name, String[] hobbies, List<String> duties, Map<String, String> family, Set<String> carts, Properties workExperience) {
this.stu = stu;
this.name = name;
this.hobbies = hobbies;
this.duties = duties;
this.family = family;
this.carts = carts;
this.workExperience = workExperience;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
public List<String> getDuties() {
return duties;
}
public void setDuties(List<String> duties) {
this.duties = duties;
}
public Map<String, String> getFamily() {
return family;
}
public void setFamily(Map<String, String> family) {
this.family = family;
}
public Set<String> getCarts() {
return carts;
}
public void setCarts(Set<String> carts) {
this.carts = carts;
}
public Properties getWorkExperience() {
return workExperience;
}
public void setWorkExperience(Properties workExperience) {
this.workExperience = workExperience;
}
public Stu getStu() {
return stu;
}
public void setStu(Stu stu) {
this.stu = stu;
}
}
<?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">
<bean id = "stuA" class = "com.zt.entity.StuA"/>
<bean id = "stuB" class = "com.zt.entity.StuB"/>
<bean id="teacher" class="com.zt.entity.Teacher">
<!--注入基本属性-->
<property name="name" value="zzt"/>
<!--注入引用类型-->
<property name="stu" ref="stuB"/>
<!--注入null-->
<property name="adress">
<null/>
</property>
<!--注入数组-->
<property name="hobbies">
<array>
<value>羽毛球</value>
<value>橄榄球</value>
<value>排球</value>
</array>
</property>
<!--注入list-->
<property name="duties">
<list>
<value>猎头</value>
<value>顾问</value>
</list>
</property>
<!--注入map-->
<property name="family">
<map>
<entry key="husband" value="zjm"/>
</map>
</property>
<!--注入set-->
<property name="carts">
<set>
<value>衬衫</value>
<value>鞋子</value>
</set>
</property>
<!--注入property-->
<property name="workExperience">
<props>
<prop key="IT">项目经理</prop>
</props>
</property>
</bean>
</beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zt.entity.Teacher;
public class TestHasDI {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans_experient.xml");
Teacher teacher = applicationContext.getBean("teacher", Teacher.class);
System.out.println(teacher);
}
}