Spring相关面试题(三)

29 如何在所有的BeanDefinition注册完成后,进行扩展

Bean工厂的后置处理器,在所有的Bean注册完成后,就被执行。

复制代码
public class A implements BeanFactoryPostProcessor {
    private String name = "a class";
    private B b;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //在所有的BeanDefinition注册完成之后调用
        for (String beanDefinitionName : configurableListableBeanFactory.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }
}
30 Bean的生产顺序由什么决定的?

创建一个ApplicationContext,会调用refresh方法,再去调用finishBeanFactoryInitialization(),完成BeanFactory的初始化,创建所有的BeanDefinitionNames,是个ArrayList,按照注册顺序,进行创建,依赖关系也会影响Bean的创建顺序。

BeanDefinition的注册顺序的由来?

主要是由注解(配置)的解析顺序进行的。

  1. @Configuration

  2. @Component

  3. @Import 导入的一个类

  4. @Bean

  5. @Import------ImportBeanDefinitionRegister

也可以使用@Order来决定顺序,后面注册的会覆盖掉前面的注册的。

BeanDefinitionRegistryPostProcessor

31 Spring有哪几种配置方式?
  1. 注解 Spring 2.5 +

    1. Spring.xml
  2. XML

    1. Spring.xml <component-scan base-package=""/> @Component @Autowired
  3. 基于Java配置

    1. JavaConfig Spring 3.0+

    2. @Configuration @Component

32 JavaConfig 如何替代 Spring.XML的配置方式的

应用

  1. 以前XML

    1. Spring容器是new ClassPathXMLApplicationContext()

    2. Spring.XML

    3. <Bean scope="" >

    4. <component-scan />

    5. 引入外部属性配置文件:<property-placeHodeler resource="">

    6. 指定其他配置文件 @PropertySource("classpath:db.properties")

    7. <property name="pass" value="${mysql.password}"></property>

  2. JavaConfig

    1. Spring容器是 nw AnnotationConfigApplicationContext(javaConfig.class)

    2. JavaConfig.java ===> @Configuration

    3. @Bean @Scope @Lazy

    4. @ComponentScan("")

    5. 引入外部配置文件@PropertySource() 外部属性配置文件。

    6. @Import 使用配置比较灵活 @Import({Config.class})

    7. @Value("${mysql.pass}")

源码

总的来说,整体的执行步骤都是一样的

即:读取配置文件或配置文件XML,解析配置文件或者XML配置文件,后面的步骤就是相同的,注册BeanDefinition,生产Bean.

29 如何在所有的BeanDefinition注册完成后,进行扩展

Bean工厂的后置处理器,在所有的Bean注册完成后,就被执行。

复制代码
public class A implements BeanFactoryPostProcessor {
    private String name = "a class";
    private B b;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //在所有的BeanDefinition注册完成之后调用
        for (String beanDefinitionName : configurableListableBeanFactory.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }
}
30 Bean的生产顺序由什么决定的?

创建一个ApplicationContext,会调用refresh方法,再去调用finishBeanFactoryInitialization(),完成BeanFactory的初始化,创建所有的BeanDefinitionNames,是个ArrayList,按照注册顺序,进行创建,依赖关系也会影响Bean的创建顺序。

BeanDefinition的注册顺序的由来?

主要是由注解(配置)的解析顺序进行的。

  1. @Configuration

  2. @Component

  3. @Import 导入的一个类

  4. @Bean

  5. @Import------ImportBeanDefinitionRegister

也可以使用@Order来决定顺序,后面注册的会覆盖掉前面的注册的。

BeanDefinitionRegistryPostProcessor

31 Spring有哪几种配置方式?
  1. 注解 Spring 2.5 +

    1. Spring.xml
  2. XML

    1. Spring.xml <component-scan base-package=""/> @Component @Autowired
  3. 基于Java配置

    1. JavaConfig Spring 3.0+

    2. @Configuration @Component

32 JavaConfig 如何替代 Spring.XML的配置方式的

