Spring 中的设计模式学习
工厂模式: BeanFActory 和 ApplicationContext创建对象
单例模式: Bean默认为单例
策略模式: Resource的实现类,针对不同资源文件,实现不同的资源获取策略
代理模式: Spring的AOP功能用到了JDK的动态代理和CGLIB的字节码生成技术
模板方法:将相同的部分代码放在父类中,将不同的代码放入不同的子类中,解决代码重复的问题。RestTemplate、JmsTemplate、JpaTemplate
适配器模式: Spring AOP增强或通知使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
观察者模式:Spring时间驱动模式就是观察者模式的一个经典应用
桥接模式:可以根据客户的需求能哦故动态切换不同的数据源
1、工厂模式
1.1 工厂模式学习
工厂模式的目的是将创建对象的具体过程屏蔽隔离起来
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
1.1.1 简单工厂模式
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。
- 工厂类角色: 该模式的核心,用来创建产品,含有一定的商业逻辑和判断逻辑
- 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
- 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。
代码
// ---------------产品类-----------------
abstract class BMW {
public BMW(){}
}
public class BMW320 extends BMW {
public BMW320() {
System.out.println("制造-->BMW320");
}
}
public class BMW523 extends BMW{
public BMW523(){
System.out.println("制造-->BMW523");
}
}
// -----------------工厂类------------------------
public class Factory {
public BMW createBMW(int type) {
switch (type) {
case 320:
return new BMW320();
case 523:
return new BMW523();
default:
break;
}
return null;
}
}
// -----------------用户类-------------------------
public class Customer {
public static void main(String[] args) {
Factory factory = new Factory();
BMW bmw320 = factory.createBMW(320);
BMW bmw523 = factory.createBMW(523);
}
}
优缺点分析
简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需要知道所创建的具体产品类的雷鸣以及创建工程,只需要知道具体产品类所对应的参数即可,通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
但缺点在于不符合“开闭原则”,每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
为了解决简单工厂模式的问题,出现了工厂方法模式。
1.1.2 工厂方法模式
工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。
但缺点在于,每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
抽象工厂 AbstractFactory: 工厂方法模式的核心,是具体工厂角色必须实现的接口或者必须继承的父类,在 Java 中它由抽象类或者接口来实现。
具体工厂 Factory:被应用程序调用以创建具体产品的对象,含有和具体业务逻辑有关的代码
抽象产品 AbstractProduct:是具体产品继承的父类或实现的接口,在 Java 中一般有抽象类或者接口来实现。
具体产品 Product:具体工厂角色所创建的对象就是此角色的实例
代码
// 产品类
abstract class BMW {
public BMW(){}
}
public class BMW320 extends BMW {
public BMW320() {
System.out.println("制造-->BMW320");
}
}
public class BMW523 extends BMW{
public BMW523(){
System.out.println("制造-->BMW523");
}
}
// 工厂类
interface FactoryBMW {
BMW createBMW();
}
public class FactoryBMW320 implements FactoryBMW{
@Override
public BMW320 createBMW() {
return new BMW320();
}
}
public class FactoryBMW523 implements FactoryBMW {
@Override
public BMW523 createBMW() {
return new BMW523();
}
}
// 客户类
public class Customer {
public static void main(String[] args) {
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
BMW320 bmw320 = factoryBMW320.createBMW();
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
BMW523 bmw523 = factoryBMW523.createBMW();
}
}
将工厂方法模式改为采用反射机制的工厂方法模式
// 产品类
abstract class BMW {
public BMW(){}
}
public class BMW320 extends BMW {
public BMW320() {
System.out.println("制造-->BMW320");
}
}
public class BMW523 extends BMW{
public BMW523(){
System.out.println("制造-->BMW523");
}
}
// 工厂类
interface Factory {
Object create(String className);
}
public class RealFactory implements Factory{
@Override
public Object create(String className) {
Class<?> cls = Class.forName(className);
Object obj = cls.newInstance();
return obj;
}
}
// 客户类
public class Customer {
public static void main(String[] args) {
Factory factory = new RealFactory();
BMW320 bmw320 = (BMW320)factory.create("com.rmm.pojo.BMW320");
BMW523 bmw320 = (BMW523)factory.create("com.rmm.pojo.BMW523");
}
}
1.1.3 抽象工厂模式
在工厂方法模式中,我们使用一个工厂创建一个产品,一个具体工厂对应一个具体产品,但有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。
- 产品等级结构:产品等级结构指的是产品的继承结构,例如一个空调抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个空调抽象类和他的子类就构成了一个产品等级结构。
- 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调、海尔冰箱,那么海尔空调则位于空调产品族中。
抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
但该模式的缺点在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。
抽象工厂 AbstractFactory:定义了一个接口,这个接口包含了一组方法用来生产产品,所有的具体工厂都必须实现此接口。
具体工厂 ConcreteFactory:用于生产不同产品族,要创建一个产品,用户只需使用其中一个工厂进行获取,完全不需要实例化任何产品对象。
抽象产品 AbstractProduct:这是一个产品家族,每一个具体工厂都能够生产一整组产品。
具体产品 Product
代码
通过抽象工厂模式,我们可以实现以下的效果:比如宝马320系列使用空调型号A和发动机型号A,而宝马230系列使用空调型号B和发动机型号B,在为320系列生产相关配件时,就无需制定配件的型号,它会自动根据车型生产对应的配件型号A。
也就是说,当每个抽象产品都有多于一个的具体子类的时候(空调有型号A和B两种,发动机也有型号A和B两种),工厂角色怎么知道实例化哪一个子类呢?抽象工厂模式提供两个具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化,每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。
//发动机以及型号
public interface Engine {}
public class EngineA implements Engine{
public EngineA(){
System.out.println("制造-->EngineA");
}
}
public class EngineB implements Engine{
public EngineB(){
System.out.println("制造-->EngineB");
}
}
//空调以及型号
public interface Aircondition {}
public class AirconditionA implements Aircondition{
public AirconditionA(){
System.out.println("制造-->AirconditionA");
}
}
public class AirconditionB implements Aircondition{
public AirconditionB(){
System.out.println("制造-->AirconditionB");
}
}
//创建工厂的接口
public interface AbstractFactory {
//制造发动机
public Engine createEngine();
//制造空调
public Aircondition createAircondition();
}
//为宝马320系列生产配件
public class FactoryBMW320 implements AbstractFactory{
@Override
public Engine createEngine() {
return new EngineA();
}
@Override
public Aircondition createAircondition() {
return new AirconditionA();
}
}
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {
@Override
public Engine createEngine() {
return new EngineB();
}
@Override
public Aircondition createAircondition() {
return new AirconditionB();
}
}
public class Customer {
public static void main(String[] args){
//生产宝马320系列配件
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
factoryBMW320.createEngine();
factoryBMW320.createAircondition();
//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW523.createEngine();
factoryBMW523.createAircondition();
}
}
1.1.4 总结
1、工厂方法模式与抽象工厂模式的区别在于:
(1)工厂方法只有一个抽象产品类和一个抽象工厂类,但可以派生出多个具体产品类和具体工厂类,每个具体工厂类只能创建一个具体产品类的实例。
(2)抽象工厂模式拥有多个抽象产品类(产品族)和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类;抽象工厂类也可以派生出多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例
1.2 Spring中的工厂模式
Spring中对象实例的创建和管理就是通过BeanFactory和ApplicationContext完成的
Spring factory UML类图
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//省略...
}
没看懂具体怎么使用了工厂模式的细节
2. 单例模式
2.1 单例模式的概念
单例模式可以确保系统中某个类只有一个实例,该类自行实例化并向整个系统提供这个实例的公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
这种模式的优点是,1. 系统中只存在一个共用的实例对象,无需频繁创建和销毁对象,节约系统资源,提高性能;2. 可以严格控制用户怎么样以及合适访问单例对象
单例模式的特点是,单例类只能有一个实例,单例类必须自己创建自己的唯一实现,单例类必须给所有其他对象提供这一实例。
2.2 懒汉式单例
public class Singleton {
private Singleton() {}
private static Singleton sigle = null;
public static Singleton getInstance() {
// 在这里可能会发生线程不安全的问题
// 导致产生多个实例
if (single == null) {
single = new Singleton();
}
return single;
}
}
Singleton 通过私有化构造函数,避免类在外部被实例化,而且只能通过 getInstance() 方法获取 Singleton 的唯一实例。但是以上懒汉式单例的实现是线程不安全的,在并发环境下可能出现多个 Singleton 实例的问题。要实现线程安全,有以下三种方式,都是对 getInstance() 这个方法改造,保证了懒汉式单例的线程安全:
- 在 getInstance() 方法上加同步机制:
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的。
2. 双重检查锁定:
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private volatile static Singleton singleton=null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
(1)为什么 getInstance() 方法内需要使用两个 if (singleton == null) 进行判断呢?
假设高并发下,线程A、B 都通过了第一个 if 条件。若A先抢到锁,new 了一个对象,释放锁,然后线程B再抢到锁,此时如果不做第二个 if 判断,B线程将会再 new 一个对象。使用两个 if 判断,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。
(2)volatile 关键字的作用?
volatile 的作用主要是禁止指定重排序。假设在不使用 volatile 的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行 singleton = new Singleton(),但由于构造方法不是一个原子操作,编译后会生成多条字节码指令,由于 JAVA的 指令重排序,可能会先执行 singleton 的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后 singleton 便不为空了,但是实际的初始化操作却还没有执行。如果此时线程B进入,就会拿到一个不为空的但是没有完成初始化的singleton 对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。
3. 静态内部类:
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
利用了类加载机制来保证初始化 instance 时只有一个线程,所以也是线程安全的,同时没有性能损耗,这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
2.3 饿汉式单例
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
饿汉式和懒汉式区别:
(1)初始化时机与首次调用:
饿汉式是在类加载时,就将单例初始化完成,保证获取实例的时候,单例是已经存在的了。所以在第一次调用时速度也会更快,因为其资源已经初始化完成。
懒汉式会延迟加载,只有在首次调用时才会实例化单例,如果初始化所需要的工作比较多,那么首次访问性能上会有些延迟,不过之后就和饿汉式一样了。
(2)线程安全方面:饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,懒汉式本身是非线程安全的,需要通过额外的机制保证线程安全
2.4 登记式单例
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if(name == null) {
name = Singleton3.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。
2.5 Spring 中的单例模式
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 保存单例Objects的缓存集合ConcurrentHashMap,key:beanName --> value:bean实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//检查缓存中是否有实例,如果缓存中有实例,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//省略...
try {
//通过singletonFactory获取单例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//省略...
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
//返回实例
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
从源码中可以看出,是通过ConcurrentHashMap的方式,如果在Map中存在则直接返回,如果不存在则创建,并且put进Map集合中,并且整段逻辑是使用同步代码块包住的,所以是线程安全的。
3. 策略模式
3.1 策略模式学习
3.1.1 为什么会有策略模式?
在开发中经常遇到这种情况,实现某个功能有多种算法策略,我们可以根据不同环境或者条件选择不同的算法策略来完成该功能,比如查找、排序等,一种常用方式是硬编码在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过 if-else 或者 case 等条件判断语句来进行选择。但是如果需要增加新的算法策略,就需要修改封装算法类的源代码;更换查找算法,也需要修改客户端的调用代码。并且在这个类中封装了大量算法,也会使得该类代码较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。
如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?解决方法就是使用策略模式。
3.1.2 什么是策略模式
将类中经常改变或者可能改变的部分提取为作为一个抽象策略接口类,然后在类中包含这个对象的实例,这样类实例在运行时就可以随意调用实现了这个接口的类的行为。
比如定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化,这就是策略模式。
(1)环境类(Context):通过 ConcreteStrategy 具体策略类来配置,持有 Strategy 对象并维护对Strategy 对象的引用。可定义一个接口来让 Strategy 访问它的数据。
(2)抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy 定义的算法。
(3)具体策略类(ConcreteStrategy): Strategy 接口的具体算法。
策略模式的优点在于可以动态改变对象的行为;但缺点是会产生很多策略类,同时客户端必须知道所有的策略类,并自行决定使用哪一个策略类;
策略模式适用用于以下几种场景:
(1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需要动态地在几种算法中选择一种
(2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
3.1.3 代码
场景如下,刘备要到江东娶老婆了,走之前诸葛亮给赵云三个锦囊妙计,说是按天机拆开能解决棘手问题。场景中出现三个要素:三个妙计(具体策略类)、一个锦囊(环境类)、赵云(调用者)。
// 抽象策略类
public interface Strategy {
public void operate();
}
// 三个实现类
//妙计一:初到吴国
public class BackDoor implements IStrategy {
@Override
public void operate() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力,使孙权不能杀刘备");
}
}
//求吴国太开绿灯放行
public class GivenGreenLight implements IStrategy {
@Override
public void operate() {
System.out.println("求吴国太开个绿灯,放行");
}
}
//孙夫人断后,挡住追兵
public class BlackEnemy implements IStrategy {
@Override
public void operate() {
System.out.println("孙夫人断后,挡住追兵");
}
}
// 环境类
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy){
this.strategy = strategy;
}
public void operate(){
this.strategy.operate();
}
}
使用
public class Zhaoyun {
public static void main(String[] args) {
Context context;
System.out.println("----------刚到吴国使用第一个锦囊---------------");
context = new Context(new BackDoor());
context.operate();
System.out.println("\n");
System.out.println("----------刘备乐不思蜀使用第二个锦囊---------------");
context.setStrategy(new GivenGreenLight());
context.operate();
System.out.println("\n");
System.out.println("----------孙权的追兵来了,使用第三个锦囊---------------");
context.setStrategy(new BlackEnemy());
context.operate();
System.out.println("\n");
}
}
以上就是策略模式,多种不同解决方案动态切换,起到改变对象行为的效果。
3.2 Spring中的策略模式 (Resource、ResourceLoader)
以Resource为例
简单介绍一下Resource的实现类:
UrlResource:访问网络资源的实现类。
ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类。
ByteArrayResource:访问字节数组资源的实现类。
PathResource:访问文件路径资源的实现类。
ClassPathResource:访问类加载路径里资源的实现类。
@RequestMapping(value = "/resource", method = RequestMethod.GET)
public String resource(@RequestParam(name = "type") String type,
@RequestParam(name = "arg") String arg) throws Exception {
Resource resource;
//这里可以优化为通过工厂模式,根据type创建Resource的实现类
if ("classpath".equals(type)) {
//classpath下的资源
resource = new ClassPathResource(arg);
} else if ("file".equals(type)) {
//本地文件系统的资源
resource = new PathResource(arg);
} else if ("url".equals(type)) {
//网络资源
resource = new UrlResource(arg);
} else {
return "fail";
}
InputStream is = resource.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
os.write(i);
}
String result = new String(os.toByteArray(), StandardCharsets.UTF_8);
is.close();
os.close();
return "type:" + type + ",arg:" + arg + "\r\n" + result;
}
这里自己修改作者代码,优化为通过工厂模式,根据type创建Resource的实现类
public class ResourceBeanFactory {
private ResourceBeanFactory() {}
public static class SingleHolder {
public static final INSTANCE= new ResourceBeanFactory ();
}
public static final ResourceBeanFactory getInstance() {
return SingleHolder.INSTANCE;
}
public Object getResource(String resourceClassName) {
Class<?> clazz = Class.forName(resourceCClassName);
return clazz.newInstance();;
}
}
@RequestMapping(value = "/resource", method = RequestMethod.GET)
public String resource(@RequestParam(name = "type") String type,
@RequestParam(name = "arg") String arg) throws Exception {
Resource resource;
ResourceBeanFactory factory = ResourceBeanFactory.getInstance();
// 工厂模式采用反射机制获取对象实例,这里type应当是全限定类名
resource = (resource)factory.getResource(type);
InputStream is = resource.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
os.write(i);
}
String result = new String(os.toByteArray(), StandardCharsets.UTF_8);
is.close();
os.close();
return "type:" + type + ",arg:" + arg + "\r\n" + result;
}
这就是策略模式的思想,通过外部条件使用不同的算法解决问题。其实很简单,因为每个实现类的getInputStream()方法都不一样,我们看ClassPathResource的源码,是通过类加载器加载资源
public class ClassPathResource extends AbstractFileResolvingResource {
private final String path;
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
//通过类加载器加载类路径下的资源
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
//如果输入流is为null,则报错
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
//返回InputStream
return is;
}
}
再看UrlResource的源码,获取InputStream的实现又是另一种策略。
public class UrlResource extends AbstractFileResolvingResource {
@Nullable
private final URI uri;
private final URL url;
private final URL cleanedUrl;
@Override
public InputStream getInputStream() throws IOException {
//获取连接
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
//获取输入流,并返回
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
}
4. 代理模式
4.1 代理模式的学习
4.1.1 为什么会有代理模式
我们一般在租房子时会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做;再比如我们打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法;再比如在淘宝上面买东西,你使用支付宝平台支付,卖家请物流公司发货,在这个过程汇总支付宝、物流公司都扮演者“第三者”的角色在帮你完成物品的购买,这里的第三者我们可以将其称之为代理者,在我们实际生活中这种代理情况无处不在!
4.1.2 什么是代理模式
代理模式的设计动机是通过代理对象来访问真实对象,通过建立一个对象代理类,由代理对象控制原对象的引用,从而实现对真实对象的操作。在代理模式中,代理对象主要起到一个中介的作用,用于协调与连接调用者(即客户端)和被调用者(即目标对象),在一定程度上降低了系统的耦合度,同时也保护了目标对象。但缺点是在调用者与被调用者之间增加了代理对象,可能会造成请求的处理速度变慢,
UML图
Subject:抽象角色,声明了真实对象和代理对象的共同接口;
Proxy:代理角色,实现了与真实对象相同的接口,所以在任何时刻都能够代理真实对象,并且代理对象内部包含了真实对象的引用,所以它可以操作真实对象,同时也可以附加其他的操作,相当于对真实对象进行封装。
RealSubject:真实对象,是我们最终要引用的对象。
4.1.3 代码
这里没有使用作者原来的例子,想自己举一个。比如,你打算去考研,如果你自己去考研就直接选择学校,学习然后考试,但是如果你报了考研机构他们会帮助你择校、定计划、找专业课老师,帮助你复试等一系列操作。代码如下
public interface KaoYan {
void chooseSchool();
void study();
void test();
}
public class YouSelfKaoYan implements KaoYan {
@Override
public void chooseSchool() {
System.out.println("choose school");
}
@Override
public void study() {
System.out.println("study");
}
@Override
public void test() {
System.out.println("test");
}
}
public class KaoYanInstituteProxy implements KaoYan {
YouSelfKaoYan you;
public KaoYanInstituteProxy (YouSelfKaoYan you) {
this.you = you;
}
@Override
public void chooseSchool() {
System.out.println("机构帮助完成择校规划");
System.out.println("确定学校");
you.chooseSchool();
}
@Override
public void study() {
System.out.println("机构帮助进行学习规划");
you.study();
}
@Override
public void test() {
System.out.println("初试辅导");
you.test();
System.out.println("复试辅导");
}
}
public class Client {
public static void main(String[] args) {
KaoYanInstituteProxy proxy = new KaoYanInstituteProxy (new YouSelfKaoYan());
proxy.chooseSchool();
proxy.study();
proxy.test();
}
}
代理模式跟装饰器模式比较类似,有些读者可能容易将他们混淆,这里我们简单介绍下两者的区别:
(1)装饰器模式关注于在对象上动态添加新行为,而代理模式虽然也可以增加新的行为,但关注于控制对象的访问。
(2)装饰器模式执行主体是原类;代理模式是代理原类进行操作,执行主体是代理类。
(3)代理模式中,代理类可以对客户端隐藏真实对象的具体信息,因此使用代理模式时,我们常常在代理类中创建真实对象的实例。而装饰器模式的通常做法是将原始对象作为参数传给装饰者的构造器。也就是说。代理模式的代理和真实对象之间的对象通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
4.1.4 JDK动态代理
public interface SomeService {
void doSome();
void doOther();
}
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行doSome");
}
@Override
public void doOther() {
System.out.println("执行doOther");
}
}
public class ServiceTools {
public static void doLog(){
System.out.println("方法的执行时间:" + new Date());
}
public static void doTrans(){
System.out.println("方法执行完毕,提交事务");
}
}
public class MyInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
//通过代理对象执行方法时,会调用执行这个invoke()方法
/**
*
* @param proxy 代理类实例
* @param method 被代理的方法
* @param args 方法的参数数组
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行MyInvocationHandler中的invoke方法");
//打印被调用的目标类中的方法名
System.out.println(method.getName());
Object res = null;
//此方法是通过动态代理实现的增强的功能
ServiceTools.doLog();
//执行目标类方法,通过Method实现
res = method.invoke(target, args);
//此方法是通过动态代理实现的增强的功能
ServiceTools.doTrans();
//返回目标方法的执行结果
return res;
}
}
public class MyApp {
public static void main(String[] args) {
//创建目标对象
SomeService target = new SomeServiceImpl();
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);
//使用Proxy创建代理
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
//通过代理执行方法,会调用handle中的invoke()方法
proxy.doSome();
}
}
4.2 Spring中的代理模式
AOP是Spring的一个核心特性(面向切面编程),作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。
Spring AOP实现的关键在于动态代理,主要有两种方式,JDK动态代理和CGLIB动态代理:
(1)JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;
(2)如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
我们看DefaultAopProxyFactory的createAopProxy()方法,Spring通过此方法创建动态代理类:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}
从源码中可以看出,Spring会先判断是否实现了接口,如果实现了接口就使用JDK动态代理,如果没有实现接口则使用Cglib动态代理,也可以通过配置,强制使用Cglib动态代理,配置如下:
<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和Cglib动态代理的区别:
JDK动态代理只能对实现了接口的类生成代理,没有实现接口的类不能使用。
Cglib动态代理即使被代理的类没有实现接口,也可以使用,因为Cglib动态代理是使用继承被代理类的方式进行扩展。
Cglib动态代理是通过继承的方式,覆盖被代理类的方法来进行代理,所以如果方法是被final修饰的话,就不能进行代理。
5. 模板模式
5.1 模板方法模式学习
5.1.1 什么是模板方法
模板方法是基于继承实现的,在抽象父类中声明一个模板方法,并在模板方法中定义算法的执行步骤(即算法骨架)。在模板方法模式中,可以将子类共性的部分放在父类中实现,而特性的部分延迟到子类中实现,只需将特性部分在父类中声明成抽象方法即可,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤,不同的子类可以以不同的方式来实现这些逻辑。
模板方法模式的优点在于符合“开闭原则”,也能够实现代码复用,将不变的行为转移到父类,去除子类中的重复代码。但是缺点是不同的实现都需要定义一个子类,导致类的个数的增加使得系统更加庞大,设计更加抽象。
模板方法是一个方法,那么他与普通方法有什么不同呢?模板方法是定义在抽象类中,把基本操作方法组合在一起形成一个总算法或者一组步骤的方法。而普通的方法是实现各个步骤的方法,我们可以认为普通方法是模板方法的一个组成部分。
抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。
5.1.2 代码
举个例子,以准备去学校所要做的工作(prepareGotoSchool)为例,假设需要分三步:穿衣服(dressUp),吃早饭(eatBreakfast),带上东西(takeThings)。学生和老师要做得具体事情肯定有所区别。
//抽象类定义整个流程骨架
public abstract class AbstractPerson{
//模板方法,使用final修改,防止子类改变算法的实现步骤
public final void prepareGotoSchool(){
dressUp();
eatBreakfast();
takeThings();
}
//以下是不同子类根据自身特性完成的具体步骤
protected abstract void dressUp();
protected abstract void eatBreakfast();
protected abstract void takeThings();
}
public class Student extends AbstractPerson{
@Override
protected void dressUp() {
System.out.println(“穿校服");
}
@Override
protected void eatBreakfast() {
System.out.println(“吃妈妈做好的早饭");
}
@Override
protected void takeThings() {
System.out.println(“背书包,带上家庭作业和红领巾");
}
}
public class Teacher extends AbstractPerson{
@Override
protected void dressUp() {
System.out.println(“穿工作服");
}
@Override
protected void eatBreakfast() {
System.out.println(“做早饭,照顾孩子吃早饭");
}
@Override
protected void takeThings() {
System.out.println(“带上昨晚准备的考卷");
}
}
public class Client {
public static void main(String[] args) {
Student student = new Student()
student.prepareGotoSchool();
Teacher teacher = new Teacher()
teacher.prepareGotoSchool();
}
}
5.2 Spring中的模板模式
所谓模板就是一个方法,这个方法定义了算法的骨架,即将算法的实现定义成了一组步骤,并将一些步骤延迟到子类中实现,子类重写抽象类中的模板方法实现算法骨架中特定的步骤。模板模式可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。在模板方法模式中,我们可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,从而解决代码重复的问题。
Spring中的事务管理器就运用模板模式的设计,首先看PlatformTransactionManager类。这是最底层的接口,定义提交和回滚的方法。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
// AbstractPlatformTransactionManager类
// public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable
@Override
public final void commit(TransactionStatus status) throws TransactionException {
//省略...
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, true);
return;
}
//调用processCommit()
processCommit(defStatus);
}
//这个方法定义了骨架,里面会调用一个doRollback()的模板方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//调用doRollback()模板方法
doRollback(status);
}
else {
//省略...
}
//省略了很多代码...
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
//省略...
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//省略...
//调用doCommit()模板方法
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
//省略了很多代码...
}
//模板方法doRollback(),把重要的步骤延迟到子类去实现
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
//模板方法doCommit(),把重要的步骤延迟到子类去实现
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
模板方法则由各种事务管理器的实现类去实现,也就是把骨架中重要的doRollback()延迟到子类。一般来说,Spring默认是使用的事务管理器的实现类是DataSourceTransactionManager。
//通过继承AbstractPlatformTransactionManager抽象类
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
//重写doCommit()方法,实现具体commit的逻辑
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
//重写doRollback()方法,实现具体的rollback的逻辑
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
}
如果你是用Hibernate框架,Hibernate也有自身的实现,这就体现了设计模式的开闭原则,通过继承或者组合的方式进行扩展,而不是直接修改类的代码。Hibernate的事务管理器则是HibernateTransactionManager。
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
//重写doCommit()方法,实现Hibernate的具体commit的逻辑
@Override
protected void doCommit(DefaultTransactionStatus status) {HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Transaction hibTx = txObject.getSessionHolder().getTransaction();
Assert.state(hibTx != null, "No Hibernate transaction");
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "]");
}
try {
hibTx.commit();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
//省略...
}
//重写doRollback()方法,实现Hibernate的具体rollback的逻辑
@Override
protected void doRollback(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Transaction hibTx = txObject.getSessionHolder().getTransaction();
Assert.state(hibTx != null, "No Hibernate transaction");
//省略...
try {
hibTx.rollback();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
//省略...
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
txObject.getSessionHolder().getSession().clear();
}
}
}
}
如果这样的话,我是不是可以定义自己的处理具体commit和RollBack过程的具体实现,只要继承AbstractPlatformTransactionManager这个抽象类
其实模板模式在日常开发中也经常用,比如一个方法中,前后代码都一样,只有中间有一部分操作不同,就可以使用模板模式进行优化代码,这可以大大地减少冗余的代码,非常实用。
6. 适配器与责任链模式
6.1 适配器模式学习
6.1.1 什么是适配器模式
适配器模式主要用于将一个类的接口转化成客户端希望的目标类格式,使得原本不兼容的类可以在一起工作,将目标类和适配者类解耦;同时也符合“开闭原则”,可以在不修改原代码的基础上增加新的适配器类;将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性,但是缺点在于更换适配器的实现过程比较复杂。
所以,适配器模式比较适合以下场景:
(1)系统需要使用现有的类,而这些类的接口不符合系统的接口。
(2)使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。
6.1.2 适配器的三种实现方式
6.1.2.1 类的适配器模式
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {
public void specificRequest() {
System.out.println("被适配类具有 特殊功能...");
}
}
// 目标接口,或称为标准接口
interface Target {
public void request();
}
// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {
public void request() {
System.out.println("普通类 具有 普通功能...");
}
}
// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
public void request() {
super.specificRequest();
}
}
// 测试类public class Client {
public static void main(String[] args) {
// 使用普通功能类
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();
// 使用特殊功能类,即适配类
Target adapter = new Adapter();
adapter.request();
}
}
6.1.2.2 对象的适配器模式
// 适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target{
// 直接关联被适配类
private Adaptee adaptee;
// 可以通过构造函数传入具体需要适配的被适配类对象
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
// 这里是使用委托的方式完成特殊功能
this.adaptee.specificRequest();
}
}
// 测试类
public class Client {
public static void main(String[] args) {
// 使用普通功能类
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();
// 使用特殊功能类,即适配类,
// 需要先创建一个被适配类的对象作为参数
Target adapter = new Adapter(new Adaptee());
adapter.request();
}
}
测试结果与上面的一致。从类图中我们也知道需要修改的只不过就是 Adapter 类的内部结构,即 Adapter 自身必须先拥有一个被适配类的对象,再把具体的特殊功能委托给这个对象来实现。使用对象适配器模式,可以使得 Adapter 类(适配类)根据传入的 Adaptee 对象达到适配多个不同被适配类的功能,当然,此时我们可以为多个被适配类提取出一个接口或抽象类。这样看起来的话,似乎对象适配器模式更加灵活一点。
6.1.2.3 接口的适配器模式
有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
public interface Sourceable {
public void method1();
public void method2();
}
public abstract class Wrapper2 implements Sourceable{
public void method1(){}
public void method2(){}
}
public class SourceSub1 extends Wrapper2 {
public void method1(){
System.out.println("the sourceable interface's first Sub1!");
}
}
public class SourceSub2 extends Wrapper2 {
public void method1(){
System.out.println("the sourceable interface's second Sub2!");
}
}
public class WrapperTest {
public static void main(String[] args) {
Sourceable source1 = new SourceSub1();
Sourceable source2 = new SourceSub2();
source1.method1();
source1.method2();
source2.method1();
source2.method2();
}
}
6.2 责任链模式学习
6.2.1 什么是责任链模式
职责链可以将请求的处理者组织成一条链,并将请求沿着链传递,如果某个处理者能够处理请求则处理,否则将该请求交由上级处理。客户端只需将请求发送到职责链上,无须关注请求的处理细节,通过职责链将请求的发送者和处理者解耦了,这也是职责链的设计动机。
职责链模式可以简化对象间的相互连接,因为客户端和处理者都没有对方明确的信息,同时处理者也不知道职责链中的结构,处理者只需保存一个指向后续者的引用,而不需要保存所有候选者的引用。
另外职责链模式增加了系统的灵活性,我们可以任意增加或更改处理者,甚至更改处理者的顺序,不过有可能会导致一个请求无论如何也得不到处理,因为它可能被放置在链末端。
所以责任链模式有以下几个优点:
(1)降低耦合度,将请求的发送者和接收者解耦。反映在代码上就是不需要在类中写很多丑陋的 if….else 语句,如果用了职责链,相当于我们面对一个黑箱,只需将请求递交给其中一个处理者,然后让黑箱内部去负责传递就可以了。
(2)简化了对象,使得对象不需要链的结构。
(3)增加系统的灵活性,通过改变链内的成员或者调动他们的次序,允许动态地新增或者删除处理者
(4)增加新的请求处理类很方便。
但是责任链模式也存在一些缺点:
(1)不能保证请求一定被成功处理
(2)系统性能将受到一定影响,并且可能会造成循环调用。
(3)可能不容易观察运行时的特征,而且在进行代码调试时不太方便,有碍于除错。
(1)Handler:抽象处理者,定义了一个处理请求的方法。所有的处理者都必须实现该抽象类。
(2)ConcreteHandler:具体处理者,处理它所负责的请求,同时也可以访问它的后继者,如果它能够处理该请求则处理,否则将请求传递到它的后继者。
(3)Client: 客户类
public class ConcreteHandler extends Handler
{
public void handleRequest(String request)
{
if(请求request满足条件)
{
...... //处理请求;
}
else
{
this.successor.handleRequest(request); //转发请求
}
}
}
6.2.2 代码
public class LeaveNode {
/** 请假天数 **/
private int number;
/** 请假人 **/
private String person;
public LeaveNode(String person,int number){
this.person = person;
this.number = number;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getPerson() {
return person;
}
public void setPerson(String person) {
this.person = person;
}
}
public abstract class Leader {
/** 姓名 **/
public String name;
/** 后继者 **/
protected Leader successor;
public Leader(String name){
this.name = name;
}
public void setSuccessor(Leader successor) {
this.successor = successor;
}
public abstract void handleRequest(LeaveNode LeaveNode);
}
public class Instructor extends Leader{
public Instructor(String name){
super(name);
}
public void handleRequest(LeaveNode LeaveNode) {
if(LeaveNode.getNumber() <= 3){ //小于3天辅导员审批
System.out.println("辅导员" + name + "审批" +LeaveNode.getPerson() + "同学的请假条,请假天数为" + LeaveNode.getNumber() + "天。");
}
else{ //否则传递给系主任
if(this.successor != null){
this.successor.handleRequest(LeaveNode);
}
}
}
}
public class DepartmentHead extends Leader{
public DepartmentHead(String name) {
super(name);
}
public void handleRequest(LeaveNode LeaveNode) {
if(LeaveNode.getNumber() <= 7){ //小于7天系主任审批
System.out.println("系主任" + name + "审批" +LeaveNode.getPerson() + "同学的请假条,请假天数为" + LeaveNode.getNumber() + "天。");
}
else{ //否则传递给院长
if(this.successor != null){
this.successor.handleRequest(LeaveNode);
}
}
}
}
public class Dean extends Leader{
public Dean(String name) {
super(name);
}
public void handleRequest(LeaveNode LeaveNode) {
if(LeaveNode.getNumber() <= 10){ //小于10天院长审批
System.out.println("院长" + name + "审批" +LeaveNode.getPerson() + "同学的请假条,请假天数为" + LeaveNode.getNumber() + "天。");
}
else{ //否则传递给校长
if(this.successor != null){
this.successor.handleRequest(LeaveNode);
}
}
}
}
public class President extends Leader{
public President(String name) {
super(name);
}
public void handleRequest(LeaveNode LeaveNode) {
if(LeaveNode.getNumber() <= 15){ //小于15天校长长审批
System.out.println("校长" + name + "审批" +LeaveNode.getPerson() + "同学的请假条,请假天数为" + LeaveNode.getNumber() + "天。");
}
else{ //否则不允批准
System.out.println("请假天天超过15天,不批准...");
}
}
}
public class Client {
public static void main(String[] args) {
Leader instructor = new Instructor("陈毅"); //辅导员
Leader departmentHead = new DepartmentHead("王明"); //系主任
Leader dean = new Dean("张强"); //院长
Leader president = new President("王晗"); //校长
instructor.setSuccessor(departmentHead); //辅导员的后续者是系主任
departmentHead.setSuccessor(dean); //系主任的后续者是院长
dean.setSuccessor(president); //院长的后续者是校长
//请假3天的请假条
LeaveNode leaveNode1 = new LeaveNode("张三", 3);
instructor.handleRequest(leaveNode1);
//请假9天的请假条
LeaveNode leaveNode2 = new LeaveNode("李四", 9);
instructor.handleRequest(leaveNode2);
//请假15天的请假条
LeaveNode leaveNode3 = new LeaveNode("王五", 15);
instructor.handleRequest(leaveNode3);
//请假20天的请假条
LeaveNode leaveNode4 = new LeaveNode("赵六", 20);
instructor.handleRequest(leaveNode4);
}
}
(1)纯的责任链模式要求处理者对象只能在两个行为中选择一个:一是承担责任,二是把责任推给下家,不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。
(2)在纯的责任链模式里面,请求必须被某一个处理者对象所接收;在不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
6.3 Spring中的适配器模式与责任链模式
适配器模式能使接口不兼容的对象能够相互合作,将一个类的接口,转换成客户期望的另外一个接口。
在SpringAOP中有一个很重要的功能就是使用的 Advice(通知) 来增强被代理类的功能,Advice主要有MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice这几种。每个Advice都有对应的拦截器,如下所示:
Spring需要将每个 Advice 都封装成对应的拦截器类型返回给容器,所以需要使用适配器模式对 Advice 进行转换。对应的就有三个适配器,我们看个类图:
public interface AdvisorAdapter {
//判断通知类是否匹配
boolean supportsAdvice(Advice advice);
//传入通知类,返回对应的拦截类
MethodInterceptor getInterceptor(Advisor advisor);
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
//判断是否匹配MethodBeforeAdvice通知类
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
//传入MethodBeforeAdvice,转换为MethodBeforeAdviceInterceptor拦截类
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
//成员变量,通知类
private MethodBeforeAdvice advice;
//定义了有参构造器,外部通过有参构造器创建MethodBeforeAdviceInterceptor
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
//当调用拦截器的invoke方法时,就调用通知类的before()方法,实现前置通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//调用通知类的before()方法,实现前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
public DefaultAdvisorAdapterRegistry() {
//初始化适配器,添加到adapters集合,也就是注册
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
//获取所有的拦截器
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//遍历adapters集合
for (AdvisorAdapter adapter : this.adapters) {
//调用supportsAdvice()方法,判断入参的advisor是否有匹配的适配器
if (adapter.supportsAdvice(advice)) {
//如果匹配,则调用getInterceptor()转换成对应的拦截器,添加到interceptors集合中
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
//返回拦截器集合
return interceptors.toArray(new MethodInterceptor[0]);
}
}
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
//这里就是获取拦截器集合,最后就会调用到上文说的getInterceptors()
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//省略...
}else {
//创建一个MethodInvocation
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//调用proceed()方法,底层会通过指针遍历拦截器集合,然后实现前置通知等功能
retVal = invocation.proceed();
}
//省略...
}
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
protected final List<?> interceptorsAndDynamicMethodMatchers;
//指针
private int currentInterceptorIndex = -1;
protected ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
//省略...
//拦截器的集合
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//递归结束
return invokeJoinpoint();
}
//获取拦截器,并且当前的指针+1
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//匹配失败,跳过,递归下一个
return proceed();
}
}
else {
//匹配拦截器,强转为拦截器,然后执行invoke()方法,然后就会调用拦截器里的成员变量的before(),afterReturning()等等,实现前置通知,后置通知,异常通知
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
7. 观察者模式
7.1 观察者模式学习
7.1.1 什么是观察者模式
观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
但观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有相应的机制让观察者知道被观察对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
Subject:抽象主题(被观察者),每一个主题可以有多个观察者,并将所有观察者对象的引用保存在一个集合里,被观察者提供一个接口,可以增加和删除观察者角色
ConcreteSubject:具体主题,将有关状态存入具体观察者对象,在主题发生改变时,给所有的观察者发出通知
Observer:抽象观察者,为所有的具体观察者定义一个更新接口,该接口的作用是在收到主题的通知时能够及时的更新自己
ConcreteObserver:具体观察者,实现抽象观察者角色定义的更新接口,以便使本身的状态与主题状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
7.1.2 代码
情景:在气象观测站中,它能够追踪目前的天气状况,包括温度、适度、气压。需要实现一个布告板,能够分别显示目前的状态,气象统计和简单的预报。当气象站中获取最新的测量数据时,三种布告板必须实时更新,下面是这个案例的设计图:
public interface Subject {
/**
* 注册观察者
* @param observer
*/
public void registerObserver(Observer observer);
/**
* 删除观察者
* @param observer
*/
public void removeOberver(Observer observer);
/**
* 当主题状态发生改变时,这个方法需要被调用,以通知所有观察者
*/
public void notifyObserver();
}
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
public interface DisplayElement {
public void display();
}
public class WeatherData implements Subject{
private List<Observer> observers;
private float tempterature;
private float pressure;
private float humidity;
public WeatherData(){
observers = new ArrayList<Observer>();
}
@Override
public void notifyObserver() {
for(int i = 0; i < observers.size();i++){
Observer observer = observers.get(i);
observer.update(tempterature, humidity, pressure);
}
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeOberver(Observer observer) {
int i = observers.indexOf(observer);
if(i >= 0){
observers.remove(i);
}
}
/**
* 气象站得到更新的观测数据时,通知观察者
*/
public void measurementChanged(){
notifyObserver();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.tempterature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
}
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this); //注册观察者
}
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
}
}
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 78, 40.4f);
}
}
(1)拉取模式:目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到,这种模式称为拉取模式,就是说变化的信息是观察者角色主动从目标角中“拉”出来的。
(2)推送模式:目标角色发生变化时,通知观察者的同时,通过参数将变化的细节传递到观察者角色中去。
这两种模式的使用取决于系统设计,如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适,如果目标角色比较简单,则“拉模式”就很合适啦。
7.2 Spring中的观察者模式
7.2.1 事件角色
在Spring事件驱动模型中,首先有事件角色ApplicationEvent,这是一个抽象类,抽象类下有四个实现类代表四种事件。
ContextStartedEvent:ApplicationContext启动后触发的事件。
ContextStoppedEvent:ApplicationContext停止后触发的事件。
ContextRefreshedEvent:ApplicationContext初始化或刷新完成后触发的事件。
ContextClosedEvent:ApplicationContext关闭后触发的事件。
7.2.2 事件发布者
有了事件之后,需要有个发布者发布事件,发布者对应的类是ApplicationEventPublisher。
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
@FunctionalInterface表示这是一个函数式接口,函数式接口只有一个抽象方法。ApplicationContext类又继承了ApplicationEventPublisher类,所以我们可以使用ApplicationContext发布事件。
7.2.3 事件监听者
发布事件后需要有事件的监听者,事件监听者通过实现接口ApplicationListener来定义,这是一个函数式接口,并且带有泛型,要求E参数是ApplicationEvent的子类。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
7.2.4 演示使用
下面我们演示一下怎么使用,首先继承抽象类ApplicationEvent定义一个事件角色PayApplicationEvent。
public class PayApplicationEvent extends ApplicationEvent {
private String message;
public PayApplicationEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
接着定义一个PayApplicationEvent事件的监听者PayListener。
@Component
public class PayListener implements ApplicationListener<PayApplicationEvent> {
@Override
public void onApplicationEvent(PayApplicationEvent event) {
String message = event.getMessage();
System.out.println("监听到PayApplicationEvent事件,消息为:" + message);
}
}
最后我们使用ApplicationContext发布事件。
@SpringBootApplication
public class SpringmvcApplication {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = SpringApplication.run(SpringmvcApplication.class, args);
applicationContext.publishEvent(new PayApplicationEvent(applicationContext,"成功支付100元!"));
}
}