【Java中级】26.0 SSM之Spring框架(二)——bean对象管理细节、依赖注入、构造注入和set方法注入...

1.0 再次强调

IOC解耦只是削减计算机的耦合,但是不能消除,只能降低。

2.0 新建一个项目
image.png

填充基本的代码:
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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.edp</groupId>
    <artifactId>second_project_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

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

</project>

bean.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">
    <!--    把对象的创建交给spring来管理-->
    <!--    id:获取时的唯一标志-->
    <!--    class:反射要创建的权限定位名称-->
    <bean id="accountService" class="com.edp.service.impl.IAccountServiceImpl"></bean>
    <bean id="accountDao" class="com.edp.dao.impl.AccountDaoImpl"></bean>

</beans>

AccountDaoImpl.java

package com.edp.dao.impl;

import com.edp.dao.IAccountDao;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 账户持久层实现类
 * @date 2020/2/23下午 5:45
 */
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存了账户。");
    }
}

IAccountDao.java

package com.edp.dao.impl;
import com.edp.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存了账户。");
    }
}

IAccountServiceImpl .java

package com.edp.service.impl;

import com.edp.dao.impl.AccountDaoImpl;
import com.edp.service.IAccountService;
import com.edp.dao.IAccountDao;

public class IAccountServiceImpl implements IAccountService {

    public IAccountServiceImpl(){
        System.out.println("IAccountServiceImpl对象创建了!");
    }
    public void saveAccount() {
        System.out.println("IAccountService中的saveAccount方法执行了……");
    }
}

IAccountService .java

package com.edp.service;

public interface IAccountService {
  
    void saveAccount();
}

Client .java

package com.edp.ui;

import com.edp.service.IAccountService;
import com.edp.dao.IAccountDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        //根据id获取bean对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");

        System.out.println(accountService);
        accountService.saveAccount();
    }
}
3.0 spring对bean对象管理的细节——创建bean的三种方式
3.1 第一种方式:使用默认构造函数创建

在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创造bean对象,此时如果类中没有默认构造函数,则对象无法创建。
比如我们修改IAccountServiceImpl .java

public class IAccountServiceImpl implements IAccountService {

    public IAccountServiceImpl(String value){
        System.out.println("IAccountServiceImpl对象创建了!");
    }
    public void saveAccount() {
        System.out.println("IAccountService中的saveAccount方法执行了……");
    }
}

将原来的默认构造函数修改为有参构造函数,意味着构造函数没有了,运行Client类:

image.png

而且,这样没有无参构造函数的情况下,bean.xml都在会编译环境下报错:

image.png
3.2 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建,并存入spring容器)

这个方法解决加入需要配置的类在class字节码中(即集成的jar包中的类。)
创建一个模拟的工厂类

image.png

InstanceFactory.java

package com.edp.factory;

import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;

/**
 * @author EdPeng
 * @Title: 模拟一个工厂类
 * @Package
 * @Description:模拟工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
 * @date 2020/2/29下午 5:46
 */
public class InstanceFactory {
    public IAccountService  getAccountService(){
        return  new IAccountServiceImpl();
    }
}

这时候,bean.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="instanceFactory" class="com.edp.factory.InstanceFactory"></bean>
   <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

</beans>

运行:

image.png

成功运行,证明这样配置没有问题。

image.png
3.3 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

新建一个StaticFactory.java类:

package com.edp.factory;

import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;

/**
 * @author EdPeng
 * @Title:模拟一个工厂类
 * @Package
 * @Description:模拟工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
 * @date 2020/2/29下午 9:00
 */
public class StaticFactory {

    public static IAccountService getAccountService() {
        return new IAccountServiceImpl();
    }
}

bean.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="accountService" class="com.edp.factory.StaticFactory" factory-method="getAccountService"></bean>

</beans>

运行:

image.png

注意,上面看上去两个Factory类其实已经耦合了,但是我们只是在模拟jar包中的类,jar包中有普通方法也有静态方法,spring都给我们提供配置的方法。

在jar包中的类都是“.class”文件,是无法改动的,当我们需要获得某个方法的返回值存入spring容器中,就需要用到以上3种中的第2、3种bean对象创建方法。

4.0 spring对bean对象管理的细节——bean对象的作用范围
4.1 spring的bean对象默认情况下就是单例的。

重置一下bean.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="accountService" class="com.edp.service.impl.IAccountServiceImpl"></bean>

</beans>

修改Client.java类测试一下:

public class Client {
    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        //根据id获取bean对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
        IAccountService accountService2 = (IAccountService) applicationContext.getBean("accountService");
        System.out.println(accountService);
        System.out.println(accountService2);
        
        accountService.saveAccount();
        accountService2.saveAccount();
        
        System.out.println(accountService==accountService2);
    }
}

执行:

image.png
4.2 bean标签的scope属性

修改bean.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标签的scope属性
    作用:用于指定bean的作用范围
    取值:singleton:单例的(默认值,常用)
         prototype:多例的(常用)
         request:作用于web应用的请求范围
         session:作用于web应用的会话范围
         global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
    -->
    <bean id="accountService" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl"></bean>

</beans>

执行:

image.png

其中 global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session,一般情况下接触不到这个方面,但在此做以解释。

当我们用户访问服务器时,服务器往往都有一个访问地址,比如“192.168.0.1”,但在超大型项目中,一个服务器往往扛不住这么大的数据访问量,当在集群环境时,往往会有多个服务器ip地址供用户访问。

当我们访问一个大型网站,比如淘宝时,首先访问的是“https://www.taobao.com/”,用户打开的是一个login.jsp页面,然后网页就会寻找空闲的服务器ip地址提供数据服务。(专业名词叫“负载均衡”)

在用户注册账户时,将由一个空闲的服务器进行数据存储,但当用户登录的时候,服务器的情况已经瞬息万变,再次访问的时候,注册时的物理服务器已经不是现在登录的物理服务器,而这个时候,新的服务器不会存储用户的账户信息。

这时候就需要一个全局的集群环境的会话范围,能够让用户找到服务器所在信息的调度。这个会话模式在spring中的配置属性就是global-session。

5.0 spring对bean对象管理的细节——bean对象的生命周期

bean对象的生命周期

  • 单例对象
  • 出生:当容器创建时对象出生
  • 活着:只要容器还在,对象一直活着
  • 死亡:容器销毁,对象消亡
  • 总结:单例对象的生命周期和容器相同
  • 多例对象
  • 出生:当我们使用对象时,spring框架为我们创建
  • 活着:对象在使用过程中,一直活着
  • 死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
6.0 Spring的依赖注入
6.1 什么是依赖注入(Dependency Injection)?
  • IOC的作用:降低程序间的耦合(依赖关系),也就是降低程序间的依赖关系,而依赖关系的管理,以后都交给spring来维护。
  • 在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
  • 而针对这依赖关系的维护,就称之为依赖注入
6.2 依赖注入分为三类

依赖注入能注入的数据分为三类:

  • 基本类型和String
  • 其他bean类型(在配置文件中或者注解配置过的bean)
  • 复杂类型/集合类型
6.3 依赖注入的注入方式

总共有3种:

  • 第1种:使用构造函数提供
  • 第2种:使用set方法提供
  • 第3种:使用注解提供
7.0 使用构造注入

首先在类中,定义一些可注入的变量。
修改IAccountServiceImpl.java

public class IAccountServiceImpl implements IAccountService {

    //如果是经常变换的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;

    public IAccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public void saveAccount() {
        System.out.println("IAccountService中的saveAccount方法执行了……"+name+" , "+age+" , "+birthday);
    }
}

当然,我们不应该关注这些变量是不是应该出现在Dao层,只是说作为一个对象,以一种方便理解的方式命名。
配置bean.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">

    <!--构造函数注入:
            使用的标签:constructor-arg
            标签出现的位置:bean标签内部
            标签中的属性
                type:用于指定要注入的数据的数据类型,同时该数据类型也是构造函数中某个或某些参数的类型
                index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
                name:用于指定给构造函数中指定名称的参数赋值(常用)
                ==============================以上三个用于指定给构造函数中那个参数赋值==============================
                value:用于提供基本类型和String类型的数据
                ref:用于指定其他的bean类型数据,指的是在spring的IOC核心容器中出现过得bean对象

            优势:
                在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
            弊端:
                改变了bean对象的是开花方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
     -->
    <bean id="accountService"  class="com.edp.service.impl.IAccountServiceImpl">
        <constructor-arg name="name" value="泰斯特"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>

    </bean>

    <!--配置一个日期对象-->
    <bean id="now" class="java.util.Date"></bean>

</beans>

运行:

image.png

发现,没有出现乱码,这就是 Spring的强大之处:能把涉及到的可能的它能处理的细节都提前替我们处理。
8.0 set方法注入

