Spring框架

一、什么是spring框架

spring框架主要是分为两个部分

1、IoC:控制反转

2、aop:面向切面编程,扩展功能不修改源代码实现。

通过反射的方式创建对象。

快速创建一个spring

1、使用idea创建maven空项目,在pom.xml配置文件中引入spring。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.6</version>
    </dependency>
</dependencies>

2、创建Student实体类(采用Lombok)

package top.qinxiangyu.entity;

import lombok.Data;

/**
 * @author decqxy
 */
@Data
public class Student {
    private Integer id;
    private String name;
    private Integer age;

}

3、在resources文件夹下创建 spring.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 http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="student" class="top.qinxiangyu.entity.Student"></bean>
</beans>

4、通过IoC容器来读取spring.xml配置文件,加载bean标签来创建对象。

5、调用API获取IoC容器中以及创建的对象。

// 使用spring容器创建对象。
// 读取配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 通过id类获取对象
Student student1 = (Student) applicationContext.getBean("student");
System.out.println(student1);

6、传统的方式创建对象

// 传统方式创建对象,人工创建对象
Student student = new Student();
System.out.println(student);

IoC容器创建bean的两种方式

  1. 无参构造

    <bean id="student" class="top.qinxiangyu.entity.Student"></bean>
    

    给成员变量赋值,通过 property

    <bean id="student" class="top.qinxiangyu.entity.Student">
        <property name="id" value="1"/>
        <property name="name" value="李四"/>
        <property name="age" value="19"/>
    </bean>
    

    通过property进行赋值,地层是通过set方法设置的,会自动将需要转换为对应的类型,将字符串转换为指定的量。

  2. 有参构造

    • 通过name实现

      <!--通过有参构造方式-->
      <bean id="student1" class="top.qinxiangyu.entity.Student">
          <constructor-arg name="id" value="2"/>
          <constructor-arg name="name" value="张三"/>
          <constructor-arg name="age" value="20"/>
      </bean>
      
    • 通过下标实现

      <!--通过有参构造方式-->
      <bean id="student1" class="top.qinxiangyu.entity.Student">
          <constructor-arg index="0" value="2"/>
          <constructor-arg index="1" value="张三"/>
          <constructor-arg index="2" value="20"/>
      </bean>
      

      通过constructor-arg设置值。

      **注意:**这种通过有参构造方式去创建容器,需要在实体类中实现有参构造。

IoC容器中获取bean的两种方式

  1. 通过id获取bean

    Student student = (Student) applicationContext.getBean("student");
    

    **注意:**通过id获取bean,在配置文件中不能存在相同的id标签。

  2. 通过类型取值。

    Student student = (Student) applicationContext.getBean(Student.class);
    

    **注意:**使用这种方式获取bean时,如果IoC容器中同时存在两个及以上的bean时,就会抛出异常,原因是没有唯一的bean

IOC DI

DI指bean之间的依赖注入(一个对象包含另一个对象),设置对象之间的练级关系。

一个学生中包含一个班级的注入

1、创建实体类

Classes类

package top.qinxiangyu.entity;

import lombok.Data;

/**
 * @author decqxy
 */
@Data
public class Classes {
    private Integer id;
    private String name;

}

Student类

package top.qinxiangyu.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author decqxy
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private Classes classes;
}

2、创建配置文件spring-di.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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="classes" class="top.qinxiangyu.entity.Classes">
        <property name="id" value="1"/>
        <property name="name" value="一班"/>
    </bean>

    <bean id="student" class="top.qinxiangyu.entity.Student">
        <property name="id" value="1"/>
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <!--通过容器将classes注入到student对象中-->
        <!--ref 将ioc容器中寻找所要的对象-->
        <property name="classes" ref="classes"/>
    </bean>
</beans>

ref:在该容器中寻找具体的bean并注入到该对象中

bean之间的级联需要使用ref属性来完成映射,而不能直接使用value,不然就会出现类型转换异常的错误。

3、编写测试

package top.qinxiangyu.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import top.qinxiangyu.entity.Student;

/**
 * @author decqxy
 */
public class SpringDiTest {
    @Test
    public void test01() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-di.xml");
        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);
    }
}

4、测试结果

Student(id=1, name=张三, age=18, classes=Classes(id=1, name=一班))

**注意:**bean中属性出席那特殊字符使用

