【ssm入门#5-Spring5】IOC控制反转降低耦合核心思想:工厂模式配置文件解耦+ApplicationContext找不到getBean()方法+bean标签和管理对象细节+依赖注入各数据类型

本文介绍了Spring5的IOC核心思想,通过bean标签配置管理对象,详细讲解了singleton和prototype作用域,展示了对象的生命周期。此外,文章探讨了依赖注入的不同方式,包括构造器注入和setter注入,以及如何处理集合类型的注入。最后,讨论了构造函数注入处理复杂集合类型的注意事项。
摘要由CSDN通过智能技术生成

本文基于下述教程编写:【B站】ssm教程

工厂模式解耦思想


在这里插入图片描述
简单应用,实现业务层和持久层解耦:

public class BeanFactory {

    //加载配置文件操作类
    private static Properties props = null;
    //存放已经生成好的实例对象的容器
    private static Map<String, Object> beans = null;

    static {
        try {
            System.out.println("工厂里面执行静态代码块......");
            props = new Properties();
            //使用类加载器获取配置文件,得到流
            InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            //Properties加载流
            props.load(inputStream);
            beans = new HashMap<String, Object>();
            //返回properties文件字段名内容
            Enumeration keys = props.keys();
            while (keys.hasMoreElements()) {
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = props.getProperty(key);
                //把配置文件里面的所有类都实例化一遍
                Object value = Class.forName(beanPath).newInstance();
                //存入容器
                beans.put(key, value);
            }
            } catch(Exception e){
                e.printStackTrace();
            }
    }
    /**
     * 根据Bean的名称获取Bean对象
     */
    public static Object getBean(String beanName) {
        //多例工厂模式,未能实现全局单类对象的需求
        /*Object bean = null;
        try {
            //得到配置文件下对应Bean的包路径
            String beanPath = props.getProperty(beanName);
            //反射创建对象
            bean = Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }*/
        return beans.get(beanName);
    }
}
/**
 * 模拟表现层,用于调用业务层
 */
public class Client {
    public static void main(String[] args) {
        //IAccountService iAccountService=new IAccountServiceImpl();
        //降低耦合,利用工厂模式创建类对象
        for (int i=0;i<5;i++){
            IAccountService iAccountService= (IAccountService) BeanFactory.getBean("accountService");
            iAccountService.saveAccount();
            System.out.println(iAccountService);
        }

        IAccountService iAccountService= (IAccountService) BeanFactory.getBean("accountService");
        iAccountService.saveAccount();
        System.out.println(iAccountService);
    }
}

输出结果:
在这里插入图片描述
实现了业务层和持久层的依赖解耦,并且实现了单例模式,只调用一次实现类的实例化,只要名称一致由始至终返回同一个已经实例化对象。

遇到一个坑:傻傻分不清整体项目和小module之间maven依赖的情况,以为只是在pom.xml同时引入就可以一起构建依赖了。真的天真。
ApplicationContext找不到getBean()方法

Spring初体验 bean标签和管理对象细节


万事前提,导入依赖,资源文件夹下 bean.xml引入约束:

<!--Spring核心-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
</dependency>
<?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:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围,即什么类型的对象

  • singleton :默认值,单例的.
  • prototype :多例的.
  • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
  • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
  • global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 globalSession 相当于 session

单例对象:scope=“singleton” 一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象:scope=“prototype” 每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

实例化 Bean 的三种方式:
第一种方式:使用默认无参构造函数(只需idclass属性即可)

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

第二种方式:spring管理静态工厂-使用静态工厂的方法创建对象

/** 
 * 模拟一个静态工厂,创建业务层实现类  */ 
 * public class StaticFactory {   
 * public static IAccountService createAccountService(){   
 * return new AccountServiceImpl();  
 * } }
/**
此种方式是:   
使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器   
id 属性:指定bean的id,用于从容器中获取   
class 属性:指定静态工厂的全限定类名   
factory-method 属性:指定生产对象的静态方法  */ 
<bean id="accountService"  
   class="com.itheima.factory.StaticFactory"     
   factory-method="createAccountService">
