javaSpring设计模式
FANSEASpring的设计模式
(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
(2)单例模式:Bean默认为单例模式
(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate
模板方法模式的体现
在 HibernateTemplate
和 JdbcTemplate
中,模板方法模式主要体现在它们定义了一套完整的操作流程,例如执行 SQL 语句、处理结果集等,而这些流程中的某些步骤(如 SQL 语句的具体内容、参数的设置等)是可以由子类或外部调用者来指定的。这种模式的好处是既保证了操作的一致性和安全性,又提供了足够的灵活性供开发者定制业务逻辑。
(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。
(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库
(9) 装饰器模式:
适用场景:当需要扩展一个类的功能或者给类添加一个附加职责时,可以使用装饰器模式。例如,在项目中需要连接多个数据库,并且需要根据客户的需求动态切换不同的数据源时,装饰器模式就可以派上用场。通过为数据源对象添加不同的装饰器,可以实现在不修改原有数据源对象的前提下,动态地改变其行为或添加额外的功能。
工厂模式
用工厂代理创建对象,减小代码耦合
BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化
1 2 3 4
| circle=com.demo.Circle rectangle=com.demo.Rectangle square=com.demo.Square
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.demo;
import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties;
public class BeanFactory { private static final Properties props; private static Map<String, Object> beans; static { props = new Properties(); InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); try { props.load(in); beans = new HashMap<>(); Enumeration<Object> keys = props.keys(); while (keys.hasMoreElements()){ String key = keys.nextElement().toString(); String beanPath = props.getProperty(key); Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance(); beans.put(key, value); } } catch (IOException e) { throw new ExceptionInInitializerError("初始化properties失败!程序终止!"); } catch (Exception e){ e.printStackTrace(); } } public static Object getBean(String beanName){ return beans.get(beanName); } }
|
IOC实现原理:
- 编写xml配置文件,一般为key-value结构(类别名-全类名)类别名用于调用对象
1
| <bean id="circle" class="com.demo.Circle"/>
|
- 读取配置类的key和value,value为全类名,就可以利用反射机制将类创建出来
1
| Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
|
- 在Map中保存 类别名:反射创建的对象
- 实现getBean方法,本质就是取
1 2 3 4
| public static Object getBean(String beanName){ return beans.get(beanName); }
|
策略模式
当出现多种情况执行不同方法时,一直写if很不优雅,这样可以将业务功能分出来,写一个统一接口继承,这样有不同的实现类,直接调用不同的实现类方法,这样就是策略模式!
1 2 3
| public interface NomalService { void handle(); }
|
1 2 3 4 5 6 7
| @Service("A") public class AService implements NomalService { @Override public void handle() { System.out.println("HandleMethod:A"); } }
|
1 2 3 4 5 6 7
| @Service("B") public class BServcie implements NomalService { @Override public void handle() { System.out.println("HandleMethod:B"); } }
|
1 2 3 4 5 6 7
| @Autowired Map<String, NomalService> map; @RequestMapping("/user/{name}") public void handle(@PathVariable("name") String name) { map.get(name).handle(); }
|
单例模式
在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。
使用单例模式的好处:
对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private volatile static Singleton singleton;
public Singleton() { }
public static Singleton getInstance(){ if (singleton == null){ synchronized (Singleton.class){ singleton = new Singleton(); } } return singleton; } }
|
使用volatile防止指令重排
创建一个对象,在JVM中会经过三步:
(1)为singleton分配内存空间
(2)初始化singleton对象
(3)将singleton指向分配好的内存空间(执行完这一步代表已经singleton不为空)
指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。

适配器模式
在实际业务中,我们遇到支付功能会首先创建一个统一接口,然后实现类去实现各种支付方式:比如支付宝,微信支付;这些可以手写实现;但是当引入第三方支付jar包,例如银联支付,这样不能修改他底层代码;我们只能使用适配器来实现!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public interface PaymentService {
void pay(String orderId); }
public class AliPayService implements PaymentService { @Override public void pay(String orderId) { System.out.println("支付宝支付:支付订单 " + orderId + " 成功!"); } }
public class WeChatPayService implements PaymentService { @Override public void pay(String orderId) { System.out.println("微信支付:支付订单 " + orderId + " 成功!"); } }
public class UnionPay {
void payWithUnionPay(String orderId) { System.out.println("银联支付:支付订单 " + orderId + " 成功!"); } }
public class UnionPayAdapter implements PaymentService {
private UnionPay unionPay;
public UnionPayAdapter(UnionPay unionPay) { this.unionPay = unionPay; }
@Override public void pay(String orderId) { System.out.println("适配器:使用银联支付的适配器..."); unionPay.payWithUnionPay(orderId); } }
|
1 2 3 4 5 6 7 8 9 10
| public void test2(){ PaymentService aliPayService = PayFactory.createPay("Ali"); PaymentService weChatPayService = PayFactory.createPay("WeChat"); PaymentService unionPay = PayFactory.createPay("Union");
aliPayService.pay("1001"); weChatPayService.pay("1002"); unionPay.pay("1003"); }
|
装饰模式
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class CoffeeShop { public static void main(String[] args) { Coffee coffee = new SimpleCoffee(); System.out.println("Simple Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost()); coffee = new MilkCoffee(coffee); System.out.println("Milk Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost()); coffee = new SugarCoffee(coffee); System.out.println("Sugar and Milk Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost()); } }
|
装饰器模式是在原有类的基础上动态添加新的功能,这种添加功能的方式不同于继承,它是在对象层面实现功能扩展,而不是继承的类层面,因此说装饰器模式比继承更加灵活。大逻辑未变,本质是对方法的增强
- 先新建奶茶类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public interface MilkTea {
String getDescription();
BigDecimal cost(); }
public class BoBoMilkTea implements MilkTea { @Override public String getDescription() { return "波波奶茶"; }
@Override public BigDecimal cost() { return new BigDecimal(8.0); } }
public class HerbalJelly implements MilkTea{ @Override public String getDescription() { return "烧仙草"; }
@Override public BigDecimal cost() { return new BigDecimal(7.0); } }
|
- 再建调料类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| public interface Condiment extends MilkTea { }
public class Pearl implements Condiment{
private MilkTea milkTea;
public Pearl(MilkTea milkTea) { this.milkTea = milkTea; }
@Override public String getDescription() { return milkTea.getDescription() + ",加珍珠"; }
@Override public BigDecimal cost() { return milkTea.cost().add(new BigDecimal(2.0)); } }
public class Coconut implements Condiment{
private MilkTea milkTea;
public Coconut(MilkTea milkTea) { this.milkTea = milkTea; }
@Override public String getDescription() { return milkTea.getDescription() + ",加椰果"; }
@Override public BigDecimal cost() { return milkTea.cost().add(new BigDecimal(1.5)); } }
public class Ormosia implements Condiment{
private MilkTea milkTea;
public Ormosia(MilkTea milkTea) { this.milkTea = milkTea; } @Override public String getDescription() { return milkTea.getDescription() + ",加红豆"; }
@Override public BigDecimal cost() { return milkTea.cost().add(new BigDecimal(2.5)); } }
|
- 测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void test2(){ MilkTea boBoMilkTea = new BoBoMilkTea(); boBoMilkTea = new Pearl(boBoMilkTea); boBoMilkTea = new Coconut(boBoMilkTea); System.out.println("饮料名称: "+boBoMilkTea.getDescription() + " 价格:"+boBoMilkTea.cost());
MilkTea herbalJelly = new HerbalJelly();
herbalJelly = new Pearl(herbalJelly); herbalJelly = new Coconut(herbalJelly); herbalJelly = new Ormosia(herbalJelly); System.out.println("饮料名称: "+herbalJelly.getDescription() + " 价格:"+herbalJelly.cost());
}
|
代理模式
- 静态代理
就是在继承的基础上对业务更改,在调用目标对象的方法之前或之后添加一些额外的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class UserServiceProxy implements UserService { private UserService userService; public UserServiceProxy(UserService userService) { this.userService = userService; } @Override public void addUser(String username, String password) { System.out.println("开始记录日志..."); userService.addUser(username, password); System.out.println("结束记录日志..."); } @Override public void deleteUser(String username) { System.out.println("开始记录日志..."); userService.deleteUser(username); System.out.println("结束记录日志..."); } }
|
- 动态代理
动态代理利用Java的反射机制,在运行时动态地生成代理类。Java的动态代理要求被代理对象必须实现一个或多个接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler { private Object target;
public DynamicProxyHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); System.out.println("代理开始..."); Object result = method.invoke(target, args); System.out.println("代理结束..."); long end = System.currentTimeMillis(); System.out.println("代理方法运行时间:" + (end - start) + "ms"); return result; } }
public class DynamicProxyDemo { public static void main(String[] args) { UserService userService = new UserServiceImpl(); DynamicProxyHandler handler = new DynamicProxyHandler(userService); UserService proxy = (UserService) Proxy.newProxyInstance( UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), handler ); proxy.addUser(); } }
|
在这个示例中,DynamicProxyHandler
实现了InvocationHandler
接口,它会在代理对象调用方法时被调用。Proxy.newProxyInstance
方法用于创建代理对象,它需要三个参数:类加载器、代理对象实现的接口列表以及处理器对象。
动态代理的优点在于它可以动态地创建代理对象,而不需要手动编写代理类代码。这使得代理模式更加灵活和可重用。
模板方法模式
提供一个抽象类,子类可以重写他其中的一个方法做各种实现,但是主体作用依旧是做咖啡
模板方法模式的优点在于它提高了代码的复用性,减少了子类中的重复代码,同时保持了算法的整体结构不变。这种模式在需要定义固定操作序列,同时允许灵活定制某些步骤的场合非常有用。
模板方法模式是一种软件设计模式,它定义了一个算法的步骤,并允许子类别为一个或多个步骤提供具体的实现方式。这使得子类别在不改变算法整体架构的情况下,能够重新定义算法中的某些特定步骤。
在模板方法模式中,通常包含两种角色:抽象模板(AbstractClass)和具体实现(ConcreteClass)。抽象模板类定义了一套算法框架或流程,而具体实现类则对算法框架或流程的某些步骤进行了具体的实现。
一个常见的模板方法模式示例如下:
假设我们有一个咖啡机的类,它有一个制作咖啡的算法,包括加水、加咖啡粉、煮制和倒出咖啡等步骤。我们可以定义一个抽象的咖啡机类,其中包含一个模板方法(如makeCoffee
),该方法定义了制作咖啡的整体流程,但其中的某些步骤(如加咖啡粉的具体方式)可以延迟到具体的子类中去实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public abstract class CoffeeMachine { public final void makeCoffee() { addWater(); grindCoffee(); boil(); dispense(); }
protected void addWater() { System.out.println("Adding water..."); }
protected void boil() { System.out.println("Boiling..."); }
protected void dispense() { System.out.println("Dispensing coffee..."); }
protected abstract void grindCoffee(); }
public class CoffeeMachineA extends CoffeeMachine { @Override protected void grindCoffee() { System.out.println("Grinding coffee using method A..."); } }
public class CoffeeMachineB extends CoffeeMachine { @Override protected void grindCoffee() { System.out.println("Grinding coffee using method B..."); } }
|
在这个例子中,CoffeeMachine
是一个抽象模板类,它定义了制作咖啡的算法骨架,但研磨咖啡粉的具体步骤(grindCoffee
方法)是抽象的,需要在子类中实现。CoffeeMachineA
和CoffeeMachineB
是具体的咖啡机实现类,它们分别提供了研磨咖啡粉的不同实现方式。
当我们想要制作咖啡时,可以创建CoffeeMachineA
或CoffeeMachineB
的实例,并调用其makeCoffee
方法。由于makeCoffee
是final
的,它确保了算法的整体流程不会被子类改变,但研磨咖啡粉的具体步骤会根据子类的不同而有所不同。
模板方法模式的优点在于它提高了代码的复用性,减少了子类中的重复代码,同时保持了算法的整体结构不变。这种模式在需要定义固定操作序列,同时允许灵活定制某些步骤的场合非常有用。
观察者模式
观察者模式在Java中有广泛的应用,尤其是在事件处理中。以下是一个简单的Java示例来说明观察者模式:
首先,我们定义一个Subject
接口,代表被观察的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.util.ArrayList; import java.util.List;
public interface Subject { void registerObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); void setState(String state); String getState(); }
|
接下来,我们实现Subject
接口的RealSubject
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import java.util.ArrayList; import java.util.List;
public class RealSubject implements Subject { private List<Observer> observers; private String state;
public RealSubject() { observers = new ArrayList<>(); }
@Override public void registerObserver(Observer o) { observers.add(o); }
@Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } }
@Override public void notifyObservers() { for (Observer observer : observers) { observer.update(this); } }
@Override public void setState(String state) { this.state = state; notifyObservers(); }
@Override public String getState() { return state; } }
|
然后,我们定义Observer
接口,代表观察者:
1 2 3 4
| public interface Observer { void update(Subject subject); }
|
接着,我们实现Observer
接口的BinaryObserver
类:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class BinaryObserver implements Observer { @Override public void update(Subject subject) { System.out.println("BinaryObserver: State is " + subject.getState()); } }
public class HexObserver implements Observer { @Override public void update(Subject subject) { System.out.println("HexObserver: State is " + subject.getState()); } }
|
最后,我们编写测试代码来演示观察者模式的工作过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ObserverPatternDemo { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); Observer binaryObserver = new BinaryObserver(); Observer hexObserver = new HexObserver();
realSubject.registerObserver(binaryObserver); realSubject.registerObserver(hexObserver);
realSubject.setState("1101"); realSubject.setState("1010");
realSubject.removeObserver(hexObserver);
realSubject.setState("1111"); } }
|
在上面的代码中,RealSubject
是被观察的对象,BinaryObserver
和HexObserver
(这里未给出实现)是观察者。当RealSubject
的状态改变时,它会调用notifyObservers()
方法来通知所有注册的观察者。每个观察者通过实现update()
方法来响应状态的改变。
在实际应用中,观察者模式可以用于多种场景,比如GUI编程中的事件监听、数据库连接的监听、用户界面的变化监听等。它使得对象之间的依赖关系解耦,降低了系统的耦合度,提高了代码的可维护性和可扩展性。
责任链设计模式
适用于流水线任务,对每一个环节进行类的定义,顺序执行,可以保障流程扩展性
组成:
- Handler(环节类):包含执行当前环节任务的方法,以及下一个环节的指针
- Factory(工厂类):用于创建环节链表关系,从第一个环节开始执行

