亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入了解Spring中最常用的11個(gè)擴(kuò)展點(diǎn)

 更新時(shí)間:2022年09月20日 09:57:30   作者:蘇三說(shuō)技術(shù)  
我們一說(shuō)到spring,可能第一個(gè)想到的是?IOC(控制反轉(zhuǎn))?和?AOP(面向切面編程)。除此之外,我們?cè)谑褂胹pring的過(guò)程中,有沒(méi)有發(fā)現(xiàn)它的擴(kuò)展能力非常強(qiáng)。今天就來(lái)跟大家一起聊聊,在Spring中最常用的11個(gè)擴(kuò)展點(diǎn)

前言

我們一說(shuō)到spring,可能第一個(gè)想到的是 IOC(控制反轉(zhuǎn)) 和 AOP(面向切面編程)。

沒(méi)錯(cuò),它們是spring的基石,得益于它們的優(yōu)秀設(shè)計(jì),使得spring能夠從眾多優(yōu)秀框架中脫穎而出。

除此之外,我們?cè)谑褂胹pring的過(guò)程中,有沒(méi)有發(fā)現(xiàn)它的擴(kuò)展能力非常強(qiáng)。由于這個(gè)優(yōu)勢(shì)的存在,讓spring擁有強(qiáng)大的包容能力,讓很多第三方應(yīng)用能夠輕松投入spring的懷抱。比如:rocketmq、mybatis、redis等。

今天跟大家一起聊聊,在Spring中最常用的11個(gè)擴(kuò)展點(diǎn)。

1.自定義攔截器

spring mvc攔截器根spring攔截器相比,它里面能夠獲取HttpServletRequestHttpServletResponse等web對(duì)象實(shí)例。

spring mvc攔截器的頂層接口是:HandlerInterceptor,包含三個(gè)方法:

  • preHandle 目標(biāo)方法執(zhí)行前執(zhí)行
  • postHandle 目標(biāo)方法執(zhí)行后執(zhí)行
  • afterCompletion 請(qǐng)求完成時(shí)執(zhí)行

為了方便我們一般情況會(huì)用HandlerInterceptor接口的實(shí)現(xiàn)類(lèi)HandlerInterceptorAdapter類(lèi)。

假如有權(quán)限認(rèn)證、日志、統(tǒng)計(jì)的場(chǎng)景,可以使用該攔截器。

第一步,繼承HandlerInterceptorAdapter類(lèi)定義攔截器:

public class AuthInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String requestUrl = request.getRequestURI();
        if (checkAuth(requestUrl)) {
            return true;
        }

        return false;
    }

    private boolean checkAuth(String requestUrl) {
        System.out.println("===權(quán)限校驗(yàn)===");
        return true;
    }
}

第二步,將該攔截器注冊(cè)到spring容器:

@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
 
    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor());
    }
}

第三步,在請(qǐng)求接口時(shí)spring mvc通過(guò)該攔截器,能夠自動(dòng)攔截該接口,并且校驗(yàn)權(quán)限。

2.獲取Spring容器對(duì)象

在我們?nèi)粘i_(kāi)發(fā)中,經(jīng)常需要從Spring容器中獲取Bean,但你知道如何獲取Spring容器對(duì)象嗎?

2.1 BeanFactoryAware接口

@Service
public class PersonService implements BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void add() {
        Person person = (Person) beanFactory.getBean("person");
    }
}

實(shí)現(xiàn)BeanFactoryAware接口,然后重寫(xiě)setBeanFactory方法,就能從該方法中獲取到spring容器對(duì)象。

2.2 ApplicationContextAware接口

@Service
public class PersonService2 implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void add() {
        Person person = (Person) applicationContext.getBean("person");
    }
}

實(shí)現(xiàn)ApplicationContextAware接口,然后重寫(xiě)setApplicationContext方法,也能從該方法中獲取到spring容器對(duì)象。

2.3 ApplicationListener接口

@Service
public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        applicationContext = event.getApplicationContext();
    }

    public void add() {
        Person person = (Person) applicationContext.getBean("person");
    }
}

3.全局異常處理

以前我們?cè)陂_(kāi)發(fā)接口時(shí),如果出現(xiàn)異常,為了給用戶一個(gè)更友好的提示,例如:

