Spring-学习笔记

Posted 程序航

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring-学习笔记相关的知识,希望对你有一定的参考价值。

官网

Spring官网

Spring组件

Spring 中的组件,大致上提供了如下功能:

2-1.png

IOC

概念

Ioc (Inversion of Control),中文叫做控制反转。这是一个概念,也是一种思想。控制反转,实际上就是指对一个对象的控制权的反转。

Ioc介绍

首先创建一个普通的 Maven 项目,然后引入 spring-context 依赖,如下:

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

接下来,在 resources 目录下创建一个 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 http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

在这个文件中,我们可以配置所有需要注册到 Spring 容器的 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">

    <bean class="org.javaboy.Book" id="book"/>
</beans>

class 属性表示需要注册的 bean 的全路径,id 则表示 bean 的唯一标记,也开可以 name 属性作为 bean 的标记,在超过 99% 的情况下,id 和 name 其实是一样的,特殊情况下不一样。

接下来,加载这个配置文件:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

执行 main 方法,配置文件就会被自动加载,进而在 Spring 中初始化一个 Book 实例。此时,我们显式的指定 Book 类的无参构造方法,并在无参构造方法中打印日志,可以看到无参构造方法执行了,进而证明对象已经在 Spring 容器中初始化了。

最后,通过 getBean 方法,可以从容器中去获取对象:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Book book = (Book) ctx.getBean("book");
        System.out.println(book);
    }
}

加载方式,除了ClassPathXmlApplicationContext 之外(去 classpath 下查找配置文件),另外也可以使用 FileSystemXmlApplicationContext ,FileSystemXmlApplicationContext 会从操作系统路径下去寻找配置文件。

public class Main {
    public static void main(String[] args) {
        FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("F:\\\\workspace5\\\\workspace\\\\spring\\\\spring-ioc\\\\src\\\\main\\\\resources\\\\applicationContext.xml");
        Book book = (Book) ctx.getBean("book");
        System.out.println(book);
    }
}

通过 Class获取Bean

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Book book = ctx.getBean(Book.class);
        System.out.println(book);
    }
}

这种方式有一个很大的弊端,如果存在多个实例,这种方式就不可用,例如,xml 文件中存在两个 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">

    <bean class="org.javaboy.Book" id="book"/>
    <bean class="org.javaboy.Book" id="book2"/>
</beans>

此时,如果通过 Class 去查找 Bean,会报如下错误:

3-2-1.png

所以,一般建议使用 name 或者 id 去获取 Bean 的实例。

属性注入

构造方法注入

通过 Bean 的构造方法给 Bean 的属性注入值。

  1. 第一步首先给 Bean 添加对应的构造方法:
public class Book implements Serializable {

    private static final long serialVersionUID = 5492270562431552420L;

    private Integer id;

    private String name;

    private Double price;

    public Book() {
    }

    public Book(Integer id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
}
  1. 在 xml 文件中注入 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">

    <!--构造方法注入:不能清晰看到定义的哪个属性,不推荐使用-->
    <bean id="book" class="cn.sail.training.spring.ioc.Book">
        <constructor-arg index="0" value="1"/>
        <constructor-arg index="1" value="红楼梦"/>
        <constructor-arg index="2" value="33"/>
    </bean>
    
</beans>

constructor-arg 中的 index 和 Book 中的构造方法参数一一对应。写的顺序可以颠倒,但是 index 的值和 value 要一一对应。

另一种构造方法中的属性注入,则是通过直接指定参数名来注入:

<?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="book1" class="cn.sail.training.spring.ioc.Book">
        <constructor-arg name="id" value="2"/>
        <constructor-arg name="name" value="西游记"/>
        <constructor-arg name="price" value="40"/>
    </bean>
    
</beans>

如果有多个构造方法,则会根据给出参数个数以及参数类型,自动匹配到对应的构造方法上,进而初始化一个对象。

set 方法注入

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



    <!--set方法注入:可以较直观的看到定义的属性,推荐使用-->
    <bean id="user" class="cn.sail.training.spring.ioc.User">
        <property name="id" value="1"/>
        <property name="age" value="27"/>
        <property name="name" value="廖航"/>
    </bean>
    
</beans>

set 方法注入,不是根据属性名对应的值,而是根据 get/set 方法分析出来的属性名。

如果改变set方法的名称,就算属性名不变,也会找不到值而报错。

p 名称空间注入

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

