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之BeanPostProcessor(bean后置处理器) -> 正文阅读

[Java知识库]Spring之BeanPostProcessor(bean后置处理器)

一句话:bean后置处理器就是对bean的生命周期的各阶段的增强

1、bean生命周期

启动类:

@SpringBootApplication
public class A03 {
    public static void main(String[] args) {
        //创建容器
        ConfigurableApplicationContext context = 
        SpringApplication.run(A03.class, args);
        //关闭容器
        context.close();
    }
}

添加扫描bean

@Component
@Slf4j
public class LifeCycleBean {

    public LifeCycleBean() {
        log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>构造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}") String home) {
        log.debug("bean生命周期>>>>>>>>>>>>>>>>>>依赖注入: {}", home);
    }

    @PostConstruct
    public void init() {
        log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>初始化");
    }

    @PreDestroy
    public void destroy() {
        log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>销毁");
    }
}

输出结果:

[INFO ] 18:03:31.555 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat initialized with port(s): 8080 (http) 
[INFO ] 18:03:31.563 [main] o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"] 
[INFO ] 18:03:31.564 [main] o.a.catalina.core.StandardService   - Starting service [Tomcat] 
[INFO ] 18:03:31.564 [main] o.a.catalina.core.StandardEngine    - Starting Servlet engine: [Apache Tomcat/9.0.53] 
[INFO ] 18:03:31.662 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring embedded WebApplicationContext 
[INFO ] 18:03:31.663 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 994 ms 
[DEBUG] 18:03:31.710 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>构造 
[DEBUG] 18:03:31.713 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>依赖注入: E:\java\jdk\jdk8\tools 
[DEBUG] 18:03:31.714 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>初始化 
[INFO ] 18:03:31.917 [main] o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page: class path resource [static/index.html] 
[INFO ] 18:03:31.986 [main] c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource 
[INFO ] 18:03:32.055 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} inited 
[INFO ] 18:03:32.416 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"] 
[INFO ] 18:03:32.429 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring DispatcherServlet 'dispatcherServlet' 
[INFO ] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Initializing Servlet 'dispatcherServlet' 
[TRACE] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4eed2acf 
[TRACE] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@36fc05ff 
[TRACE] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.theme.FixedThemeResolver@57c47a9e 
[TRACE] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Detected DefaultRequestToViewNameTranslator 
[TRACE] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Detected SessionFlashMapManager 
[DEBUG] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 
[INFO ] 18:03:32.430 [main] o.s.web.servlet.DispatcherServlet   - Completed initialization in 0 ms 
[INFO ] 18:03:32.432 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat started on port(s): 8080 (http) with context path '' 
[INFO ] 18:03:32.441 [main] com.xc.a03.A03                      - Started A03 in 2.152 seconds (JVM running for 3.009) 
[INFO ] 18:03:32.713 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closing ... 
[INFO ] 18:03:32.714 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closed 
[DEBUG] 18:03:32.715 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>销毁 

Process finished with exit code 0

2、常用的bean后置处理器接口

2.1、初始化-Bean后置处理器

  • BeanPostProcessor是bean后置处理器最底层接口
  • 两个方法返回的bean会替换原来的bean
  • 初始化阶段在实例化和属性填充之后
  • 初始化之前执行:解析注解@PostConstruct、@ConfigurationProperties
  • 初始化之后执行:代理增强
public interface BeanPostProcessor {
	//bean初始化前执行
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
	//bean初始化后执行
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
  • 自定义初始化后置处理器
@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("初始化后置处理器>>>>>>> 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("初始化后置处理器>>>>>>>  初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}

2.2、实例化-Bean后置处理器

  • BeanPostProcessor的扩展接口(子接口)
  • 实例化之前执行:返回的bean对象可能是要使用的代理,而不是目标bean,从而有效地抑制了目标bean的默认实例化。如果此方法返回非null对象,则bean创建过程将被短路(也就是正常bean实例化的后续流程不再执行)
  • 实例化之后执行:返回结果为true时,说明应该为bean设置属性。如果返回结果为false,跳过bean属性赋值环节
  • 依赖注入阶段执行:解析注解@Autowired、@Value、@Resource
  • postProcessPropertyValues:这个方法在spring低版本中使用,在高版本已经过时了,使用postProcessProperties代替
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
	//bean实例化前执行
    @Nullable
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }
	//bean实例化后执行
    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }
	//依赖注入阶段执行
    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return null;
    }

    /** @deprecated */
    @Deprecated
    @Nullable
    default PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        return pvs;
    }
}
  • 自定义实例化后置处理器