应用

  1. 以前XML

    1. Spring容器是new ClassPathXMLApplicationContext()

    2. Spring.XML

    3. <Bean scope="" >

    4. <component-scan />

    5. 引入外部属性配置文件:<property-placeHodeler resource="">

    6. 指定其他配置文件 @PropertySource("classpath:db.properties")

    7. <property name="pass" value="${mysql.password}"></property>

  2. JavaConfig

    1. Spring容器是 nw AnnotationConfigApplicationContext(javaConfig.class)

    2. JavaConfig.java ===> @Configuration

    3. @Bean @Scope @Lazy

    4. @ComponentScan("")

    5. 引入外部配置文件@PropertySource() 外部属性配置文件。

    6. @Import 使用配置比较灵活 @Import({Config.class})

    7. @Value("${mysql.pass}")

源码

总的来说,整体的执行步骤都是一样的

即:读取配置文件或配置文件XML,解析配置文件或者XML配置文件,后面的步骤就是相同的,注册BeanDefinition,生产Bean.

33 @Component @Controller @Repository @Service 有何区别?

Controller、Repository和Service都是基于Component注解。

@Controller 放在控制层

@Service 放在业务层

@Repository 放在数据层

提高代码可阅读性。

34 @Import有几种用法。
  1. 直接指定类(如果配置类会按配置类正常解析、如果是个普通类就会解析成Bean)

  2. 写个实现ImportSelector的实现类

    复制代码
    public class MyInportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.xing.Import.heihei"};
        }
    }

    一次性可以注册多个。

  3. 实现ImportBeanDefinitionRegistrar,RegisterBeanDefinitions方法中,创建BeanDefinition。

35 如何让自动注入没有找到依赖Bean时不报错

@Autowired(required = false)

36 如何让找到多个依赖Bean的时候不报错

可以是在主要Bean上加入:@Primary注解。

37 @Autowired的作用

@Autowired的是自动注入,先按照类型,再按照名字注入。

38 @Autowired和@Resource的区别
  1. Autowired是Spring提供的,Resource是JDK提供的。

  2. Autowired默认根据类型匹配注入,Resource根据名字进行匹配,然后再根据类型进行匹配。

39 @Autowired注解自动装配的过程是怎样的

记住:@Autowired是通过Bean的后置处理器,进行解析的,可以用在:构造方法、普通方法、参数、字段和注解上。

  1. 核心方法:buildAutowiringMetadata

    1. 通过反射获取该类的每一个字段和方法,并且分别用 findAutowiredAnnotation方法遍历每一个字段和方法,如果有@Autowired注解修饰,则返回注解相关属性。最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。
  2. InjectionMetadata类,这个类由两部分组成: targetClass目标类和我们要获得的injectedElements集合。

  3. 实现注入逻辑:调用InjectionMetadata中的公共inject方法遍历调用protect的inject方法

  4. 调用InjectionMetadata中的公共inject

  5. 遍历调用protect的inject方法

40 @Configuration作用及原理解析

作用

  1. @Configuration用来代替xml配置方式spring.xml配置文件<bean>

  2. 但是没有@Configuration也可以配置@Bean

    1. 那加和不加有什么区别呢?

    2. 加了@Configuration会为配置类创建一个cblib动态代理(保证配置类中的@Bean是单例),@Bean方法的调用就会通过容器的getBean获取。

原理

  1. 创建Spring上下文的时候会注册一个解析配置类的处理器:ConfigurationClassPostProcessor(实现了BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)

  2. 调用invokeBeanFactoryPostProcessor方法,就会去调用ConfigurationClassPostProcessor的对应的方法(postProcessBeanDefinitionRegistry)进行解析配置(解析配置类说白了就是去解析各种注解(@Bean,@Configuration,@Import ),就是注册BeanDefinition)

  3. ConfigurationClassPostProcessor.postProcessorBeanFactory创建cblib动态代理。

@Bean怎么保证Bean的单例的。

@Configuration如何保证@Bean单例语义?_@bean 单例-CSDN博客

  1. @Bean方法返回的对象是单例的,需要在类上加上@Configuration
