IOC -> XML

概念和作用

程序耦合

在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合

解决耦合思路:反射 + 配置文件,减少 new 关键字的使用

工厂模式解耦

实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用。这个读取配置文件,创建和获取三层对象的类就是工厂。

控制反转

问题一:对象存哪 ?

由于我们是很多对象,肯定要找个集合来存,这时候有 MapList 供选择。由于我们有查找需求,所以选择 Map,把这个 Map称为容器

问题二:什么是工厂 ?

工厂就是负责给我们从容器中获取指定对象的类

问题三:怎么获取对象 ?

主动方式:采用 new 的方式

在这里插入图片描述
被动方式:向工厂要,工厂为我们查找或者创建对象(这就是控制反转思想

在这里插入图片描述

IOC 入门案例

引入依赖

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

创建接口及其配置类

public interface AccountService {
    void saveAccount();
}

import com.service.AccountService;
public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount() {
        System.out.println("调用了 AccountServiceImpl 的 save 方法");
    }
}

创建配置文件

<?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="accountService" class="com.service.impl.AccountServiceImpl">			</bean>
</beans>

主方法进行测试

import com.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationTest {
    public static void main(String[] args) {
        // 使用 ApplicationContext
        ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
        AccountService accountService = application.getBean("accountService", AccountService.class);
        accountService.saveAccount();
        
        //使用 BeanFactory
        Resource resoure = new ClassPathResource("application.xml");
        BeanFactory factory = new XmlBeanFactory(resoure);
        AccountService as = (AccountService) factory.getBean("accountService");
        as.saveAccount();
    }
}

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lRreWG3-1587955611402)(F:\笔记\Spring\image\IOC 入门案例执行结果.png)]

了解常用类

ApplicationCOntext的三个常用类

ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下

FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须要有访问权限)

AnnotationConfigApplicationContext基于注解使用的,它不需要配置文件,采用配置类和各种注解来配置

核心容器的两个接口

ApplicationContext:单例适用,它在构建核心容器时,创建对象采取的策略是采用立即加载,也就是说,只要已读取完配置文件马上就创建配置文件中配置的对象

BeanFactory:多例适用,它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象

IOC 中的 Bean 标签

bean 标签的常用属性

属性作用
id给对象在容器中提供一个唯一标识,用于获取对象
class指定类的全限定类名。用于反射创建对象(默认调用无参构造函数)
scope指定对象的作用范围(具体见下面的scope表)
name可指定 id、name(用逗号、分号、空格隔开)
autowire自动装配(具体见下面的autowire表)
init-methodbean 属性设置完成后,会调用这个方法
destroy-methodbean 销毁后的回调方法
lazy-init是否懒加载(如果被非懒加载的bean依赖了,那么也就不能懒加载了)
factory-bean用于指定实例工厂 bean 的 id
factory-method用于指定实例工厂中创建对象的方法
property设置属性的值
constructor-arg指定构造参数

scope属性详解

属性值作用
singleton默认值,表示单例的
prototype表示多例的
request在 web 项目中,Spring 创建一个 Bean 对象,将对象存在 request 域中
session在 web 项目中,Spring 创建一个 Bean 对象,将对象存在 session 域中
global session在 web 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么就相当于session

证明scope(使用上面定义的 accountService):

<!--测试单例多例-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="`singleton`"></bean>
//测试 scope:singleton
AccountService accountService1 = application.getBean("accountService", AccountService.class);
AccountService accountService2 = application.getBean("accountService", AccountService.class);
System.out.println(accountService1 == accountService2);*///true
//测试 scope : prototype
AccountService accountService3 = application.getBean("accountService", AccountService.class);
AccountService accountService4 = application.getBean("accountService", AccountService.class);
System.out.println(accountService3 == accountService4);//false

实例化bean的方式

使用默认无参构造函数:在 spring 的配置文件中使用 bean标签,配以 idclass 属性之后,且没有其他属性和标签时采用的是默认构造函数创建 bean对象,此时如果类中没有默认构造函数,则对象无法创建

<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="singleton"></bean>

使用普通工厂中的方法创建对象:使用某个类中的方法创建对象,并存入 spring 容器中

<!-- factory-bean属性:用于指定实例工厂bean的id -->
<!-- factory-method属性:用于指定实例工厂中创建对象的方法 -->

<!--创建工厂 Bean-->
<bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
<!--使用工厂中的普通方法创建对象-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
// java 工厂
public class InstanceFactory {
    private AccountService accountService = new AccountServiceImpl();
    public AccountService getAccountService(){
        return accountService;
    }
}

