第1章: Spring 基础

第1章:Spring 基础

spring 简史

  1. xml配置
    spring1.x,主要配置各种Bean
  2. 注解配置
    spring2.x,基本配置用xml,业务配置用注解
  3. Java 配置
    spring3.x,spring boot 都推荐使用 Java 配置

spring 概述

spring framework runtime
spring framework runtime

包括内容很多,每一块都能单独拿出来讲。

spring 在 idea 中的搭建

为了体验spring,所以建立的是一个 maven quick start 的项目,建立后的 pom.xml 配置如下:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wisely</groupId>
    <artifactId>highlight_spring5_idea</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>highlight_spring5_idea</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <!-- put your configurations here -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

spring 基础配置

spring 框架四大原则:

  1. 使用 POJO(Plain Old Java Object) 进行轻量级和最小侵入式开发
  2. 依靠依赖注入和接口实现松耦合
  3. 使用 AOP 和默认习惯进行声明式编程
  4. 使用 AOP 和模板减少代码冗余

依赖注入

依赖注入是通过配置+容器的方式实现的,配置可以通过 xml 配置、注解配置、java 配置实现,这些配置称为元配置数据,这些元配置本身不会有运行的能力,是通过程序解析代码后,根据元数据来做相应操作。

先介绍一个概念 Beans,Beans 是被 spring 容器管理的 POJO,Beans 以及 Beans 之间的依赖关系都是通过元数据来配置的,而这些元数据的使用方则是称为 IoC container 的容器。

Spring Framework 中 IoC container 主要代码是在 org.springframework.beansorg.springframework.context 两个包中。

在前面设置maven的依赖的时候,我们导入了 context, 看下依赖图:

dependence
dependence

可以看到org.springframework.context本身依赖于org.springframework.beans

IoC 容器具体到具体的类是BeanFactory,里面定义了基本的接口来获取 Beans,而ApplicationContext是为了企业级应用而对BeanFactory的一个扩展,具体可以看依赖图:

image_1bucpv7ih1nr81g9p1mfj14vv1kvlm.png-61.5kB
image_1bucpv7ih1nr81g9p1mfj14vv1kvlm.png-61.5kB

接口org.springframework.context.ApplicationContext的职责是负责Beans的实例化,配置,组装等工作,而这些对Beans的具体操作都是通过配置数据来控制的。因为ApplicationContext是一个接口,只要实现这个接口,就可以作为一个容器来使用,常用的容器有ClassPathXmlApplicationContextFileSystemXmlApplicationContext

下面是一个ApplicationContext工作的概括图:

image_1bucqalkp17dh13m9utv1ku71kfq13.png-10.1kB
image_1bucqalkp17dh13m9utv1ku71kfq13.png-10.1kB

ApplicationContext通过我们配置的元数据,棒我们实例化、配置好我们需要的Beans,当具体的一个ApplicationContext对象生成结束的时候,我们的系统也就做好运行的准备了。

元配置数据

配置有3类方式:xml、注解和java,我们此处主要讲xml。一个典型的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="..." class="...">
                <!-- collaborators and configuration for this bean go here -->
        </bean>

        <bean id="..." class="...">
                <!-- collaborators and configuration for this bean go here -->
        </bean>

        <!-- more bean definitions go here -->

</beans>

id 是对Bean的唯一标识,class则是具体的类名。

容器实例化

可以通过下面的代码得到一个具体的容器:

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

我们可以看到对于Beans的声明我们可以放到多个不同的xml文件中,还有另一个方式是在一个xml中导入其他的xml文件,如下:

<beans>
        <import resource="services.xml"/>
        <import resource="resources/messageSource.xml"/>

        <bean id="bean1" class="..."/>
        <bean id="bean2" class="..."/>
</beans>

Bean 概览

IoC 容器管理的Beans都是通过xml中<bean/>定义的,在IoC中这些定义被表示为BeanDefinition对象,包含的信息有:

  • bean 具体的实现类
  • bean 在 IoC 中的行为(scope, lifecycle callbacks 等)
  • bean 的依赖
  • bean 的其他配置信息

