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知识库 -> Spring中的AOP概念介绍使用、AOP相关术语、切入点表达式(面向切面编程上篇) -> 正文阅读

[Java知识库]Spring中的AOP概念介绍使用、AOP相关术语、切入点表达式(面向切面编程上篇)

💨更多相关知识👇

💖Spring中的创建对象的三种方式、第三方资源配置管理详细描述及使用(XML版完结篇)

💖Spring中的bean的配置、作用范围、生命周期详细描述及使用(XML版上篇)

💖Spring中的依赖注入、setter与构造器注入、自动装配与集合注入详细描述及使用(XML版中篇)

💖异常处理与解决方案详解上篇

💖异常处理与解决方案详解下篇

💖Math类与System类的常用方法使用

💖JavaEE中的静态方法定义、方法重载要求、return作用详解

💖List接口的常用方法,精华总结

💖JavaEE中的Stream流的常用方法

💖JavaEE中的Stream流知识点使用,精华总结,一文直接上手


🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈
?
🍂个人博客首页: KJ.JK
?
💖系列专栏:JavaEE进阶教程系列


一、AOP概念的介绍


🍂AOP的开发思想(Aspect Oriented Programing/面向切面编程)


                  * 编写代码的时候只关注主业务功能 (非共性功能)
                      
                  * 将共性功能抽取出来
                      
                  * 在代码执行的时候将"共性功能与非共性功能","织入"到一起生成"代理对象"
                      
/*

 底层是动态代理模式
 
*/
             

🍂AOP的应用场景


       银行系统会有一个取款流程,还会有一个查询余额流程,两者有一个相同的验证流程

请添加图片描述


       验证用户的功能是相同的,但又在不同地方出现,我们可以把它提取出来,做成切面类在程序执行的时候动态的添加到业务程序中去,这个切面类可以在不同的地方重用  
             
      * 有了AOP,写代码时不需要把这个验证用户步骤写进去,即完全不考虑验证用户。只写取款和显示余额的业务代码
      
      * "而在另一个地方,写好验证用户的代码。这个验证用户的代码就是切面代码",以后在执行取款和显示余额的时候,利用代理模式。将验证用户的功能在执行取款和显示余额前调用

🍂AOP的真正目的


            "AOP真正目的是": 你写代码的时候,只需考虑主流程,而不用考虑那些不重要的,但又必须要写的其它相同的代码,
                            "这些其它的相同代码所在的类就是切面类"
                            
             "主要应用场景:"
                            1.事务处理
                            2.日志记录
                            3.用户权限


🌴总结


            * 什么是AOP?

                      * 面向切面编程
                      
            * 它的特点是什么?
            
                      * 将切面代码(多个方法中的相同代码)与主业务代码分开编写
                      
                      * 在程序执行过程中动态将"切面代码""主业务代码织入"到一起
                      
                      * "底层使用的是动态代理模式"
                      

🍂AOP相关术语


🔸JoinPoint(连接点)


           * JoinPoint(连接点): 在程序执行过程中的某个阶段点,就是指主业务方法的调用,它是客观存在的
           
                指的"接口或实现类中所有的方法"

🔸Pointcut(切入点)、 Advice(通知)、Aspect(切面)


       * Pointcut(切入点):
                        "需要加强的方法", 是连接点中的部分方法。满足某一规则的类或方法都是切入点
                        
------------------------------------------------------------------------------------------------------------------------------------------------ 
   
       * Advice(通知):
                       "切入点处所要执行的程序代码",即"要执行的公共方法"  (公共的功能)
                           
                        通知的类型有: 前置通知、后置通知、异常通知、最终通知、环绕通知

------------------------------------------------------------------------------------------------------------------------------------------------ 
                           
       * Aspect(切面):   切面指的是 "切入点(规则)""通知(织入方法)的类"
                            
                        "切面类 = 切入点规则 + 通知方法"
                   


🔸Target(目标对象)、 Weaving(织入)、Proxy(代理)


      * Target(目标对象): 被代理的对象,即主业务类 , 比如动态代理案例中的明星  (主要功能)
------------------------------------------------------------------------------------------------------------------------------------------------ 
      
      * Weaving(织入): 织入指的是"把新增的功能用于目标对象",创建代理对象的过程  (把共性功能放在主要功能里面)