    <!--p名称空间注入:本质上也是调用了set方法,层次结构不清晰,且需要额外引进标签值,不推荐使用-->
    <bean id="book2" class="cn.sail.training.spring.ioc.Book" p:id="2" p:name="西游记" p:price="33"/>

</beans>

静态工厂注入

首先提供一个 OkHttpClient 的静态工厂:

import okhttp3.OkHttpClient;

public class OkHttpUtils {

    private static OkHttpClient okHttpClient;

    public static OkHttpClient getInstance() {
        if (okHttpClient == null) {
            okHttpClient = new OkHttpClient.Builder().build();
        }
        return okHttpClient;
    }

}

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

    <!--静态工厂注入-->
    <bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>

</beans>

这个配置表示 OkHttpUtils 类中的 getInstance 是我们需要的实例,实例的名字就叫 okHttpClient。然后,在 Java 代码中,获取到这个实例,就可以直接使用了。

实例工厂注入

实例工厂就是工厂方法是一个实例方法,这样,工厂类必须实例化之后才可以调用工厂方法。

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

    <!--实例工厂注入-->
    <bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>

    <bean id="okHttpClient1" class="okhttp3.OkHttpClient" factory-bean="okHttpUtils"/>

</beans>

复杂属性注入

对象注入

可以通过 xml 注入对象,通过 ref 来引用一个对象。

<bean id="user1" class="cn.sail.training.spring.ioc.User">
    <property name="cat" ref="cat"/>
</bean>

<bean id="cat" class="cn.sail.training.spring.ioc.Cat">
    <property name="name" value="小白"/>
    <property name="color" value="白色"/>
</bean>

数组注入

<bean id="user2" class="cn.sail.training.spring.ioc.User">
    <property name="favorites">
        <array>
            <value>足球</value>
            <value>篮球</value>
            <value>乒乓球</value>
        </array>
    </property>
</bean>

集合注入

即可以通过 ref 使用外部定义好的 Bean,也可以直接在 list 或者 array 节点中定义 bean。

<bean id="user3" class="cn.sail.training.spring.ioc.User">
    <property name="cats">
        <list>
            <ref bean="cat"/>
            <bean id="cat2" class="cn.sail.training.spring.ioc.Cat">
                <property name="name" value="小黑"/>
                <property name="color" value="黑色"/>
            </bean>
        </list>
    </property>
</bean>

Map注入

<bean id="user4" class="cn.sail.training.spring.ioc.User">
    <property name="map">
        <map>
            <entry key="name" value="sail"/>
            <entry key="age" value="27"/>
        </map>
    </property>
</bean>

Properties注入

<bean id="user5" class="cn.sail.training.spring.ioc.User">
    <property name="info">
        <props>
            <prop key="name">sail</prop>
            <prop key="age">27</prop>
        </props>
    </property>
</bean>

Java配置

在 Spring 中,想要将一个 Bean 注册到 Spring 容器中,整体上来说,有三种不同的方式。

  • XML 注入
  • Java 配置(通过 Java 代码将 Bean 注册到 Spring 容器中)
  • 自动化扫描

Java配置的方式注册有以下步骤:

  1. 配置类上加@Configuration注解,这个注解表示这个类不是一个普通类,而是一个配置类,它的作用相当于 applicationContext.xml

  2. 定义方法,方法返回对象,方法上添加@Bean注解,表示将这个方法的返回值注入的Spring容器中去。也就是说,@Bean所对应的方法,就相当于applicationContext.xml中的bean节点。

@Configuration
public class Config {

    @Bean
    User user() {
        return new User();
    }

}

配置加载

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean(User.class);

Bean的默认名称是方法名。如果想自定义方法名,直接在@Bean中进行配置。如下配置表示修改Bean的名字为sail:

@Configuration
public class Config {

    @Bean("sail")
    User user() {
        return new User();
    }

}

配置加载

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean("sail", User.class);

注:配置类需要在项目启动时加载才生效。

以上是关于Spring-学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

spring学习笔记AOP

Spring 学习笔记

Spring学习笔记--Spring IOC

Spring学习笔记:尝试Lombok简化实体类代码

Spring源码学习笔记

Spring4基础 学习笔记 ---- Bean