@RequestMapping("/test")
@RestController
public class TestController {

    @GetMapping("/add")
    public String add() {
        int a = 10 / 0;
        return "成功";
    }
}

如果不做任何處理請(qǐng)求add接口結(jié)果直接報(bào)錯(cuò):

what?用戶能直接看到錯(cuò)誤信息?

這種交互方式給用戶的體驗(yàn)非常差,為了解決這個(gè)問(wèn)題,我們通常會(huì)在接口中捕獲異常:

@GetMapping("/add")
public String add() {
    String result = "成功";
    try {
        int a = 10 / 0;
    } catch (Exception e) {
        result = "數(shù)據(jù)異常";
    }
    return result;
}

接口改造后,出現(xiàn)異常時(shí)會(huì)提示:“數(shù)據(jù)異常”,對(duì)用戶來(lái)說(shuō)更友好。

看起來(lái)挺不錯(cuò)的,但是有問(wèn)題。。。

如果只是一個(gè)接口還好,但是如果項(xiàng)目中有成百上千個(gè)接口,都要加上異常捕獲代碼嗎?

答案是否定的,這時(shí)全局異常處理就派上用場(chǎng)了:RestControllerAdvice。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        if (e instanceof ArithmeticException) {
            return "數(shù)據(jù)異常";
        }
        if (e instanceof Exception) {
            return "服務(wù)器內(nèi)部異常";
        }
        retur nnull;
    }
}

只需在handleException方法中處理異常情況,業(yè)務(wù)接口中可以放心使用,不再需要捕獲異常(有人統(tǒng)一處理了)。真是爽歪歪。

4.類(lèi)型轉(zhuǎn)換器

spring目前支持3中類(lèi)型轉(zhuǎn)換器:

  • Converter<S,T>:將 S 類(lèi)型對(duì)象轉(zhuǎn)為 T 類(lèi)型對(duì)象
  • ConverterFactory<S, R>:將 S 類(lèi)型對(duì)象轉(zhuǎn)為 R 類(lèi)型及子類(lèi)對(duì)象
  • GenericConverter:它支持多個(gè)source和目標(biāo)類(lèi)型的轉(zhuǎn)化,同時(shí)還提供了source和目標(biāo)類(lèi)型的上下文,這個(gè)上下文能讓你實(shí)現(xiàn)基于屬性上的注解或信息來(lái)進(jìn)行類(lèi)型轉(zhuǎn)換。

這3種類(lèi)型轉(zhuǎn)換器使用的場(chǎng)景不一樣,我們以Converter<S,T>為例。假如:接口中接收參數(shù)的實(shí)體對(duì)象中,有個(gè)字段的類(lèi)型是Date,但是實(shí)際傳參的是字符串類(lèi)型:2021-01-03 10:20:15,要如何處理呢?

第一步,定義一個(gè)實(shí)體User:

@Data
public class User {

    private Long id;
    private String name;
    private Date registerDate;
}

第二步,實(shí)現(xiàn)Converter接口:

public class DateConverter implements Converter<String, Date> {

    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public Date convert(String source) {
        if (source != null && !"".equals(source)) {
            try {
                simpleDateFormat.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

第三步,將新定義的類(lèi)型轉(zhuǎn)換器注入到spring容器中:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter());
    }
}

第四步,調(diào)用接口

@RequestMapping("/user")
@RestController
public class UserController {

    @RequestMapping("/save")
    public String save(@RequestBody User user) {
        return "success";
    }
}

請(qǐng)求接口時(shí)User對(duì)象中registerDate字段會(huì)被自動(dòng)轉(zhuǎn)換成Date類(lèi)型。

5.導(dǎo)入配置

有時(shí)我們需要在某個(gè)配置類(lèi)中引入另外一些類(lèi),被引入的類(lèi)也加到spring容器中。這時(shí)可以使用@Import注解完成這個(gè)功能。

如果你看過(guò)它的源碼會(huì)發(fā)現(xiàn),引入的類(lèi)支持三種不同類(lèi)型。

但是我認(rèn)為最好將普通類(lèi)和@Configuration注解的配置類(lèi)分開(kāi)講解,所以列了四種不同類(lèi)型:

5.1 普通類(lèi)

這種引入方式是最簡(jiǎn)單的,被引入的類(lèi)會(huì)被實(shí)例化bean對(duì)象。

public class A {
}

@Import(A.class)
@Configuration
public class TestConfiguration {
}

通過(guò)@Import注解引入A類(lèi),spring就能自動(dòng)實(shí)例化A對(duì)象,然后在需要使用的地方通過(guò)@Autowired注解注入即可:

@Autowired
private A a;

是不是挺讓人意外的?不用加@Bean注解也能實(shí)例化bean。

5.2 配置類(lèi)

這種引入方式是最復(fù)雜的,因?yàn)?code>@Configuration注解還支持多種組合注解,比如:

  • @Import
  • @ImportResource
  • @PropertySource等。
