IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot -> 正文阅读

[Java知识库]SpringBoot

前言

springboot: springboot整合了所有框架,并默认配置了很多框架的使用方式。

本文主要是从实际开发的角度来写springboot相关的内容,当前并未更新完,会持续更新。

1.模块一

1. 基础依赖

<dependencies>
    <!--核心依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--导入配置文件处理器,配置文件进行绑定就会有提示-->
    <dependency>
        groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

<!-- 
	这个插件,可以将应用打包成一个可执行的jar包;
	直接使用 java -jar 进行打包
-->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • spring-boot-starter
    • springboot场景启动器,帮我们导入模块正常运行所依赖的组件
    • springboot会将所有的功能场景都抽取出来,做成一个个starter,只需要在项目中引入starter,相关的所有依赖都会自动导入

2. SpringBootApplication注解

@SpringBootApplication 
	- 标注一个主程序类,说明这是一个springboot应用
    - springboot运行改类的main方法来启动springboot应用
    - 由三个注解构成(除去四个元注解之外):@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan
  • @SpringBootConfiguration
    	- 标注在类上,表明这是一个springboot配置类
        - 底层是@Configuration
    
  • @EnableAutoConfiguration
    	- 开启自动配置功能的注解
        - 其底层由两个注解构成:
            - @AutoConfigurationPackage 自动配置包
              将添加该注解的类所在的package作为 自动配置package 进行管理
            - @Import({AutoConfigurationImportSelector.class}) 
              - 将 AutoConfigurationImportSelector类 导入到spring容器中
              - AutoConfigurationImportSelector类 可以帮助springboot应用将所有符合条件(@Conditional)的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器
              	- 需要导入的组件会以全类名的方式返回,然后添加到容器中;同时会给容器中加入很多自动配置类 xxxAutoConfiguration(免去我们手动配置的过程)
              	- springboot会在启动的时候从类路径 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值,将这些值自动配置导入容器中
    
  • @ComponentScan
    	- 定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
    	- 默认装配标识了@Component注解的类到spring容器中
    	- @Component的派生注解有:@Controller@Service@Repository
    

3. Conditional注解

@Conditional
	- 按照一定的条件进行判断,满足条件给容器注册bean,配置配里面的内容才生效
	- 加在方法上(和@Bean搭配使用): 表明要满足对应条件,才注册此方法的组件
	- 加在类上(搭配@Component使用): 表明要满足对应条件,此配置类才会生效

派生注解:

注解说明
@ConditionalOnJava系统的java版本符合要求
@ConditionalOnBean容器中存在指定的Bean
@ConditionalOnMissingBean容器中不存在指定的Bean
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性有指定的值
@ConditionalOnResource 类路径下存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication当前不是web环境
@ConditionalOnJndiJNDI存在指定项

5. Bean注解

@Bean
	- 用于方法方法上:产生一个Bean对象,然后这个Bean对象交给Spring管理
		- 产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中
		- 搭配@Scope("prototype") 可以使得方法被多次调用,每次都实例一个Bean对象
	- 用于注解上:在运行时提供注册

4. yml配置文件

1.格式

格式:key: value

  • 缩进:

    # 对象:
    man:
     tom:
      name: 汤姆
      age: 17
    # 数组
    roles:
     - jack
     - rose
    
  • 字符串默认不加引号

    • ‘’:会转义特殊字符:a: '\n' 输出:\n
    • “”:不会转义特殊字符:a: '\n' 输出:换行
  • 占位符

    num: 16
    human:
     name: 李四
    
    man:
     name: ${human.name}
     age: ${num}
     salary: ${random.int} 
    

2.加载与读取

加载配置文件:

  • 加载指定的配置文件:@PropertySource(value = {"classpath: 配置文件名.后缀"})

  • 导入Spring的配置文件(springmvc.xml、spring.xml):@ImportResource( locations = "classpath = bean.xml" )放在主入口函数的类上

