Java-Spring-使用篇之快速入门

1、概述

spring-5.0.1 官方文档

2、XML 与 XSD

2.1 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                           http://www.springframework.org/schema/context/spring-context.xsd
       ">

    <bean id="car" class="com.lfc.spring.bean.Car"></bean>
</beans>

beans标签是xml的根节点。属性格式为xmlns:namespace-prefix="namespaceURI"。以下均为命名空间前缀,只不过是业界约定俗称的。

命名空间前缀

前缀说明
xmlnsXML NameSpace的缩写,因为XML文件的标签名称都是自定义的,自己写的和其他人定义的标签很有可能会重复命名,而功能却不一样,所以需要加上一个namespace来区分这个xml文件和其他的xml文件,类似于java中的package。
xmlns:xsiXML文件遵守XML规范,xsi全名:xml schema instance,是指具体用到的schema资源文件里定义的元素所准守的规范
xsi:schemaLocation它定义了XML Namespace和对应的XSD(Xml Schema Definition)文档的位置的关系。它的值由一个或多个URI引用对组成,两个URI之间以空白符分隔(空格和换行均可)。第一个URI是定义的XML Namespace的值,第二个URI给出Schema文档的位置,Schema处理器将从这个位置读取Schema文档,该文档的targetNamespace必须与第一个URI相匹配。

beans标签

属性说明
profile激活某个环境下配置参数,可理解成SpringBoot下的 properties 或 yml
default-lazy-init延时加载,项目启动时不会实例化注解的bean
default-merge在继承关系时在子类中合并父类的值
default-autowire是否自动注入
default-autowire-candidates自动注入bean的候选者
default-init-method初始化方法
default-destroy-method销毁的方法

小结
1、带了命名空间之后,我们就可通过前缀来引用namespaceURI的元素时,就需要用到该前缀。以context前缀为例扫描包: <context:component-scan base-package="lfc.com.controller">

2、Schema文档的targetNamespace 必须与 XML Namespace 一致。比如:

<!-- spring-beans.xsd 的targetNamespace的值是http://www.springframework.org/schema/beans(和第一个namespaceURI一致)  -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		targetNamespace="http://www.springframework.org/schema/beans">

	<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

	<xsd:annotation>
	......

2.2 XSD

以下为spring-context.xsd文件的部分内容:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/context"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		xmlns:beans="http://www.springframework.org/schema/beans"
		xmlns:tool="http://www.springframework.org/schema/tool"
		targetNamespace="http://www.springframework.org/schema/context"
		elementFormDefault="qualified"
		attributeFormDefault="unqualified">

	<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="https://www.springframework.org/schema/beans/spring-beans.xsd"/>
	<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="https://www.springframework.org/schema/tool/spring-tool.xsd"/>

	<xsd:annotation>
		<xsd:documentation><![CDATA[
	Defines the configuration elements for the Spring Framework's application
	context support. Effects the activation of various configuration styles
	for the containing Spring ApplicationContext.
		]]></xsd:documentation>
	</xsd:annotation>
</xsd:schema>
属性说明
schemaxsd文件根元素
xmlnsxml元素的命名空间
xmlns:xsd表明xsd文件的元素与数据类型来自该 XMLSchema文件
elementFormDefault为 qualified时: 任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定

备注:Spring默认在启动时是要加载XSD文件来验证xml文件的,所以如果有的时候断网了,或者一些开源软件切换域名,那么就很容易碰到应用启动不了。为了防止这种情况,Spring提供了一种机制,默认从本地加载XSD文件。

3、IOC

IOC即为控制反转,将对象的管理权限交予Spring。

以下实例基于汽车的数据模型编写,包含元素:

  • Car接口
  • Benz类:奔驰类
  • Eclass类:奔驰E级类
  • Cclass类:奔驰C级类
  • Speaker:音响类(可以是BOSE、HarmanKardon、MarkLevinson)
  • BoseSpeaker:Speaker的子类,大名鼎鼎的奔驰车所用的BOSE音响

Car Interface

package com.lfc.spring.bean;

/**
 * 汽车抽象类,规范所有的汽车类
 */
interface Car {

