SSM框架学习——工厂模式、Spring核心容器与Bean

工厂模式、核心容器与Spring Bean

工厂模式

工厂模式是Java中常用的一种设计模式,这种类型的设计模式属于创建型模式。说白了在代码层面就是取消了new的使用。

工厂模式有三种:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

举个例子,我们去买手机,假设手机品牌有两种,分别是Xphone和Luwei,你显然不用关心手机是怎么生产的,手机零件怎么组装的,这都是工厂干的活。下面我们以这个例子来讲解三种工厂模式。

简单工厂模式

简单工厂模式示意图如下:

简单工厂模式示意图

我们用Java代码来表示,首先需要定义一个接口,来表示手机(Phone),不妨声明一个方法,用来获取品牌名称。

public interface Phone{String getBrand();
}

接下来我们该写这两种品牌的手机类,它们都实现Phone这个接口。

Xphone手机类如下

public class Xphone implements Phone{@Overridepublic String getBrand(){return "Xphone";}
}

同理Luwei手机类如下

public class Luwei implements Phone{@Overridepublic String getBrand(){return "Luwei";}
}

我们需要一个手机工厂PhoneFactory

public class PhoneFactory{public static Phone getPhone(String phoneBrand){if(phoneBrand.equals("Xphone")){return new Xphone();}if(phoneBrand.equals("Luwei")){return new Luwei();}}
}

作为消费者的你,想要获取手机只需要通过工厂即可