41 要将一个第三方的类配置成为Bean有哪些方式
  1. @Bean

  2. @Import(DruidDataSource.class) 和@ImportSelector不能干预实例化过程。

  3. 写个类实现ImportBeanDefinitionRegistrar

    1. 实现registerBeanDefinitions

      复制代码
      public class index implements ImportBeanDefinitionRegistrar {
      ​
          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
              RootBeanDefinition A = new RootBeanDefinition(A.class);
              //拿到props values
              A.getPropertyValues().addPropertyValue("name","wangxing");
              registry.registerBeanDefinition("a",A);
          }
      }
    2. 这样可以干涉bean的创建

  4. 实现BeanDefinitionRegistryPostProcessor

    1. 也是拿到注册器,和上面一样。
  5. 后两种方法都是在SpringIOC的扩展点拿到BeanDefinitionRegistrar,然后注入BeanDefinition。

42 为什么@ComponentScan 不设置basePackage也会扫描?

因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有涉及basePackage会把当前所在类的位置,作为扫描包。

43 什么是AOP,能做什么?

AOP(Aspect - Originted Proaramming),一般称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为"切面"(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。 -

AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。0OP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清浙高效的逻辑单元划分。而AOP作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取,已达到业务代码和公共行为代码之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

44 在Spring中AOP如何使用?

链接:Spring中使用AOP的几种方式_doaround-CSDN博客

XML方式

XML + 注解

注解方式

45 Spring AOP 中常见的名词
  1. 切面(Aspect):在Spring AOP指的就是切面类,切面类会管理着通知和切入点。

  2. 连接点(Join point) :被增强的业务方法。

  3. 通知(Advice) : 就是需要增强到业务方法中的公共代码,通知有很多种类分别可以在需要增强的业务方法不同位置进行(前置,后置,异常,返回,环绕通知)。

  4. 切入点(Pointcut):由切点表达式决定需要增强的方法,通常通过切点表达式进行。

  5. 目标对象(Target Object):增强的对象。

  6. 顾问:Advisor的一个包装,Advisor是Pointcut以及Advice的一个结合,用来管理Advice和Pointcut。

  7. 织入(Weaving) :AspectJ中独有的内容,Spring AOP :动态代理。为目标对象创建动态代理的过程叫做织入。

    1. 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种

    2. 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器之可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。

    3. 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。

46 Spring通知有哪些类型?

AOP:在某个特定的连接点(方法),执行的动作(代码)。

  1. 前置通知(Before)

  2. 后置通知(After)

  3. 返回通知(After-returning)

  4. 异常通知(After-throwing)

  5. 环绕通知(Around)

执行顺序

正常: 前置 -> 方法 -> 后置 -> 返回。

异常:前置 -> 方法 -> 后置 -> 异常

Spring 5.2.7 之后

正常: 前置 -> 方法 -> 返回 -> 后置

异常: 前置 -> 方法 -> 异常 -> 后置

47 SpringAOP和AspectJ AOP有什么区别?

AspectJ AOP也是一个面向切面的开源框架,如果要使用Spring中的AOP功能,需要引入对应的AspectJ AOP 依赖,但SpringAOP使用的仅仅是AspectJ AOP 的切点解析和匹配,AspectJ AOP 中的一些注解。

  • AspectJ AOP 使用的静态代理。

    • AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将Aspect(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
  • Spring AOP 使用的是动态代理。

    • 如果实现接口,是JDK的动态代理

    • 没有实现接口就是CGLIB实现动态代理

SpringAOP致力解决企业中开发的普遍问题,AspectJ是AOP编程的完全解决方案。还有SpringAOP的性能不如AspectJ,因为SpringAOP做的是动态代理,会在容器初始化的时候加载,会加深调用栈。

48 JDK动态代理和CGLIB动态代理的区别
  • 如果实现接口,是JDK的动态代理

    • JDK会在运行的时候,为目标类生成一个动态代理类$proxy*.class

    • 该代理类是实现了接目标类接口,并且代理类会实现接口所有的方法增强代码。

    • 调用时 通过代理类先去调用处理类进行增强,再通过反射的方式进行调用目标方法。从而实现AOP

  • 没有实现接口就是CGLIB实现动态代理

    • 并且会重写父类所有的方法增强代码,CGLIB的底层是通过ASM在运行时动态的生成目标类的一个子类。 (还有其他相关类,主要是为增强调用时效率)会生成多个

    • 调用时先通过代理类进行增强,再直接调用父类对应的方法进行调用目标方法。从而实现AOP。

      • final标记的类是无法实现CGLIB代理的。

JDK生成的快,调用慢;CGLIB生产慢,调用快,但是在JDK老版本中,CGLIB的速度会比JDK快,但是随着JDK的更新,JDK是要比CGLIB快

33 @Component @Controller @Repository @Service 有何区别?

Controller、Repository和Service都是基于Component注解。

@Controller 放在控制层

@Service 放在业务层

@Repository 放在数据层

提高代码可阅读性。

34 @Import有几种用法。
  1. 直接指定类(如果配置类会按配置类正常解析、如果是个普通类就会解析成Bean)

  2. 写个实现ImportSelector的实现类

    复制代码
    public class MyInportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.xing.Import.heihei"};
        }
    }

    一次性可以注册多个。

  3. 实现ImportBeanDefinitionRegistrar,RegisterBeanDefinitions方法中,创建BeanDefinition。