    /**
     * 行驶方法
     */
    void drive();

    /**
     * 听音乐
     */
    void music();

}

Benz Class:

package com.lfc.spring.bean;

public class Benz implements Car {
    /**
     * 名称
     */
    public String name;

    /**
     * 品牌
     */
    public String brand;

    @Override
    public void drive() {
        System.out.println("奔驰在行驶");
    }

    @Override
    public void music() {
        System.out.println("奔驰在播放音乐");
    }

}

Speaker Class:

package com.lfc.spring.bean;

public abstract class Speaker {

    /**
     * 音响的名称
     */
    public String name;

    /**
     * 型号
     */
    public String modelNumber;

    /**
     * 声道
     */
    public int soundChannel;

    /**
     * 展示音响的品牌
     */
    abstract void show();
}

BoseSpeaker Class:

package com.lfc.spring.bean;

public class BoseSpeaker extends Speaker {

    /**
     * 空参构造器
     */
    public BoseSpeaker() {

    }

    /**
     * 构造器实例化对象
     * @param name
     * @param modelNumber
     * @param soundChannel
     */
    public BoseSpeaker(String name, String modelNumber, int soundChannel) {
        this.name = name;
        this.modelNumber = modelNumber;
        this.soundChannel = soundChannel;
    }

    @Override
    public void show() {
        System.out.println("您正在使用音响是:" + this.name + ", 型号为:" + this.modelNumber + ", 声道:" + this.soundChannel);
    }

    /**
     * 必须有此方法,xml才能配置对应属性
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setModelNumber(String modelNumber) {
        this.modelNumber = modelNumber;
    }

    public String getModelNumber() {
        return this.modelNumber;
    }

    public void setSoundChannel(Integer soundChannel) {
        this.soundChannel = soundChannel;
    }

    public int getSoundChannel() {
        return this.soundChannel;
    }
}

Eclass Class:

package com.lfc.spring.bean;

/**
 * 奔驰E级类
 */
public class Eclass extends Benz {

    /**
     * 音响(外部类)
     */
    private BoseSpeaker speaker;

    public Eclass() {
        this.brand = "奔驰E级";
    }

    public Eclass(String name, BoseSpeaker boseSpeaker) {
        this.name = name;
        this.brand = "奔驰E级";
        this.speaker = boseSpeaker;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }


    public BoseSpeaker getSpeaker() {
        return speaker;
    }

    public void setSpeaker(BoseSpeaker speaker) {
        this.speaker = speaker;
    }

    @Override
    public void music() {
        super.music();
        System.out.println("奔驰E级-用的是高品质扬声器:" + this.speaker.getName());
    }

}

Cclass Class:

package com.lfc.spring.bean;

/**
 * 奔驰C级类
 */
public class Cclass extends Benz {

    /**
     * 音响(外部类)
     */
    private BoseSpeaker speaker;

    public Cclass() {
        this.brand = "奔驰C级";
    }

    public Cclass(String name, BoseSpeaker boseSpeaker) {
        this.name = name;
        this.brand = "奔驰C级";
        this.speaker = boseSpeaker;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }


    public BoseSpeaker getSpeaker() {
        return speaker;
    }

    public void setSpeaker(BoseSpeaker speaker) {
        this.speaker = speaker;
    }