public class Customer{public static void main(String[] args){//获取手机实例对象Phone xphone = PhoneFactory.getPhone("Xphone");System.out.println(xphone.getBrand());}
}

但是细心观察你可能会发现,我们目前只有两个手机品牌,但是当品牌增多的时候,工厂内部的代码也需要修改,我们不得不面临一个麻烦——内部代码也会增加。这违反了设计模式的一个原则,即对扩展开放,对修改关闭

工厂方法模式

为了解决简单工厂模式的问题,聪明的你可能会想到,干脆将工厂也变为抽象的接口,我们让每个手机厂商去实现它们各自的工厂不久行啦!没错,这正是工厂方法模式

工厂方法模式示意图

我们在上文的基础上将PhoneFactory从类变为接口

public interface PhoneFactory{Phone getPhone();
}

然后为Xphone类实现它的工厂XphoneFactory

public class XphoneFactory implements PhoneFactory{@Overridepublic Phone getPhone(){return new Xphone();}
}

同理为Luwei手机类也实现它的工厂

public class LuweiFactory implements PhoneFactory{@Overridepublic Phone getPhone(){return new Luwei();}
}

而对于消费者我们可以这样调用

public class Customer{public static void main(String[] args){Phone xphone = (new XphoneFactory).getPhone();System.out.println(xphone.getBrand());}
}

这样我们就不必去更改Factory的代码就能增加品牌了,但工厂方法模式会带来另外一个问题,当我们增加品牌的时候就需要增加工厂,而且我们如果新增一款产品,那么每个工厂就必须增加相应的方法,这大大降低了代码的可维护性。

抽象工厂模式

抽象工厂模式相对来讲比较复杂,在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

假设我们前面提到的两个品牌都新增了一样产品——个人电脑。

我们来定义手机接口和电脑接口。

public interface Phone{String getBrand();
}
public interface Pc{String getBrand();
}

然后两家供应商都各自实现这两个产品

对于Luwei品牌

public class LuweiPhone implements Phone{@Overridepublic String getBrand(){return "Phone_Luwei";}
}
public class LuweiPc implements Pc{@Overridepublic String getBrand(){return "PC_Luwei";}
}

对于Xphone品牌

public class Xphone implements Phone{@Overridepublic String getBrand(){return "Phone_Xphone";}
}
public class XphonePc implements Pc{@Overridepublic String getBrand(){return "PC_Xphone";}
}

接下来就是工厂了,这两个品牌必须提供工厂来实现这两个产品。

我们定义抽象的工厂,即工厂接口IFactory

public interface IFactory{Phone getPhone();Pc getPc();
}

各自实现这个接口

public class LuweiFactory implements IFactory{@Overridepublic Phone getPhone(){return new LuweiPhone();}@Overridepublic Pc getPc(){return new LuweiPc();}
}
public class XphoneFactory implements IFactory{@Overridepublic Phone getPhone(){return new Xphone();}@Overridepublic Pc getPc(){return new XphonePc();}
}

我们为了统一它们的工厂,还需要一个超级工厂,即工厂的工厂,者有些类似于供应商与代理商之间的关系,供应商有自己的品牌和工厂生产自己的产品,然后交给代理商售卖给客户,同时代理商可以代理多家供应商的产品。

为了方便,我们直接在工厂接口里定义一个静态方法来完成这个“代理商”

public interface IFactory{public static IFactory createFactory(String factoryName){if(factoryName.equals("Xphone")){return new XphoneFactory();}if(factoryName.equals("Luwei")){return new LuweiFactory();}}Phone getPhone();Pc getPc();
}

这样我们就完成了多个品牌多个产品统一对客户的供应。

当然这个模型还可以更复杂一些,比如每个品牌每个商品都有自己的工厂,然后这些品牌创建自己商品工厂的工厂来对外,与客户之间还有一个超级工厂来创建大工厂,不过我们仅是演示原理没必要这么麻烦。

Spring核心容器

Spring容器会负责控制程序之间的关系,而不是由程序代码直接控制。

我们在上一节中,我们提到了IoC,而Spring的Ioc依赖于Spring的核心容器。

框架为我们提供了两种核心容器——BeanFactoryApplicationContext

前者位于spring-beans模块中,后者位于spring-context模块。(我们曾在pom.xml里引入过后者)

BeanFactoryBean都是懒加载的方式,也就是说只有你去调用的时候才被实例化,而ApplicationContext容器启动时Bean就被实例化了。如果不是性能要求特别苛刻或者有其它限制,一般会用后者。

BeanFactory与FactoryBean

看到BeanFactoryFactoryBean,你是不是就明白我们刚才为什么要花这么大的篇幅来讲解工厂模式了。

BeanFactory的重要程度我们用框架源代码中的一句话来讲——IoC的根接口!!!

但是我们只是个简单版本的教程,BeanFactory创建Bean的流程以及生命周期非常复杂,我们篇幅有限,而且并非是框架的文档,所以这里仅会简单描述一下它。

对于BeanFactory的主要作用就是:处理的BeanDefinition经过BeanFactory来生成Bean

BeanFactory作用

BeanFactory是一个大工厂,IoC的根基,可以生产各种Bean;而FactoryBean是一个小工厂,自己也是一个Bean,可以生产其它Bean

看不懂没关系,我们来了解下BeanFactory怎么用。

还记得我们前两章创建的top.cairbin.test1项目吗,现在打开它的App.java文件,就是主类所在的文件,我们目前内容如下:

package top.cairbin.test1;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App 
{public static void main( String[] args ){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("AppCtx.xml");User user = (User)applicationContext.getBean("user");user.testSay();}
}

可以看到我们是用的ApplicationContext来加载的AppCtx.xml文件,现在我们可以使用BeanFactory来加载它(不过我们一般不这么做),不要忘记导入相应的包。

BeanFactory beanFactory = new BeanFactory(new FileSystemResource("这里写AppCtx.xml的绝对路径"));

ApplicationContext

BeanFactory相对ApplicationContext比较低层,后者是前者的一个子接口,在开发中我们经常用后者。

获取ApplicationContext的方法这里举两个例子,分别是ClassPathXmlApplicationContext创建和FileSystemXmlApplicationContext创建。

前者形式如下

ApplicationContext app = new ClassPathXmlApplicationContext(String configLocation);

也就是说ClassPathXmlApplicationContext是从类路径来找XML的配置文件。

如果你想要使用绝对路径来寻找可以使用FileSystemXmlApplicationContext

ApplicationContext app = new FileSystemXmlApplicationContext(String configLocation);

什么,你问我啥是相对路径绝对路径?前者是相对于你当前位置的;后者是目标文件在文件系统下真实路径,比如Windows中以盘符开始(例如C:\\),在Mac/Linux下以/开始。

Spring Bean

Bean是被实例化的、组装的以及被Spring容器接管的Java对象。听起来很费解,但是没关系我们往下看。

我们在编写AppCtx.xml文件的时候就注意到,根元素为<beans></beans>,其中又包含了若干个子元素<bean>,这些子元素通常携带一些属性来帮助Spring实现依赖注入。

Bean的实例化

Bean的实例化方法大致有三种:

  • 构造器实例化(最常用)
  • 静态工厂实例化
  • 实例工厂实例化
构造器实例化

构造器实例化需要类中拥有一个默认构造方法来实例化Bean,实际上你如果不写构造方法也会有一个默认的构造方法。

你只要创建符合条件的类,然后在AppCtx.xml文件里配置下就行了。我们之前章节就是使用构造器实例化的,所以这里不给出代码。

静态工厂实例化

我们要自己创建一个工厂,并实现一个用于实例化的静态方法,假设我们的Bean对应类名为MyBean

public class MyBeanStaticFactory{public static MyBean createBean(){return new MyBean();}
}

我们需要在AppCtx.xml<beans></beans>里添加

<bean id="myBean" class="top.cairbin.test1.MyBean" factory-method="createBean"/>

这个标签的factory-method属性指定了工厂实例化Bean的静态方法。

实例工厂实例化

实例工厂实例化Bean使用的是非静态方法,即没有static关键字修饰的方法。

public class MyBeanFactory{public MyBean createBean(){return new MyBean();}
}

只不过这种情况下,配置文件的标签要配置两个属性,除了指定工厂的方法外还要通过factory-bean指定实例化后的工厂。

<bean id="myBean" class="top.cairbin.test1.MyBean" factory-bean="myBeanFactory" factory-method="createBean"/>

Bean的属性

我们来介绍下之前涉及的几个属性:

  • id:Bean的唯一标识符。
  • name:该属性可以为Bean指定多个名称,用英文逗号,隔开。
  • class:指定Bean对应的类,它必须是一个完整的类名,从包名一直到类。
  • scope:用来设置Bean的作用域,至于作用域有哪些我们之后讲。

Bean的作用域与生命周期

Bean的作用域用下图来描述

Bean的作用域

至于Bean的生命周期,可讲解的点太多了,这里仅放一张部分的示意图

Bean声明周期

Bean的装配方式

Bean的装配方式对应我们前面的Spring实现DI的几种方式。Spring Bean的方式有以下几种:

