本文基于下述教程编写:【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 的三种方式:
第一种方式:使用默认无参构造函数(只需id
、class
属性即可)
<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的创建中。
那么问题又来了,如果成员属性中有list
、map
集合,该怎么通知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
方法。