Spring中用到的设计模式

一、工厂模式 BeanFactory
1、简单工厂模型,是指由一个工厂对象决定创建哪一种产品类的实例,工厂类负责创建的对象较少,客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心
优点:
只需传入一个正确的参数,就可以获取你所需要的对象,无须知道其创建的细节
缺点:
工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则,不易于扩展过于复杂的产品结构
2、工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则
适用场景:
创建对象需要大量重复的代码,客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。一个类通过其子类来指定创建哪个对象。
优点:
用户只需关心所需产品对应的工厂,无须关心创建细节。
加入新产品符合开闭原则,提高了系统的可扩展性
缺点:
类的个数容易过多,增加了代码结构的复杂度。
增加了系统的抽象性和理解难度。
3、抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。属于创建型设计模式
适用场景:
客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点:
具体产品在应用层代码隔离,无须关心创建细节
将一个系列的产品族统一到一起创建
缺点:
规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
增加了系统的抽象性和理解难度

二、装饰器模式 BeanWrapper

三、代理模式 AopProxy
在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(ProxyPattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。代理模式属于结构型模式,有静态代理和动态代理。
Java中的代理分为三种角色:
代理类(ProxySubject)
委托类(RealSubject)
接口(Subject)
在这里插入图片描述
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。
静态代理:
Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委托类都需要修改。
动态代理:
Java中的动态代理依靠反射来实现,代理类和委托类不需要实现同一个接口。委托类需要实现接口,否则无法创建动态代理。代理类在JVM运行时动态生成,而不是编译期就能确定。Java动态代理主要涉及到两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。代理类需要实现InvocationHandler接口或者创建匿名内部类,而Proxy用于创建动态动态。
Proxy用来动态创建一个代理对象的类,它提供了许多的方法,但是用的最多的就是newProxyInstance 这个方法。一般情况下,使用下面的newProxyInstance方法。

//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)//loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
//interfaces: 一个Interface对象的数组,表示的是将要给需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样就能调用这组接口中的方法了
//h: 一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

InvocationHandler每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。 在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法。

//在代理实例上处理方法调用并返回结果
Object invoke(Object proxy,Method method,Object[] args)
//proxy:指代所代理的那个真实对象
//method:指代的是所要调用真实对象的某个方法的Method对象
//args:指代的是调用真实对象某个方法时接受的参数
/*** @author : lssffy* @Description : 接口类* @date : 2023/12/21 14:02*/
public interface HelloService {void sayHello();
}
/*** @author : lssffy* @Description : 实现* @date : 2023/12/21 14:03*/
public class HelloServiceImpl implements HelloService {public void sayHello() {System.out.println("Hello");}
}
/*** @author : lssffy* @Description : 代理类* @date : 2023/12/21 14:05*/
public class HelloServiceProxy {private HelloService helloService;public HelloServiceProxy(HelloService helloService) {this.helloService = helloService;}public Object getProxyInstance(){return Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before hello service");Object obj = method.invoke(helloService, args);System.out.println("after hello service");return obj;}});}
}
/*** @author : lssffy* @Description :* @date : 2023/12/21 10:21*/
public class Test {public static void main(String[] args) {try{HelloService helloService = new HelloServiceImpl();HelloService dynamic = (HelloService) new HelloServiceProxy(helloService).getProxyInstance();dynamic.sayHello();}catch (Exception e){e.printStackTrace();}}
}

在这里插入图片描述
CGLIB动态代理类的机制,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。” cglib 创建某个类A的动态代理类的模式 - 查找A上的所有非final的public类型的方法定义; - 将这些方法的定义转换成字节码; - 将组成的字节码转换成相应的代理的class对象; - 实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)

/*** @author : lssffy* @Description : 代理类* @date : 2023/12/21 14:42*/
public class CGlibClass {public void cglibProxy(){System.out.println("cglibProxy");}
}
/*** @author : lssffy* @Description : 代理实现类* @date : 2023/12/21 14:44*/
public class CGLIBHandler implements MethodInterceptor{public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("before execution");methodProxy.invokeSuper(o,objects);System.out.println("after execution");return null;}
}
/*** @author : lssffy* @Description :* @date : 2023/12/21 10:21*/
public class Test {public static void main(String[] args) {CGLIBHandler cglibHandler = new CGLIBHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(CGlibClass.class);enhancer.setCallback(cglibHandler);CGlibClass proxy =(CGlibClass) enhancer.create();proxy.cglibProxy();}
}

在这里插入图片描述
JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。
4、编译新生成的 Java 代码.class。
5、再重新加载到 JVM 中运行

CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高。

静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
JDK动态代理不要求代理类和委托类实现同一个接口,但是委托类需要实现接口,代理类需要实现InvocationHandler接口。
动态代理要求代理类InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步
新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开
闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,
无需修改代理类的代码。

优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强
缺点:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度

四、单例模式 ApplicationContext
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛。例如,国家主席、公司 CEO、部门经理等。在 J2EE 标准中,ServletContext、ServletContextConfig 等;在 Spring 框架应用中 ApplicationContext;数据库的连接池也都是单例形式。
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题。
优点:
没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
缺点:
类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能占着茅坑不拉屎
懒汉式单例是被外部类调用的时候内部类才会加载
注册式单例为登记式单例,就是将每一个实例都登记到某一个地方,使用唯一的标识获取实例。注册式单例有两种写法:一种为容器缓存,一种为枚举登记。
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
使用场景:
类初始化消耗资源较多
new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
构造函数比较复杂
循环体中生产大量对象时
浅拷贝:如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
深拷贝:一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象,深拷贝有两种方式,一种是跟浅拷贝一样实现 Cloneable 接口,另一种是实现 Serializable 接口,用序列化的方式来实现深拷贝

五、委派模式 DispatcherServlet

六、策略模式 HandlerMapping

七、适配器模式 HandlerApdapter

八、模板方法模式 JdbcTemplate

九、观察者模式 ContextLoaderListener