  • 基于XML装配
  • 基于注解装配
  • 自动装配

看完这里你就应该明白IoC、DI、工厂、Bean以及Bean的装配它们之间的关系了。

基于XML装配

基于XML装配主要常用于两种注入方式——Setter注入构造器注入

Setter注入要求有Setter和一个无参的构造方法,在XML配置文件中用<property>来为每个属性注入值。

构造器注入必须提供有参数的构造方法,在XML配置文件中使用<constructor-arg>来为参数注入值。

基于注解的装配

用于装配的注解主要有以下几种

自动装配

自动装配要求属性有对应的Setter方法,在XML文件里进行属性设置,实际上是在<bean>元素标签上添加属性autowire,其值可以如下。

@Autowired注解与@Resource注解

@Resource的作用相当于@Autowired,只不过@AutowiredbyType自动注入,而@Resource默认按 byName自动注入。

Spring将@Resourcename属性解析为bean的名字,而type属性则解析为bean的类型。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

另外需要注意@Resource这个注解不属于Spring,它是属于J2EE的。

自动扫描与装配的关系

自动扫描主要用到了后面两种装配方式,按照基于注解的装配方式将相应的注解转换成Bean纳入Spring容器后,会继续扫描@Resource@Autowired注解,然后按自动装配的方式进行关系建立。

然而后两种装配方式本身就是对XML的一种自动化处理,可能借助了Java的某些机制,例如反射,但实际上这几种装配方式在根本上还是一样的,不要将它们割裂来看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/586183.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目:USB键盘和鼠标的复合设备

我们的复合设备使用一个物理设备就可以完成多个功能。 使用复合设备同时完成USB键盘和鼠标功能&#xff0c;它的主要实现方式有两种&#xff0c; 第一个就是我们将多个设备描述符合并成一个&#xff0c;这个相对比较简单&#xff0c;我们只要根据相应的报告描述符处理数据就可…

基于springboot的房屋租赁系统平台

功能描述 流程&#xff1a;房主登陆系统录入房屋信息》发布租赁信息&#xff08;选择房屋&#xff09;》租客登陆系统浏览租赁信息》和房主联系、看房&#xff08;根据租赁信息单的电话线下沟通&#xff09;》房主发起签约&#xff08;生成邀请码&#xff09;》租客登陆系统根…

RabbitMQ高级笔记

视频链接&#xff1a;【黑马程序员RabbitMQ入门到实战教程】 文章目录 1.发送者的可靠性1.1.生产者重试机制1.2.生产者确认机制1.3.实现生产者确认1.3.1.开启生产者确认1.3.2.定义ReturnCallback1.3.3.定义ConfirmCallback 2.MQ的可靠性2.1.数据持久化2.1.1.交换机持久化2.1.2.…

扫地机器人(蓝桥杯)

文章目录 扫地机器人题目描述解题思路二分贪心 扫地机器人 题目描述 小明公司的办公区有一条长长的走廊&#xff0c;由 N 个方格区域组成&#xff0c;如下图所 示。 走廊内部署了 K 台扫地机器人&#xff0c;其中第 i 台在第 Ai 个方格区域中。已知扫地机器人每分钟可以移动…

10个最佳3D角色下载站

每个人都喜欢免费的东西。 无论是免费的 3D 角色还是游戏资产&#xff0c;我们都喜欢它们。 以下是可以为你的游戏获取免费 3D 角色的前 10 个网站的列表。 你可以将它们用于多种用途&#xff0c;例如 3D 打印或动画剪辑。 如果需要将下载的3D角色转化为其他格式&#xff0c;可…

TSINGSEE青犀智慧工厂视频汇聚与安全风险智能识别和预警方案

在智慧工厂的建设中&#xff0c;智能视频监控方案扮演着至关重要的角色。它不仅能够实现全方位、无死角的监控&#xff0c;还能够通过人工智能技术&#xff0c;实现智能识别、预警和分析&#xff0c;为工厂的安全生产和高效运营提供有力保障。 TSINGSEE青犀智慧工厂智能视频监…

【反悔贪心】【优先队列】3049. 标记所有下标的最早秒数 II

本文涉及知识点 反悔贪心 堆&#xff08;优先队列&#xff09; 二分查找算法合集 LeetCode3049. 标记所有下标的最早秒数 II 给你两个下标从 1 开始的整数数组 nums 和 changeIndices &#xff0c;数组的长度分别为 n 和 m 。 一开始&#xff0c;nums 中所有下标都是未标记的…

抽象类 与 接口 的区别

前言 这个知识点我之前就已经学过&#xff0c;但是我学的半桶水&#xff0c;就是只理解了比较表面的意思。我第一次面试的时候&#xff0c;面试官刚好就问了我这个问题&#xff0c;我一紧张&#xff0c;回答的磕磕绊绊的&#xff0c;很是尴尬。之后我就反思&#xff0c;发现其…

WordPress外贸建站Astra免费版教程指南(2024)

在WordPress的外贸建站主题中&#xff0c;有许多备受欢迎的主题&#xff0c;如Avada、Astra、Hello、Kadence等最佳WordPress外贸主题&#xff0c;它们都能满足建站需求并在市场上广受认可。然而&#xff0c;今天我要介绍的是一个不断颠覆建站人员思维的黑马——Astra主题。 原…

注册接口和前置SQL及数据生成及封装

注册接口 演示注册接口的三步操作&#xff1a;【注册流程逻辑】 第一步&#xff1a;发送注册短信验证码接口请求 请求方法&#xff1a; put 请求地址&#xff1a;http://shop.lemonban.com:8107/user/sendRegisterSms 请求参数&#xff1a;{“mobile”:“13422337766”} 请求头…

[实时流基础 flink] 窗口

在批处理统计中&#xff0c;我们可以等待一批数据都到齐后&#xff0c;统一处理。但是在实时处理统计中&#xff0c;我们是来一条就得处理一条&#xff0c;那么我们怎么统计最近一段时间内的数据呢&#xff1f;引入“窗口”。 文章目录 6.1 窗口的概念6.2 窗口的分类**1&#x…

C语言第三十八弹---编译和链接

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 编译和链接 1、翻译环境和运行环境 2、翻译环境 2.1、预处理&#xff08;预编译&#xff09; 2.2、编译 2.2.1、词法分析 2.2.2、语法分析 2.2.3、语义分…