读取配置文件:

  • 类读取配置文件 @ConfigurationProperties(prefix = "xxx")

    tom:
     name: 汤姆
     num: 17.00
    
    /**
     * @Component 注册为一个组件,只有容器中的组件才能享有springboot提供的各种功能
     * @ConfigurationProperties 
     * 		- 将该类中的所有属性与配置文件中的相关配置进行绑定
     *		- prefix = "xxx" 与配置文件中哪一个key下的所有属性进行一一映射
     */
    @PropertySource(value = {"classpath: person.yml"})
    @Component
    @ConfigurationProperties(prefix = "tom")
    public class Tom {
        private String name;
        private double num;
    }
    
  • 属性读取配置文件 @Value("${xxx}")

    tom:
     name: 汤姆
     num: 17.00
    
    /**
     * @Value("${}") 获取对应属性文件中定义的属性值
     * @Value("#{}") 表示 SpEl 表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法
     *
     */
    @Component
    public class Tom {
        @Value("${tom.name}")
        private String name;
        @Value(@Value("#{ T(java.lang.Math).random() * 100.0 }"))
        private String age;
    }
    

3.profile

在实际项目中,配置文件可能会有多个,如下图,这时候就需要通过spring.profile.active指定生效的文件了

spring:
  profiles:
    active: dev,datasource,websecurity,token

4.加载

配置文件的加载优先级(从高到低):
springboot启动后会扫描application.properties或者application.yml文件作为springboot的默认配置文件

  • 高优先级的配置文件内容会覆盖低优先级的

  • springboot中的classpath路径为

    • src/main/java
    • src/main/resouces
  • springboot中的classpath*除了上述两个路径,还包含:

    • 第三方jar包的根路径
  • file: ./config/
  • file:./
  • classpath:/config/
  • classpath:./

5. Slf4j日志

依赖:

        <!--提供Slf4j日志-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

使用:

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LogDemo {
	public static String test() {
		logger.trace("trace日志");
        logger.debug("debug日志");
        logger.info("info日志");
        logger.warn("warn日志");
        logger.error("error日志");
        return "日志系统";
	}
}
  • 默认日志级别是:info
  • 可在配置文件中通过如下配置设置默认日志级别
    logging:
      level:
        com:
          example:
            demo: warn
    

2.模块二

1. json接口

1. 代码实例

代码:

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-25 15:27
 */
@RestController // 这里也可以直接用@Controller
@RequestMapping("/boot")
public class Controller {
    @PostMapping("/chgGend")
    public Object chgGend(@RequestBody Map<String, Object> params) {
        Object gend = params.get("gend");
        if (gend.equals("1")) {
            params.put("gend", "男");
        } else if (gend.equals("0")) {
            params.put("gend", "女");
        } else {
            params.put("gend", "性别未知");
        }
        return params;
    }

    @PostMapping("/chgName")
    public Map<String, Object> chgName(
            @RequestParam(value = "gend", required = false, defaultValue = "男") String gend,
            @RequestParam("name") String name) {
        Map<String, Object> res = new HashMap<>();
        String resName =  name == "张三" ? "法外狂徒" : name;
        res.put("name", resName);
        res.put("gend", gend);
        return res;
    }
}

接口测试:

2. RequestBody注解和RequestParam注解

@RequestBody
	- 主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的)
	- 一般都用POST方式进行提交
	- 一个请求,只有一个RequestBody
	- 可以和@RequestParam()同时使用

@RequestParam
	- 获取请求中的参数(不支持post请求)
	- value 请求中的参数名
	- required 是否必传该参数
	- defaultValue 当请求中没有传递该参数的时候的默认值(需要与required 搭配使用)

两者对比:

注解支持的类型支持的请求类型支持的Content-Type请求示例
@RequestParamurlGET所有chgName?name=张三
@RequestBodyBodyPOST/PUT/DELETE/PATCHjson{“name”: “张三”}