<bean id="classes" class="top.qinxiangyu.entity.Classes">
	<property name="id" value="1"/>
	<property name="name">
		<value><![CDTAT[<一班>]]></value>
	</property>
</bean>

一个班级包含多个学生

1、创建实体类

Classes类

package top.qinxiangyu.entity;

import lombok.Data;

import java.util.List;

/**
 * @author decqxy
 */
@Data
public class Classes {
    private Integer id;
    private String name;
    private List<Student> studentList;

}

Student类

package top.qinxiangyu.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author decqxy
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

2、配置spring-di.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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="classes" class="top.qinxiangyu.entity.Classes">
        <property name="id" value="1"/>
        <property name="name" value="一班"/>
        <property name="studentList">
            <list>
                <ref bean="student"/>
                <ref bean="student1"/>
            </list>
        </property>
    </bean>

    <bean id="student" class="top.qinxiangyu.entity.Student">
        <property name="id" value="1"/>
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="student1" class="top.qinxiangyu.entity.Student">
        <property name="id" value="2"/>
        <property name="name" value="李四"/>
        <property name="age" value="30"/>
    </bean>
</beans>

**注意:**通过一对多的情况下,使用list集合需要如下书写

<property name="studentList">
    <list>
        <ref bean="student"/>
        <ref bean="student1"/>
    </list>
</property>

3、编写测试类

@Test
public void test02() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-di.xml");
    Classes classes = (Classes) applicationContext.getBean("classes");
    System.out.println(classes);
}

4、测试结果

Classes(id=1, name=一班, studentList=[Student(id=1, name=张三, age=18), Student(id=2, name=李四, age=30)])

5、整个过程IoC的实现方式

image-20210508002129638

Spring中bean的作用域

bean是根据scope来生成的,表示bean的作用域,scorp有4中类型。

  1. singleton:单例,表示通过spring容器获取的对象是唯一的,也是scope的默认值。

    image-20210508145325843

  2. prototype:原型,表示通过spring容器获取对象是不同的。

    image-20210508145433618

singleton模式下,只要加载IoC容器,无论是否从IoC中取出bean,配置文件中所配置的bean都会被创建。

prototype模式下,如果不从IoC容器中取bean,则不会创建对象,取一次则创建一次。

Spring的继承

spring继承不同于Java中的继承,区别:Java中的继承是针对类的继承,而spring是对象bean。

spring的继承中,子 bean可以继承父 bean中所有成员变量的值。

<bean id="user1" class="top.qinxiangyu.entity.User">
    <property name="id" value="1"/>
    <property name="name" value="张三"/>
</bean>

<!--继承并修改成员变量的name值-->
<bean id="user2" class="top.qinxiangyu.entity.User" parent="user1">
    <property name="name" value="李四"/>
</bean>

bean标签的 parent属性去继承一个父bean,并通过改变继承成员变量的值。

由于spring的继承是针对的对象,所以子bean 和 父 bean并不需要属于同一个数据类型,只需要有相同的成员变量即可。

image-20210508151424001

Srping中的依赖

用于设置两个bean加载的顺序

IoC容器默认是通过配置文件中bean的配置顺序进行加载的,配置在前面的bean会先被加载。

在不修改配置文件顺序的情况下,通过设置bean直接的依赖关系来调整bean的加载顺序。

通过配置bean属性depends-on来调整顺序。

<?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">

    <bean id="user" class="top.qinxiangyu.entity.User" depends-on="student"></bean>
    <bean id="student" class="top.qinxiangyu.entity.Student"></bean>
</beans>

首先创建的是student对象,然后再是user对象

Spring读取外部资源文件

通常的应用:数据库的配置信息修改,后缀为properties文件中

jdbc.properties

username = root
password = root
url = com.mysql.jdbc.Driver
driverName = jdbc:mysql://localhost:3306/book

spring.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="myDataSource" class="top.qinxiangyu.entity.MyDataSource">
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
        <property name="url" value="${url}"/>
        <property name="driverName" value="${driverName}"/>
    </bean>

</beans>

需要添加<context:property-placeholder location="classpath:jdbc.properties"/>才能被后面的识别,并通过${}来获取相应的值。

Spring工厂模式

主要是有两种工程模式:静态工厂、实例工厂

  1. 静态工厂
  2. 实例工厂

静态工厂方法创建Car对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用spring.xml中只需要配置一个bean,最后的几个和Car相同。