35 如何让自动注入没有找到依赖Bean时不报错

@Autowired(required = false)

36 如何让找到多个依赖Bean的时候不报错

可以是在主要Bean上加入:@Primary注解。

37 @Autowired的作用

@Autowired的是自动注入,先按照类型,再按照名字注入。

38 @Autowired和@Resource的区别
  1. Autowired是Spring提供的,Resource是JDK提供的。

  2. Autowired默认根据类型匹配注入,Resource根据名字进行匹配,然后再根据类型进行匹配。

39 @Autowired注解自动装配的过程是怎样的

记住:@Autowired是通过Bean的后置处理器,进行解析的,可以用在:构造方法、普通方法、参数、字段和注解上。

  1. 核心方法:buildAutowiringMetadata

    1. 通过反射获取该类的每一个字段和方法,并且分别用 findAutowiredAnnotation方法遍历每一个字段和方法,如果有@Autowired注解修饰,则返回注解相关属性。最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。
  2. InjectionMetadata类,这个类由两部分组成: targetClass目标类和我们要获得的injectedElements集合。

  3. 实现注入逻辑:调用InjectionMetadata中的公共inject方法遍历调用protect的inject方法

  4. 调用InjectionMetadata中的公共inject

  5. 遍历调用protect的inject方法

40 @Configuration作用及原理解析

作用

  1. @Configuration用来代替xml配置方式spring.xml配置文件<bean>

  2. 但是没有@Configuration也可以配置@Bean

    1. 那加和不加有什么区别呢?

    2. 加了@Configuration会为配置类创建一个cblib动态代理(保证配置类中的@Bean是单例),@Bean方法的调用就会通过容器的getBean获取。

原理

  1. 创建Spring上下文的时候会注册一个解析配置类的处理器:ConfigurationClassPostProcessor(实现了BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)

  2. 调用invokeBeanFactoryPostProcessor方法,就会去调用ConfigurationClassPostProcessor的对应的方法(postProcessBeanDefinitionRegistry)进行解析配置(解析配置类说白了就是去解析各种注解(@Bean,@Configuration,@Import ),就是注册BeanDefinition)

  3. ConfigurationClassPostProcessor.postProcessorBeanFactory创建cblib动态代理。

@Bean怎么保证Bean的单例的。

@Configuration如何保证@Bean单例语义?_@bean 单例-CSDN博客

  1. @Bean方法返回的对象是单例的,需要在类上加上@Configuration
41 要将一个第三方的类配置成为Bean有哪些方式
  1. @Bean

  2. @Import(DruidDataSource.class) 和@ImportSelector不能干预实例化过程。

  3. 写个类实现ImportBeanDefinitionRegistrar

    1. 实现registerBeanDefinitions

      复制代码
      public class index implements ImportBeanDefinitionRegistrar {
      ​
          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
              RootBeanDefinition A = new RootBeanDefinition(A.class);
              //拿到props values
              A.getPropertyValues().addPropertyValue("name","wangxing");
              registry.registerBeanDefinition("a",A);
          }
      }
    2. 这样可以干涉bean的创建

  4. 实现BeanDefinitionRegistryPostProcessor

    1. 也是拿到注册器,和上面一样。
  5. 后两种方法都是在SpringIOC的扩展点拿到BeanDefinitionRegistrar,然后注入BeanDefinition。

