当前位置: 首页 > news >正文

鞍山哪里做网站做网站有弹窗叫什么

鞍山哪里做网站,做网站有弹窗叫什么,标题优化怎么做,网站建设佰首选金手指六文章目录 前言正文一、项目总览二、核心代码展示2.1 自定义AbstractRoutingDataSource2.2 动态数据源DynamicDataSource2.3 动态数据源自动配置2.4 动态数据源上下文DynamicDataSourceContextHolder2.5 动态数据源修改注解定义2.6 修改切面DynamicDataSourceAspect2.7 动态数据… 文章目录 前言正文一、项目总览二、核心代码展示2.1 自定义AbstractRoutingDataSource2.2 动态数据源DynamicDataSource2.3 动态数据源自动配置2.4 动态数据源上下文DynamicDataSourceContextHolder2.5 动态数据源修改注解定义2.6 修改切面DynamicDataSourceAspect2.7 动态数据源工具类 三、start模块调试3.1 Mybatis-Plus配置3.2 application.yml配置3.3 启动类3.4 调试3.5 数据库sql 四、调试结果4.1 启动项目4.2 执行请求 前言 本文旨在SpringBoot3整合Mybatis-Plus实现动态数据源切换。 不使用Mybatis-Plus本身的依赖。自己动手造轮子。 之前有写过一个SpringBoot切换动态数据源的文章 https://blog.csdn.net/FBB360JAVA/article/details/124610140 本次使用了Java17SpringBoot3.0.2 Mybatis-Spring 3版本。并且自定义starter提供自定义注解使用切面实现切换数据源。 本文中对应的代码仓库如下 https://gitee.com/fengsoshuai/dynamic-datasource-mp-starter-demo 其中代码分支master是多数据源提供静态切换方法注解方式切换。 代码分支dev是动态多数据源在master的基础上提供运行时新增或修改或删除数据源。 以上只考虑单机模式如果是分布式项目建议使用中间件如redis等维护数据源信息或者创建独立项目专门提供数据源必要信息的接口 正文 一、项目总览 本次使用聚合maven项目内部包含两个模块 dynamic-datasource-mp-starter自定义starter实现数据源的基本功能包含切换数据源等。start启动测试模块整合mybatis-plus 提供代码配置以及提供测试接口 二、核心代码展示 注意本节代码展示只粘贴了dev分支的代码 2.1 自定义AbstractRoutingDataSource 如果不需要动态新增或修改数据源则不需要自定义这个类。直接使用spring-jdbc中的该类即可。 package org.feng.datasource.util;import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource;import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.datasource.AbstractDataSource; import org.springframework.jdbc.datasource.lookup.DataSourceLookup; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils;public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {Nullableprivate MapObject, Object targetDataSources;Nullableprivate Object defaultTargetDataSource;private boolean lenientFallback true;private DataSourceLookup dataSourceLookup new JndiDataSourceLookup();Nullableprivate MapObject, DataSource resolvedDataSources;Nullableprivate DataSource resolvedDefaultDataSource;public AbstractRoutingDataSource() {}public void setTargetDataSources(MapObject, Object targetDataSources) {this.targetDataSources targetDataSources;}public void setTargetDataSourcesAndRefresh(String newMerchant, DataSource newDataSource) {if (this.targetDataSources null) {throw new IllegalArgumentException(Property targetDataSources is required);}this.targetDataSources.put(newMerchant, newDataSource);if (this.resolvedDataSources null) {this.resolvedDataSources CollectionUtils.newHashMap(this.targetDataSources.size());}Object value this.targetDataSources.get(newMerchant);Object lookupKey this.resolveSpecifiedLookupKey(newMerchant);DataSource dataSource this.resolveSpecifiedDataSource(value);this.resolvedDataSources.put(lookupKey, dataSource);}public void removeDataSourcesByMerchant(String newMerchant) {if (this.targetDataSources null) {throw new IllegalArgumentException(Property targetDataSources is required);}this.targetDataSources.remove(newMerchant);this.resolvedDataSources.remove(newMerchant);}public void setDefaultTargetDataSource(Object defaultTargetDataSource) {this.defaultTargetDataSource defaultTargetDataSource;}public void setLenientFallback(boolean lenientFallback) {this.lenientFallback lenientFallback;}public void setDataSourceLookup(Nullable DataSourceLookup dataSourceLookup) {this.dataSourceLookup dataSourceLookup ! null ? dataSourceLookup : new JndiDataSourceLookup();}public void afterPropertiesSet() {if (this.targetDataSources null) {throw new IllegalArgumentException(Property targetDataSources is required);} else {this.resolvedDataSources CollectionUtils.newHashMap(this.targetDataSources.size());this.targetDataSources.forEach((key, value) - {Object lookupKey this.resolveSpecifiedLookupKey(key);DataSource dataSource this.resolveSpecifiedDataSource(value);this.resolvedDataSources.put(lookupKey, dataSource);});if (this.defaultTargetDataSource ! null) {this.resolvedDefaultDataSource this.resolveSpecifiedDataSource(this.defaultTargetDataSource);}}}protected Object resolveSpecifiedLookupKey(Object lookupKey) {return lookupKey;}protected DataSource resolveSpecifiedDataSource(Object dataSourceObject) throws IllegalArgumentException {if (dataSourceObject instanceof DataSource dataSource) {return dataSource;} else if (dataSourceObject instanceof String dataSourceName) {return this.dataSourceLookup.getDataSource(dataSourceName);} else {throw new IllegalArgumentException(Illegal data source value - only [javax.sql.DataSource] and String supported: dataSourceObject);}}public MapObject, DataSource getResolvedDataSources() {Assert.state(this.resolvedDataSources ! null, DataSources not resolved yet - call afterPropertiesSet);return Collections.unmodifiableMap(this.resolvedDataSources);}Nullablepublic DataSource getResolvedDefaultDataSource() {return this.resolvedDefaultDataSource;}public Connection getConnection() throws SQLException {return this.determineTargetDataSource().getConnection();}public Connection getConnection(String username, String password) throws SQLException {return this.determineTargetDataSource().getConnection(username, password);}public T T unwrap(ClassT iface) throws SQLException {return iface.isInstance(this) ? (T) this : this.determineTargetDataSource().unwrap(iface);}public boolean isWrapperFor(Class? iface) throws SQLException {return iface.isInstance(this) || this.determineTargetDataSource().isWrapperFor(iface);}protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, DataSource router not initialized);Object lookupKey this.determineCurrentLookupKey();DataSource dataSource this.resolvedDataSources.get(lookupKey);if (dataSource null (this.lenientFallback || lookupKey null)) {dataSource this.resolvedDefaultDataSource;}if (dataSource null) {throw new IllegalStateException(Cannot determine target DataSource for lookup key [ lookupKey ]);} else {return dataSource;}}Nullableprotected abstract Object determineCurrentLookupKey(); } 2.2 动态数据源DynamicDataSource package org.feng.datasource;import org.feng.datasource.util.AbstractRoutingDataSource;/*** 动态数据源** author feng*/ public class DynamicDataSource extends AbstractRoutingDataSource {Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceKey();} }2.3 动态数据源自动配置 package org.feng.datasource.config;import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.feng.datasource.DynamicDataSource; import org.feng.datasource.constant.DataSourceConstant; import org.feng.datasource.entity.DataSourceProperties; import org.feng.datasource.entity.SpringDataSourceProperties; import org.feng.datasource.util.DataSourceUtil; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary;import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;/*** 动态数据源自动配置** author feng*/ Data Slf4j AutoConfiguration ConfigurationPropertiesScan({org.feng.datasource.entity}) public class DynamicDataSourceAutoConfiguration {Resourceprivate SpringDataSourceProperties springDataSourceProperties;PrimaryBeanpublic DataSource dynamicDataSource() {MapObject, Object dataSourceMap new HashMap(16);MapString, DataSourceProperties dataSourcePropertiesMap springDataSourceProperties.getConfigMap();dataSourcePropertiesMap.forEach((merchant, properties) - dataSourceMap.put(merchant, DataSourceUtil.dataSource(properties)));// 设置动态数据源DynamicDataSource dynamicDataSource new DynamicDataSource();dynamicDataSource.setTargetDataSources(dataSourceMap);// 设置默认数据源dynamicDataSource.setDefaultTargetDataSource(dataSourceMap.get(DataSourceConstant.MASTER));return dynamicDataSource;}PostConstructprivate void init() {MapString, DataSourceProperties configMap springDataSourceProperties.getConfigMap();configMap.forEach((k, v) - {log.info(merchantKey {}, config {}, k, v);});} } 2.4 动态数据源上下文DynamicDataSourceContextHolder package org.feng.datasource;import lombok.extern.slf4j.Slf4j; import org.feng.datasource.constant.DataSourceConstant;import java.util.Optional;/*** 动态数据源上下文保持类** version v1.0* author: fengjinsong* date: 2022年05月05日 15时19分*/ Slf4j public class DynamicDataSourceContextHolder {/*** 动态数据源的上下文*/private static final ThreadLocalString DATASOURCE_CONTEXT_MERCHANT_HOLDER new InheritableThreadLocal();/*** 切换数据源** param merchant 租户Key*/public static void setDataSourceKey(String merchant) {log.info(切换数据源merchant 为 {}, merchant);DATASOURCE_CONTEXT_MERCHANT_HOLDER.set(merchant);}/*** 获取当前数据源名称** return 当前数据源名称*/public static String getDataSourceKey() {return Optional.ofNullable(DATASOURCE_CONTEXT_MERCHANT_HOLDER.get()).orElse(DataSourceConstant.MASTER);}/*** 删除当前数据源名称*/public static void removeDataSourceKey() {DATASOURCE_CONTEXT_MERCHANT_HOLDER.remove();} }2.5 动态数据源修改注解定义 package org.feng.datasource.annotation;import org.feng.datasource.constant.DataSourceConstant;import java.lang.annotation.*;/*** 改变数据源注解** author feng*/ Documented Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface ChangeDataSource {String merchant() default DataSourceConstant.MASTER; } 2.6 修改切面DynamicDataSourceAspect package org.feng.datasource.aop;import jakarta.annotation.PostConstruct; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.feng.datasource.DynamicDataSourceContextHolder; import org.feng.datasource.annotation.ChangeDataSource; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** 动态数据源切面** author feng*/ Aspect Component Order(1) Slf4j public class DynamicDataSourceAspect {SneakyThrowsBefore(annotation(org.feng.datasource.annotation.ChangeDataSource))public void changeDataSource(JoinPoint joinPoint) {log.info(开始切换数据源...);// 获取方法名参数String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();Class?[] paramsTypes new Class[args.length];if(args.length 0) {for (int i 0; i args.length; i) {paramsTypes[i] args[i].getClass();}}// 获取注解Class? aClass joinPoint.getTarget().getClass();Method currentMethod aClass.getDeclaredMethod(methodName, paramsTypes);ChangeDataSource changeDataSource currentMethod.getDeclaredAnnotation(ChangeDataSource.class);// 获取租户String merchant changeDataSource.merchant();log.info(当前数据源租户切换为{}, merchant);// 切换数据源DynamicDataSourceContextHolder.setDataSourceKey(merchant);}After(annotation(org.feng.datasource.annotation.ChangeDataSource))public void changeDataSourceOver() {log.info(释放数据源...);DynamicDataSourceContextHolder.removeDataSourceKey();}PostConstructprivate void init() {log.info(注册动态数据源切换注解);} } 2.7 动态数据源工具类 提供动态新增数据源删除数据源的方法。 因为本身是维护map所以同时支持修改根据merchant来新增或修改或删除 package org.feng.datasource.util;import lombok.extern.slf4j.Slf4j; import org.feng.datasource.DynamicDataSource; import org.feng.datasource.entity.DataSourceProperties; import org.springframework.beans.BeansException; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component;import javax.sql.DataSource;/*** 数据源工具** author feng*/ Slf4j Component public class DataSourceUtil implements ApplicationContextAware {private static ApplicationContext applicationContext;/*** 设置数据源用于动态新增修改数据源** param newMerchant 租户* param dataSourceProperties 数据源属性对象*/public static void setNewDynamicDataSource(String newMerchant, DataSourceProperties dataSourceProperties) {log.info(merchantKey {}, config {}, newMerchant, dataSourceProperties);DynamicDataSource dataSource applicationContext.getBean(DynamicDataSource.class);dataSource.setTargetDataSourcesAndRefresh(newMerchant, dataSource(dataSourceProperties));}/*** 移除数据源** param newMerchant 租户*/public static void removeDynamicDataSource(String newMerchant) {log.info(正在移除数据源 merchantKey {}, newMerchant);DynamicDataSource dataSource applicationContext.getBean(DynamicDataSource.class);dataSource.removeDataSourcesByMerchant(newMerchant);}Overridepublic void setApplicationContext(NonNull ApplicationContext applicationContext) throws BeansException {DataSourceUtil.applicationContext applicationContext;}/*** 构建数据源** param dataSourceProperties 数据源属性配置* return 数据源*/public static DataSource dataSource(DataSourceProperties dataSourceProperties) {return DataSourceBuilder.create().driverClassName(dataSourceProperties.getDriverClassName()).url(dataSourceProperties.getJdbcUrl()).username(dataSourceProperties.getUsername()).password(dataSourceProperties.getPassword()).build();} } 三、start模块调试 3.1 Mybatis-Plus配置 package org.feng.start.config;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import lombok.SneakyThrows; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver;import javax.sql.DataSource;/*** mybatis-plus配置** author feng*/ Configuration public class MybatisPlusConfig {BeanPrimarySneakyThrowspublic SqlSessionFactory sqlSessionFactory(Autowired DataSource dataSource) {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();MybatisConfiguration configuration new MybatisConfiguration();sqlSessionFactoryBean.setConfiguration(configuration);// 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射configuration.setMapUnderscoreToCamelCase(true);// 如果查询结果中包含空值的列则 MyBatis 在映射的时候不会映射这个字段configuration.setCallSettersOnNulls(true);// 日志configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);// 实体包sqlSessionFactoryBean.setTypeAliasesPackage(org.feng.start.entity);// mapper.xml位置ResourcePatternResolver resourceResolver new PathMatchingResourcePatternResolver();Resource[] resources resourceResolver.getResources(classpath*:mapper/**Mapper.xml);sqlSessionFactoryBean.setMapperLocations(resources);// 设置数据源sqlSessionFactoryBean.setDataSource(dataSource);// 设置GlobalConfigGlobalConfigUtils.setGlobalConfig(configuration, globalConfig());// 返回SqlSessionFactoryreturn sqlSessionFactoryBean.getObject();}private GlobalConfig globalConfig() {GlobalConfig globalConfig new GlobalConfig();GlobalConfig.DbConfig dbConfig new GlobalConfig.DbConfig();dbConfig.setIdType(IdType.AUTO);globalConfig.setDbConfig(dbConfig);return globalConfig;} } 3.2 application.yml配置 spring:datasource:config-map:master:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://192.168.110.68:3306/db_master?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8username: rootpassword: root123456slave1:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://192.168.110.68:3306/db_slave1?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8username: rootpassword: root123456 # slave2: # driver-class-name: com.mysql.cj.jdbc.Driver # jdbc-url: jdbc:mysql://192.168.110.68:3306/db_slave2?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8 # username: root # password: root123456server:port: 80servlet:context-path: /dynamic_datasource 3.3 启动类 package org.feng.start;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.transaction.annotation.EnableTransactionManagement;MapperScan(basePackages {org.feng.start.mapper}) EnableAspectJAutoProxy(exposeProxy true) EnableTransactionManagement SpringBootApplication(exclude DataSourceAutoConfiguration.class, scanBasePackages {org.feng.datasource, org.feng.start}) public class StartApplication {public static void main(String[] args) {SpringApplication.run(StartApplication.class, args);}}3.4 调试 定义控制器提供接口。 package org.feng.start.controller;import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.feng.datasource.DynamicDataSourceContextHolder; import org.feng.datasource.annotation.ChangeDataSource; import org.feng.datasource.entity.DataSourceProperties; import org.feng.datasource.util.DataSourceUtil; import org.feng.start.entity.Student; import org.feng.start.service.IStudentService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** 学生控制器-测试切换数据源** author feng*/ Slf4j RequestMapping(/student) RestController public class StudentController {Resourceprivate IStudentService studentService;GetMapping(/list/{merchant})public ListStudent list(PathVariable String merchant) {try {// 切换数据源DynamicDataSourceContextHolder.setDataSourceKey(merchant);// 查库ListStudent resultList studentService.list();log.info(查询结果{}, resultList);return resultList;} finally {// 清除当前数据源DynamicDataSourceContextHolder.removeDataSourceKey();}}GetMapping(/listStu/master)public ListStudent listStu() {// 查库ListStudent resultList studentService.list();log.info(查询结果{}, resultList);return resultList;}ChangeDataSource(merchant slave1)GetMapping(/listStu/slave1)public ListStudent listStu1() {// 查库ListStudent resultList studentService.list();log.info(查询结果{}, resultList);return resultList;}ChangeDataSource(merchant slave2)GetMapping(/listStu/slave2)public ListStudent listStu2() {// 查库ListStudent resultList studentService.list();log.info(查询结果{}, resultList);return resultList;}GetMapping(/listStu/newDataSource)public ListStudent newDataSource() {String merchant slave2;DataSourceProperties dataSourceProperties newSlave2();DataSourceUtil.setNewDynamicDataSource(merchant, dataSourceProperties);// 切换数据源DynamicDataSourceContextHolder.setDataSourceKey(merchant);// 查库ListStudent resultList studentService.list();log.info(查询结果{}, resultList);DynamicDataSourceContextHolder.removeDataSourceKey();return resultList;}private DataSourceProperties newSlave2() {DataSourceProperties dataSourceProperties new DataSourceProperties();dataSourceProperties.setDriverClassName(com.mysql.cj.jdbc.Driver);dataSourceProperties.setJdbcUrl(jdbc:mysql://192.168.110.68:3306/db_slave2?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8);dataSourceProperties.setUsername(root);dataSourceProperties.setPassword(root123456);return dataSourceProperties;} } 3.5 数据库sql 按照自己的需要创建多个数据库并创建好自己使用的数据表即可。 本文中测试使用的如下 CREATE TABLE tb_student (student_name varchar(100) DEFAULT NULL,id bigint unsigned NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;四、调试结果 4.1 启动项目 启动项目可以看到注册了两个数据源。 4.2 执行请求 Get 请求 http://localhost:80/dynamic_datasource/student/listStu/master 响应 [{id: 1,studentName: master_student2312s} ]Get 请求 http://localhost:80/dynamic_datasource/student/listStu/slave1 响应 [{id: 1,studentName: slave1_studew12321} ]Get 请求 http://localhost:80/dynamic_datasource/student/listStu/slave2 因为此时还没有slave2所以默认请求的是master的数据源查询结果如下 [{id: 1,studentName: master_student2312s} ]Get 请求 http://localhost:80/dynamic_datasource/student/listStu/newDataSource 新增数据源代码中写的是新增slave2的数据源 日志如下 查询结果如下 [{id: 1,studentName: slave2_213dqwa} ]Get 请求 http://localhost:80/dynamic_datasource/student/listStu/slave2 此时已经新增了slave2数据源因此能够切换到slave2中查询结果如下 [{id: 1,studentName: slave2_213dqwa} ]
http://www.zqtcl.cn/news/137389/