注入只需要set方法,不需要get方法。
创建一个com.edp.service.impl.IAccountServiceImpl2 .java

public class IAccountServiceImpl2 implements IAccountService {

    //如果是经常变换的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;

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

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void saveAccount() {
        System.out.println("IAccountService中的saveAccount方法执行了……"+name+" , "+age+" , "+birthday);
    }
}

配置bean.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="now" class="java.util.Date"></bean>

    <!--set方法注入:
         设计的标签:property
         标签出现的位置:bean标签内部
         标签中的属性
          name:用于指定注入时所调用的set方法名称
          value:用于提供基本类型和String类型的数据
          ref:用于指定其他的bean类型数据,指的是在spring的IOC核心容器中出现过得bean对象

      优势:
        创建对象时,没有明确的限制。可以直接使用默认构造函数
      弊端:
        如果有某个成员必须有值,则获取对象时,有可能set方法没有执行
         -->
    <bean id="accountService2" class="com.edp.service.impl.IAccountServiceImpl2">
        <property name="name" value="泰斯特"></property>
        <property name="age" value="18"></property>
        <property name="birthday" ref="now"></property>
    </bean>
</beans>

修改Client.java

public class Client {
    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService2");
        accountService.saveAccount();
    }
}

执行:

image.png

set方法注入的优势就是就是构造函数注入的弊端,当然set方法注入的弊端就是构造函数注入的优势,但我们更常用的方式是set方法注入。

9.0 复杂类型的注入

创建com.edp.service.impl.IAccountServiceImpl3.java

package com.edp.service.impl;

import com.edp.service.IAccountService;

import java.util.*;

public class IAccountServiceImpl3 implements IAccountService {

    private String[] myStr;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProps;

    public void setMyStr(String[] myStr) {
        this.myStr = myStr;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

    @Override
    public String toString() {
        return "IAccountServiceImpl3{" +
                "\n myStr=" + Arrays.toString(myStr) +
                ",\n myList=" + myList +
                ",\n mySet=" + mySet +
                ",\n myMap=" + myMap +
                ",\n myProps=" + myProps +
                '}';
    }

    public void saveAccount() {
        System.out.println( toString()); 
    }
}

修改bean.xml

image.png
<?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="accountService3" class="com.edp.service.impl.IAccountServiceImpl3">
        <property name="myStr">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
    </bean>

</beans>

修改Client.java

public class Client {

    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService3");
        accountService.saveAccount();
    }
}

执行:

image.png

继续完善bean.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="accountService3" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl3">
        <property name="myStr">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <property name="myList">
            <list>
                <value>DDD</value>
                <value>EEE</value>
                <value>FFF</value>
            </list>
        </property>

        <property name="mySet">
            <set>
                <value>GGG</value>
                <value>HHH</value>
                <value>III</value>
            </set>
        </property>

        <property name="myMap">
            <map>
                <entry key="testA" value="aaaa"></entry>
                <entry key="testB">
                    <value>bbbb</value>
                </entry>
                <entry key="testC" value="cccc"></entry>
            </map>
        </property>

        <property name="myProps">
            <props>
                <prop key="testD"></prop>
                <prop key="testE"></prop>
                <prop key="testF"></prop>
            </props>
        </property>

    </bean>

</beans>

执行:

image.png

接下来,我们如下操作,修改bean.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="accountService3" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl3">
        <property name="myStr">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <property name="myList">
            <array>
                <value>DDD</value>
                <value>EEE</value>
                <value>FFF</value>
            </array>
        </property>
        <property name="mySet">
            <list>
                <value>GGG</value>
                <value>HHH</value>
                <value>III</value>
            </list>
        </property>
        <property name="myMap">
            <props>
                <prop key="testD"></prop>
                <prop key="testE"></prop>
                <prop key="testF"></prop>
            </props>
        </property>
        <property name="myProps">
            <map>
                <entry key="testA" value="aaaa"></entry>
                <entry key="testB">
                    <value>bbbb</value>
                </entry>
                <entry key="testC" value="cccc"></entry>
            </map>

        </property>
    </bean>

</beans>

执行:

image.png

将标签内的对应关系打乱,发现还是可以运行。

复杂类型(集合类型)的注入,用于给List结构集合注入的标签有:list、array、set;用于给map结构集合注入的标签有:map、props,结构相同的,标签可以互换。

10.0 使用注解注入

【Java中级】27.0 SSM之Spring框架(三)——注解注入

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值