@Component
@Slf4j
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("实例化后置处理器>>>>>>>  实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("实例化后置处理器>>>>>>>实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("实例化后置处理器>>>>>>> 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }
}

2.3、销毁-Bean后置处理器

  • BeanPostProcessor的扩展接口(子接口)
  • 销毁之前执行:解析注解@PreDestroy
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
    //这里实现销毁对象的逻辑
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
    //判断是否需要处理这个对象的销毁
    default boolean requiresDestruction(Object bean) {
        return true;
    }
}
  • 自定义销毁后置处理器
@Component
@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("销毁后置处理器>>>>>>>  销毁之前执行, 如 @PreDestroy");
    }
}

生命周期bean添加后置处理器后输出结果:

[INFO ] 18:07:14.639 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat initialized with port(s): 8080 (http) 
[INFO ] 18:07:14.646 [main] o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"] 
[INFO ] 18:07:14.646 [main] o.a.catalina.core.StandardService   - Starting service [Tomcat] 
[INFO ] 18:07:14.646 [main] o.a.catalina.core.StandardEngine    - Starting Servlet engine: [Apache Tomcat/9.0.53] 
[INFO ] 18:07:14.715 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring embedded WebApplicationContext 
[INFO ] 18:07:14.715 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 867 ms 
[DEBUG] 18:07:14.748 [main] c.x.a.MyInstantiationAwareBeanPostProcessor - 实例化后置处理器>>>>>>>  实例化之前执行, 这里返回的对象会替换掉原本的 bean 
[DEBUG] 18:07:14.749 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>构造 
[DEBUG] 18:07:14.750 [main] c.x.a.MyInstantiationAwareBeanPostProcessor - 实例化后置处理器>>>>>>>实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段 
[DEBUG] 18:07:14.750 [main] c.x.a.MyInstantiationAwareBeanPostProcessor - 实例化后置处理器>>>>>>> 依赖注入阶段执行,@Autowired@Value@Resource 
[DEBUG] 18:07:14.751 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>依赖注入: E:\java\jdk\jdk8\tools 
[DEBUG] 18:07:14.752 [main] com.xc.a03.MyBeanPostProcessor      - 初始化后置处理器>>>>>>> 初始化之前执行, 这里返回的对象会替换掉原本的 bean,@PostConstruct@ConfigurationProperties 
[DEBUG] 18:07:14.752 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>初始化 
[DEBUG] 18:07:14.752 [main] com.xc.a03.MyBeanPostProcessor      - 初始化后置处理器>>>>>>>  初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强 
[INFO ] 18:07:14.920 [main] o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page: class path resource [static/index.html] 
[INFO ] 18:07:14.986 [main] c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource 
[INFO ] 18:07:15.056 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} inited 
[INFO ] 18:07:15.273 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"] 
[INFO ] 18:07:15.285 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring DispatcherServlet 'dispatcherServlet' 
[INFO ] 18:07:15.285 [main] o.s.web.servlet.DispatcherServlet   - Initializing Servlet 'dispatcherServlet' 
[TRACE] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@57c47a9e 
[TRACE] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@642505c7 
[TRACE] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.theme.FixedThemeResolver@4339e0de 
[TRACE] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Detected DefaultRequestToViewNameTranslator 
[TRACE] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Detected SessionFlashMapManager 
[DEBUG] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 
[INFO ] 18:07:15.286 [main] o.s.web.servlet.DispatcherServlet   - Completed initialization in 0 ms 
[INFO ] 18:07:15.287 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat started on port(s): 8080 (http) with context path '' 
[INFO ] 18:07:15.295 [main] com.xc.a03.A03                      - Started A03 in 1.781 seconds (JVM running for 2.529) 
[INFO ] 18:07:15.581 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closing ... 
[INFO ] 18:07:15.582 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closed 
[DEBUG] 18:07:15.584 [main] c.x.a.MyDestructionAwareBeanPostProcessor - 销毁后置处理器>>>>>>>  销毁之前执行,@PreDestroy 
[DEBUG] 18:07:15.584 [main] com.xc.a03.LifeCycleBean            - bean生命周期>>>>>>>>>>>>>>>>>>>销毁 

Process finished with exit code 0