2.Filter

1.代码实例

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-25 16:18
 */
@Slf4j
@Order(1)
@WebFilter(urlPatterns = "/*")
public class RequestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("过滤器初始化......");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("过滤器执行......");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        log.info("过滤器销毁......");
    }
}

记得在主程序上加上注解@ServletComponentScan,不然过滤器无法生效。

启动项目,过滤器初始化:

发送请求,过滤器执行:

结束程序,过滤器销毁:

2. 小结

  • 过滤器工作流程:

  • 注解:

    @WebFilter(urlPatterns = "/*") 
    	- 将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器
    	- 常用属性
    		- urlPatterns 指定一组过滤器的url匹配模式
    		- value等同于urlPatterns,但两者不能同时使用
    		- servletNames 指定过滤器将应用于哪些servlet
    		以上三个属性,至少得使用一个
    		- initParams 指定一组过滤器初始化参数
    
    @Order(1) 
    	- 设置过滤器的优先级,数值越小,优先级越高
    
  • 方法:

    • init

      • 初始化过滤器,可以在init()方法中获取Filter中的初始化参数
      • 只在应用启动的时候执行一次
    • doFilter

      • 完成过滤操作,当请求发过来的时候,过滤器将执行doFilter方法
      • 每次发送请求都会执行
    • destroy

      • Filter对象创建后会驻留在内存,当web应用移除或服务器停止时调用destroy()方法进行销毁
      • 仅执行一次,执行后可以释放过滤器占用的资源

3.在配置类中注册filter

public class RequestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("过滤器初始化......");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("过滤器执行......");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        log.info("过滤器销毁......");
    }
}
import com.example.sb_demo.common.filter.RequestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-25 16:52
 */
@Configuration
public class WebConfiguration {
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new RequestFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.addInitParameter("paramName", "paramValue");
        registrationBean.setName("RequestFilter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

可以在配置类中注册多个过滤器,通过setOrder指定其优先级,如下:

    @Bean
    public FilterRegistrationBean filterRegistrationBean1() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new RequestFilter());
        ...
        registrationBean.setOrder(1);
        return registrationBean;
    }
    @Bean
    public FilterRegistrationBean filterRegistrationBean1() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new AnotherFilter());
        ...
        registrationBean.setOrder(2);
        return registrationBean;
    }

3. Interceptor

拦截器类(implements HandlerInterceptor):

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-26 7:58
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("执行了拦截器的preHandle方法");
        String userName = request.getParameter("name");
        Object password = request.getParameter("password");
        if (userName.equals("张三") && password.equals("123456")) {
            log.info("身份验证通过");
            return true;
        }
        return false;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("执行了拦截器的postHandle方法");
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("执行了拦截器的afterCompletion方法");
    }
}

WebMvcConfig(implements WebMvcConfigurer

import com.example.sb_demo.common.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-26 8:07
 */
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = registry.addInterceptor(new LoginInterceptor());
        /*
            addPathPatterns 需要拦截的url
            excludePathPatterns 不需要拦截的url
         */
        registration.addPathPatterns("/boot/**");
    }
}

测试:

1. 代码实例

2. 小结

过滤器与拦截器的执行流程:

拦截器的运行流程:

拦截器(Interceptor):类似于filter,都是面向切面编程。

  • preHandler 在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去

    • 返回 false,请求结束,后续的 Interceptor 和 Controller 都不会再执行
    • 返回 true,继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法
  • postHandler 在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,可以对 Controller 处理之后的 ModelAndView 对象进行操作

  • afterCompletion 在当前对应的 Interceptor 类的 postHandler 方法返回值为 true 时才会执行,在 DispatcherServlet 渲染了对应的视图之后执行,主要用来进行资源清理

4. 整合mybatis-plus