实例工厂创建Car对象,需要实例化对象,因为getCar方法是非静态的就必须通过实例化对象才能调用,所以就必须要创建工厂对象,spring.xml中就需要配置两个bean,一个是工厂bean,一个是Car bean。

spring.xml 中 class + factory-method的形式是直接调用类中的工厂方法

spring.xml 中 factory-bean + factory-method 的形式是调用工厂中的bean中的工厂方法,就必须先创建工厂bean。

Spring IoC自动装载 Autowire

自动装载是Spring提供的一种更加简便的方式来完成DI,不需要手动配置property,IoC容器会自动选择bean完成注入。

自动装载有两种方式:

  1. ByName,通过属性名称完成自动装载。

    Person类

    package top.qinxiangyu.entity;
    
    import lombok.Data;
    
    /**
     * @author decqxy
     */
    @Data
    public class Person {
        private Integer id;
        private String name;
        private Car car;
    }
    

    Car 类

    package top.qinxiangyu.entity;
    
    import lombok.Data;
    
    /**
     * @author decqxy
     */
    @Data
    public class Car {
        private Integer id;
        private String name;
    
    }
    

    spring.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person" class="top.qinxiangyu.entity.Person" autowire="byName">
            <property name="id" value="1"/>
            <property name="name" value="张三"/>
        </bean>
    
        <bean id="car" class="top.qinxiangyu.entity.Car">
            <property name="id" value="1"/>
            <property name="name" value="奥迪"/>
        </bean>
    
    </beans>
    

    **注意:**该方法通过name进行自动装配,也就是通过Person 成员变量中Car 的名称car来进行自动装载的。在IoC容器中取寻找一个id为car的bean,然后完成自动装载。

  2. ByType,通过属性对应的数据完成自动装载。

    同理,通过属性进行自动装载就是直接通过 Person 成员变量Car直接装配的。使用byType进行自动装载时,必须保证ioc中只有一个唯一类型的bean,否则会抛出异常。

    Spring IoC基于注解的开发

    spring ioc主要作用,帮助开发者创建项目所需要的bean,同时进行依赖注入(DI)。

    实现该功能有两种方式

    1. 基于xml配置
    2. 基于注解

    基于注解有两步,缺一不可:

    1. 配置自动扫包
    2. 添加注解

    配置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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="top.qinxiangyu.entity"/>
    </beans>
    

    添加注解@Component Repo类使其称自动注册为bean

    @Data
    @Component
    public class Repo {
        private MyDataSource myDataSource;
    }
    

    进行注解开发测试

    @Test
    public void test07() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-anno.xml");
        Repo repo = (Repo) applicationContext.getBean("repo");
        System.out.println(repo);
    }
    

    这里通过类名首字母小写进行获取bean

    还可以通过别名的方式获取,在@Component后这种value (value = "ds")

    @Data
    @Component(value = "ds")
    public class Repo {
        private MyDataSource myDataSource;
    }
    

    并在测试中写上获取bean的名称

    @Test
    public void test07() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-anno.xml");
        Repo repo = (Repo) applicationContext.getBean("ds");
        System.out.println(repo);
    }
    

    DI注入myDataSource,只需要将Repo中的对象添加注解@Autowired

    @Data
    @Component(value = "ds")
    public class Repo {
    
        @Autowired
        private MyDataSource myDataSource;
    }
    

    然后将MyDataSource添加@Component注解,设置为自动注册bean

    @Data
    @Component
    public class MyDataSource {
        private String username;
        private String password;
        private String url;
        private String driverName;
    }
    

    输出的结果为:

    Repo(myDataSource=MyDataSource(username=null, password=null, url=null, driverName=null))
    

    如何将username等设置有值呢?

    在MyDataSource中每个成员变量上添加@Value注解并设置值

    @Data
    @Component
    public class MyDataSource {
        @Value("root")
        private String username;
        @Value("root")
        private String password;
        @Value("com.mysql.jdbc.Driver")
        private String url;
        @Value("jdbc:mysql://localhost:3306/book")
        private String driverName;
    }
    

    输出的结果为:

    Repo(myDataSource=MyDataSource(username=root, password=root, url=com.mysql.jdbc.Driver, driverName=jdbc:mysql://localhost:3306/book))
    

Q.E.D.


一个在读大学生