public class A {
}

public class B {
}

@Import(B.class)
@Configuration
public class AConfiguration {

    @Bean
    public A a() {
        return new A();
    }
}

@Import(AConfiguration.class)
@Configuration
public class TestConfiguration {
}

通過(guò)@Import注解引入@Configuration注解的配置類(lèi),會(huì)把該配置類(lèi)相關(guān)@Import、@ImportResource@PropertySource等注解引入的類(lèi)進(jìn)行遞歸,一次性全部引入。

5.3 ImportSelector

這種引入方式需要實(shí)現(xiàn)ImportSelector接口:

public class AImportSelector implements ImportSelector {

private static final String CLASS_NAME = "com.sue.cache.service.test13.A";
    
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CLASS_NAME};
    }
}

@Import(AImportSelector.class)
@Configuration
public class TestConfiguration {
}

這種方式的好處是selectImports方法返回的是數(shù)組,意味著可以同時(shí)引入多個(gè)類(lèi),還是非常方便的。

5.4 ImportBeanDefinitionRegistrar

這種引入方式需要實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口:

public class AImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(A.class);
        registry.registerBeanDefinition("a", rootBeanDefinition);
    }
}

@Import(AImportBeanDefinitionRegistrar.class)
@Configuration
public class TestConfiguration {
}

這種方式是最靈活的,能在registerBeanDefinitions方法中獲取到BeanDefinitionRegistry容器注冊(cè)對(duì)象,可以手動(dòng)控制BeanDefinition的創(chuàng)建和注冊(cè)。

6.項(xiàng)目啟動(dòng)時(shí)

有時(shí)候我們需要在項(xiàng)目啟動(dòng)時(shí)定制化一些附加功能,比如:加載一些系統(tǒng)參數(shù)、完成初始化、預(yù)熱本地緩存等,該怎么辦呢?

好消息是springboot提供了:

  • CommandLineRunner
  • ApplicationRunner

這兩個(gè)接口幫助我們實(shí)現(xiàn)以上需求。

它們的用法還是挺簡(jiǎn)單的,以ApplicationRunner接口為例:

@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    private LoadDataService loadDataService;

    public void run(ApplicationArguments args) throws Exception {
        loadDataService.load();
    }
}

實(shí)現(xiàn)ApplicationRunner接口,重寫(xiě)run方法,在該方法中實(shí)現(xiàn)自己定制化需求。

如果項(xiàng)目中有多個(gè)類(lèi)實(shí)現(xiàn)了ApplicationRunner接口,他們的執(zhí)行順序要怎么指定呢?

答案是使用@Order(n)注解,n的值越小越先執(zhí)行。當(dāng)然也可以通過(guò)@Priority注解指定順序。

7.修改BeanDefinition

Spring IOC在實(shí)例化Bean對(duì)象之前,需要先讀取Bean的相關(guān)屬性,保存到BeanDefinition對(duì)象中,然后通過(guò)BeanDefinition對(duì)象,實(shí)例化Bean對(duì)象。

如果想修改BeanDefinition對(duì)象中的屬性,該怎么辦呢?

答:我們可以實(shí)現(xiàn)BeanFactoryPostProcessor接口。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("id", 123);
        beanDefinitionBuilder.addPropertyValue("name", "蘇三說(shuō)技術(shù)");
        defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
    }
}

在postProcessBeanFactory方法中,可以獲取BeanDefinition的相關(guān)對(duì)象,并且修改該對(duì)象的屬性。