------------------------------------------------------------------------------------------------------------------------------------------------ 
      
      * Proxy(代理): 一个类被AOP织入增强后产生的结果类,即代理类 , 比如动态代理案例中的经纪人 
                    (一个完整的类,有主要功能和共性功能)


二、AOP入门案例

在这里插入图片描述


     
   "AOP入门案例(注解版)操作顺序"
------------------------------------------------------------------------------------------------------------

                           1.导入aop相关坐标
         
                                 //spring-context坐标依赖spring-aop坐标

                                  <!--spring核心依赖,会将spring-aop传递进来-->
                            <dependency>
                                <groupId>org.springframework</groupId>
                                <artifactId>spring-context</artifactId>
                                <version>5.2.10.RELEASE</version>
                            </dependency>

                                  <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
                            <dependency>
                                <groupId>org.aspectj</groupId>
                                <artifactId>aspectjweaver</artifactId>
                                <version>1.9.4</version>
                            </dependency>

   
                                 <!-- 导入Spring坐标 -->
                               
                            <dependency>
                                <groupId>org.springframework</groupId>
                                <artifactId>spring-test</artifactId>
                                <version>5.2.10.RELEASE</version>
                            </dependency>
         
         
                           2.定义service接口与实现类
         
                           3.定义通知类,制作通知
         
                             public class MyAdvice {
                                 public void before(){
                                     System.out.println("现在的时间:" + new Date().toLocaleString());
                                 }
                             }

                           4.定义切入点(切入点规则)
                               
                               public class MyAdvice {
                                   @Pointcut("execution(void com.itheima.service.AccountService.update())")                                     private void pt(){}
                               }

                           5."绑定切入点与通知关系",并指定通知添加到目标连接点的具体执行位置
                               
                               public class MyAdvice {
                                   
                                   @Pointcut("execution(void com.itheima.service.AccountService.update())")
                                   private void pt(){}
                                   
                                   @Before("pt()")
                                   public void before(){
                                       System.out.println("现在的时间:" + new Date().toLocaleString());
                                   }}

                           6.使用@Aspect把切入点和通知关系一起的这个类变成通知类
                                
                               @Component
                               @Aspect
                               public class MyAdvice {
                                   @Pointcut("execution(void com.itheima.service.AccountService.update())")
                                   private void pt(){}
                                   
                                   @Before("pt()")
                                   public void before(){
                                       System.out.println("现在的时间:" + new Date().toLocaleString());
                                   }
                               }

                           7.开启Spring对AOP注解驱动支持

                               @Configuration
                               @ComponentScan("com.itheima")
                               @EnableAspectJAutoProxy   // 开启切面自动代理
                               public class SpringConfig {}



?代码演示


//pom.xml类
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>spring-07-aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>

        <!--spring核心依赖,会将spring-aop传递进来-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>


    </dependencies>
</project>

------------------------------------------------------------------------------------------------------------

//核心配置类

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration

@ComponentScan("com.itheima")


@EnableAspectJAutoProxy  // 开启切面自动代理


public class SpringConfig {
}

------------------------------------------------------------------------------------------------------------


//切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    @Pointcut("execution(public void  com.itheima.service.impl.AccountServiceImpl.update())")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

------------------------------------------------------------------------------------------------------------


//接口类

/**
 * 业务层接口
 */
public interface AccountService {

    /**
     * 添加账户
     */
    int save(String name);

    /**
     * 更新账户
     */
    void update();
}

------------------------------------------------------------------------------------------------------------

//接口实现类

import com.itheima.service.AccountService;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    /**
     * 保存账户
     */
    @Override
    public int save(String name) {
        System.out.println("保存" + name + "账户");
        return 1;
    }

    /**
     * 更新账户
     */
    @Override
    public void update() {

        System.out.println("更新账户");
    }
}

------------------------------------------------------------------------------------------------------------

//测试类


import com.itheima.config.SpringConfig;
import com.itheima.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AopTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testUpdate() {
        accountService.update();

    }

    @Test
    public void testSave() {
        accountService.save("张三");
        // accountService.save("李四");
    }
}


请添加图片描述


三、切入点表达式


🍂切入点表达式标准格式


       * 切入点表达式标准格式:
       
             动作关键字(访问修饰符  返回值      包名.接口/类名.                      方法名(参数) 异常名 )
 
       例子: execution(public    void    com.itheima.service.AccountService.save(String))

             