    @Override
    public void music() {
        super.music();
        System.out.println("奔驰C级-用的是高品质扬声器:" + this.speaker.getName());
    }

}

3.1 普通bean

3.1.1 xml注入属性

3.1.1.1 普通property注入

在xml配置文件的bean标签使用其property属性注入值。
<property name="nameA" value="valueA"/>

XML配置 boseSpeaker.xml

<!-- 普通属性注入 -->
    <bean id="boseSpeaker" class="com.lfc.spring.bean.BoseSpeaker">
        <property name="name" value="BOSE音响"/>
        <property name="modelNumber" value="BOSE Soundlink III"/>
        <property name="soundChannel" value="2"/>
    </bean>

单元测试

/**
     * 测试Bose音响-普通属性注入
     */
    @Test
    public void bossSpeakerGeneralTest() {
        // 创建并配置
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");

       // 取出配置了的对象
        BoseSpeaker boseSpeaker = context.getBean("boseSpeaker2", BoseSpeaker.class);

        // 调用对象的方法
        boseSpeaker.show();
    }

控制台输出


您正在使用音响是:BOSE音响, 型号为:BOSE Soundlink III, 声道:2

3.1.1.2 构造器注入

XML配置 boseSpeaker.xml
注意:要求实体类需要由属性对应的setter方法

<!-- 构造器注入 -->
    <bean id="boseSpeaker2" class="com.lfc.spring.bean.BoseSpeaker">
        <constructor-arg name="name" value="BOSE音响Ⅱ"/>
        <constructor-arg name="modelNumber" value="BOSE SoundLink Colour"/>
        <constructor-arg name="soundChannel" value="6"/>
    </bean>

单元测试

/**
     * 测试Bose音响-构造器注入
     */
    @Test
    public void bossSpeakerConstructorTest() {
        // 创建并配置
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");

        // 取出配置了的对象
        BoseSpeaker boseSpeaker = context.getBean("boseSpeaker2", BoseSpeaker.class);

        // 调用对象的方法
        boseSpeaker.show();
    }

控制台输出


您正在使用音响是:BOSE音响Ⅱ, 型号为:BOSE SoundLink Colour, 声道:6

3.1.2 p名称空间注入

XML配置 boseSpeaker.xml

<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名称空间注入 -->
    <bean id="boseSpeaker3" class="com.lfc.spring.bean.BoseSpeaker" p:name="BOSE音响Ⅲ" p:modelNumber="BOSE SoundTouch 20III" p:soundChannel="4"/>
......
</beans>

单元测试

/**
     * 测试Bose音响-p命名空间注入
     */
    @Test
    public void bossSpeakerPnamesapceTest() {
        // 创建并配置
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");

        // 取出配置了的对象
        BoseSpeaker boseSpeaker = context.getBean("boseSpeaker3", BoseSpeaker.class);

        // 调用对象的方法
        boseSpeaker.show();
    }

控制台输出


您正在使用音响是:BOSE音响Ⅲ, 型号为:BOSE SoundTouch 20III, 声道:4

3.1.3 xml注入特殊属性

XML配置 boseSpeaker.xml

<!-- 其他属性注入:null, 特殊字符 -->
    <bean id="boseSpeaker4" class="com.lfc.spring.bean.BoseSpeaker">
        <property name="name" value="BOSE音响Ⅳ"/>
        <property name="modelNumber">
            <value><![CDATA[<<BOSE Companion2 III/C2>>]]></value>
        </property>
        <property name="soundChannel">
            <value>8</value>
        </property>
    </bean>

单元测试

/**
     * 测试Bose音响-注入特殊属性
     */
    @Test
    public void bossSpeakerSpecialTest() {
        // 创建并配置
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");

        // 取出配置了的对象
        BoseSpeaker boseSpeaker = context.getBean("boseSpeaker4", BoseSpeaker.class);

        // 调用对象的方法
        boseSpeaker.show();
    }

控制台输出


您正在使用音响是:BOSE音响Ⅳ, 型号为:<<BOSE Companion2 III/C2>>, 声道:8

3.1.4 注入属性外部bean

XML配置 benzall.xml

<!-- 引入外部的bean -->
    <import resource="boseSpeaker.xml"/>
    <bean id="e300l" class="com.lfc.spring.bean.Eclass" name="eclass">
        <property name="name" value="奔驰E级 300L"/>
        <property name="speaker" ref="boseSpeaker"/>
    </bean>

单元测试

/**
     * 测试奔驰E级-引入外部的bean
     */
    @Test
    public void eclassTests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("benzall.xml");
        Eclass e300l = context.getBean("e300l", Eclass.class);

        // 使用BOSE音响播放音乐
        e300l.music();
        System.out.println("当前汽车的名称是:" + e300l.getName());
    }

控制台输出


奔驰在播放音乐
奔驰E级-用的是高品质扬声器:BOSE音响
当前汽车的名称是:奔驰E级 300L

3.1.5 注入属性内部bean

XML配置 benzall.xml

<!-- 其他属性注入:null, 特殊字符 -->
    <bean id="boseSpeaker4" class="com.lfc.spring.bean.BoseSpeaker">
        <property name="name" value="BOSE音响Ⅳ"/>
        <property name="modelNumber">
            <value><![CDATA[<<BOSE Companion2 III/C2>>]]></value>
        </property>
        <property name="soundChannel">
            <value>8</value>
        </property>
    </bean>

单元测试

/**
     * 测试奔驰C级-引入内部bean
     */
    @Test
    public void cclassTests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("benzall.xml");
        Cclass c260l = context.getBean("c260l", Cclass.class);

        // 使用BOSE音响播放音乐
        c260l.music();
        System.out.println("当前汽车的名称是:" + c260l.getName());
    }

控制台输出


奔驰在播放音乐
奔驰C级-用的是高品质扬声器:低配版BOSE音响
当前汽车的名称是:奔驰C级 260L

3.1.6 注入属性级联赋值

XML配置
注意:此方法在上述的"注入外部bean"基础上,在目标bean内通过property属性直接为外部bean的属性赋值,要求外部bean的属性有getter。

<!-- 级联赋值 -->
    <bean id="e200" class="com.lfc.spring.bean.Eclass">
        <property name="name" value="E200 低配短轴版本"/>
        <!-- 赋值 -->
        <property name="speaker" ref="boseSpeaker5"/>
        <property name="speaker.name" value="BOSE音响Ⅴ"/>
        <property name="speaker.modelNumber" value="BOSE-AWRCC7"/>
        <property name="speaker.soundChannel" value="4"/>
    </bean>
    <bean id="boseSpeaker5" class="com.lfc.spring.bean.BoseSpeaker"/>

单元测试

/**
     * 测试奔驰E级-级联赋值
     */
    @Test
    public void eclassCascadeAssignmentTests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("benzall.xml");
        Eclass e200 = context.getBean("e200", Eclass.class);

        // 使用BOSE音响播放音乐
        e200.music();
        System.out.println("当前汽车的名称是:" + e200.getName());
    }

控制台输出


奔驰在播放音乐
奔驰E级-用的是高品质扬声器:BOSE音响Ⅴ
当前汽车的名称是:E200 低配短轴版本

3.1.7 注入数组/集合属性

XML配置
Gclass类

package com.lfc.spring.bean;

import java.util.*;

public class Gclass implements Car {
    /**
     * 名称
     */
    public String name;

    /**
     * 品牌
     */
    public String brand;

    /**
     * 音响列表-集合对象属性
     */
    public List<BoseSpeaker> boseSpeakerList;

    /**
     * 车轮列表-list集合类型属性
     */
    public List<String> wheelList;

    /**
     * 内饰集合-set集合类型属性
     */
    public Set<String> interiorSet;

    /**
     * 发动机-Map集合类型属性
     */
    public Map<String, String> engineMap;

    /**
     * 底盘-Properties类型属性,key和value只能是String类型
     */
    public Properties chassisProperties;


    public Gclass(String name, String brand) {
        this.name = name;
        this.brand = brand;
    }

    public List<BoseSpeaker> getBoseSpeakerList() {
        return boseSpeakerList;
    }

    public void setBoseSpeakerList(List<BoseSpeaker> boseSpeakerList) {
        this.boseSpeakerList = boseSpeakerList;
    }

    public List<String> getWheelList() {
        return wheelList;
    }

    public void setWheelList(List<String> wheelList) {
        this.wheelList = wheelList;
    }

    public Set<String> getInteriorSet() {
        return interiorSet;
    }

    public void setInteriorSet(Set<String> interiorSet) {
        this.interiorSet = interiorSet;
    }

    public Map<String, String> getEngineMap() {
        return engineMap;
    }

    public void setEngineMap(Map<String, String> engineMap) {
        this.engineMap = engineMap;
    }

    public Properties getChassisProperties() {
        return chassisProperties;
    }

    public void setChassisProperties(Properties chassisProperties) {
        this.chassisProperties = chassisProperties;
    }
    @Override
    public void drive() {
        System.out.println("奔驰在越野");
    }

    @Override
    public void music() {
        System.out.println("奔驰在播放音乐");
    }

    /**
     * 展示所有bean属性
     */
    public void showProperty() {
        System.out.println(this.wheelList);
        System.out.println(this.boseSpeakerList);
        System.out.println(this.chassisProperties);
        System.out.println(this.interiorSet);
        System.out.println(this.engineMap);
    }
}

<!-- 奔驰G级别 注入集合属性和集合对象属性 -->
    <bean id="g65" class="com.lfc.spring.bean.Gclass">
        <constructor-arg name="name" value="奔驰G65越野车"/>
        <constructor-arg name="brand" value="奔驰"/>
        <!-- List<String> -->
        <property name="wheelList">
            <array>
                <value>左前轮</value>
                <value>右前轮</value>
                <value>左后轮</value>
                <value>右后轮</value>
            </array>
        </property>

        <!-- Set<String> -->
        <property name="interiorSet">
            <set>
                <value>中控台</value>
                <value>真皮座椅</value>
                <value>安全气囊</value>
            </set>
        </property>

        <!-- Map<String, String> -->
        <property name="engineMap">
            <map>
                <entry key="transmissionCase" value="7G-tronic 变速箱"/>
                <entry key="modelNumber" value="5.5升AMG发动机"/>
                <entry key="weight" value="200"/>
            </map>
        </property>

        <!-- Properties -->
        <property name="chassisProperties">
            <props>
                <prop key="shockAbsorber">G级减震器</prop>
                <prop key="brakeDisc">G级刹车盘</prop>
            </props>
        </property>

        <property name="boseSpeakerList">
            <list>
                <ref bean="boseSpeakerForGclassLeft"/>
                <ref bean="boseSpeakerForGclassRight"/>
            </list>
        </property>
    </bean>

    <bean id="boseSpeakerForGclassLeft" class="com.lfc.spring.bean.BoseSpeaker">
        <constructor-arg name="name" value="G级左边的音响"/>
        <constructor-arg name="modelNumber" value="G级左边音响型号"/>
        <constructor-arg name="soundChannel" value="2"/>
    </bean>
    <bean id="boseSpeakerForGclassRight" class="com.lfc.spring.bean.BoseSpeaker">
        <constructor-arg name="name" value="G级右边的音响"/>
        <constructor-arg name="modelNumber" value="G级右边音响型号"/>
        <constructor-arg name="soundChannel" value="4"/>
    </bean>

单元测试

/**
     * 测试奔驰G级-集合属性和集合对象属性
     */
    @Test
    public void gclassCollectionTests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("benzall.xml");
        Gclass g65 = context.getBean("g65", Gclass.class);

        // 打印所有的属性
        g65.showProperty();
    }

控制台输出


[左前轮, 右前轮, 左后轮, 右后轮]
[com.lfc.spring.bean.BoseSpeaker@d6da883, com.lfc.spring.bean.BoseSpeaker@45afc369]
{shockAbsorber=G级减震器, brakeDisc=G级刹车盘}
[中控台, 真皮座椅, 安全气囊]
{transmissionCase=7G-tronic 变速箱, modelNumber=5.5升AMG发动机, weight=200}

3.2 工厂bean

XML配置
BenE260 类

package com.lfc.spring.bean;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BenE260 implements FactoryBean<Eclass> {
    @Override
    public Eclass getObject() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");
        BoseSpeaker boseSpeaker = context.getBean("boseSpeaker2", BoseSpeaker.class);

        Eclass e260 = new Eclass();
        e260.setName("奔驰E260");
        e260.setSpeaker(boseSpeaker);

        return e260;
    }

    @Override
    public Class<Eclass> getObjectType() {
        return null;
    }
}


<!-- 通过FactoryBean获取bean -->
    <bean id="e260" class="com.lfc.spring.bean.BenE260"></bean>
    

单元测试

/**
     * 测试奔驰E级-工厂bean获取
     */
    @Test
    public void eclass2Tests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("benzall.xml");
        Eclass e260 = context.getBean("e260", Eclass.class);

        // 使用BOSE音响播放音乐
        e260.music();
        System.out.println("当前汽车的名称是:" + e260.getName());
    }

控制台输出


奔驰在播放音乐
奔驰E级-用的是高品质扬声器:BOSE音响Ⅱ
当前汽车的名称是:奔驰E260

3.3 bean的作用域

作用域说明
singleton在IOC容器内只创建一个实例,第二次获取到的只是该实例的引用
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
application一般用于ServletContext应用环境,该作用域仅适用于WebApplicationContext环境
websocket应用与websocket环境, 该作用域仅适用于WebApplicationContext环境

XML配置

<!-- singleton -->
    <bean id="boseSpeaker5" class="com.lfc.spring.bean.BoseSpeaker" scope="singleton">
        <property name="name" value="BOSE音响 Ⅴ"/>
        <property name="modelNumber" value="BOSE Ⅴ"/>
        <property name="soundChannel" value="5"/>
    </bean>

    <!-- prototype -->
    <bean id="boseSpeaker6" class="com.lfc.spring.bean.BoseSpeaker" scope="prototype">
        <property name="name" value="BOSE音响Ⅵ"/>
        <property name="modelNumber" value="BOSE Ⅵ"/>
        <property name="soundChannel" value="6"/>
    </bean>

单元测试

/**
     * 测试作用域
     */
    @Test
    public void scopeTests() {
        ApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");
        BoseSpeaker bs1 = context.getBean("boseSpeaker5", BoseSpeaker.class);
        BoseSpeaker bs2 = context.getBean("boseSpeaker5", BoseSpeaker.class);
        System.out.println("bs1 == bs2: " + bs1.equals(bs2));

        BoseSpeaker bs3 = context.getBean("boseSpeaker6", BoseSpeaker.class);
        BoseSpeaker bs4 = context.getBean("boseSpeaker6", BoseSpeaker.class);
        System.out.println("bs3 == bs4: " + bs3.equals(bs4));
    }

控制台输出


bs1 == bs2: true
bs3 == bs4: false

3.4 bean的生命周期

XML配置

<!-- 实例化和销毁执行指定操作 -->
    <bean id="boseSpeaker7" class="com.lfc.spring.bean.BoseSpeaker" init-method="init" destroy-method="destroy">
        <property name="name" value="BOSE音响 Ⅶ"/>
        <property name="modelNumber" value="BOSE Ⅶ"/>
        <property name="soundChannel" value="7"/>
    </bean>

单元测试

 /**
     * 测试生命周期
     */
    @Test
    public void lifecycleTests() {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("boseSpeaker.xml");
        BoseSpeaker bs7 = context.getBean("boseSpeaker7", BoseSpeaker.class);

        bs7.show();
        // 触发销毁对象
        context.registerShutdownHook();
    }

控制台输出

正在实例化。。。
您正在使用音响是:BOSE音响 Ⅶ, 型号为:BOSE Ⅶ, 声道:7
正在销毁。。。

3.5 注解操作bean

常用的几个注解(集合SpringBoot体会和使用):

注解说明
@Required用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常 。spring 5里边已经被废弃
@Autowired用于自动装配 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。
@ResourceJSR-250 类型注解,可作用于属性或属性的setter

4、AOP

在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

4.1 底层原理

  1. 有接口情况,使用 JDK 动态代理。
    创建接口实现类代理对象,增强类的方法。

  2. 没有接口情况,使用 CGLIB 代理。
    创建子类的代理对象,增强类的方法。
    会在另开篇幅进行详细说明。

4.2 概念

术语说明
Aspect切面;把通知应用到切入点的过程
Join point连接点;类中有哪些方法可以被增强,例如:方法的执行或者异常的处理
Advice实际增强的逻辑部分称之为通知,通常是以拦截器存在
Pointcut切入点;实际被增强的方法,Advice是和Pointcut 表达式相关联的并且在匹配到的任何连接点上执行
Introduction允许你添加新方法或属性到现有的类中。
Target object被一个或者多个切面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
AOP proxyAOP为了实现切面契约所创建的对象,通常是JDK动态代理或者CGLIB代理。
Weaving把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。

** 通知类型 **

类型说明
Before advice前置通知;在一个方法执行前进行通知,它不能阻止连接点的执行,除非出现异常
After advice后置通知; 在一个方法执行后进行通知,无论连接点如何退出(正常或异常),都会执行
After returning advice成功返回后通知;只有在方法成功完成时,才能执行通知。
After throwing advice异常后通知
Around advice环绕通知; 在方法的前后执行

** 切入点表达式 **
语法:execution( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )

示例:
举例 1:对 com.lfc.dao.BenzDao 类里面的 add 进行增强
execution(* com.lfc.dao.BenzDao.add(..))

举例 2:对 com.lfc.dao.BenzDao 类里面的所有的方法进行增强
execution(* com.lfc.dao.BenzDao.* (..))

举例 3:对 com.lfc.dao 包里面所有类,类里面所有方法进行增强
execution(* com.lfc.dao.*.* (..))

4.3 完整使用案例

要使用AspectJ需要引入对应的jar包,并且导入当前的项目lib目录下。 下载AspectJ

  1. 被增强类Chair.java
package com.lfc.spring.bean;

import org.springframework.stereotype.Component;

/**
 * 座椅类:被实施增强的类
 */
@Component
public class Chair {
    /**
     * 座椅被坐:AOP的切入点,也属于连接点的一个
     */
    public void seat() {
        System.out.println("座椅被坐");
    }
}

  1. 增强的代理类 ChairProxy.java
package com.lfc.spring.bean;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 座椅代理类:实施增强逻辑的类
 */
@Component
@Aspect // 生成代理对象
@Order(3) // 当有多个增强类对同一个方法增强时,可以通过此注解设置增强类的优先级,数值越小优先级越高。
public class ChairProxy {

    /**
     * 提取公共切入点
     */
    @Pointcut(value = "execution(* com.lfc.spring.bean.Chair.seat())")
    public void pointCommon() {

    }

    /**
     * 前置通知
     */
    @Before(value = "pointCommon()")
    public void before() {
        System.out.println("before......");
    }

    /**
     * 最终通知(返回或异常)
     */
    @After(value = "pointCommon()")
    public void after() {
        System.out.println("after......");
    }

    /**
     * 后置通知(正常返回)
     */
    @AfterReturning(value = "pointCommon()")
    public void afterReturning() {
        System.out.println("afterReturning......");
    }

    /**
     * 异常通知
     */
    @AfterThrowing(value = "pointCommon()")
    public void afterThrowing() {
        System.out.println("afterThrowing......");
    }

    /**
     * 环绕通知(前后)
     */
    @Around(value = "execution(* com.lfc.spring.bean.Chair.seat())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前。。。");

        // 执行被增强的方法
        proceedingJoinPoint.proceed();

        System.out.println("环绕之后。。。");
    }

}

  1. 开启AOP的配置类 AppConfig.java
package com.lfc.spring.config;

import com.lfc.spring.bean.Chair;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 声明这是一个配置类
@ComponentScan(basePackages = {"com.lfc"}) // 扫描包配置 <=> <context:component-scan base-package="com.lfc"></context:component-scan>
@EnableAspectJAutoProxy(proxyTargetClass = true) // 开启代理 <=>  <aop:aspectj-autoproxy/>
public class AppConfig {

    @Bean(name = "chair")
    public Chair chair() {
        return new Chair();
    }

}

  1. 单元测试
/**
     * 测试AOP注解增强
     */
    @Test
    public void chairTests() {
        // 使用注解的方式
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Chair chair1 = context.getBean("chair", Chair.class);

        chair1.seat();
    }
  1. 控制台输出

环绕之前。。。
before......
座椅被坐
环绕之后。。。
after......
afterReturning......

5、总结

通过本篇文章,介绍了IOC和AOP的基础概念和使用方法,参考SpringBoot的代码分层,可以发现跟Spring是一脉相承的。要求敏捷开发的现代互联网行业我们使用SpringBoot作为独立的服务开发平台足以,但是基础的实现机制能让我们更好地驾驭Sping生态系统。后续文章中将会深入IOC和AOP的具体实现原理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值