zoukankan      html  css  js  c++  java
  • Spring中使用的设计模式

    创建型

    单例模式

    单例模式概念是一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。Spring中默认Bean创建可以认为是一种使用ConcurrentHashMap实现的特殊的单例模式

    @Bean

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	// Quick check for existing instance without full singleton lock
    	Object singletonObject = this.singletonObjects.get(beanName);
    	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    		singletonObject = this.earlySingletonObjects.get(beanName);
    		if (singletonObject == null && allowEarlyReference) {
    			synchronized (this.singletonObjects) {
    				// Consistent creation of early reference within full singleton lock
    				singletonObject = this.singletonObjects.get(beanName);
    				if (singletonObject == null) {
    					singletonObject = this.earlySingletonObjects.get(beanName);
    					if (singletonObject == null) {
    						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    						if (singletonFactory != null) {
    							singletonObject = singletonFactory.getObject();
    							this.earlySingletonObjects.put(beanName, singletonObject);
    							this.singletonFactories.remove(beanName);
    						}
    					}
    				}
    			}
    		}
    	}
    	return singletonObject;
    }
    

    ProxyFactoryBean

    private synchronized Object getSingletonInstance() {
    	if (this.singletonInstance == null) {
    		this.targetSource = freshTargetSource();
    		if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
    			// Rely on AOP infrastructure to tell us what interfaces to proxy.
    			Class<?> targetClass = getTargetClass();
    			if (targetClass == null) {
    				throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
    			}
    			setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
    		}
    		// Initialize the shared singleton instance.
    		super.setFrozen(this.freezeProxy);
    		this.singletonInstance = getProxy(createAopProxy());
    	}
    	return this.singletonInstance;
    }
    
    简单工厂模式

    Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象

    @Override
    public Object getBean(String name) throws BeansException {
    	return doGetBean(name, null, null, false);
    }
    
    工厂方法(Factory Method)

    定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

    在这里插入图片描述

    建造者模式

    建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。

    建造者模式结构
    在这里插入图片描述

    public final class BeanDefinitionBuilder {
    	public static BeanDefinitionBuilder genericBeanDefinition() {
    		return new BeanDefinitionBuilder(new GenericBeanDefinition());
    	}
    	
    	public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName, @Nullable String factoryMethodName) {
    		BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new RootBeanDefinition());
    		builder.beanDefinition.setBeanClassName(beanClassName);
    		builder.beanDefinition.setFactoryMethodName(factoryMethodName);
    		return builder;
    	}
    	
    	public static BeanDefinitionBuilder childBeanDefinition(String parentName) {
    		return new BeanDefinitionBuilder(new ChildBeanDefinition(parentName));
    	}
    
    	public BeanDefinitionBuilder setParentName(String parentName) {
    		this.beanDefinition.setParentName(parentName);
    		return this;
    	}
    
    	public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {
    		this.beanDefinition.setFactoryMethodName(factoryMethod);
    		return this;
    	}
    
    	public BeanDefinitionBuilder setFactoryMethodOnBean(String factoryMethod, String factoryBean) {
    		this.beanDefinition.setFactoryMethodName(factoryMethod);
    		this.beanDefinition.setFactoryBeanName(factoryBean);
    		return this;
    	}
    
    	public BeanDefinitionBuilder addConstructorArgValue(@Nullable Object value) {
    		this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
    				this.constructorArgIndex++, value);
    		return this;
    	}
    	...
    }
    
    原型模式

    原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能

    private synchronized Object newPrototypeInstance() {
    	// In the case of a prototype, we need to give the proxy
    	// an independent instance of the configuration.
    	// In this case, no proxy will have an instance of this object's configuration,
    	// but will have an independent copy.
    	ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory());
    
    	// The copy needs a fresh advisor chain, and a fresh TargetSource.
    	TargetSource targetSource = freshTargetSource();
    	copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());
    	if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
    		// Rely on AOP infrastructure to tell us what interfaces to proxy.
    		Class<?> targetClass = targetSource.getTargetClass();
    		if (targetClass != null) {
    			copy.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
    		}
    	}
    	copy.setFrozen(this.freezeProxy);
    
    	return getProxy(copy.createAopProxy());
    }
    

    结构型

    适配器模式

    适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。

    对象适配器
    在这里插入图片描述

    类适配器

    在这里插入图片描述

    SpringMVC中的适配器HandlerAdatper
    实现原理:

    HandlerAdatper根据Handler规则执行不同的Handler。

    实现过程:

    DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。
    HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。

    实现意义:

    HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。
    因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

    public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    
    	@Override
    	@Nullable
    	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    
    		return ((Controller) handler).handleRequest(request, response);
    	}
    }
    
    代理模式

    在代理模式(Proxy Pattern)中,一个类代表另一个类的功能

    final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    
    	private final AdvisedSupport advised;
    	private final Class<?>[] proxiedInterfaces;
    	private boolean equalsDefined;
    	private boolean hashCodeDefined;
    
    	@Override
    	public Object getProxy(@Nullable ClassLoader classLoader) {
    		if (logger.isTraceEnabled()) {
    			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    		}
    		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
    	}
    
    	@Override
    	@Nullable
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object oldProxy = null;
    		boolean setProxyContext = false;
    
    		TargetSource targetSource = this.advised.targetSource;
    		Object target = null;
    
    		try {
    			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    				// The target does not implement the equals(Object) method itself.
    				return equals(args[0]);
    			}
    			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
    				// The target does not implement the hashCode() method itself.
    				return hashCode();
    			}
    			else if (method.getDeclaringClass() == DecoratingProxy.class) {
    				// There is only getDecoratedClass() declared -> dispatch to proxy config.
    				return AopProxyUtils.ultimateTargetClass(this.advised);
    			}
    			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
    					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    				// Service invocations on ProxyConfig with the proxy config...
    				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    			}
    
    			Object retVal;
    
    			if (this.advised.exposeProxy) {
    				// Make invocation available if necessary.
    				oldProxy = AopContext.setCurrentProxy(proxy);
    				setProxyContext = true;
    			}
    
    			// Get as late as possible to minimize the time we "own" the target,
    			// in case it comes from a pool.
    			target = targetSource.getTarget();
    			Class<?> targetClass = (target != null ? target.getClass() : null);
    
    			// Get the interception chain for this method.
    			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
    			// Check whether we have any advice. If we don't, we can fallback on direct
    			// reflective invocation of the target, and avoid creating a MethodInvocation.
    			if (chain.isEmpty()) {
    				// We can skip creating a MethodInvocation: just invoke the target directly
    				// Note that the final invoker must be an InvokerInterceptor so we know it does
    				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
    				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    			}
    			else {
    				// We need to create a method invocation...
    				MethodInvocation invocation =
    						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    				// Proceed to the joinpoint through the interceptor chain.
    				retVal = invocation.proceed();
    			}
    
    			// Massage return value if necessary.
    			Class<?> returnType = method.getReturnType();
    			if (retVal != null && retVal == target &&
    					returnType != Object.class && returnType.isInstance(proxy) &&
    					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
    				// Special case: it returned "this" and the return type of the method
    				// is type-compatible. Note that we can't help if the target sets
    				// a reference to itself in another returned object.
    				retVal = proxy;
    			}
    			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
    				throw new AopInvocationException(
    						"Null return value from advice does not match primitive return type for: " + method);
    			}
    			return retVal;
    		}
    		finally {
    			if (target != null && !targetSource.isStatic()) {
    				// Must have come from TargetSource.
    				targetSource.releaseTarget(target);
    			}
    			if (setProxyContext) {
    				// Restore old proxy.
    				AopContext.setCurrentProxy(oldProxy);
    			}
    		}
    	}
    }
    
    class CglibAopProxy implements AopProxy, Serializable {
    	@Override
    	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);
    		}
    	}
    
    	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    		enhancer.setInterceptDuringConstruction(false);
    		enhancer.setCallbacks(callbacks);
    		return (this.constructorArgs != null && this.constructorArgTypes != null ?
    				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
    				enhancer.create());
    	}
    }
    
    装饰器模式

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构

    在这里插入图片描述

    HttpRequestWrapper不改变HttpRequest结构,但可以在此类中添加新功能

    public class HttpRequestWrapper implements HttpRequest {
    
    	private final HttpRequest request;
    
    	public HttpRequestWrapper(HttpRequest request) {
    		Assert.notNull(request, "HttpRequest must not be null");
    		this.request = request;
    	}
    
    	public HttpRequest getRequest() {
    		return this.request;
    	}
    
    	@Override
    	@Nullable
    	public HttpMethod getMethod() {
    		return this.request.getMethod();
    	}
    
    	@Override
    	public String getMethodValue() {
    		return this.request.getMethodValue();
    	}
    
    	@Override
    	public URI getURI() {
    		return this.request.getURI();
    	}
    
    	@Override
    	public HttpHeaders getHeaders() {
    		return this.request.getHeaders();
    	}
    
    }
    

    ServerHttpRequestDecorator不改变ServerHttpRequest结构,但可以在此类中添加新功能

    public class ServerHttpRequestDecorator implements ServerHttpRequest {
    
    	private final ServerHttpRequest delegate;
    
    
    	public ServerHttpRequestDecorator(ServerHttpRequest delegate) {
    		Assert.notNull(delegate, "Delegate is required");
    		this.delegate = delegate;
    	}
    
    
    	public ServerHttpRequest getDelegate() {
    		return this.delegate;
    	}
    
    
    	// ServerHttpRequest delegation methods...
    
    	@Override
    	public String getId() {
    		return getDelegate().getId();
    	}
    
    	@Override
    	@Nullable
    	public HttpMethod getMethod() {
    		return getDelegate().getMethod();
    	}
    
    	@Override
    	public String getMethodValue() {
    		return getDelegate().getMethodValue();
    	}
    
    	@Override
    	public URI getURI() {
    		return getDelegate().getURI();
    	}
    
    	@Override
    	public RequestPath getPath() {
    		return getDelegate().getPath();
    	}
    }
    

    行为型模式

    观察者模式

    建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者

    Spring中Observer模式常用的地方是listener的实现。如ApplicationListener

    public class SpringApplication {
        public ConfigurableApplicationContext run(String... args) {
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            // 启动事件
            listeners.starting(bootstrapContext, this.mainApplicationClass);
        }
    }
    
    策略模式

    策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)

    Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

    这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

    public interface Resource extends InputStreamSource {
    	boolean exists();
    
    	default boolean isReadable() {
    		return exists();
    	}
    
    	default boolean isOpen() {
    		return false;
    	}
    
    	default boolean isFile() {
    		return false;
    	}
    	URL getURL() throws IOException;
    	URI getURI() throws IOException;
    	File getFile() throws IOException;
    
    	default ReadableByteChannel readableChannel() throws IOException {
    		return Channels.newChannel(getInputStream());
    	}
    
    	long contentLength() throws IOException;
    	long lastModified() throws IOException;
    	Resource createRelative(String relativePath) throws IOException;
    	String getFilename();
    	String getDescription();
    
    }
    

    在这里插入图片描述

    模板方法模式(Template Method)

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    Spring几乎所有的外接扩展都采用这种模式。如JdbcTemplate、kafkaTemplate、RabbitmqTemplate

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    Template Method模式一般是需要继承的。这里想要探讨另一种对Template Method的理解。Spring中的JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。这可能是Template Method不需要继承的另一种实现方式吧

    public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    	public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
    		Assert.notNull(action, "Callback object must not be null");
    
    		Connection con = DataSourceUtils.getConnection(obtainDataSource());
    		try {
    			// Create close-suppressing Connection proxy, also preparing returned Statements.
    			Connection conToUse = createConnectionProxy(con);
    			return action.doInConnection(conToUse);
    		}
    		catch (SQLException ex) {
    			// Release Connection early, to avoid potential connection pool deadlock
    			// in the case when the exception translator hasn't been initialized yet.
    			String sql = getSql(action);
    			DataSourceUtils.releaseConnection(con, getDataSource());
    			con = null;
    			throw translateException("ConnectionCallback", sql, ex);
    		}
    		finally {
    			DataSourceUtils.releaseConnection(con, getDataSource());
    		}
    	}
    }
    
    解释器模式
    public class SpelExpressionParser extends TemplateAwareExpressionParser {
        private final SpelParserConfiguration configuration;
    
        public SpelExpressionParser() {
            this.configuration = new SpelParserConfiguration();
        }
    
        public SpelExpressionParser(SpelParserConfiguration configuration) {
            Assert.notNull(configuration, "SpelParserConfiguration must not be null");
            this.configuration = configuration;
        }
    
        public SpelExpression parseRaw(String expressionString) throws ParseException {
            return this.doParseExpression(expressionString, (ParserContext)null);
        }
    
        protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {
            return (new InternalSpelExpressionParser(this.configuration)).doParseExpression(expressionString, context);
        }
    }
    
    职责链模式
    public class DefaultWebFilterChain implements WebFilterChain {
    
    	private final List<WebFilter> allFilters;
    
    	private final WebHandler handler;
    
    	@Nullable
    	private final WebFilter currentFilter;
    
    	@Nullable
    	private final DefaultWebFilterChain chain;
    
    	public DefaultWebFilterChain(WebHandler handler, List<WebFilter> filters) {
    		Assert.notNull(handler, "WebHandler is required");
    		this.allFilters = Collections.unmodifiableList(filters);
    		this.handler = handler;
    		DefaultWebFilterChain chain = initChain(filters, handler);
    		this.currentFilter = chain.currentFilter;
    		this.chain = chain.chain;
    	}
    
    	private static DefaultWebFilterChain initChain(List<WebFilter> filters, WebHandler handler) {
    		DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, null, null);
    		ListIterator<? extends WebFilter> iterator = filters.listIterator(filters.size());
    		while (iterator.hasPrevious()) {
    			chain = new DefaultWebFilterChain(filters, handler, iterator.previous(), chain);
    		}
    		return chain;
    	}
    
    	private DefaultWebFilterChain(List<WebFilter> allFilters, WebHandler handler,
    			@Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain chain) {
    
    		this.allFilters = allFilters;
    		this.currentFilter = currentFilter;
    		this.handler = handler;
    		this.chain = chain;
    	}
    
    	public List<WebFilter> getFilters() {
    		return this.allFilters;
    	}
    
    	public WebHandler getHandler() {
    		return this.handler;
    	}
    
    
    	@Override
    	public Mono<Void> filter(ServerWebExchange exchange) {
    		return Mono.defer(() ->
    				this.currentFilter != null && this.chain != null ?
    						invokeFilter(this.currentFilter, this.chain, exchange) :
    						this.handler.handle(exchange));
    	}
    
    	private Mono<Void> invokeFilter(WebFilter current, DefaultWebFilterChain chain, ServerWebExchange exchange) {
    		String currentName = current.getClass().getName();
    		return current.filter(exchange, chain).checkpoint(currentName + " [DefaultWebFilterChain]");
    	}
    
    }
    

    https://mp.weixin.qq.com/s/gz2-izPrgW1AGbqqovT0cA
    https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/adapter.html
    https://mp.weixin.qq.com/s/3AWW1OX5KwMDX4CM4c39kg
    https://mp.weixin.qq.com/s/YarbOYMN5orskbgYiTQ-lA
    https://www.cnblogs.com/yuefan/p/3763898.html
    https://www.jianshu.com/p/09432974d7b9

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出。
  • 相关阅读:
    迅为4412开发板实验Menuconfig_Kconfig(上)
    迅为IMX6ULL开发板Linux 4G通信实验
    迅为IMX6ULL开发板Linux RS232/485驱动实验(下)
    迅为4412开发板实验_Makefile编译(下)
    迅为IMX6Q开发板QtE5.7编译(上)
    迅为干货 | iTOP-4418/6818移植mt6620热点
    UDT源码剖析(十一)之SendQueue And RecvQueue
    UDT源码剖析(九)之CCC
    UDT源码剖析(十)之Channel
    UDT源码剖析(八)之Cache
  • 原文地址:https://www.cnblogs.com/caozibiao/p/14300113.html
Copyright © 2011-2022 走看看