赞
踩
项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KefEFxpz-1617086101760)(./SpringBoot项目结构.bmp)]
入口类
需要放在包的最外层,以便能够扫描到所有子包中的类
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RC1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--用来提供相关的Maven默认依赖,使用后,常用的依赖包可以省去version标签-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--该依赖中包含多个Web依赖,如spring-web、spring-webmvc等-->
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
### mysql连接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
开启监控功能的方式有多种:
@Configuration:Spring中有很多XML配置文件,文件中会配置很多bean。在类上添加@Configuration
注解,可以理解为该类变成了一个XML配置文件。(配置类)
@Bean:等同于XML配置文件中的<bean>
配置。Spring Boot会把加上该注解的方法返回值装载进Spring IoC容器,方法的名称对应<bean>
标签的id属性值。
@NoRepositoryBean:使用此注解表明此接口不是一个Repository Bean
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
name
属性指定。name
属性,当注解写在字段上时,就默认读取字段名进行查找;required
属性为false;如果想使用名称装配,结合@Qulifier
注解使用在使用Spring Boot框架进行页面设计时,一般都会选用Thymeleaf模板。
常用表达式:
常用标签:
常用函数:
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.properties添加Thymeleaf配置
#thymeleaf配置
#模板的设置,支持HTML、XML、TEXT、JAVASCRIPT等
spring.thymeleaf.mode=HTML5
#编码,可不用配置
spring.thymeleaf.encoding=UTF-8
#内容类别,可不用配置
spring.thymeleaf.content-type=text/html
#开发内容为false,避免修改模板还需要重启服务器
spring.thymeleaf.cache=false
#配置模板路径,默认为templates,可以不用配置
#spring.thymeleaf.prefix=classpath:/templates
注意:Thymeleaf模板引擎默认会读取项目资源文件夹resource下的templates目录,这个目录是用来存放HTML文件的。如果添加了Thymeleaf依赖,而没有进行任何配置,或者添加默认目录,启动应用时就会报错。
控制层
Spring支持编程式事务管理(也称编码式事务),也支持声明式事务管理。在大多情况下,声明式事务管理比编程式事务管理更好用。Spring通过SpringAOP框架支持声明式事务管理。
数据访问的内容有很多,包括JDBC、JPA、Hibernate、分布式事务等。Spring在不同的事务管理API上定义了一个抽象层PlatformTransactionManager。
Spring并不直接管理事务,而是提供了许多内置事务管理器实现,常见的有DataSourceTransactionManager、JdoTransactionManager、JpaTransactionManager以及HibernateTransactionManager等。
Spring配置文件中关于事务配置总是由三部分组成,分别是DataSource、TransactionManger和代理机制。
无论哪种配置方式,一般变化的只是代理机制部分,DataSource和TransactionManager这两部分只会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实现为SessionFactory,TransactionManager的实现为HibernateTransactionManager
注意:@Transactional注解来自org.springframework.transaction.annotation
。Spring提供了@EnableTransactionManagement
注解在配置类上来开启声明式事物的支持。使用@EnableTransactionManagement
后,Spring容器会自动扫描注解@Transactional
的方法和类。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播;
例如,方法可能继续在现有事务中运行,也可能开启新事务,并在自己的事务中运行。
@Transacional
的propagation
属性中指定,Spring定义了7种传播行为,具体如下。
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务;如果已经存在一个事务,就加入这个事务中 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRED_NEW | 新建事务,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NOT_SUPPORT | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行操作,如果当前存在事务,就抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与PROPAGATION_REQUIRED类似的操作 |
isolation
中定义隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用数据库默认的事务隔离级别,另外4个与JDBC的隔离级别相对应 |
ISOLATION_READ_UNCOMMITTED | |
ISOLATION_READ_COMMITTED | 新建当前的事务,如果当前没有事务,就抛出异常 |
ISOLATION_REPATABLE_READ | 新建事务,如果当前存在事务,就把当前事务 |
ISOLATION_SERIALIZABLE | 以非事务方式执行操作。如果存在当前事务,就把当前事务挂起 |
timeout
属性设置事务过期时间;通过指定readOnly
指定当前事务是否是只读事务;通过RollbackFor(noRollbackFor)
指定哪个或这哪些事务可以引起(或不可以引起)事务回滚。Spring Boot开启事务很简单,只需要一个注解
@Transactional
就可以了,因为Spring Boot中已经默认对JPA、JDBC、Mybatis开启了事务,引入它们的依赖时,事务就默认开启。Spring Boot用于配置事务的类为
TransactionAutoConfiguration
,此配置类依赖于JtaAutoConfiguration
和DataSourceTransactionManagerAutoConfiguration
,而DataSourceTransactionManagerAutoConfiguration
开启了对声明式事务的支持,所以无需显示开启使用@EnableTransactionManager
@Transactional
可以注解在类上或者方法上。注解在类上,是此类的所有public方法都是开启事务的;如果类级别和方法级别同时使用了@Transactional
注解,就使用方法级别注解覆盖类级别注解。实现Filter接口
@WebFilter:用于将一个类声明为过滤器,该注解将会在应用部署时被容器处理,容器根据具体的数项配置将相应的类部署为过滤器;该注解常见的属性有filerName、urlPattern、value
等。filerName
属性用于指定过滤器的name,urlPattern
属性用于指定一组过滤器的URL匹配模式。value
属性等价于urlPattern
,但是两者不可以同时使用。
在入口类中添加注解@ServletComponetScan
@ServletComponetScan:使用该注解后,Servlet、Filter、Listener可以直接通过@WebServlet
、@WebFilter
、@WebListener
注解自动注册,无需其他代码。
Redis是一个基于内存的单线程高性能key-value型数据库。
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.properties配置
## Redis缓存配置
## 默认名Redis的数据库位db0
spring.redis.database=0
## 服务器地址,默认为localhost
spring.redis.host=localhost
## 链接端口,默认为6379
spring.redis.port=6379
## redis的默认密码为空
spring.redis.passoword=
Spring提供了模板类:RedisTemplate、StringRedisTemplate(只针对键值是字符串的数据进行操作)等。在应用启动时,Spring会初始化这两个类,通过@Resource
注解注入。
RedisTemplate和StringRedisTemplate除了提供opsForValue方法用来操作简单属性数据之外,还提供了以下数据访问方法。
将数据存放到Redis中时,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用 JdkSerializationRedisSerializer,而 StringRedisTemplate 默认使用的是 StringRedisSerializer
例子:利用Listener中的初始化上下文监听器,在init方法中加载数据库中的所有用户数据,并存放在Redis缓存中。因为用户的数据变动不大,适合存放在缓存中,当应用需要用户数据时,在Redis缓存中获取,提高数据的访问速度。
redisTemplate.opsForList().leftPushAll(String,Object);
//查询缓存中所有的用户数据,若String键不存在,则会创建该键及其关联的List,之后在将参数中的List从左到右依次插入。
redisTemplate.opsForList().range(Object,int,int);
//取链表的全部元素,其中用 0 表示第一个元素, -1 表示最后一个元素。
Log4j有三个主要组件,分别是Loggers(记录器)、Appenders(输出源)和Layouts(布局),这三个组件可以简单地理解为 日志类别 、日志要输出的地方 和 日志以哪种形式输出。
Log4j支持XML文件配置方式和 log4j.properties 格式的配置文件
Spring Boot 默认使用 Logback 日志框架来记录日志,并用 Info 级别输出,所以在引入 Log4j2 之前,要排除该包的依赖,在引入 Log4j 的依赖。
具体做法:找到
pom.xml
文件中 的spring-boot-starter-web
依赖,使用exclusion
标签排除Logback
,具体如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排查Spring Boot默认日志--> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!--log4j2--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
添加 Log4j 配置
在 application.propertoes中添加配置信息
## Log4j配置
logging.config=classpath:log4j2.xml
创建、配置log4j2.xml
文件
<Console>
:指定控制台输出<PatternLayout>
:控制日志的输出格式<RollingFile>
:fileName 用于定义日志的数据路径,filePattern 用于定义日志的匹配方式<Filters>
:日志过滤策略,<ThresholdFilter>
用于指定日志信息的最低默认输出级别,默认为DEBUG//...
Logger logger = LogManager.getLogger(this.getClass());
//...
引入依赖
<!-- quarta定时器 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>auartz</artifactId>
<version>2.23</version>
</dependency>
定时器配置文件
使用XML文件配置方式
/src/main/resources
目录下新建配置文件spring-mvc-xml
<import resource="spring-auartz.xml" />
<import>
:利用 import 标签导入定时器的配置文件,改标签可以根据具体业务分离配置文件
spring-auartz.xml
使用注解的方式
创建定时器类
基于注解方式:
@Component
@Configurable
@EnableScheduling
public class SendMailQuartz {
//日志对象
private static final Loggers logger = LoggerManager.getLogger(SendMailQuzrtz.class);
//每5秒执行一次
@Scheduled(corn="* /5 * * * * *")
}
@EnableScheduling
来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled
声明这是一个计划任务cron
表达式里写执行的时机Spring Boot 扫描配置文件
在入口类中添加 @ImportResource
注解
@SpringBootApplicatiom
@ServletComponentScan
@ImportResource(locations{"classpath:spring-mvc.xml"})
public class MySpringBoot{ ... }
@ImportResource:导入资源配置文件,让Spring Boot可以读取到,类似于 XML 配置文件中的<impotr>
标签
JDK的API:JavaMail
Spring:JavaMailSender -> Spring Boot的starter模块中已为此提供了自动化配置
引入依赖
<!-- mail-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在application.properties中添加Emial配置
## Mail 邮件设置
##邮箱主机
spring.mail.host=smtp.qq.com
##用户名
spring.mail.username=XXX@qq.com
##设置的密码
spring.mail.password=*****
##默认编码
spring.mail.default-encoding=UTF-8
spring.mail.properites.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
发送邮件
@Service public class SendMail { @Autowired JavaMailSender mailSender; @Value("${spring.mail.username}") private String from; //日志 public static final Logger logger = LogManager.getLogger(SendMail.class); public boolean send(User user){ try{ MimeMessage mimeMessage = this.mailSender.createMimeMessage(); MimeMessageHelper message = new MimeMessageHelper(mimeMessage); //邮件发送方 message.setFrom(from); //邮件主题 message.setSubject("试发"); //邮件接收方 message.setTo("xxx@xxx.com"); //邮件内容 message.setText("Demo"); //发送邮件 this.mailSender.send(mimeMessage) } catch(Exception e){ logger.error("sendError"); return Boolean.FALSE; } return Boolean.TRUE; } }
@Value:可以将application.properties
配置文件中的配置设置到属性中。
JavaMailSender:邮件发送接口。Spring Boot的starter模块中已为此提供了自动化配置,只需要通过注解@Autowired
注解使用
注意:发送方邮件一定要开启smtp/pop3服务
依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybais-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
在application.properties
中添加配置
### Mybatis配置
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.dao
mybatis.mapper-locations:Mapper资源文件存放的路径
mybatis.type-aliases-package:Dao接口文件存放的目录
Dao和Mapper文件开发
在application.properties
中声明的dao文件位置新建接口
@Mapper
public interface UserDao {
User findByName(@Param("name") String name);
}
Mapper.xml
配置文件中,可以采用#{}
的方式对@Param
注解括号内的参数进行引用在application.properties
中声明的mapper.xml中添加xml文件
<mapper>
:该标签的namespace属性用于绑定Dao接口JMS(Java Message Service,Java消息服务)是一组Java应用程序接口,提供消息的创建、发送、读取等一系列服务。
JMS支持两种消息发送和接收模型:
1、P2P(Point to Point):基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息。
特点:每个消息只有一个消费者,发送者和接收者在时间上没有依赖性。
2、Pub/Sub(Publish/Subscribe):在一对多广播中采用
特点:有多个消费者,时间上有依赖性
MQ全称为MessageQueue(消息队列),是一个消息的接收和转发的容器,可用于消息推送。
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
在application.properties
中配置
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.in-memory=true
spring.activemq.pool.enabled=false
spring.activemq.package.trust-all=true
spring.activemq.package.trust-all :ObjectMessage的使用机制是不安全的,ActiveMQ新版本强制Consumer端声明一份可信任的包列表,只有当ObjectMessage中的Object在可信任包内,才能被提取出来。该配置表示信任所有的包。
创建生产者
@Service
public class MessageProducer {
@Resource
private JmsMessagingTemplate jmsMesssagingTemplate;
public void sendMessage(Destination destination,final String message){
jmsMessagingTemplate.comvertAndSend(destination,message);
}
}
JmsMessagingTemplate:发消息的工具类,也可以注入JmsTemplate,JmsMessagingTemplate对JmsTemplate进行了封装。参数destination是发送到队列的,message是待发送的消息。
创建消费者
@Component
public class MessageConsumer {
@JmsListener(destination="ay.queue")
public void receiveQueue(String text){
System.out.println(text);
}
}
@JmsListener:使用
JmsListener
配置消费者监听的队列ay.queue
,其中text是接受到的消息
测试
@Resource
MessageProducer messageProducer;
public void test{
Destination destination = new ActiveMQQueue("ay.queue");
messageProducer.sendMessage(destination,"hello,world");
}
同步调用:程序按照顺序执行
异步调用:无需等待上一步程序执行完毕即可执行;
当访问的接口较慢或者做耗时任务时,除了可以使用多线程来并行的处理认为,还可以使用Spring Boot提供的异步处理方式
@Async
来处理,通过此注解,可将普通的同步任务改为异步调用 任务。
@EnableAsync
开启异步调用@Async
自定义错误页面
新建error包,包下新建ErrorPageConfig
配置类
@Configuration
public class ErrorPageConfig {
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND,"/404.html");
container.addErrorPage(error404Page);
}
}
}
}
EmbeddedServletContainerCustomizer:Spring Boot的自动配置有一个特性就是能够通过代码来进行修改配置,因此只需要实现Spring Boot的接口即可。
这里需要注册
EmbeddedServletContainerCustomizer
的bean
,在此ErrorPageConfig
类中,实现EmbeddedServletContainerCustomizer
接口,实现该接口的方法,自定义错误页面。
全局异常类开发
业务异常,能够被统一处理。
封装自定义业务异常BussinessException
继承自RuntimeException
public class BusinessException extends RuntimeException {
public BusinessException(){};
public BusinessException(String message){ super(message); }
}
在error包下新建错误信息类ErrorInfo,用于封装错误信息
public class ErrorInfo<T> {
public static final Integer SUCCESS = 200;
public static final Integer ERROR = 100;
private Integer code;
private String message;
private String url;
private T data;
//getter and setter
}
在error包下新建统一异常处理类GlobalDefaultExceptionHnadler
@ControllerAdvice(basePackage={"com.example.demo",})
public class GloalDefaultExceptionHandler {
@ExceptionHandler({BusinessException.class})
//@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public ErrorInfo defaultErrorHandler(HttpServletRequest req,Exception e) {
ErrorInfo errorInfo = new ErrorInfo();
errorInfo.set...
return errorInfo;
}
}
@ControllerAdvice:定义统一的异常处理类,basePackage 属性用于定义扫描那些包,默认可不设置。(@RestControllerAdvice)
@ExceptionHandler :用于定义函数针对的类型异常,可以传入多个需要捕获的异常类
调用一个接口时,由于某些原因造成第一次失败,再次尝试可能成功,这就是重试机制。
依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<denpendency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</denpendency>
在入口类添加注解@EnablerRetry
开启Retry重试
例子
@Retryable(value={BusinessException.class},maxAttemps=5,backoff=@Backoff(delay=5000,multiplier=2))
@Retryable:
value
属性表示当出现哪些异常的时候触发重试,maxAttemps
表示最大重试次数默认是3,delay
表示重试的延迟时间,multiplier
表示上一次延时时间是这一次的倍数
特点:面向集合存储、易存储对象类型的数据、支持动态查询、文件存储格式为BSON、支持复制和故障恢复
缺点:不能建立实体关系、没有事务管理机制等
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
application.properties
中配置v
### mongodb配置
### host地址
spring.data.mongodb.host=localhost
### 默认数据库端口号27017
spring.data.mongodb.port=27017
### 连接数据库名 test
spring.data.mongodb.database=test
MongoRepository
类,MongoRepository
类在spring-data-mongodb
包下,类似于前面的Spring Data JPA
,而MongoRepository
最顶级的父类就是Reposoitory
,继承后就能使用MongoRepositoy
提供的增删改查方法Spring Security 安全框架提供了认证和授权功能、加密解密、统一登录等支持。
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
security配置类
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //路由策略和访问权限的简单配置 http.formLogin() //启用默认登陆页面 .failureUrl("/login?error") //登录失败返回URL:/login?error .defaultSuccessUrl("/ayUser/test") //登陆成功跳转URL,这里跳转到用户首页 .permitAll(); //登录页面全部权限可访问 super.configure(http); } /** * 配置内存用户 */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("test").password("123").roles("ADMIN") .and() .withUser("demo").password("123").roles("USER"); } }
WebSecurityConfig
继承WebSecurityConfigureAdapter
类需要重写configure
方法,通过formLogin
方法配置启用默认登录页面AuthenticationManagerBuilder
类的方法inMemoryAuthentication
可添加内存中的用户,并可给用户指定角色权限。Spring Boot提供了spring-boot-starter-actuator模块,主要用于管理和监控应用
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>
application.properties
中配置
### 应用监控配置
# 指定访问这些监控方法的端口
# management.port
# 指定地址,比如只能通过本机监控,可以设置
# management.address=127.0.0.1
# 敏感信息访问权限
# endpoint.bean.sensitive=false
# 设置关闭安全限制
management.security.enabled=false
Zookeeper是一个开源的分布式应用程序协调服务,提供的功能包括命名服务、配置管理、集群管理、分布式锁等。
1、命名服务:在分布式环境下,经常需要对应用 / 服务进行统一命名,便于识别不同服务,类似于DNS。Zookeeper通过名称来获取资源或服务的地址、提供者等信息。
2、配置管理:分布式系统中都有大量服务器,Zookeeper提供了一种集中管理配置的方法,在这个集中的地方修改了配置,所有对这个配置感兴趣的都会获得变更。
3、集群管理:包括两点:是否有机器退出和加入、选取Master
4、分布式锁:Zookeeper的一致性文件系统使得锁的问题变得容易。锁服务可以分为两类:一类是保持独占,另一类是时序控制。
Dubbo是一款Java服务平台框架以及SOA治理方案。其功能主要包括:高性能NIO通信及多协议集成、服务动态寻址与路由、软负载均衡与容错、依赖分析与降级等。
Registry是服务注册与发现的注册中心,Provider是暴露服务的服务提供方,Consumer是调用远程服务的服务消费方,Monitor是统计服务的调用次数和调用时间的监控中心,Container是服务运行容器。
Duubo简单的调用关系如下:
1、服务容器Container负责启动、加载、运行服务提供者Provider
2、服务提供者Provider在启动时,向注册中心Registry注册自己的服务
3、服务消费者Consumer在启动时,向注册中心Registry订阅自己所需的服务
4、注册中心Register返回服务提供者地址列表给消费者Provider,如果有变更,注册中心Registry将基于长连接推送,变更数据给消费者Consumer
5、服务调用者Consumer从提供者地址列表中基于软负载均衡算法选一台提供者进行调用,如果调用失败,就在线另一台进行调用
6、服务消费者Consumer和提供者Provider在内存中累计调用次数和调用时间,按时每分钟发送一次统计数据到监控中心Monitor
@SpringAplication
开启了Spring的组件扫描和Spring Boot自动配置功能。实际上是一个复合注解,包含了@SpringBootConfigutation
、EnableAutoConfigutarion
、@ComponentScan
@SpringBootConfiguration
是对@Configuration
进行了包装。@Controller
、@Service
、@Component
注解等,都可以被扫描到赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。