springboot原理实战(2)-- applicationContext获取的3种方式和原理

简介: springboot原理实战(2)-- applicationContext获取的3种方式和原理

目录


先看下脑图

1dc618a0ed9580ce8bfa6facb208c08f.png


方式1:@Autowire


@Component
public class Demo1 {
    @Autowired
    private ApplicationContext applicationContext;
    public void show() {
        System.out.println("Demo1: " + applicationContext);
    }
}


测试


public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
        context.getBean(Demo1.class).show();
    }
}


运行结果,显示已经拿到ApplicationContext 了:

5d4c6812c8535adbb050f4ddf2e1bce8.png


方式2:构造方法


spring4.3新特性:构造方法,注入 只能有一个构造函数,此时,如如果有多个spring会使用无参的构造函数


@Component
public class Demo2 {
    private ApplicationContext applicationContext;
    //4.3+的新特性
    //只能有一个构造函数
    public Demo2(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    public void show() {
        System.out.println("Demo2: " + applicationContext.getClass());
    }
}


测试:


public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
//        context.getBean(Demo1.class).show();
        context.getBean(Demo2.class).show();
    }
}


运行结果,显示已经拿到ApplicationContext 了:

1dc618a0ed9580ce8bfa6facb208c08f.png


方式3:实现ApplicationContextAware


1.获取方式demo


import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class Demo3 implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    public void show() {
        System.out.println("Demo3: " + applicationContext);
    }
}


测试:


import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
//        context.getBean(Demo1.class).show();
//        context.getBean(Demo2.class).show();
        context.getBean(Demo3.class).show();
    }
}

运行结果,显示已经拿到ApplicationContext 了:



2.获取原理实现BeanPostProcessor接口


①BeanPostProcessor是啥?有啥用?


作用就是可以在spring装配和初始bean的过程中,搞点小动作。

5d4c6812c8535adbb050f4ddf2e1bce8.png


他有2个方法:


postProcessBeforeInitialization:在bean依赖装配(属性设置完)完成之后触发,这里可以指定的bean做一些处理,比如说,返回该对象的代理对象。


postProcessAfterInitialization:bean init方法执行之后触发。


用代码说明:


我们自己实现这个BeanPostProcessor接口:


/**
 * EchoBeanPostProcessor 会在每个bean初始化的时候,调用一次
 * 两个方法不能返回null,否则,从容器中获取不到
 */
@Component
public class EchoBeanPostProcessor implements BeanPostProcessor {
    /**
     * 实在bean依赖装配(属性设置完)完成之后触发
     *
     *  这里可以指定的bean做一些处理,比如说,返回该对象的代理对象
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {       System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass());
        return bean;
    }
    /**
     * 是在bean init方法执行之后触发
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass());
        return bean;
    }
}


这个类被spring容器管理后,会影响每个bean的装配加载初始化,就像过滤器filter对应于每个请求一样,可以理解为spring装配完bean后加了个bean的后置处理器。

测试一波:


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(Book.class).show();
        context.getBean(Bank.class).show();
    }
}


看见这2个bean装载后,调用自己的方法前(使用前),调用了我们自己的逻辑。


1dc618a0ed9580ce8bfa6facb208c08f.png


②利用BeanPostProcessor做个代理对象的小demo


我们来实现一个功能: 当User对象装载时,加入我们的日志信息:

User


@Component
public class User {
     @PostConstruct
    public void init() {
        System.out.println("user init2");
    }
    private ApplicationContext applicationContext;
    public void show() {
        System.out.println("User: " + applicationContext);
    }
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        System.out.println("========set===========");
    }
}


子类LoginUser


public class LogUser extends User{
    public void show() {
        System.out.println("log start ..");
        super.show();
        System.out.println("log end ..");
    }
}


添加我们的逻辑判断,如果是User装载,就调用LoginUser的方法:


@Component
public class EchoBeanPostProcessor implements BeanPostProcessor {
    /**
     * 实在bean依赖装配(属性设置完)完成之后触发
     *
     *  这里可以指定的bean做一些处理,比如说,返回该对象的代理对象
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User) {
            return new LogUser();
        }
        System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass());
        return bean;
    }
    /**
     * 是在bean init方法执行之后触发
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass());
        return bean;
    }
}

测试:


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(User.class).show();
    }
}



结果显示:


①在bean设置完属性后开始调用postProcessBeforeInitialization,

②在这个方法中,直接返回了loginUser,然后User替换成LoginUser后,

③执行loginUser初始化后,

④调用postProcessAfterInitialization,

⑤ 调用了LoginUser的show方法。

5d4c6812c8535adbb050f4ddf2e1bce8.png


②方式3通过BeanPostProcessor就获取applcationContext具体做法:


这个得读源码:


方式3:从外观上看是实现了ApplicationContextAware就可以获取applcationContext了。


那么我们debug起来:


在AnnotationConfigApplicationContext源码中


1dc618a0ed9580ce8bfa6facb208c08f.png5d4c6812c8535adbb050f4ddf2e1bce8.png46a9d80a6e05e4e3b19d57a0ee70bcdf.png


进入这个类:


ApplicationContextAwareProcessor

发现1,实现了beanPostProcessor接口,

66ba272a0bfc97be54a5fa679e3d5482.png

发现2:这个类下面的代码就是最终答案:在这里判断如果实现了ApplicationContextWare,就注入了ApplicationContext这个属性。

1dc618a0ed9580ce8bfa6facb208c08f.png


方式4:自己实现获取


我们了解了这个原理,就可自己实现个ApplicationWare的功能,然后我们也可以获取ApplicationContext对象。


import org.springframework.context.ApplicationContext;
public interface SpringContentAware {
    public void setApplicationContext(ApplicationContext applicationContext);
}


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class ContextBeanPostProcessor implements BeanPostProcessor {
    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof  SpringContentAware){
            SpringContentAware sca = (SpringContentAware) bean;
            sca.setApplicationContext(applicationContext);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}


测试下我们写的这个SpringContentAware 能否拿到applicationContext:


@Component
public class Dog implements SpringContentAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    public void show() {
        System.out.println("Dog: " + applicationContext);
    }
}


test类测试:


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(Dog.class).show();
    }
}


运行结果,显示已经拿到了:



相关文章
|
3天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
13 0
|
7天前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
44 17
|
28天前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
51 4
|
4月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
118 0
|
2月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
2月前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
|
4月前
|
SQL Java 数据库连接
springboot~mybatis-pagehelper原理与使用
【7月更文挑战第15天】MyBatis-PageHelper是用于MyBatis的分页插件,基于MyBatis的拦截器机制实现。它通过在SQL执行前动态修改SQL语句添加LIMIT子句以支持分页。使用时需在`pom.xml`添加依赖并配置方言等参数。示例代码: PageHelper.startPage(2, 10); List<User> users = userMapper.getAllUsers(); PageInfo<User> pageInfo = new PageInfo<>(users); 这使得分页查询变得简单且能获取总记录数等信息。
102 2
|
4月前
|
Java 开发者 Spring
深入理解Spring Boot中的自动配置原理
深入理解Spring Boot中的自动配置原理
|
4月前
|
开发框架 Java 开发者
Spring Boot中的自动装配原理
Spring Boot中的自动装配原理
|
5月前
|
消息中间件 Java Maven
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter