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"
。以下均为命名空间前缀,只不过是业界约定俗称的。
命名空间前缀
前缀 | 说明 |
---|---|
xmlns | XML NameSpace的缩写,因为XML文件的标签名称都是自定义的,自己写的和其他人定义的标签很有可能会重复命名,而功能却不一样,所以需要加上一个namespace来区分这个xml文件和其他的xml文件,类似于java中的package。 |
xmlns:xsi | XML文件遵守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>
属性 | 说明 |
---|---|
schema | xsd文件根元素 |
xmlns | xml元素的命名空间 |
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 方法,构造函数和属性。 |
@Resource | JSR-250 类型注解,可作用于属性或属性的setter |
4、AOP
在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
4.1 底层原理
-
有接口情况,使用 JDK 动态代理。
创建接口实现类代理对象,增强类的方法。 -
没有接口情况,使用 CGLIB 代理。
创建子类的代理对象,增强类的方法。
会在另开篇幅进行详细说明。
4.2 概念
术语 | 说明 |
---|---|
Aspect | 切面;把通知应用到切入点的过程 |
Join point | 连接点;类中有哪些方法可以被增强,例如:方法的执行或者异常的处理 |
Advice | 实际增强的逻辑部分称之为通知,通常是以拦截器存在 |
Pointcut | 切入点;实际被增强的方法,Advice是和Pointcut 表达式相关联的并且在匹配到的任何连接点上执行 |
Introduction | 允许你添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个切面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
AOP proxy | AOP为了实现切面契约所创建的对象,通常是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
- 被增强类Chair.java
package com.lfc.spring.bean;
import org.springframework.stereotype.Component;
/**
* 座椅类:被实施增强的类
*/
@Component
public class Chair {
/**
* 座椅被坐:AOP的切入点,也属于连接点的一个
*/
public void seat() {
System.out.println("座椅被坐");
}
}
- 增强的代理类 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("环绕之后。。。");
}
}
- 开启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();
}
}
- 单元测试
/**
* 测试AOP注解增强
*/
@Test
public void chairTests() {
// 使用注解的方式
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Chair chair1 = context.getBean("chair", Chair.class);
chair1.seat();
}
- 控制台输出
环绕之前。。。
before......
座椅被坐
环绕之后。。。
after......
afterReturning......
5、总结
通过本篇文章,介绍了IOC和AOP的基础概念和使用方法,参考SpringBoot的代码分层,可以发现跟Spring是一脉相承的。要求敏捷开发的现代互联网行业我们使用SpringBoot作为独立的服务开发平台足以,但是基础的实现机制能让我们更好地驾驭Sping生态系统。后续文章中将会深入IOC和AOP的具体实现原理。