</bean> 

第三种方式:spring管理实例工厂-使用实例工厂的方法创建对象

/** 
 * 模拟一个实例工厂,创建业务层实现类,此工厂创建对象,必须现有工厂实例对象,再调用方法  */
public class InstanceFactory {   
public IAccountService createAccountService(){   
return new AccountServiceImpl();  } 
} 
<!-- 此种方式是:    
先把工厂的创建交给 spring 来管理。然后在使用工厂的bean来调用里面的方法   
factory-bean 属性:用于指定实例工厂bean的id。   
factory-method 属性:用于指定实例工厂中创建对象的方法。  --> 
 
<bean id="instancFactory" 
class="com.itheima.factory.InstanceFactory">
</bean>  

<bean id="accountService"      
factory-bean="instancFactory"      
factory-method="createAccountService">
</bean> 

第三种方式需要指定两个bean,工厂产生的bean需要传入指定工厂名称以及其生产方法。

那么问题来了,如果要创建的bean带有属性值呢?怎么通知spring帮我们生成成员属性带有值的对象?

依赖注入

所谓依赖注入就是给bean的成员变量赋值,从而生成一个有内容的对象。
有两种注入方式,构造函数注入(类中需要提供一个对应参数列表的构造函数) 、set方式注入(需要在实现类文件中写好对应各成员属性的set函数)

<!--构造函数注入-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
 <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> 
<!--set函数注入-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">   
<property name="name" value="test"></property> 
<property name="age" value="21"></property>   
<property name="birthday" ref="now"></property> 

</bean> <bean id="now" class="java.util.Date"></bean> 

总结:
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型

name:指定参数在构造函数中的名称,用这个定位给哪个变量赋值,需要注意在set注入方式中它的值与属性名字无关,并且只是关系到set方法名指向的变量名(小写形式)

value:它能赋的值是基本数据类型String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的bean,上述代码将 Date类型也看做Bean对象交由spring创建了,并且被引用到其他bean的创建中。

那么问题又来了,如果成员属性中有listmap集合,该怎么通知spring指定每一项的值?

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

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
 <!-- 在注入集合数据时,只要结构相同,标签可以互换,下列已将Array、List、Set以及Map、Props交换打乱
	后续测试亦能通过
 -->  
 <!-- 给数组注入数据 -->  
 <property name="myStrs"> 
  	<set>    
  	<value>AAA</value>    
  	<value>BBB</value>    
  	<value>CCC</value>   
  	</set> 
 </property> 
 
 <!-- 注入 list 集合数据 -->  
 <property name="myList">   
 	<array>    
 	<value>AAA</value>   
 	<value>BBB</value>    
 	<value>CCC</value>   
 	</array>  
 </property> 
 
 <!-- 注入 set 集合数据 -->  
 <property name="mySet"> 
  	<list>    
  	<value>AAA</value>   
  	<value>BBB</value>    
 	 <value>CCC</value>   
 	 </list>  
  </property> 
  
 <!-- 注入 Map 数据 -->  
 <property name="myMap">  
 	 <props> 
  	 <prop key="testA">aaa</prop>    
  	 <prop key="testB">bbb</prop>   
 	 </props> 
 </property>
 
<property name="myProps"> 
  <map>    
  	<entry key="testA" value="aaa"></entry> 
  	<entry key="testB">     
 		 <value>bbb</value> 
  	</entry>  
  </map>  
</property> 
</bean>

注入集合数据可以分成两大类,类之间property标签内下一级标签随意指定,不做严格限制。
List结构的: array,list,set
Map结构的:map,entry,props,prop

脑洞时间:
构造函数依赖注入方式引入复杂的集合类型时,constructor-arg亦可以像set依赖注入方式的property标签引用各类集合标签完成赋值。此时一定要注意类中要有对应的构造函数(参数类型、顺序必须一致),还有别忘记把复杂对象类型用ref标签依赖进来。
同理,set方式注入也注意一定要存在set方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值