使用工厂中的静态方法创建对象: 使某个类中的静态方法创建对象,并存入spring容器中

<!-- id属性:指定bean的id,用于从容器中获取 -->
<!-- class属性:指定静态工厂的全限定类名 -->
<!-- factory-method属性:指定生产对象的静态方法 -->

<!--使用静态方法创建对象-->
<bean id="accountService" class="com.factory.StaticFactory" factory-method="getAccountService"></bean>
// java 工厂
public class StaticFactory {
    private static AccountService accountService = new AccountServiceImpl();
    public static AccountService getAccountService(){
        return accountService;
    }
}
Spring 依赖注入

构造函数注入

属性值作用
type用于指定要注入数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始
name用于指定给构造函数中指定名称的参数赋值(常用名称)
value用于提供基本类型和 String 类型的数据
ref用于指定其他的 bean 类型数据,它指的就是在 SpringIOC 核心容器中出现过的bean对象

测试下列情况所使用的 Person

import java.util.Date;
public class Person {
    private String name;
    private int age;
    private Date date;
    
    // 下列情况演示中使用的构造方法
    public Person(String name,int age,Date date){
        this.name = name;
        this.age = age;
        this.date = date;
    } 
}

情况一:index + value / index + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p" class="com.domain.Person">
    <constructor-arg index="0" value="张三"></constructor-arg>
    <constructor-arg index="1" value="15"></constructor-arg>
    <constructor-arg index="2" ref="date"></constructor-arg>
</bean>

情况二:type + value / type + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p1" class="com.domain.Person">
    <constructor-arg type="java.lang.String" value="张三"></constructor-arg>
    <constructor-arg type="int" value="15"></constructor-arg>
    <constructor-arg type="java.util.Date" ref="date"></constructor-arg>
</bean>

情况三:name + value / name + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p2" class="com.domain.Person">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="15"></constructor-arg>
    <constructor-arg name="date" ref="date"></constructor-arg>
</bean>

测试验证

// 测试的代码
ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
Person p = application.getBean("p", Person.class);
Person p1 = application.getBean("p1", Person.class);
Person p2 = application.getBean("p2", Person.class);
System.out.println(p);
System.out.println(p1);
System.out.println(p2);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XkNeeO5p-1587955611405)(F:\笔记\Spring\image\构造方法注入.png)]

Set 方法注入

属性值作用
name用于指定注入时调用的 set 方法的名称
value用于提供基本类型和 String 类型的数据
ref用于指定其他的 bean 类型数据,指的是在 SpringIOC 核心容器中出现过的bean 对象

说明:使用的是上面的 Person 类,但是需要添加 Getter / Setter方法,同时跟上面的验证使用的是同样的方法。

<bean id="p3" class="com.domain.Person">
    <property name="name" value="张三"></property>
    <property name="age" value="15"></property>
    <property name="date" ref="date"></property>
</bean>

复杂类型的注入

给数组类型注入

测试下列情况所使用的的 School

import java.util.Arrays;
public class School {
    private String[] schoolNames = new String[3];
    public School() {
    }
    public School(String[] schoolNames) {
        this.schoolNames = schoolNames;
    }
    public String[] getSchoolNames() {
        return schoolNames;
    }
    public void setSchoolNames(String[] schoolNames) {
        this.schoolNames = schoolNames;
    }
    toString();  // 自行补充
}

情况一 :使用构造方法

<bean id="s2" class="com.domain.School">
    <constructor-arg name="schoolNames">
        <array>
            <value>重庆大学</value>
            <value>重庆文理学院</value>
            <value>重庆师范大学</value>
        </array>
    </constructor-arg>
</bean>

情况二:使用 set 方法

<bean id="s1" class="com.domain.School">
    <property name="schoolNames">
        <array>
            <value>重庆大学</value>
            <value>重庆师范大学</value>
            <value>重庆文理学院</value>
        </array>
    </property>
</bean>

List 类型进行注入

测试下列情况所使用的的 Dates

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Dates {
    private List<Date> dates = new ArrayList<>();
    public Dates() {
    }
    public Dates(List<Date> dates) {
        this.dates = dates;
    }
    public List<Date> getDates() {
        return dates;
    }
    public void setDates(List<Date> dates) {
        this.dates = dates;
    }
    toString();  // 自行补充
}

情况一 :使用构造方法