前言

  • 此部分只演示自定义SQL的查询(也就是用mybatis-plus来写mybatis,因为我这样我就不用再写springboot集成mybatis了,毕竟mybatis-plus只是对于mybatis的扩展)
  • 想深入了解springboot集成mybatis-plus的一些特性和操作,请移步我的这篇文章:MyBatis-Plus

1.依赖 + yml配置

依赖:

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!--druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.11</version>
        </dependency>

配置:

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.alibaba.druid.pool.DruidDataSource
    # 配置连接数据库的信息
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo1?serverTimezone=GMT&characterEncoding=utf-8&useSSL=false
    username: root
    password: admin

# 配置打印mybatis-plus的SQL执行日志
mybatis-plus:
  # 配置实体类所在包
  type-aliases-package: com.example.mp_demo.pojo
  # 配置mapper.xml路径
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    # org.apache.ibatis.logging.stdout.StdOutImpl 可以打印sql、参数、查询结果
    # org.apache.ibatis.logging.log4j.Log4jImpl   不打印查询结果
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置端口
server:
  port: 8080

实体类:

import java.io.Serializable;
import lombok.Data;
/**
* 用户表
* @TableName user
*/
@Data
public class User implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private String status;
    private String gender;
}

2.ResultBean

package com.example.mp_demo.common.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-26 10:12
 */
@Data
public class ResultBean <T> implements Serializable {
    private static final long serialVersionUID = 1L;

    public static final int SUCCESS = 1;
    public static final int FAIL = -1;
    public static final int NO_LOGIN = -2;
    public static final int NO_PERMISSION = -3;

    private int code = SUCCESS;
    private String message = "success";
    private transient T data;

    public ResultBean(){
        super();
    }

    public ResultBean(T data){
        super();
        this.data = data;
    }

    public boolean isSuccess() {
        return SUCCESS == this.code;
    }
}

3.mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mp_demo.mapper.UserMapper">
    <select id="getAll" resultType="user">
        select
            id,
            name,
            age,
            status,
            gender
        from
            user
        where
            status = '1'
    </select>
</mapper>

4.UserMapper接口

package com.example.mp_demo.mapper;

import com.example.mp_demo.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;
import java.util.Map;

/**
* @author chen
* @description 针对表【user(用户表)】的数据库操作Mapper
* @createDate 2022-10-26 10:06:23
* @Entity com.example.mp_demo.pojo.User
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
    List<User> getAll();
}

5.UserService

package com.example.mp_demo.service;

import com.example.mp_demo.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;
import java.util.Map;

/**
* @author chen
* @description 针对表【user(用户表)】的数据库操作Service
* @createDate 2022-10-26 10:06:23
*/
public interface UserService extends IService<User> {
    List<User> getAll();
}
package com.example.mp_demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mp_demo.pojo.User;
import com.example.mp_demo.service.UserService;
import com.example.mp_demo.mapper.UserMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
* @author chen
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2022-10-26 10:06:23
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{

    @Resource
    private UserMapper userMapper;

    @Override
    public List<User> getAll() {
        return userMapper.getAll();
    }
}

6.controller

package com.example.mp_demo.controller;

import com.example.mp_demo.common.entity.ResultBean;
import com.example.mp_demo.pojo.User;
import com.example.mp_demo.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author: chenJY
 * @Description:
 * @Date: 2022-10-26 10:09
 */
@RestController
@RequestMapping("user")
public class UserController {
    @Resource
    private UserService userService;

    @RequestMapping("getAll")
    public ResultBean getAll() {
        ResultBean resultBean = new ResultBean();
        List<User> data = userService.getAll();
        if (!data.isEmpty()) {
            resultBean.setCode(ResultBean.SUCCESS);
            resultBean.setData(data);
            resultBean.setMessage("成功获取数据");
        } else {
            resultBean.setCode(ResultBean.FAIL);
            resultBean.setMessage("获取数据失败");
        }
        return resultBean;
    }
}

测试: 不需要传参数。

3.模块三

未完待续…

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:46  更:2022-10-31 11:43:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:20:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码