http://c.biancheng.net/view/1348.html
一个类只有一个实例,且该类能自行创建这个实例的一种模式。
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。
public class Hungry {
//私有化构造器,避免外部创建对象
private Hungry() {};
//定义一个静态成员属性,初始化实例对象
private static final Hungry instance=new Hungry();
/**
* 定义一个公共的静态方法,返回唯一的对象时实例
* @return
*/
public static Hungry getInstance() {
return instance;
}
}
public class Lazy {
//私有化构造器,防止外部调用
private Lazy() {};
/**
* 静态内部类中定义一个外部类对象的成员属性
* 为了解决线程安全问题
* synchronized代码块效率低
* 利用静态内部类在外部类初始化时不加载,调用静态内部类时才初始化
* static属性只加载一次
* @author 83998
*
*/
private static class Inner {
private static Lazy instance=new Lazy();
}
public static Lazy getInstance() {
return Inner.instance;
}
}
/**
* 单例模式:在多线程环境下,对外仅存在一个对象
* 1.提供私有的静态属性,存储对象的地址
* 2.构造器私有化,避免外部新建对象
* 3.提供公共的静态方法,获取属性
* 懒汉式套路
* @author Chill Lyn
*
*/
public class DoubleCheckedLocking {
// 1.提供私有的静态属性,存储对象的地址
// volatile避免多线程double check时出现指令重排
private static volatile DoubleCheckedLocking instance;
// 2.构造器私有化,避免外部新建对象
private DoubleCheckedLocking() {
}
// 3.提供公共的静态方法,获取属性
public static DoubleCheckedLocking get() {
// double check
if (null != instance) {
return instance;
}
synchronized (DoubleCheckedLocking.class) {
if (null == instance) {
instance = new DoubleCheckedLocking();
}
return instance;
}
}
}
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先尝试从单例对象缓存中读取
Object singletonObject = this.singletonObjects.get(beanName);
//如果没有,加锁
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//double check
singletonObject = this.earlySingletonObjects.get(beanName);
//如果没有就创建
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
所有的Filter,Servlet和Listener的实现类。
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.*;
public class CreateObject {
public static void main(String[] args) {
//1.直接new一个person对象
Person p1 = new Person("cl", 18, new Clothes("black"));
//2. clone()
try {
Person p4= (Person) p1.clone();
p4.setName("ddd");
System.out.println(p4.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Serializable, Cloneable {
private String name;
private transient Integer age;
private Clothes clothes;
/**
* deep clone;
*
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.setClothes((Clothes) person.getClothes().clone());
return person;
}
}
@Data
@AllArgsConstructor
class Clothes implements Serializable,Cloneable {
private String color;
/**
* shallow clone;
*
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
被创建的对象称为“产品”,创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”
/**
* 抽象工厂
*/
public interface PetFactory {
Pet getPet();
}
/**
* 抽象产品
*/
interface Pet {
void voice();
}
/**
* 具体工厂
*/
class DogFactory implements PetFactory {
@Override
public Pet getPet() {
return new Dog();
}
}
/**
* 具体工厂
*/
class CatFactory implements PetFactory {
@Override
public Pet getPet() {
return new Cat();
}
}
/**
* 具体产品
*/
class Dog implements Pet {
@Override
public void voice() {
System.out.println("wang");
}
}
/**
* 具体产品
*/
class Cat implements Pet {
@Override
public void voice() {
System.out.println("mew");
}
}
class Test {
public static void main(String[] args) {
DogFactory dogFactory = new DogFactory();
dogFactory.getPet().voice();
CatFactory catFactory = new CatFactory();
catFactory.getPet().voice();
}
}
FactoryBean相当于抽象工厂
public interface FactoryBean<T> {
@Nullable
T getObject() throws Exception;
}
FactoryBean的实现类相当于具体工厂
• java.lang.Object#toString() (在其子类中可以覆盖该方法)
• java.lang.Class#newInstance()
• java.lang.Integer#valueOf(String) (Boolean, Byte, Character,Short, Long, Float 和 Double与之类似)
• java.lang.Class#forName()
• java.lang.reflect.Array#newInstance()
• java.lang.reflect.Constructor#newInstance()
Tomcat在启动的过程中关于SeverSocket
的生成使用的是工厂生成的而不是自己new出来的。实际上这些socket就是后期tomcat接收请求时的socket。
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
Tomcat在处理请求的时候,当请求到达StandardWrapperValve
的invoke()
方法中,在调用指定的servelt实例之前,先创建对应servlet的filterChain
,代码如下:
//获取工厂实例
ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
//使用工厂实例创建 filterChain实例
ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。
public class PetFactory {
public static Pet getInstance(String className){
Pet pet=null;
try {
pet= (Pet) Class.forName(className).newInstance();
} catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) {
e.printStackTrace();
}
return pet;
}
}
interface Pet {
void voice();
}
class Cat implements Pet {
@Override
public void voice() {
System.out.println("mew");
}
}
class Dog implements Pet {
@Override
public void voice() {
System.out.println("wang");
}
}
Spring中BeanFactory
相当于抽象工厂,getBean(String name)
根据名字来获得bean对象,相当于简单工厂模式
工厂方法模式中考虑的是一类产品的生产。同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品。
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
import lombok.extern.java.Log;
/**
* 抽象工厂
*/
public interface Factory {
Phone createPhone();
PC createPC();
}
/**
* 具体工厂1
*/
class MiFactory implements Factory{
@Override
public Phone createPhone() {
return new MiPhone();
}
@Override
public PC createPC() {
return new MiBook();
}
}
/**
* 具体工厂2
*/
class AppleFactory implements Factory{
@Override
public Phone createPhone() {
return new Iphone();
}
@Override
public PC createPC() {
return new MacBook();
}
}
/**
* 抽象产品1
*/
interface Phone {
void info();
}
/**
* 具体产品1
*/
@Log
class MiPhone implements Phone{
@Override
public void info() {
log.info("MI 10");
}
}
/**
* 具体产品2
*/
@Log
class Iphone implements Phone{
@Override
public void info() {
log.info("iphoneX");
}
}
/**
* 抽象产品2
*/
interface PC {
void info();
}
/**
* 具体产品3
*/
@Log
class MiBook implements PC{
@Override
public void info() {
log.info("MiBook air");
}
}
/**
* 具体产品4
*/
@Log
class MacBook implements PC{
@Override
public void info() {
log.info("MacBook pro");
}
}
class Test2{
public static void main(String[] args) {
MiFactory miFactory=new MiFactory();
miFactory.createPC().info();
AppleFactory appleFactory=new AppleFactory();
appleFactory.createPhone().info();
}
}
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
各个具体的建造者相互独立,有利于系统的扩展。
客户端不必知道产品内部组成的细节,便于控制细节风险。
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,该模式会增加很多的建造者类。
建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log;
@Log
public class TestBuilder {
public static void main(String[] args) {
Director director=new Director(new MacBuilder());
log.info(director.build().getCpu().getInfo());
}
}
/**
* 产品
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class PC{
private Cpu cpu;
private Disk disk;
}
/**
* 组件
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class Cpu {
private String info;
}
/**
* 组件
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class Disk{
private String info;
}
/**
* 抽象建造者
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
abstract class PCBuilder{
protected PC pc=new PC();
public abstract void buildCpu();
public abstract void buildDisk();
public PC build(){
return pc;
}
}
/**
* 具体建造者
*/
class MacBuilder extends PCBuilder{
@Override
public void buildCpu() {
pc.setCpu(new Cpu("A13"));
}
@Override
public void buildDisk() {
pc.setDisk(new Disk("Toshiba"));
}
}
/**
* 指挥者
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class Director{
private PCBuilder pcBuilder;
public PC build(){
pcBuilder.buildCpu();
pcBuilder.buildDisk();
return pcBuilder.build();
}
}
建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。
UriComponent类中有很多属性
通过UriComponentBuilder这个类构建
• java.lang.StringBuilder#append()
• java.lang.StringBuffer#append()
• java.nio.ByteBuffer#put() (CharBuffer, ShortBuffer, IntBuffer,LongBuffer, FloatBuffer 和DoubleBuffer与之类似)
• java.sql.PreparedStatement
• java.lang.Appendable的所有实现类
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
import lombok.extern.java.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestProxy {
public static void main(String[] args) {
Proxy1 proxy1=new Proxy1();
proxy1.compute();
Add add=new Add();
Compute compute= (Compute) DynamicProxy1.getProxyInstance(add);
compute.compute();
}
}
/**
* 抽象主题
*/
interface Compute {
void compute();
}
/**
* 真实主题
*/
@Log
class Add implements Compute{
@Override
public void compute() {
log.info("1+1=2");
}
}
/**
* 代理类
*/
@Log
class Proxy1 implements Compute{
private Add add;
@Override
public void compute() {
add=new Add();
log.info("starting");
add.compute();
log.info("ending");
}
}
/**
* 动态代理类
*/
@Log
class DynamicProxy1{
public static Object getProxyInstance(Object target){
ClassLoader classLoader=target.getClass().getClassLoader();
Class<?>[] interfaces=target.getClass().getInterfaces();
InvocationHandler invocationHandler=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("starting");
method.invoke(target, args);
log.info("ending");
return null;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
其缺点是:对类适配器来说,更换适配器的实现过程比较复杂。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log;
/**
* 目标接口-电脑
*/
interface Computer{
void work();
}
/**
* 适配者-鼠标
*/
@Log
class Mouse{
public void mouseWork(){
log.info("鼠标工作了");
}
}
/**
* 适配器
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
class Adapter implements Computer{
private Mouse mouse;
@Override
public void work() {
mouse.mouseWork();
}
}
public class TestAdapt{
public static void main(String[] args) {
Computer computer=new Adapter(new Mouse());
computer.work();
}
}
SpringMVC请求处理流程中,HandlerAdapter适配Handler调用真正地处理方法。
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
HandlerAdapter 接口中有三个抽象方法
实现类有以下几种:
实现类中的handle方法则是调用handler的handleRequest方法
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
InputStreamReader implements Reader But constructs with inputStream.
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
由于抽象与实现分离,所以扩展能力强;
其实现细节对客户透明。
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log;
public class TestBridge {
public static void main(String[] args) {
DriverInfo driverInfo=new DriverInfo(new MysqlDriver());
driverInfo.connect();
}
}
/**
* 实现化对象
*/
interface Driver{
void connect();
}
/**
* 具体实现化对象1
*/
@Log
class MysqlDriver implements Driver{
@Override
public void connect() {
log.info("connected by mysql");
}
}
/**
* 具体实现化对象2
*/
@Log
class OracleDriver implements Driver{
@Override
public void connect() {
log.info("connected by Oracle");
}
}
/**
* 扩展抽象化对象(省略了抽象化对象)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
class DriverInfo{
private Driver driver;
public void connect(){
driver.connect();
}
}
DriverManager中的DriverInfo的属性之一为Driver接口,不同的数据库厂商通过实现Driver接口实现不同数据库的驱动,而程序操作驱动对象时,不用关系数据库是何种实现,只需要操作DriverInfo即可。
Handler负责日志输出,每个Handler中都会有一个Formatter引用,Formatter负责格式化日志。
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
采用装饰模式扩展对象的功能比采用继承方式更加灵活。
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log;
public class TestDecorator {
public static void main(String[] args) {
BufferedReader bufferedReader=new BufferedReader(new FileReader());
bufferedReader.readLine();
}
}
/**
* 抽象构件
*/
interface Reader{
void read();
}
/**
* 具体构件
*/
@Log
class FileReader implements Reader{
@Override
public void read() {
log.info("文件读取");
}
}
/**
* 装饰角色
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Log
class BufferedReader implements Reader{
private Reader reader;
@Override
public void read() {
reader.read();
}
public void readLine(){
read();
log.info("并且仅仅读取一行");
}
}
BufferedReader
Wrapper和Decorator相关的类
spring session核心原理
@EnableRedisHttpSession
导入RedisHttpSessionConfiguration
配置
RedisOperationSessionRepository
组件,redis操作session的crud封装类SessionRepositoryFilter
,session存储过滤器,每个请求都经过Filter
RedisOperationSessionRepository
Request
,Response
被包装成SessionRepositoryRequestWrapper
、SessionRepositoryResponseWrapper
request.getSession()
->wrapperRequest.getSession()
->从RedisOperationSessionRepository
中获取装饰器 | 功能 |
---|---|
SynchronizedCache | 同步锁,用于保证对指定缓存区的操作都是同步的 |
LoggingCache | 统计器,记录缓存命中率 |
BlockingCache | 阻塞器,基于key加锁,防止缓存穿透 |
ScheduledCache | 时效检查,用于验证缓存有效器,并清除无效数据 |
LruCache | 溢出算法,淘汰闲置最久的缓存 |
FifoCache | 溢出算法,淘汰加入时间最久的缓存 |
WeakCache | 溢出算法,基于java弱引用规则淘汰缓存 |
SoftCache | 溢出算法,基于java软引用规则淘汰缓存 |
PerpetualCache | 实际存储,内部采用HashMap进行存储 |
每个装饰器都会通过属性引用下一个装饰器,从而组成一个链条。缓存逻辑基于链条进行传递。
是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
不能很好地限制客户使用子系统类。
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
参考:https://blog.csdn.net/wwwdc1012/article/details/82729516
查看 org.springframework.jdbc.support.JdbcUtils
该工具类主要是对原生的 jdbc 进行了封装
MapperProxy
和Sqlsession
都是与数据库交互的门面,方便调用。org.apache.ibatis.session.Configuration
类中以 new
开头的方法,该类主要对一些创建对象的操作进行封装Tomcat 源码中大量使用了很多外观模式
org.apache.catalina.connector.Request
和 org.apache.catalina.connector.RequestFacade
这两个类都实现了 HttpServletRequest
接口
在 Request
中调用 getRequest()
实际获取的是 RequestFacade
的对象
protected RequestFacade facade = null;
public HttpServletRequest getRequest() {
if (facade == null) {
facade = new RequestFacade(this);
}
return facade;
}
在 RequestFacade
中再对认为是子系统的操作进行封装
public class RequestFacade implements HttpServletRequest {
/**
* The wrapped request.
*/
protected Request request = null;
@Override
public Object getAttribute(String name) {
if (request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
}
return request.getAttribute(name);
}
// ...省略...
}
SLF4J 是简单的日志外观模式框架,抽象了各种日志框架例如 Logback、Log4j、Commons-logging 和 JDK 自带的 logging 实现接口。它使得用户可以在部署时使用自己想要的日志框架。
运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的又橡来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式中存在以下两种状态:
包装类中的缓存值都属于享元模式。
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
public static Character valueOf(char c) {
if(c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
ReuseExecutor可重用执行器,底层是维护了一个Map
来捕捉到相同的SQL
,则直接取对应缓存的Statement
进行执行,所以对于相同SQL
(包括query
、update
),不同参数,则只进行一次预编译。就可以复用设置参数来执行。避免了频繁创建和销毁Statement对象,从而提升系统性能,这是享元思想的应用。
有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
设计较复杂,客户端需要花更多时间理清类之间的层次关系;
不容易限制容器中的构件;
不容易用继承的方法来增加构件的新功能;
(1) 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
(2) 安全方式:在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
Spring中的Composite相关的类都运用了组合模式
class WebMvcConfigurerComposite implements WebMvcConfigurer {
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configurePathMatch(configurer);
}
}
具体方法通过遍历集合来调用。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
它在父类中提取了公共的部分代码,便于代码复用。
部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
模板方法模式包含以下主要角色。
(1) 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。
① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
② 基本方法:是整个算法中的一个步骤,包含以下几种类型。
抽象方法:在抽象类中申明,由具体子类实现。
具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
(2) 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = 'erica'");
RestTemplate...
java.servlet.GenericServle
t定义了模板方法,同时,javax.servlet.http.HttpServlet
提供了默认的模板实现,HttpServlet的子类,不直接实现或者override了service方法,类似于doGet
,doPost
等等。
BaseExecutor是Executor的基本抽象实现,采用模板设计模式,里边提取了连接维护、一级缓存的公有功能,供子类复用。并放出了doQuery、doUpdate等抽象方法下放到子类做差异实现。
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
策略模式造成很多的策略类。
在Java的集合框架中,经常需要通过构造方法传入一个比较器Comparator,或者创建比较器传入Collections的静态方法中作为方法参数,进行比较排序等,使用的是策略模式。
在该比较架构中,Comparator就是一个抽象的策略;一个类实现该结构,并实现里面的compare方法,该类成为具体策略类;Collections类就是环境角色,他将集合的比较封装成静态方法对外提供api。
在创建线程池时,需要传入拒绝策略,当创建新线程使当前运行的线程数超过maximumPoolSize时,将会使用传入的拒绝策略进行处理。
AbortPolicy:直接抛出异常。
CallerRunsPolicy:只用调用者所在线程来运行任务。
DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。
/**
* Interface responsible for creating instances corresponding to a root bean definition.
*抽象策略接口
*/
public interface InstantiationStrategy {
/**
* Default object instantiation strategy for use in BeanFactories.
*具体策略
*/
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
/**
* 环境
*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/** Strategy for creating bean instances. */
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log;
public class TestCommand {
public static void main(String[] args) {
Guest guest=new Guest(new noodles(new Cooker()));
guest.order();
}
}
/**
* 抽象命令-菜单
*/
interface Menu{
void cook();
}
/**
* 接收者-厨师
*/
@Log
class Cooker{
public void cook(){
log.info("cooker is cooking");
}
}
/**
* 具体命令-面条
*/
@Log
@AllArgsConstructor
@NoArgsConstructor
@Data
class noodles implements Menu{
private Cooker cooker;
@Override
public void cook() {
log.info("Noodles!");
cooker.cook();
}
}
/**
* 调用者-顾客
*/
@Log
@AllArgsConstructor
@NoArgsConstructor
@Data
class Guest{
private Menu menu;
public void order(){
menu.cook();
}
}
为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
责任链模式是一种对象行为型模式,其主要优点如下。
其主要缺点如下:
Spring MVC DispatcherServlet中的HandlerExecutionChain
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
public abstract class Filter {
protected Filter() {
}
public abstract void doFilter(HttpExchange var1, Filter.Chain var2) throws IOException;
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的使用必然会增加系统的类与对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优点如下:
它的主要缺点如下:
import lombok.extern.java.Log;
import java.util.Observable;
import java.util.Observer;
public class TestObserver {
public static void main(String[] args) {
Teacher teacher=new Teacher();
Student s1=new Student();
Student s2=new Student();
teacher.addObserver(s1);
teacher.addObserver(s2);
teacher.speak("class is over!");
}
}
/**
* 具体目标-老师
*/
class Teacher extends Observable {
public void speak(String msg){
setChanged();
notifyObservers(msg);
}
}
/**
* 具体观察者- 学生
*/
@Log
class Student implements Observer{
/**
* This method is called whenever the observed object is changed. An
* application calls an Observable object's
* notifyObservers
method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the notifyObservers
*/
@Override
public void update(Observable o, Object arg) {
log.info("heard "+arg+"from"+o);
}
}
Spring 也为我们提供了很多内置事件,ContextRefreshedEvent
、ContextStartedEvent
、ContextStoppedEvent
、ContextClosedEvent
、RequestHandledEvent
。
事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent
。当监听的事件发生后该方法会被执行。
事件源:ApplicationContext,ApplicationContext
是 Spring 中的核心容器,在事件监听中 ApplicationContext
可以作为事件的发布者,也就是事件源。因为 ApplicationContext
继承自 ApplicationEventPublisher
。在 ApplicationEventPublisher
中定义了事件发布的方法:publishEvent(Object event)
事件管理:ApplicationEventMulticaster
,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext
发布的 Event 广播给它的监听器列表。
Tomcat的组件生命周期状态只要一变化,Tomcat就会通知改组件的所有的观察者,把状态变化通知到所有的观察者,看是否有观察者对相关组件的状态变化感兴趣。
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式是一种对象行为型模式,其主要优点如下。
降低了对象之间的耦合性,使得对象易于独立地被复用。
将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
java.util.Timer (所有scheduleXXX()方法)
java.util.concurrent.Executor#execute()
java.util.concurrent.ExecutorService (invokeXXX()和submit()方法)
java.util.concurrent.ScheduledExecutorService (所有scheduleXXX()方法)
java.lang.reflect.Method#invoke()
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式,其主要优点如下。
访问一个聚合对象的内容而无须暴露它的内部表示。
遍历任务交由迭代器完成,这简化了聚合类。
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
增加新的聚合类和迭代器类都很方便,无须修改原有代码。
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。
package iterator;
import java.util.*;
public class IteratorPattern
{
}
//抽象聚合
interface Aggregate
{
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
class ConcreteAggregate implements Aggregate
{
private List<Object> list=new ArrayList<Object>();
public void add(Object obj)
{
list.add(obj);
}
public void remove(Object obj)
{
list.remove(obj);
}
public Iterator getIterator()
{
return(new ConcreteIterator(list));
}
}
//抽象迭代器
interface Iterator
{
Object first();
Object next();
boolean hasNext();
}
//具体迭代器
class ConcreteIterator implements Iterator
{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list)
{
this.list=list;
}
public boolean hasNext()
{
if(index<list.size()-1)
{
return true;
}
else
{
return false;
}
}
public Object first()
{
index=0;
Object obj=list.get(index);;
return obj;
}
public Object next()
{
Object obj=null;
if(this.hasNext())
{
obj=list.get(++index);
}
return obj;
}
}
当需要为聚合对象提供多种遍历方式时。
当需要为遍历不同的聚合结构提供一个统一的接口时。
当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
java.util.Iterator
java.util.Enumeration
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。
访问者(Visitor)模式的主要缺点如下。
public class BeanDefinitionVisitor {
@Nullable
private StringValueResolver valueResolver;
/**
* Create a new BeanDefinitionVisitor, applying the specified
* value resolver to all bean metadata values.
* @param valueResolver the StringValueResolver to apply
*/
public BeanDefinitionVisitor(StringValueResolver valueResolver) {
Assert.notNull(valueResolver, "StringValueResolver must not be null");
this.valueResolver = valueResolver;
}
/**
* Traverse the given BeanDefinition object and the MutablePropertyValues
* and ConstructorArgumentValues contained in them.
* @param beanDefinition the BeanDefinition object to traverse
* @see #resolveStringValue(String)
*/
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
备忘录模式是一种对象行为型模式,其主要优点如下。
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
解释器模式是一种类行为型模式,其主要优点如下。
解释器模式的主要缺点如下。
Pattern
EpressionParser