相关文章:

  • 深圳网站制作公司哪家好艺考培训学校
  • 潍坊网站的公司电话html网站开发基础
  • 网站模板样式做地图特效的网站
  • 商标查询官方网站有没有免费找客户的软件
  • 网站开发及服务合同行业网站名称
  • 网站建设费包括什么建筑设计领域
  • 网站建设 信科网络建行网站会员注册用户名
  • 网站建设的什么是开发实施注意什么网站开发实用技术pdf
  • 网站设计的资质叫什么贵阳网站建设咨询
  • 郑州哪家公司做网站怎么做自己的销售网站
  • 北大青鸟教网站开发吗中国电信 网站备案
  • 网站目录结构图wordpress ftp连接不上
  • 使用php做的网站有哪些网站备案密码重置申请表
  • php网站开发好找工作吗一叶子电子商务网站建设策划书
  • 运营好还是网站开发好购买域名后怎样建公司官网
  • 优秀设计网站推荐晋江市住房和城乡建设局网站
  • 杭州市区网站制作单位青海公路建设服务网站
  • 大型门户网站建设美丽杭州房价
  • 素材下载解析接口网站开发网站关键词热度
  • 山东seo推广网站建设新乡手机网站建设官网
  • 网站定制公司报价wordpress清新模板下载
  • 斗鱼网站开发是用什么语言东莞人才网智通
  • 淘宝上网站建设为啥这么便宜自己如何建设个网站
  • 做网站判多少年滦南网站建设
  • 网站开发难不难学做网站会提供源代码吗
  • 一个学校怎么制作网站阿里云服务器登录
  • 网站建设哪家合适对网站建设服务公司的看法
  • 网站留住访客柳州正规网站建设加盟
  • 网站照片要求现在百度怎么优化排名
  • 国外经典平面设计网站60平米一居室装修价格