8.初始化Bean前后

有時(shí),你想在初始化Bean前后,實(shí)現(xiàn)一些自己的邏輯。

這時(shí)可以實(shí)現(xiàn):BeanPostProcessor接口。

該接口目前有兩個(gè)方法:

  • postProcessBeforeInitialization 該在初始化方法之前調(diào)用。
  • postProcessAfterInitialization 該方法再初始化方法之后調(diào)用。

例如:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User) {
            ((User) bean).setUserName("蘇三說(shuō)技術(shù)");
        }
        return bean;
    }
}

如果spring中存在User對(duì)象,則將它的userName設(shè)置成:蘇三說(shuō)技術(shù)。

其實(shí),我們經(jīng)常使用的注解,比如:@Autowired、@Value、@Resource、@PostConstruct等,是通過(guò)AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor實(shí)現(xiàn)的。

9.初始化方法

目前spring中使用比較多的初始化bean的方法有:

  • 使用@PostConstruct注解
  • 實(shí)現(xiàn)InitializingBean接口

9.1 使用@PostConstruct注解

@Service
public class AService {
    @PostConstruct
    public void init() {
        System.out.println("===初始化===");
    }
}

在需要初始化的方法上增加@PostConstruct注解,這樣就有初始化的能力。

9.2 實(shí)現(xiàn)InitializingBean接口

@Service
public class BService implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("===初始化===");
    }
}

實(shí)現(xiàn)InitializingBean接口,重寫(xiě)afterPropertiesSet方法,該方法中可以完成初始化功能。

10.關(guān)閉容器前

有時(shí)候,我們需要在關(guān)閉spring容器前,做一些額外的工作,比如:關(guān)閉資源文件等。

這時(shí)可以實(shí)現(xiàn)DisposableBean接口,并且重寫(xiě)它的destroy方法:

@Service
public class DService implements InitializingBean, DisposableBean {
 
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy");
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean afterPropertiesSet");
    }
}

這樣spring容器銷(xiāo)毀前,會(huì)調(diào)用該destroy方法,做一些額外的工作。

通常情況下,我們會(huì)同時(shí)實(shí)現(xiàn)InitializingBean和DisposableBean接口,重寫(xiě)初始化方法和銷(xiāo)毀方法。

11.自定義作用域

我們都知道spring默認(rèn)支持的Scope只有兩種:

  • singleton 單例,每次從spring容器中獲取到的bean都是同一個(gè)對(duì)象。
  • prototype 多例,每次從spring容器中獲取到的bean都是不同的對(duì)象。

spring web又對(duì)Scope進(jìn)行了擴(kuò)展,增加了:

  • RequestScope 同一次請(qǐng)求從spring容器中獲取到的bean都是同一個(gè)對(duì)象。
  • SessionScope 同一個(gè)會(huì)話從spring容器中獲取到的bean都是同一個(gè)對(duì)象。

即便如此,有些場(chǎng)景還是無(wú)法滿足我們的要求。

比如,我們想在同一個(gè)線程中從spring容器獲取到的bean都是同一個(gè)對(duì)象,該怎么辦?

這就需要自定義Scope了。

第一步實(shí)現(xiàn)Scope接口:

public class ThreadLocalScope implements Scope {
    private static final ThreadLocal THREAD_LOCAL_SCOPE = new ThreadLocal();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object value = THREAD_LOCAL_SCOPE.get();
        if (value != null) {
            return value;
        }

        Object object = objectFactory.getObject();
        THREAD_LOCAL_SCOPE.set(object);
        return object;
    }

    @Override
    public Object remove(String name) {
        THREAD_LOCAL_SCOPE.remove();
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return null;
    }
}

第二步將新定義的Scope注入到spring容器中:

@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
    }
}

第三步使用新定義的Scope:

@Scope("threadLocalScope")
@Service
public class CService {
    public void add() {
    }
}