代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| interface Handler { void setNext(Handler handler); Object handle(Object request); }
abstract class AbstractHandler implements Handler { private Handler next;
@Override public void setNext(Handler handler) { this.next = handler; }
@Override public Object handle(Object request) { if (this.next != null) { return this.next.handle(request); } return null; } }
class ConcreteHandler1 extends AbstractHandler { @Override public Object handle(Object request) { if ((Integer) request < 10) { System.out.println("Request handled in ConcreteHandler1"); } else { super.handle(request); } return request; } }
class ConcreteHandler2 extends AbstractHandler { @Override public Object handle(Object request) { if ((Integer) request >= 10 && (Integer) request < 20) { System.out.println("Request handled in ConcreteHandler2"); } else { super.handle(request); } return request; } }
public class ChainOfResponsibilityDemo { public static void main(String[] args) { Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
for (int i = 0; i <= 25; i += 5) { handler1.handle(i); } } }
|
在这个例子中,ConcreteHandler1
和 ConcreteHandler2
分别处理不同范围的请求值。当一个请求进入链后,每个处理者检查是否满足它的条件,如果满足则处理请求,否则将请求传给下一个处理者。这样就形成了一个处理请求的责任链。
注意:在实际应用中,需要确保责任链的末尾处理者能够妥善处理无法识别的请求,以避免无限递归的问题。在上面的例子中,如果没有处理者接受请求,则默认返回null
。