3、模板方法设计模式,扩展bean后置处理器

  • getBean方法执行流程步骤已确定
  • 后置处理器接口集合遍历,添加后置处理器只需要bean工厂添加到后置处理器集合汇总即可
public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }
}

// 模板方法
class MyBeanFactory {
    private final List<BeanPostProcessor> processors = new ArrayList<>();

    public void addBeanPostProcessor(BeanPostProcessor processor) {
        processors.add(processor);
    }

    public Object getBean() {
        Object bean = new Object();
        System.out.println("构造 " + bean);
        System.out.println("依赖注入 " + bean); // @Autowired, @Resource
        for (BeanPostProcessor processor : processors) {
            processor.inject(bean);
        }
        System.out.println("初始化 " + bean);
        return bean;
    }
}

interface BeanPostProcessor {
    public void inject(Object bean); // 对依赖注入阶段的扩展
}

输出结果:

构造 java.lang.Object@533ddba
依赖注入 java.lang.Object@533ddba
解析 @Autowired
解析 @Resource
初始化 java.lang.Object@533ddba

Process finished with exit code 0

4、bean后置处理器排序

  • 实现了 PriorityOrdered 接口的优先级最高
  • 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
  • 其它的排在最后
public class TestProcessOrder {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
		//排序
        List<BeanPostProcessor> list = Arrays.asList(new P1(), new P2(), new P3(), new P4(), new P5());
        list.sort(beanFactory.getDependencyComparator());
		//遍历执行后置处理器的初始化之前方法
        list.forEach(processor->{
            processor.postProcessBeforeInitialization(new Object(), "");
        });
    }
}

@Order(1)
class P1 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization @Order(1)");
        return bean;
    }
}

@Order(2)
class P2 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization @Order(2)");
        return bean;
    }
}

class P3 implements BeanPostProcessor, PriorityOrdered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization PriorityOrdered getOrder() == 100");
        return bean;
    }
    @Override
    public int getOrder() {
        return 100;
    }
}

class P4 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization Ordered getOrder() == 0");
        return bean;
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

class P5 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization 无排序接口");
        return bean;
    }
}

输出结果:

Connected to the target VM, address: '127.0.0.1:63224', transport: 'socket'
postProcessBeforeInitialization PriorityOrdered getOrder() == 100
postProcessBeforeInitialization Ordered getOrder() == 0
postProcessBeforeInitialization @Order(1)
postProcessBeforeInitialization @Order(2)
postProcessBeforeInitialization 无排序接口
Disconnected from the target VM, address: '127.0.0.1:63224', transport: 'socket'

Process finished with exit code 0

5、bean后置处理器实现类的作用

准备bean:

@Slf4j
public class Bean1 {

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }

    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
    }

    @PostConstruct
    public void init() {
        log.debug("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy() {
        log.debug("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + '\'' +
               '}';
    }
}
public class Bean2 {
}
public class Bean3 {
}
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + '\'' +
               ", version='" + version + '\'' +
               '}';
    }
}

启动类:

public class A04 {
    public static void main(String[] args) {
        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();

        // 用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        // 解析表达式
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // @Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

        // @Resource @PostConstruct @PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class); 

		//解析配置文件properties对象
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
        context.refresh(); 

        System.out.println(context.getBean(Bean1.class));

        // 销毁容器
        context.close();
    }
}

输出结果:

[DEBUG] 22:37:41.613 [main] com.xc.a04.Bean1                    - @Resource 生效: com.xc.a04.Bean3@f2f2cc1 
[DEBUG] 22:37:41.661 [main] com.xc.a04.Bean1                    - @Value 生效: E:\java\jdk\jdk8\tools 
[DEBUG] 22:37:41.664 [main] com.xc.a04.Bean1                    - @Autowired 生效: com.xc.a04.Bean2@2fd6b6c7 
[DEBUG] 22:37:41.664 [main] com.xc.a04.Bean1                    - @PostConstruct 生效 
Bean1{bean2=com.xc.a04.Bean2@2fd6b6c7, bean3=com.xc.a04.Bean3@f2f2cc1, home='E:\java\jdk\jdk8\tools'}
[DEBUG] 22:37:41.905 [main] com.xc.a04.Bean1                    - @PreDestroy 生效 

Process finished with exit code 0

总结:后置处理器解析@Autowired@Resource@PostConstruct等一些列注解处理bean生命周期阶段(依赖注入, 初始化)的扩展功能

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

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