42 为什么@ComponentScan 不设置basePackage也会扫描?

因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有涉及basePackage会把当前所在类的位置,作为扫描包。

43 什么是AOP,能做什么?

AOP(Aspect - Originted Proaramming),一般称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为"切面"(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。 -

AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。0OP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清浙高效的逻辑单元划分。而AOP作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取,已达到业务代码和公共行为代码之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

44 在Spring中AOP如何使用?

链接:Spring中使用AOP的几种方式_doaround-CSDN博客

XML方式

XML + 注解

注解方式

45 Spring AOP 中常见的名词
  1. 切面(Aspect):在Spring AOP指的就是切面类,切面类会管理着通知和切入点。

  2. 连接点(Join point) :被增强的业务方法。

  3. 通知(Advice) : 就是需要增强到业务方法中的公共代码,通知有很多种类分别可以在需要增强的业务方法不同位置进行(前置,后置,异常,返回,环绕通知)。

  4. 切入点(Pointcut):由切点表达式决定需要增强的方法,通常通过切点表达式进行。

  5. 目标对象(Target Object):增强的对象。

  6. 顾问:Advisor的一个包装,Advisor是Pointcut以及Advice的一个结合,用来管理Advice和Pointcut。

  7. 织入(Weaving) :AspectJ中独有的内容,Spring AOP :动态代理。为目标对象创建动态代理的过程叫做织入。

    1. 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种

    2. 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器之可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。

    3. 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。

46 Spring通知有哪些类型?

AOP:在某个特定的连接点(方法),执行的动作(代码)。

  1. 前置通知(Before)

  2. 后置通知(After)

  3. 返回通知(After-returning)

  4. 异常通知(After-throwing)

  5. 环绕通知(Around)

执行顺序

正常: 前置 -> 方法 -> 后置 -> 返回。

异常:前置 -> 方法 -> 后置 -> 异常

Spring 5.2.7 之后

正常: 前置 -> 方法 -> 返回 -> 后置

异常: 前置 -> 方法 -> 异常 -> 后置

47 SpringAOP和AspectJ AOP有什么区别?

AspectJ AOP也是一个面向切面的开源框架,如果要使用Spring中的AOP功能,需要引入对应的AspectJ AOP 依赖,但SpringAOP使用的仅仅是AspectJ AOP 的切点解析和匹配,AspectJ AOP 中的一些注解。

  • AspectJ AOP 使用的静态代理。

    • AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将Aspect(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
  • Spring AOP 使用的是动态代理。

    • 如果实现接口,是JDK的动态代理

    • 没有实现接口就是CGLIB实现动态代理

SpringAOP致力解决企业中开发的普遍问题,AspectJ是AOP编程的完全解决方案。还有SpringAOP的性能不如AspectJ,因为SpringAOP做的是动态代理,会在容器初始化的时候加载,会加深调用栈。

48 JDK动态代理和CGLIB动态代理的区别
  • 如果实现接口,是JDK的动态代理

    • JDK会在运行的时候,为目标类生成一个动态代理类$proxy*.class

    • 该代理类是实现了接目标类接口,并且代理类会实现接口所有的方法增强代码。

    • 调用时 通过代理类先去调用处理类进行增强,再通过反射的方式进行调用目标方法。从而实现AOP

  • 没有实现接口就是CGLIB实现动态代理

    • 并且会重写父类所有的方法增强代码,CGLIB的底层是通过ASM在运行时动态的生成目标类的一个子类。 (还有其他相关类,主要是为增强调用时效率)会生成多个

    • 调用时先通过代理类进行增强,再直接调用父类对应的方法进行调用目标方法。从而实现AOP。

      • final标记的类是无法实现CGLIB代理的。

JDK生成的快,调用慢;CGLIB生产慢,调用快,但是在JDK老版本中,CGLIB的速度会比JDK快,但是随着JDK的更新,JDK是要比CGLIB快

相关推荐
Abladol-aj1 小时前
并发和并行的基础知识
java·linux·windows
清水白石0081 小时前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
吾日三省吾码6 小时前
JVM 性能调优
java
Estar.Lee6 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi778 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610038 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员