<bean id="dates1" class="com.domain.Dates">
    <constructor-arg name="dates">
        <list>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </list>
    </constructor-arg>
</bean>

情况二:使用 set 方法

<bean id="dates2" class="com.domain.Dates">
    <property name="dates">
        <list>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </list>
    </property>
</bean>

Set 类型进行注入

测试下列情况所使用的的 Maps

import java.util.*;
public class Dates {
    private Set<Date> dates = new HashSet<>();
    public Dates() {
    }
    public Dates(Set<Date> dates) {
        this.dates = dates;
    }
    public Set<Date> getDates() {
        return dates;
    }
    public void setDates(Set<Date> dates) {
        this.dates = dates;
    }
    toString(); // 自行添加
}

情况一 :使用构造方法

<bean id="set2" class="com.domain.Dates">
    <constructor-arg name="dates">
        <set>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </set>
    </constructor-arg>
</bean>

情况二:使用 set 方法

<bean id="set1" class="com.domain.Dates">
    <property name="dates">
        <set>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </set>
    </property>
</bean>

Map 类型进行注入

测试下列情况所使用的的 Maps

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Maps {
    private Map<String, Date> map = new HashMap<>();
    public Maps() {
    }
    public Maps(Map<String, Date> map) {
        this.map = map;
    }
    public Map<String, Date> getMap() {
        return map;
    }
    public void setMap(Map<String, Date> map) {
        this.map = map;
    }
    toString();  // 自行添加
}

情况一 :使用构造方法

<bean id="m1" class="com.domain.Maps">
    <constructor-arg name="map">
        <map>
            <entry key="1" value-ref="date"></entry>
            <entry key="2">
                <ref bean="date"></ref>
            </entry>
        </map>
    </constructor-arg>
</bean>

情况二:使用 set 方法

<bean id="m2" class="com.domain.Maps">
    <property name="map">
        <map>
            <entry key="1" value-ref="date"></entry>
            <entry key="2">
                <ref bean="date"></ref>
            </entry>
        </map>
    </property>
</bean>

autowire 属性详解

属性值作用
no默认值,不启动自动装配
byName根据属性名自动装配。在容器中根据名字查找与属性名完全一致的 bean,并将其与属性自动装配
byType如果容器中存在一个与指定属性类型相同的 bean ,那么将与该属性自动装配;如果存在多个该类型 bean,那么抛出异常,并指出不能使用 byType 方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生,也可以通过设置 dependency-check=“objects” 让Spring抛出异常
constructor与 byType 方式类似,不同之处在于它应用于构造器参数。如果容器中没有找到与构造器参数类型一致的 bean, 那么抛出异常
default由上级标签的 default-autowire 属性确定。

证明 autowire 属性所使用的类

public class Man {
    private Person person;
    public Man() {
    }
    public Man(Person person) {
        this.person = person;
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
   	toString(); // 自行添加
}
import java.util.Date;
public class Person {
    private String name;
    private int age;
    private Date date;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Person(String name,int age,Date date){
        this.name = name;
        this.age = age;
        this.date = date;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
	toString(); // 自行添加
}

测试代码:

ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//测试 autowire
Person person = application.getBean("person",Person.class);
Man man = application.getBean("man",Man.class);
System.out.println(person);
System.out.println(man);

证明 byName 属性

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byName"></bean>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNeZj8oL-1587955611408)(F:\笔记\Spring\image\byName自动注入成功.png)]


证明 byType 属性

情况一 :IOC 容器中只有一个该类型的 bean

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdZu6Mxs-1587955611410)(F:\笔记\Spring\image\byName自动注入成功.png)]

情况二:IOC 容器中有多个该类型的 bean

<bean id="date" class="java.util.Date"></bean>
<bean id="p3" class="com.domain.Person">
    <property name="name" value="张三"></property>
    <property name="age" value="15"></property>
    <property name="date" ref="date"></property>
</bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQdYKyBY-1587955611412)(F:\笔记\Spring\image\byType错误情况.png)]


测试 constructor

情况一:有相对应的构造器

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="constructor"></bean>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jIwJrgyx-1587955611413)(F:\笔记\Spring\image\byName自动注入成功.png)]

情况二:没有相对应的构造器(还是使用情况一的配置,只是修改 Man 类)

public class Man {
    private Person person;
    public Man() {
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
    toString(); // 自行添加
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J50DBr0E-1587955611415)(F:\笔记\Spring\image\构造器自动注入失败.png)]

有问题可加QQ群一起交流:1076386005

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值