下面是一个具体属性和作用:


image_1bud0pssh19lt1kh0vgc1sb4ugj9.png-79.8kB
image_1bud0pssh19lt1kh0vgc1sb4ugj9.png-79.8kB

name

一个Bean可以有多个名字,也可以没有名字,在xml中可以通过id or name来指定,id只能指定唯一一个名字,name可以通过","或者";"或者空格将多个名字隔开,另一种指定名字的方式是通过alias,如下:

<alias name="fromName" alias="toName"/>

如果我们没有指定id或者name,IoC容器会自动生成一个名字。

对于不指定名字的使用场景是:inner beans 和 autowiring collaborators,后面介绍。

实例化

实例化一个Bean的方式有两种

  • 通过反射获取构造函数
  • 静态工厂

先看第一种,构造函数的方式。

构造函数的方式一般要求Bean有个默认的构造函数(没有任何参数),然后通过set方式来设置Bean的属性,配置如下:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

如果构造函数需要一些依赖注入,稍后介绍怎么完成。

另外一种方式是静态工厂,看配置:

<bean id="clientService"
        class="examples.ClientService"
        factory-method="createInstance"/>
        
public class ClientService {
        private static ClientService clientService = new ClientService();
        private ClientService() {}

        public static ClientService createInstance() {
                return clientService;
        }
}        

另一种工厂是来自于另一个Bean的方法

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
        <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
        factory-bean="serviceLocator"
        factory-method="createClientServiceInstance"/>
        
public class DefaultServiceLocator {

        private static ClientService clientService = new ClientServiceImpl();

        public ClientService createClientServiceInstance() {
                return clientService;
        }
}        

依赖关系

依赖注入的种类

常见的依赖注入有构造函数注入和set方法注入,先来看构造函数注入。
构造函数注入
假设我们有下面的类:

package x.y;

public class Foo {

        public Foo(Bar bar, Baz baz) {
                // ...
        }
}

我们可以配置下面的xml,通过标签<constructor-arg/>来指定:

<beans>
        <bean id="foo" class="x.y.Foo">
                <constructor-arg ref="bar"/>
                <constructor-arg ref="baz"/>
        </bean>

        <bean id="bar" class="x.y.Bar"/>

        <bean id="baz" class="x.y.Baz"/>
</beans>

另外一种是构造函数依赖的是普通的类型,如int,String等,看例子:

package examples;

public class ExampleBean {

        // Number of years to calculate the Ultimate Answer
        private int years;

        // The Answer to Life, the Universe, and Everything
        private String ultimateAnswer;

        public ExampleBean(int years, String ultimateAnswer) {
                this.years = years;
                this.ultimateAnswer = ultimateAnswer;
        }
}

此时参数不再是Bean了,我们通过type,value的方式实现:

<bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
</bean>

也可以通过index-value的方式:

<bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
</bean>

另外一种是在开启debug模式下使用的name-value,

<bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

当然不想开启debug,也可以通过java的注释@ConstructorProperties来做:

package examples;

public class ExampleBean {

        // Fields omitted

        @ConstructorProperties({"years", "ultimateAnswer"})
        public ExampleBean(int years, String ultimateAnswer) {
                this.years = years;
                this.ultimateAnswer = ultimateAnswer;
        }
}

下面总结下依赖解析的整个过程:

  1. ApplicationContext创建并且读取元配置信息生成BeanDefinition对象
  2. 对于每个Bean,其依赖通过properties,constructor arguments ,static-factory method来声明的。
  3. 每个properties,constructor arguments是以值的形式或者对其他Bean的引用提供的。
  4. 每个properties,constructor arguments如果是值的话,能自动转换到对应的类型(int,long,boolean等)。

下一篇将会详细介绍下依赖。

更好的阅读体验可以看 https://www.zybuluo.com/zhuanxu/note/943431

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容