以上就是深入了解Spring中最常用的11個(gè)擴(kuò)展點(diǎn)的詳細(xì)內(nèi)容,更多關(guān)于Spring擴(kuò)展點(diǎn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Security認(rèn)證機(jī)制源碼層探究

    Spring Security認(rèn)證機(jī)制源碼層探究

    SpringSecurity是基于Filter實(shí)現(xiàn)認(rèn)證和授權(quán),底層通過(guò)FilterChainProxy代理去調(diào)用各種Filter(Filter鏈),F(xiàn)ilter通過(guò)調(diào)用AuthenticationManager完成認(rèn)證 ,通過(guò)調(diào)用AccessDecisionManager完成授權(quán)
    2023-03-03
  • Java內(nèi)存結(jié)構(gòu)和數(shù)據(jù)類(lèi)型

    Java內(nèi)存結(jié)構(gòu)和數(shù)據(jù)類(lèi)型

    本文重點(diǎn)給大家介紹java內(nèi)存結(jié)構(gòu)和數(shù)據(jù)類(lèi)型知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2016-12-12
  • Spring配置文件解析之BeanDefinitionParserDelegate詳解

    Spring配置文件解析之BeanDefinitionParserDelegate詳解

    這篇文章主要介紹了Spring配置文件解析之BeanDefinitionParserDelegate詳解,對(duì)于Spring的配置文件的解析處理操作是在BeanDefinitionParserDelegate中進(jìn)行處理操作,接下來(lái)我們簡(jiǎn)單介紹一下BeanDefinitionParserDelegate所做的處理操作,需要的朋友可以參考下
    2024-02-02
  • SpringBoot多種環(huán)境自由切換的實(shí)現(xiàn)

    SpringBoot多種環(huán)境自由切換的實(shí)現(xiàn)

    本文主要介紹了SpringBoot多種環(huán)境自由切換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 自從在 IDEA 中用了熱部署神器 JRebel 之后,開(kāi)發(fā)效率提升了 10(真棒)

    自從在 IDEA 中用了熱部署神器 JRebel 之后,開(kāi)發(fā)效率提升了 10(真棒)

    在javaweb開(kāi)發(fā)過(guò)程中,使用熱部署神器 JRebel可以使class類(lèi)還是更新spring配置文件都能立馬見(jiàn)到效率,本文給大家介紹JRebel的兩種安裝方法,小編建議使用第二種方法,具體安裝步驟跟隨小編一起看看吧
    2021-06-06
  • 分享Java常用開(kāi)發(fā)編輯器工具

    分享Java常用開(kāi)發(fā)編輯器工具

    這篇文章主要給大家分享了分享Java常用開(kāi)發(fā)編輯器工具,文章內(nèi)容介紹非常詳細(xì),具有很大的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的工作或?qū)W習(xí)有一定的幫助
    2022-03-03
  • springboot+redis+阿里云短信實(shí)現(xiàn)手機(jī)號(hào)登錄功能

    springboot+redis+阿里云短信實(shí)現(xiàn)手機(jī)號(hào)登錄功能

    這篇文章主要介紹了springboot+redis+阿里云短信實(shí)現(xiàn)手機(jī)號(hào)登錄功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-01-01
  • Java中的HashMap源碼解析

    Java中的HashMap源碼解析

    這篇文章主要介紹了Java中的HashMap源碼解析,當(dāng)HashMap中的其中一個(gè)鏈表的對(duì)象個(gè)數(shù)如果達(dá)到了8個(gè),此時(shí)如果數(shù)組長(zhǎng)度沒(méi)有達(dá)到64,那么HashMap會(huì)先擴(kuò)容解決,如果已經(jīng)達(dá)到了64,那么這個(gè)鏈表會(huì)變成紅黑樹(shù),需要的朋友可以參考下
    2023-12-12
  • 淺談Java的LinkedHashSet源碼

    淺談Java的LinkedHashSet源碼

    這篇文章主要介紹了淺談Java的LinkedHashSet源碼,底層是鏈表實(shí)現(xiàn)的,是set集合中唯一一個(gè)能保證怎么存就怎么取的集合對(duì)象
    因?yàn)槭荋ashSet的子類(lèi),所以也是保證元素唯一的,與HashSet的原理一樣,需要的朋友可以參考下
    2023-09-09
  • Java模擬微信來(lái)電提醒示例

    Java模擬微信來(lái)電提醒示例

    這篇文章主要為大家介紹了Java模擬微信來(lái)電提醒示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07

最新評(píng)論