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进行加载实例化
| 12
 3
 4
 
 | circle=com.demo.Circle
 rectangle=com.demo.Rectangle
 square=com.demo.Square
 
 | 
| 12
 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方法,本质就是取
| 12
 3
 4
 
 | public static Object getBean(String beanName){
 return beans.get(beanName);
 }
 
 | 
策略模式
当出现多种情况执行不同方法时,一直写if很不优雅,这样可以将业务功能分出来,写一个统一接口继承,这样有不同的实现类,直接调用不同的实现类方法,这样就是策略模式!
| 12
 3
 
 | public interface NomalService {void handle();
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 
 | @Service("A")public class AService implements NomalService {
 @Override
 public void handle() {
 System.out.println("HandleMethod:A");
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 
 | @Service("B")public class BServcie implements NomalService {
 @Override
 public void handle() {
 System.out.println("HandleMethod:B");
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 
 | @AutowiredMap<String, NomalService> map;
 @RequestMapping("/user/{name}")
 public void handle(@PathVariable("name") String name)
 {
 map.get(name).handle();
 }
 
 | 
单例模式
在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。
使用单例模式的好处:
对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
| 12
 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包,例如银联支付,这样不能修改他底层代码;我们只能使用适配器来实现!
| 12
 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);
 }
 }
 
 | 
| 12
 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");
 }
 
 | 
装饰模式
| 12
 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());
 }
 }
 
 | 
装饰器模式是在原有类的基础上动态添加新的功能,这种添加功能的方式不同于继承,它是在对象层面实现功能扩展,而不是继承的类层面,因此说装饰器模式比继承更加灵活。大逻辑未变,本质是对方法的增强
- 先新建奶茶类
| 12
 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);
 }
 }
 
 
 | 
- 再建调料类
| 12
 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));
 }
 }
 
 | 
- 测试类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | @Testpublic 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());
 
 }
 
 | 
代理模式
- 静态代理
就是在继承的基础上对业务更改,在调用目标对象的方法之前或之后添加一些额外的逻辑
| 12
 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的动态代理要求被代理对象必须实现一个或多个接口。
| 12
 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),该方法定义了制作咖啡的整体流程,但其中的某些步骤(如加咖啡粉的具体方式)可以延迟到具体的子类中去实现。
| 12
 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的,它确保了算法的整体流程不会被子类改变,但研磨咖啡粉的具体步骤会根据子类的不同而有所不同。
模板方法模式的优点在于它提高了代码的复用性,减少了子类中的重复代码,同时保持了算法的整体结构不变。这种模式在需要定义固定操作序列,同时允许灵活定制某些步骤的场合非常有用。