* 动作关键字: 描述切入点的行为动作,例如"execution表示执行到指定切入点"
    
        1.访问修饰符:publicprivate等,可以省略
    
        2.返回值          //必须要写
    
        3.包名./接口名,可以省略
    
        4.方法名         //必须要写
    
        5.参数          //必须要写
    
        6.异常名: 方法定义中抛出指定异常,可以省略,一般不理会


🍂使用通配符描述切入点,快速描述


     1.  * "单个独立的任意符号", 表示"任意一个", 可以独立出现, 也可以作为"前缀或者后缀的匹配符出现"
        
        例子:  
             execution(public * com.itheima.*.UserService.find*(*))
                 
            //匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
                 
------------------------------------------------------------------------------------------------------------                 
      2. " .. 多个连续的任意符号", "表示任意个数(0个或1个或多个)",可以独立出现,常用于简化包名与参数的书写
                 
        例子:  
              execution(public User com..UserService.findById(..))

          // 匹配com包下或任意子包中的UserService类或接口中所有名称为findById的方法
                 
------------------------------------------------------------------------------------------------------------
    
      3. +专用于"匹配子类类型"
                 
         例子:
              execution(* com.itheima.AccountService+.update(..))
           
        //匹配com.itheima包下的AccountService类或接口的子类
                  
------------------------------------------------------------------------------------------------------------
   
/*

  参数个数通配符写法:
               ()  没有参数
               (*) 1个参数
               (..) 0个或1个或多个参数

  类全名的包通配符写法:
                 .. 表示当前包和子包


*/

?标准格式写法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    //最精确写法
    @Pointcut(
   "execution(public void com.itheima.service.impl.AccountServiceImpl.update())")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?省略 修饰符, 包名.类名写法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    //省略 修饰符, 包名.类名
    @Pointcut("execution(void update())") 
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?返回值任意演示, com包下的任意一个子包的service包中的AccountService类的update方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
   //返回值任意, com下的任意一个子包的service包中的AccountService类的update方法
    @Pointcut(" execution(* com.*.service.AccountService.update())") 
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?返回值任意, com下的任意子包中的AccountService类的update方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    //最精确写法
//    @Pointcut(
//   "execution(public void com.itheima.service.impl.AccountServiceImpl.update())")


    //返回值任意, com下的任意子包中的AccountService类的update方法
    @Pointcut(" execution(* com..AccountService.update()) ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?0个参数的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    // 0个参数的方法
    @Pointcut(" execution(* com.itheima.service.AccountService.*())  ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?1个参数的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    //最精确写法
//    @Pointcut(
//   "execution(public void com.itheima.service.impl.AccountServiceImpl.update())")


    // 1个参数的方法
    @Pointcut(" execution(* com.itheima.service.AccountService.*(*))   ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?0个或1个或多个参数演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式
    //0个或1个或多个参数
    @Pointcut("execution(* com.itheima.service.AccountService.*(..))   ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?e结尾的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式

    //e结尾的方法
    @Pointcut("execution(* com.itheima.service.AccountService.*e(..))    ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?u开头的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式

   //u开头的方法
    @Pointcut("execution(* com.itheima.service.AccountService.u*(..)) )    ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?所有的类的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //所有的类的方法
    @Pointcut("execution(* *(..)) ")
    private  void pt(){}


    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?方法名是save或update的方法演示


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式

    //方法名是save或update的方法
    @Pointcut("execution(* save(..)) || execution(* update(..)) ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


?除了方法名是save的所有方法演示



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

// 通知类
@Component // 创建对象并添加到IoC容器中
@Aspect // 声明这是一个切面类

public class MyAdvice {


    //切入点表达式

    //除了方法名是save的所有方法
    @Pointcut("!execution(* save(..)) ")
    private  void pt(){}



    //通知(共性功能)
    @Before("pt()")
    public  void before(){
        System.out.println("现在时间:"+new Date().toLocaleString());  //toLocaleString转成本地时间
    }

}

请添加图片描述


🌴总结


             * 切入点表达式由哪几个部分组成?
             
                    访问修改符 返回类型 包名.类名.方法名 (参数类型) 异常类型
                    
             * 必须的参数是哪三个?
             
                    1.返回类型
                    
                    2.方法名
                    
                    3.参数类型


作者:KJ.JK

文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,不足之处可以在评论区多多指正,交流学习

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:46  更:2022-10-31 11:41:42 
 
开发: 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年11日历 -2024/11/25 15:24:36-

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