参考文章:
https://zhuanlan.zhihu.com/p/384765501
https://www.jianshu.com/p/139f813fca80
https://www.cnblogs.com/nickup/p/9800120.html
1、工厂模式
1.1、定义
工厂模式可将Java对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则(接口),而不必关心实例的具体实现过程。工厂模式由抽象产品(接口)、具体产品(实现类)、生产者(工厂类)三种角色组成。
1.2、Spring中工厂模式的应用
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。
两者对比:
BeanFactory :延迟注入(使用到某个 bean 的时候才会注入),相比于BeanFactory来说会占用更少的内存,程序启动速度更快。
ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能之外还有额外更多功能,所以一般开发人员使用ApplicationContext会更多。
ApplicationContext的三个实现类:
ClassPathXmlApplication:把上下文文件当成类路径资源。
FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
2、单例模式
2.1、定义
单例模式具有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例
2.2 Spring中单例模式的使用
singleton:单例 prototype:多例 通过以上配置信息,Spring就会每次给客户端返回一个新的对象实例。 那么Spring对单例的底层实现,到底是饿汉式单例还是懒汉式单例呢?都不是。Spring框架对单例的支持是采用单例注册表的方式进行实现的,源码如下: public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
/**
* 充当了Bean实例的缓存,实现方式和单例注册表相同
*/
private final Map singletonCache=new HashMap();
public Object getBean(String name)throws BeansException{
return getBean(name,null,null);
}
...
public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{
//对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)
String beanName=transformedBeanName(name);
Object bean=null;
//手工检测单例注册表
Object sharedInstance=null;
//使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高
synchronized(this.singletonCache){
sharedInstance=this.singletonCache.get(beanName);
}
if(sharedInstance!=null){
...
//返回合适的缓存Bean实例
bean=getObjectForSharedInstance(name,sharedInstance);
}else{
...
//取得Bean的定义
RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);
...
//根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关
//<bean id="date" class="java.util.Date" scope="singleton"/>
//如果是单例,做如下处理
if(mergedBeanDefinition.isSingleton()){
synchronized(this.singletonCache){
//再次检测单例注册表
sharedInstance=this.singletonCache.get(beanName);
if(sharedInstance==null){
...
try {
//真正创建Bean实例
sharedInstance=createBean(beanName,mergedBeanDefinition,args);
//向单例注册表注册Bean实例
addSingleton(beanName,sharedInstance);
}catch (Exception ex) {
...
}finally{
...
}
}
}
bean=getObjectForSharedInstance(name,sharedInstance);
}
//如果是非单例,即prototpye,每次都要新创建一个Bean实例
//<bean id="date" class="java.util.Date" scope="prototype"/>
else{
bean=createBean(beanName,mergedBeanDefinition,args);
}
}
...
return bean;
}
}
1、从单例注册表中获取对象
2、如果有九直接返回
3、如果没有就获取bean的定义,再判断是否是单例创建
4、如果是单例,再次检测单例注册表
如果没有,就创建,创建好了以后加入到单例注册表中,最后从注册表中获取对象
如果有直接从注册表中获取对象
5、如果不是单例就接创建
3、策略模式
3.1 定义
1)策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
2)这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
3.2策略模式通俗易懂用例
策略模式解决鸭子问题
1)应用实例要求
1)有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
2)显示鸭子的信息
2)思路分析(类图)
策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
3)代码实现
improve.zip
package com.atguigu.strategy.improve;
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println(" 飞翔技术一般 ");
}
}
package com.atguigu.strategy.improve;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub WildDuck wildDuck = new WildDuck(); wildDuck.fly();//
ToyDuck toyDuck = new ToyDuck(); toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck(); pekingDuck.fly();
//动态改变某个对象的行为, 北京鸭 不能飞
pekingDuck.setFlyBehavior(new NoFlyBehavior());
System.out.println("北京鸭的实际飞翔能力");
pekingDuck.fly();
}
}
package com.atguigu.strategy.improve;
public abstract class Duck {
//属性, 策略接口
FlyBehavior flyBehavior;
//其它属性<->策略接口QuackBehavior quackBehavior;
public Duck() {
}
public abstract void display();//显示鸭子信息public void quack() {
System.out.println("鸭子嘎嘎叫~~");
}
public void swim() {
System.out.println("鸭子会游泳~~");
}
public void fly() {
//改进
if(flyBehavior != null) { flyBehavior.fly();
}
}
public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior;
}
}
package com.atguigu.strategy.improve;
public interface FlyBehavior {
void fly(); // 子类具体实现
}
package com.atguigu.strategy.improve;
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub System.out.println(" 飞翔技术高超 ~~~");
}
}
package com.atguigu.strategy.improve;
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println(" 不会飞翔 ");
}
}
package com.atguigu.strategy.improve;
public class PekingDuck extends Duck {
//假如北京鸭可以飞翔,但是飞翔技术一般 public PekingDuck() {
// TODO Auto-generated constructor stub flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("~~北京鸭~~~");
}
}
package com.atguigu.strategy.improve;
public interface QuackBehavior { void quack();//子类实现
}
package com.atguigu.strategy.improve;
public class ToyDuck extends Duck{
public ToyDuck() {
// TODO Auto-generated constructor stub flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("玩具鸭");
}
//需要重写父类的所有方法 public void quack() {
System.out.println("玩具鸭不能叫~~");
}
public void swim() {
System.out.println("玩具鸭不会游泳~~");
}
}
package com.atguigu.strategy.improve;
public class WildDuck extends Duck {
//构造器,传入 FlyBehavor 的对象
public WildDuck() {
// TODO Auto-generated constructor stub flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub System.out.println(" 这是野鸭 ");
}
}
3.2 Spring中策略模式的应用
当bean需要访问资源配置文件时,Spring有两种方式
代码中获取Rescource实例
依赖注入
第一种方式需要获取rescource资源的位置,代码中耦合性太高,而今我们一直使用注解,依赖注入的方式去获取。这样的话就无需修改程序,只改配置文件即可。
<beans>
<bean id="test" class="com.example.Test">
<!-- 注入资源 -->
<property name="tmp" value="classpath:book.xml"/>
</bean>
</beans>
在依赖注入的过程中,Spring会调用ApplicationContext 来获取Resource的实例。然而,Resource 接口封装了各种可能的资源类型,包括了:UrlResource,ClassPathResource,FileSystemResource等,Spring需要针对不同的资源采取不同的访问策略。在这里,Spring让ApplicationContext成为了资源访问策略的“决策者”。在资源访问策略的选择上,Spring采用了策略模式。当 Spring 应用需要进行资源访问时,它并不需要直接使用 Resource 实现类,而是调用 ApplicationContext 实例的 getResource() 方法来获得资源,ApplicationContext 将会负责选择 Resource 的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来。
ApplicationContext ctx = new Class PathXmlApplicationContext("bean.xml");
Resource res = ctx.getResource("book.xml");
上面的代码中,Spring 将采用和 ApplicationContext 相同的策略来访问资源。即: ApplicationContext 是 ClassPathXmlApplicationContext,则res 就是 ClassPathResource 实例。若将代码改为:
ApplicationContext ctx = new Class FileSystemXmlApplicationContext("bean.xml");
则再次调用ctx.getResource时,res 就是 ClassPathResource 实例。
4、装饰器模式
【参考】:装饰器模式
4.1、定义
通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
4.2、Spring中装饰器模式的使用
Spring中类中带有Wrapper的都是包装类,如下创建bean就是典型的装饰器模式
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//根据指定的bean使用对应的侧脸创建新的实例,如工厂方法,构造函数自动注入,简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
if (beanType != null) {
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
}