胖胖的枫叶
主页
展示
博客
产品设计
企业架构
全栈开发
效率工具
数据分析
项目管理
方法论
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
主页
展示
博客
产品设计
企业架构
全栈开发
效率工具
数据分析
项目管理
方法论
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
  • 标签索引
  • 2024年

    • 勇闯大A,宏观美债与美元周期
    • 勇闯大A,自我管理
    • 勇闯大A,交易系统
    • 勇闯大A,买入卖出
    • 勇闯大A,债券市场
    • 勇闯大A,微观&宏观与周期
    • 配置Mac环境
    • 勇闯大A,内外盘&换手率
    • 业务知识会计管理
    • 业务知识会计基础
    • 业务知识什么是财务
  • 2023年

    • 泰国投资项目BOI
  • 2022年

    • 企业架构故障管理
    • 企业架构开发债务
  • 2021年

    • Python3.8 Matplotlib员工数据分析
    • Python3.8 Matplotlib IP折线图
    • Python3.8 词云 IP地址
    • Redis RediSearch
    • Rust第一个CLI程序
    • Rust所有权
    • Rust函数与控制流
    • Rust变量与数据类型
    • Rust入门
    • 企业架构分布式系统
    • 编程式权限设计
    • Java JVM优化
    • SpringBoot MyBatis 批量
    • SpringBoot 测试Mock
    • SpringBoot Redis布隆过滤器
    • CentOS7 Jenkins 部署
    • SpringBoot WebClient
    • Docker Drone 部署
    • SpringBoot MyBatis
    • SpringBoot Redisson
    • SpringBoot MyBatis 雪花算法
    • Java Netty
    • Redis 扫描
    • CentOS7 Jenkins本地部署分级
    • Mac 安装 Neo4j Jupyter
    • Mac OpenJDK11 JavaFX 环境
    • Mac 安装 Jenv
    • SpringBoot Redis 延时队列
    • SpringBoot MDC日志
    • SpringBoot 定时任务
    • CentOS7 Nginx GoAccess
    • SpringBoot MyBatis 分析
    • SpringBoot Lucene
    • 企业架构分布式锁
    • 学习技巧减少学习排斥心理
    • SpringBoot 动态数据源
    • Docker Compose SpringBoot MySQL Redis
    • SpringBoot 阻塞队列
    • Docker Compose Redis 哨兵
    • Docker Compose Redis 主从
    • 网络通信
  • 2020年

    • SpringBoot 延时队列
    • MySQL基础(四)
    • Java 雪花算法
    • Redis Geo
    • 网络通信 Tcpdump
    • Spring SPI
    • Java Zookeeper
    • SpringBoot JMH
    • 网络通信 Wireshark
    • Docker Compose Redis MySQL
    • CentOS7 Docker 部署
    • Netty 源码环境搭建
    • MySQL基础(三)
    • CentOS7 Selenium运行环境
    • CentOS7 Nginx HTTPS
    • Java JMH
    • SpringBoot 修改Tomcat版本
    • Java Eureka 钉钉通知
    • SpringBoot 错误钉钉通知
    • Java JVM
    • Git 合并提交
    • CentOS7 OpenResty 部署
  • 2019年

    • Redis CLI
    • CentOS7 Nginx 日志
    • 编程式代码风格
    • IDEA 插件
    • Skywalking 源码环境搭建
    • SpringBoot Redis 超时错误
    • 编程式 gRPC
    • Java Arthas
    • Docker Compose Redis 缓存击穿
    • Docker ElasticSearch5.6.8 部署
    • Docker Mysql5.7 部署
    • Spring Redis 字符串
    • Docker Zookeeper 部署
    • Docker Redis 部署
    • SpringBoot Dubbo
    • CentOS7 CMake 部署
    • 应用程序性能指标
    • Java Code 递归
    • CentOS7 ELK 部署
    • CentOS7 Sonarqube 部署
    • Java Selenium
    • Java JJWT JUnit4
    • Spring 源码环境搭建
    • Java JUnit4
    • Java Web JSON Token
    • 编程式 FastDFS
    • Java XPath
    • Redis基础(二)
    • Redis基础(一)
    • Java MyBatis JUnit4
    • Java MyBatis H2 JUnit4
    • MyBatis 源码环境搭建
    • Git 配置
    • Java 核心
    • Java Dubbo
    • Java JavaCollecionsFramework
    • Java Maven
    • Java MyBatis
    • Java Spring
    • Java SpringMVC
    • MySQL
    • Redis
  • 2018年

    • Java HashMap
    • Java HashSet
    • Java Code 交换值
    • Spring Upgrade SpringBoot
    • Mac 编程环境
    • Java Log4j
    • 网络通信 Modbus
    • MySQL基础(二)
    • MySQL基础(一)
    • Java Stack
    • Java Vector
    • CentOS7 RabbitMQ 部署
    • CentOS7 Redis 部署
    • CentOS7 MongoDB 部署
    • CentOS7 基础命令
    • Java Eureka Zookeeper
    • CentOS7 MySQL 部署
    • Git 分支
    • CentOS7 Java环境配置
    • Java LinkedList
    • Java ArrayList
    • Spring Annotation Aop

Java Spring

本章是整理知识内容,为强化知识长期更新。

Spring

  • Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

Spring IOC = Spring Bean + Spring Context特性

  • 主要由以下几个模块组成:
    • Spring Core:核心类库,提供IOC服务
    • Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)
    • Spring AOP:AOP服务切面编程
    • Spring Type Conversion:类型转换
    • Spring Data Binding :数据绑定
    • Spring Express Language:Spring 表达式
    • Spring Resources:资源管理
    • Spring Events:事件
    • Spring i18n:国际化
    • Spring Validation:校验
    • Spring Data Access:数据存储
      • JDBC Java API:对JDBC的抽象,简化了数据访问异常的处理
      • Transactions:事务抽象 EJB简化版本
      • O/R Mapping:映射
      • DAO Support:对JDBC的抽象,简化了数据访问异常的处理
      • XML Marshalling 编列
    • Spring Integration
      • Remoting:远程调用
      • JMS:Java消息服务
      • JCA:Java连接架构
      • JMS:Java管理扩展
      • Email:Java 邮件客户端
      • Tasks:本地任务
      • Scheduling:本地调度
      • Caching:缓存抽象
      • Test : Spring 测试
        • Mock Objects:模拟对象框架
        • Test Context Framework:TestContext框架
        • Spring Mvc Test:Spring MVC 测试
        • WebTestClient :Web 测试客户端
    • Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传
      • Spring Servlet Support
        • Spring MVC:提供面向Web应用的Model-View-Controller实现
        • WebSocket
        • SockJS
      • Spring Reactive
        • Spring WebFlux
        • WebClient
        • WebSocket

Spring 编程模型

  • 面向对象
    • 契约接口:Aware、BeanPostProcess
    • 设计模式:观察者、组合模式、模版模式
  • 面向切面
    • 动态代理:JdkDynamicAopProxy
    • 字节码提升:ASM、CGLib、AspectJ
  • 面向元编程
    • 注解:模式注解 @Component @Service
    • 配置:Environment抽象、PropertySources
    • 范型:GenericTypeResolver
  • 函数驱动
    • ApplicationEventPubilsher
    • Reactive Spring WebFlux
  • 模块驱动
    • Maven Artifacts
    • OSGI Bundles
    • Java 9 Automatic Modules
    • Spring @Enable*

Sprng 设计思想

  • Object-Oriented Programming (OOP)
  • IOC / DI
  • Domain-DrivenDevelopment (DDD)
  • Test-DrivenDevelopment (TDD)
  • Event-DrivenPrograming (EDP)
  • Functional Programing (FP)

Spring IOC

IOC对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。

所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中,所以,以来注入DI 和控制反转IOC 是从不同的角度描述的同一间事情,就是值通过引用IOC容器,利用以来关系注入的方式,实现对象之间的解耦。DI是IOC的实现。

  • IoC(Inversion of Control,翻译为“控制反转”)不是一个具体的技术,而是一种设计思想。与传统控制流相比,IoC 会颠倒控制流,在传统的编程中需要开发者自行创建并销毁对象,而在 IoC 中会把这些操作交给框架来处理,这样开发者就不用关注具体的实现细节了,拿来直接用就可以了,这就是控制反转。

    • IoC 很好的体现出了面向对象的设计法则之一——好莱坞法则:“别找我们,我们找你”。即由 IoC 容器帮对象找到相应的依赖对象并注入,而不是由对象主动去找。
    • DI(Dependency Injection,翻译为“依赖注入”)表示组件间的依赖关系交由容器在运行期自动生成,也就是说,由容器动态的将某个依赖关系注入到组件之中,这样就能提升组件的重用频率。通过依赖注入机制,我们只需要通过简单的配置,就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心资源来自哪里、由谁实现等问题。
    • IoC 和 DI 其实是同一个概念从不同角度的描述的,由于控制反转这个概念比较含糊(可能只理解成了容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年被开发者尊称为“教父”的 Martin Fowler(世界顶级专家,敏捷开发方法的创始人之一)又给出了一个新的名字“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”。
  • 平常的Java开发中,程序员在某个类中需要依赖其它类的方法。通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理。 Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。依赖注入的另一种说法是"控制反转"。通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员。而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。

实现方式
  • JavaSE
    • Java Beans
    • Java ServiceLoader SPI

依赖查找

单属性查找、集合对象查找

  • 依赖查找是主动依赖查找方式,通常需要依赖容器API标准实现,而依赖注入是通过手动或者自动依赖绑定方式实现。

  • 实时查找

    • BeanFactory.getBean(String beanName) 根据名称查找
    • BeanFactory.getBean(Class<T>beanClazz) 根据Bean类型查找
  • 延迟查找

  • 查询条件

    • 单个Bean对象
    • 集合Bean对象
    • 条件查找(在查找过程中 Name 和 Id都是坚定Bean的条件,在一个上下文中只能存在一种)
      • Bean Name + Bean Type查找
      • Bean Id + Bean Type查找
    • Java 注解
演示代码
  • xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- more bean definitions go here -->
    <bean id="delayLookup" class="cn.z201.spring.domain.DelayLookup">
        <property name="name" value="spring"/>
    </bean>

    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="delayLookup"/>
    </bean>

    <!-- parent 继承等父类 primary 即使指定的 bean 标记为自动装配候选者。 -->
    <bean id="annotationDelayLookup" class="cn.z201.spring.domain.AnnotationDelayLookup"
          parent="delayLookup" primary="true">
        <property name="address" value="杭州"/>
    </bean>

</beans>
  • Java代码
/**
 * @author z201.coding@gmail.com
 * spring ioc 延时查找
 **/
public class DelayLookup {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "DelayLookup{" +
                "name='" + name + '\'' +
                '}';
    }
}

/**
 * @author z201.coding@gmail.com
 **/
@Point
public class AnnotationDelayLookup extends DelayLookup{
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "AnnotationDelayLookup{" +
                "address='" + address + '\'' +
                "} " + super.toString();
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Point {

}

public class IOCDelayLookupTest {

    @Test
    public void testLookup() {
        // 编辑 ioc-delay-lookup-context.xml 并创建对应bean。
        // 启动 spring 上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/ioc-delay-lookup-context.xml");
        lookup(beanFactory);
        lookupLazy(beanFactory);
        lookupType(beanFactory);
        lookupCollectionType(beanFactory);
        lookupAnnotationType(beanFactory);
    }


    /**
     * 实时查找
     *
     * @param beanFactory
     */
    private void lookup(BeanFactory beanFactory) {
        // 根据id 获取bean
        DelayLookup delayLookup = (DelayLookup) beanFactory.getBean("delayLookup");
        // 重写 DelayLookup.toString()方法成功输出 spring 。
        System.out.println("实时查找 " + delayLookup);
    }

    /**
     * 延时查找
     *
     * @param beanFactory
     */
    private void lookupLazy(BeanFactory beanFactory) {
        // 根据id 获取bean
        ObjectFactory<DelayLookup> delayLookup = (ObjectFactory<DelayLookup>) beanFactory.getBean("objectFactory");
        // 重写 DelayLookup.toString()方法成功输出 spring 。
        System.out.println("延时查找 " + delayLookup.getObject());
    }

    /**
     * 单一类型查找
     *
     * @param beanFactory
     */
    private void lookupType(BeanFactory beanFactory) {
        // 根据id 获取bean
        DelayLookup delayLookup = beanFactory.getBean(DelayLookup.class);
        // 重写 DelayLookup.toString()方法成功输出 spring 。
        System.out.println("单一类型查找  " + delayLookup);
    }


    /**
     * 单一类型集合查找
     *
     * @param beanFactory
     */
    private void lookupCollectionType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, DelayLookup> delayLookupMap = listableBeanFactory.getBeansOfType(DelayLookup.class);
            // getBeansOfType 匹配所有类型的 bean,无论是单例、原型还是 FactoryBean , bean name 作为key value 作为对象
            delayLookupMap.forEach((key, value) -> {
                System.out.println("单一类型集合查找  " + key + " " + value);
            });
        }
    }

    /**
     * Java注解查找
     * 通过注解查找只需要
     *
     * @param beanFactory
     */
    private void lookupAnnotationType(BeanFactory beanFactory) {
        // Point
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, AnnotationDelayLookup> delayLookupMap = (Map) listableBeanFactory.getBeansWithAnnotation(Point.class);
            // getBeansOfType 匹配所有类型的 bean,无论是单例、原型还是 FactoryBean , bean name 作为key value 作为对象
            delayLookupMap.forEach((key, value) -> {
                System.out.println("Java注解 @Point 查找  " + key + " " + value);
            });
        }

    }

}
  • Console
实时查找 DelayLookup{name='spring'}
延时查找 DelayLookup{name='spring'}
单一类型查找  AnnotationDelayLookup{address='杭州'} DelayLookup{name='spring'}
单一类型集合查找  delayLookup DelayLookup{name='spring'}
单一类型集合查找  annotationDelayLookup AnnotationDelayLookup{address='杭州'} DelayLookup{name='spring'}
Java注解 @Point 查找  annotationDelayLookup AnnotationDelayLookup{address='杭州'} DelayLookup{name='spring'}
依赖注入
  • 自定义Bean注入。

    • 通过Bean注入。
    • 通过Bean集合注入。
  • 容器内部依赖注入。

  • 容器内部建立Bean注入。

  • 构造方法注入。

    • 构造器注入是不允许循环依赖的存在,日常开发推荐该方式,如果出现循环依赖应该考虑自身代码实现是否合理。
  • 工厂的方法注入。

  • 字段注入

    • 编码注入
      • Java注解注入
        • @Autowired
        • @Resource
        • @Inject
  • 方法注入

  • 回调注入

演示代码
  • 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd">
    <!-- more bean definitions go here -->
    <!--
    1.在注入集合类等时候,引入util配置信息
    xmlns:util="http://www.springframework.org/schema/util"
    http://www.springframework.org/schema/util
    https://www.springframework.org/schema/util/spring-util.xsd
    -->

    <bean id="injectionBean" class="cn.z201.spring.injection.domain.InjectionBean">
        <property name="name" value="spring"/>
    </bean>

    <bean id="superInjectionBean" class="cn.z201.spring.injection.domain.SuperInjectionBean"
          parent="injectionBean" primary="true">
        <property name="address" value="杭州"/>
    </bean>

    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="injectionBean"/>
    </bean>

    <!-- 自动配置 Auto-Wiring -->
    <bean id="injectionRepository" class="cn.z201.spring.injection.repository.InjectionRepository" autowire="byType">
        <!--     手动配置  -->
<!--        <property name="beans">-->
<!--            <util:list>-->
<!--                <ref bean="injectionBean"/>-->
<!--                <ref bean="superInjectionBean"/>-->
<!--            </util:list>-->
<!--        </property>-->
    </bean>

</beans>
  • java代码
/**
 * @author z201.coding@gmail.com
 **/
public class InjectionBean {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "InjectionBean{" +
                "name='" + name + '\'' +
                '}';
    }
}

/**
 * @author z201.coding@gmail.com
 **/
public class InjectionRepository {

    private Collection<InjectionBean> beans;

    public Collection<InjectionBean> getBeans() {
        return beans;
    }

    public void setBeans(Collection<InjectionBean> beans) {
        this.beans = beans;
    }

    @Override
    public String toString() {
        return "InjectionRepository{" +
                "beans=" + beans +
                '}';
    }
}


/**
 * @author z201.coding@gmail.com
 **/
public class IOCInjectionTest {

    @Test
    public void testInjection() {
        // 编辑 ioc-delay-lookup-context.xml 并创建对应bean。
        // 启动 spring 上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/ioc-delay-injection-context.xml");
        lookupType(beanFactory);
        lookupCollectionType(beanFactory);
    }

    /**
     * 单一对象注入
     *
     * @param beanFactory
     */
    private void lookupType(BeanFactory beanFactory) {
        // 根据id 获取bean
        InjectionBean injectionBean = beanFactory.getBean(InjectionBean.class);
        // 重写 DelayLookup.toString()方法成功输出 spring 。
        System.out.println("单一对象注入  " + injectionBean);
    }

    /**
     * 集合对象注入
     *
     * @param beanFactory
     */
    private void lookupCollectionType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, InjectionRepository> delayLookupMap = listableBeanFactory.getBeansOfType(InjectionRepository.class);
            // getBeansOfType 匹配所有类型的 bean,无论是单例、原型还是 FactoryBean , bean name 作为key value 作为对象
            delayLookupMap.forEach((key, value) -> {
                System.out.println("集合对象注入  " + key + " " + value);
            });
        }
    }
}

  • Console
单一类型查找  SuperInjectionBean{address='杭州'} InjectionBean{name='spring'}
单一类型集合查找  injectionRepository InjectionRepository{beans=[InjectionBean{name='spring'}, SuperInjectionBean{address='杭州'} InjectionBean{name='spring'}]}
Autowired和Resource关键字
  • @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
  • Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualififier注解一起使用:
public class TestServiceImpl { 
  // 下面两种@Autowired只要使用一种即可 
  @Autowired 
  private UserDao userDao; // 用于字段上 
  
  @Autowired  // 用于属性的方法上
  public void setUserDao(UserDao userDao) 
  {  
    this.userDao = userDao; 
  } 
}
// @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允
// 许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结
// 合@Qualifier注解一起使用。如下:
public class TestServiceImpl {
  @Autowired 
  @Qualifier("userDao") private UserDao userDao;
}
  • Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
public class TestServiceImpl { 
  // 下面两种@Resource只要使用一种即可 
  
  // 用于字段上 
  @Resource(name="userDao") 
  private UserDao userDao; 
  
  // 用于属性的setter方法上 
  @Resource(name="userDao") 
  public void setUserDao(UserDao userDao) { 
    this.userDao = userDao;
  } 
}
BeanFactory与ApplicationContext

  • BeanFactory 是 Bean 容器,ApplicationContext通过组合BeanFactory提供依赖查找的能力。常用的 AOP、事务以及 事件等,这些都被 ApplicationContext 支持。

  • BeanFactory是Spring底层ioc容器,它维护存储了很多bean定义的信息、Bean配置、生命周期管理。

  • ApplicationContext是BeanFactory超子集。

BeanFactory与FactoryBean

BeanFactory与FactoryBean都是接口。

  • BeanFactory是IOC容器,就是一个Factory或者说IOC容器对象工厂。
  • BeanFactory是单一查找的核心

  • FactoryBean是创建Bean的一种方式。FactoryBean就是一个Bean。
    • 在Spring中,所有的Bean都是由BeanFactory管理。

ObjectFactory 与 BeanFactory
  • ObjectFactory 与 BeanFactory 均提供依赖查找的能力

  • ObjectFactory 仅关注一个或一种类型的 Bean 依赖查找,并

    且自身不具备依赖查找。

  • BeanFactory 则提供了单一类型、集合类型以及层次性等多种依赖查

    找方式。

生命周期
  • 容器
  • Java Beans
BeanDefinition

BeanDefinition是Spring Framework中定义Bean的配置元信息接口。

创建bean
  • 基于xml创建
  • 基于Java注解创建
    • @Bean
    • @Component
    • @Import
/**
 * @author z201.coding@gmail.com
 * 演示
 * DefaultListableBeanFactory 基于xml配置信息创建bean
 * AnnotationConfigApplicationContext 基于注解类创建bean
 **/
public class IocContainerTest {

    @Test
    public void setupXmlApplicationContext(){
        // 创建BeanFactory 容器
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
        String location = "classpath:META-INF/ioc-delay-injection-context.xml";
        // 加载 xml格式的配置文件,并获取bean的数量
        int beanDefinitionCount =  reader.loadBeanDefinitions(location);
        System.out.println("bean 定义加载的数量 " + beanDefinitionCount);
        lookupCollectionType(defaultListableBeanFactory);
    }

    @Test
    public void setupAnnotationConfigApplicationContext(){
        // 创建BeanFactory 容器
        AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext();
        // 加载 IocContainer 作为bean配置类,配置类需要添加@Configuration注解
        config.register(IocContainer.class);
        // 启动应用上下文
        config.refresh();
        lookupCollectionType(config);
        // 关闭上下文
        config.close();
    }

    /**
     * 集合对象注入
     *
     * @param beanFactory
     */
    private void lookupCollectionType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, SuperInjectionBean> delayLookupMap = listableBeanFactory.getBeansOfType(SuperInjectionBean.class);
            // getBeansOfType 匹配所有类型的 bean,无论是单例、原型还是 FactoryBean , bean name 作为key value 作为对象
            delayLookupMap.forEach((key, value) -> {
                System.out.println("集合对象注入  " + key + " " + value);
            });
        }
    }
}

  • Console
# 运行setupXmlApplicationContext
bean 定义加载的数量 4
集合对象注入  superInjectionBean SuperInjectionBean{address='杭州'} InjectionBean{name='spring'}
# 运行setupAnnotationConfigApplicationContext
集合对象注入  superInjectionBean SuperInjectionBean{address='杭州'} InjectionBean{name='spring'}
配置元信息

可以通过Bean定义、IOC容器配置、外部属性配置。

  • 基于XML文件
  • 基于Properties
  • 基于Java注解
  • 基于Java API
实际作用
  • AOP抽象、事务抽象、事件机制、SPI机制、第三方集成。

Spring AOP

  • AOP(Aspect-Oriented Programming:⾯向切⾯编程)能够将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

    • Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理。

    • Spring Aop是运行时增强。
    • Cglib Aop是编译器增强。
  • AOP专门用于处理系统中分布于各个模块中交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些横切性质的系统级服务,如事物管理、安全检查、缓存对象池管理。

    • 1.)切面 -Aspect 切面类 比如日志类
    • 2.) 连接点 - Join Point 加入切点的那个点
    • 3.)通知 -Advice 是在切面某个特定的连接点上的动作。
    • 4.)切入点 - Point Cut 匹配连接点的断点
    • 5.)引入 - Introduction
    • 6.)目标对象 - Target Object 被一个或多个通知对象,永远被代理的对象。
    • 7.) AOP代理 - AOP Proxy AOP创建的对象用来实现切面对象。
    • 8.)织入 - Weaving
    • 9.)AOP通俗的理解:
      •   一个组建A,不关心其他常用的服务组件B,但是这个组件A使用组件B的时候不是组建A自身去调用,而是通过配置等其他方式,比如Spring中可以通过XML配置文件。这样就使的A压根不需要知道服务组件B是怎么样的,A只关心自己的业务逻辑,具体A使用B的时候,配置文件去做,与具体的A组件无关。
        

Spring框架中的设计模式

  • 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例
  • 单例模式:Bean默认为单例模式
  • 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
  • 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate
  • 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener,Spring 事件驱动模型就是观察者模式很经典的⼀个应⽤。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使⽤到了适配器模式、spring MVC 中也是⽤到了适配器模式适配 Controller 。
  • 策略模式: Bean的实例化的时候决定采用何种方式初始化bean实例(反射或者CGLIB动态字节码生成),Resource 接口是具体资源访问策略的抽象。
  • 装饰器模式:Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。
  • 模版模式:Spring Template 都采用了该模式。

Bean注册方式

  • XML 配置文件的注册方式
<bean id="person" class="org.springframework.beans.Person">
   <property name="id" value="1"/>
   <property name="name" value="Java"/>
</bean>

Java 注解的注册方式

// 可以使用 @Component 注解方式来注册 Bean
@Component
public class Person {
   private Integer id;
   private String name
   // 忽略其他方法
}

// @Bean 注解方式来注册 Bean
@Configuration
public class Person {
   @Bean
   public Person  person(){
      return new Person();
   }
   // 忽略其他方法
}

Java API 的注册方式

// 使用 BeanDefinitionRegistry.registerBeanDefinition() 方法的方式注册 Bean	
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		RootBeanDefinition personBean = new RootBeanDefinition(Person.class);
		// 新增 Bean
		registry.registerBeanDefinition("person", personBean);
	}
}

Spring Bean 的作用域

Singleton Bean 无论是依赖查找还是依赖注入,均为同一个对象。

Singleton Bean 只是在当前容器内是一位,存在多个容器的时候,每个容器可以存储一份对象。

Prototype Bean 无论是依赖查找还是依赖注入,均为新生成对象。

  • singleton
    • 表示在 Spring 容器中只有一个 Bean 实例,以单例的形式存在,是默认的 Bean 作用域。
  • prototype
    • 原型作用域,每次调用 Bean 时都会创建一个新实例,也就是说每次调用 getBean() 方法时,相当于执行了 new Bean()。
  • 扩展
    • request

      • 每次 Http 请求时都会创建一个新的 Bean,该作用域仅适应于 WebApplicationContext 环境。
    • session

      • 同一个 Http Session 共享一个 Bean 对象,不同的 Session 拥有不同的 Bean 对象,仅适用于 WebApplicationContext 环境。
    • global session

      • 全局session共享一个bean对象。
    • application

      • 全局的 Web 作用域,类似于 Servlet 中的 Application。
    • webSocket

Spring 中的单例 bean 的线程安全问题

  • 单例 bean 存在线程问题,主要是因为当多个线程操作同⼀个对象的时候,对这个对象的⾮静态成员变量的写操作会存在线程安全问题。在类中定义⼀个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的⼀种⽅式)。

Bean 生命周期

  • 对于 Spring Bean 来说,并不是启动阶段就会触发 Bean 的实例化,只有当客户端通过显式或者隐式的方式调用 BeanFactory 的 getBean() 方法时,它才会触发该类的实例化方法。当然对于 BeanFactory 来说,也不是所有的 getBean() 方法都会实例化 Bean 对象,例如作用域为 singleton 时,只会在第一次,实例化该 Bean 对象,之后会直接返回该对象。但如果使用的是ApplicationContext 容器,则会在该容器启动的时候,立即调用注册到该容器所有 Bean 的实例化方法。getBean() 方法是属于 BeanFactory 接口的,它的真正实现是AbstractAutowireCapableBeanFactory 的 createBean() 方法,而 createBean() 是通过 doCreateBean() 来实现的,具体源码实现。

  • 实例化Bean
    • 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefifinition对象中的信息,实例化所有的bean。
  • 设置对象属性(依赖注入)
    • 实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefifinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
  • 处理Aware接口
    • 接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean。
      • 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值。
      • 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
      • 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文。
  • BeanPostProcessor
    • 如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
  • InitializingBean 与 init-method
    • 如果实现相关方法或者配置了 init-method则会自动调用其配置的初始化方法
  • BeanPostProcessor
    • 如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术。
  • getBean
    • 可以正常使用这个bean了。
  • DisposableBean
    • 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法。
  • destroy-method
    • 如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
  • 精简下
    • 实例化Bean: Ioc容器通过获取BeanDefinition对象中的信息进行实例化,实例化对象被包装在BeanWrapper对象中。
    • 设置对象属性(DI):通过BeanWrapper提供的设置属性的接口完成属性依赖注入。
    • 注入Aware接口(BeanFactoryAware, 可以用这个方式来获取其它 Bean,ApplicationContextAware):Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。
    • BeanPostProcessor:自定义的处理(分前置处理和后置处理)
    • InitializingBean和init-method:执行我们自己定义的初始化方法使用
    • destroy:bean的销毁

Spring配置

在 Spring Boot 中,其核心设计理念是对配置信息的管理采用约定优于配置。

Profile

Spring Boot 对配置文件的命名也做了一定的约定,分别使用 label 和 profile 概念来指定配置信息的版本以及运行环境,其中 label 表示配置版本控制信息,而 profile 则用来指定该配置文件所对应的环境。

/{application}.yml
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
  • 在yaml配置文件中指定profile
spring:
  profiles:
    active: test
  • 代码中使用
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        //创建 dev 环境下的 DataSource 
    }
 
    @Bean()
    @Profile("prod")
    public DataSource prodDataSource(){
        //创建 prod 环境下的 DataSource 
    }
  • 加载顺序
–file:./config/ 1
–file:./ 2
–classpath:/config/ 3
–classpath:/ 4
  • 站位符
${spring.application.name} // 配置文件的信息
@spring.application.name@ // maven 打包的时候信息

Spring自动装配

SpringBoot 工程的启动类上都必须要加一个 @SpringBootApplication 注解,而且还要在 run 方法中导入我们的主类,我们就从这个被导入的主类上的注解开始分析。

  • 基于添加jar依赖自动对Spring Boot程序进行配置
    • spring-boot-autoconfiguration
  • 开启自动装配
    • @EnableAutoConfiguration
    • @SpringBootApplication
@SpringBootApplication
public class BlogApplication {
    public static void main(String[] args) {
        SpringApplication.run(VueBlogApplication.class, args);
    }
}

SpringBootApplication

@SpringBootApplication 注解是一个复合注解。除去 Java 的 4 个元注解外,分别是 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   
}

SpringBootConfiguration

@SpringBootConfiguration 注解本质是一个 @Configuration 注解,而 @Configuration 本质是一个 @Component 注解。

  • @SpringBootConfiguration 注解的启动类,本质就是一个配置类(ConfigurationClass),此类中可以声明一个或者多个被 @Bean 注解标记的方法。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

ComponentScan

执行包的扫描,如果 useDefaultFilters 属性为 true,那么它会将被 @component 注解标记以及以 @component 为元注解的注解标记的类都扫描到 IOC 容器中去。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

}

EnableAutoConfiguration

通过@Import注解将特定的类导入到容器中

  • 引入AutoConfigurationImportSelector
  • 读取META-INF/spring.factories
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}


/**
	 * Return the auto-configuration class names that should be considered. By default
	 * this method will load candidates using {@link SpringFactoriesLoader} with
	 * {@link #getSpringFactoriesLoaderFactoryClass()}.
	 * @param metadata the source metadata
	 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
	 * attributes}
	 * @return a list of candidate configurations
	 */
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

  • AutoConfigurationImportSelector类中通过 SpringFactoriesLoader.loadFactoryNames() 去 classpath 下的 META-INF/spring.factories 中获取了所有需要自动装配的类,放入到了 IOC 容器中。

![image-20211102145526599](/Users/zengqingfeng/Library/Application Support/typora-user-images/image-20211102145526599.png)

  • 默认的信息
# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
....

Spring SPI

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。

  • JDK中的SPI

    • JDK 提供了用于服务查找的一个工具类 java.util.ServiceLoader 来实现 SPI 机制。当服务提供者提供了服务接口的一种实现之后,我们可以在 jar 包的 META-INF/services/ 目录下创建一个以服务接口命名的文件,该文件里配置着一组 Key-Value,用于指定服务接口与实现该服务接口具体实现类的映射关系。而当外部程序装配这个 jar 包时,就能通过该 jar 包 META-INF/services/ 目录中的配置文件找到具体的实现类名,并装载实例化,从而完成模块的注入。SPI 提供了一种约定,基于该约定就能很好地找到服务接口的实现类,而不需要在代码里硬编码指定。
  • SpringFactoriesLoader 类似这种 SPI 机制,只不过以服务接口命名的文件是放在 META-INF/spring.factories 文件夹下,对应的 Key 为 EnableAutoConfiguration。SpringFactoriesLoader 会查找所有 META-INF/spring.factories 文件夹中的配置文件,并把 Key 为 EnableAutoConfiguration 所对应的配置项通过反射实例化为配置类并加载到容器中。

    • Spring 的 SPI 配置文件是一个固定的文件 - META-INF/spring.factories,功能上和 JDK 的类似,每个接口可以有多个扩展实现,使用起来非常简单:
//获取所有factories文件中配置的LoggingSystemFactory
List<LoggingSystemFactory>> factories = 
    SpringFactoriesLoader.loadFactories(LoggingSystemFactory.class, classLoader);	

自动装配注解

  • @Configuration 主要编写JavaConfig类
  • @Conditional 条件注解
    • 类条件
      • @ConditionalOnClass 包含类的时候
      • @ConditionalOnMissingClass 不包含类的时候
    • 属性条件
      • @ConditionalOnProperty 属性条件
    • Bean条件
      • @ConditionalOnBean 存在某个Bean
      • @ConditionalOnMissingBean 不存在某个Bean
      • @ConditionalOnSingleCanditate 上下文仅存在一个Bean
    • 资源类
      • @ConditionalOnResource
    • Web应用条件
      • @ConditionalOnWebApplication 是web应用的
      • @ConditionalOnNotWebApplication 非web应用的
    • 其他
      • @ConditionalOnExpression 表达式条件
      • @ConditionalOnJava 指定不同的版本
      • @ConditionalOnJndi 满足Jndi条件
    • 执行顺序
      • @AutoConfigureBefore 某些配置之前
      • @AutoConfigureAfter 某些配置执行之后
      • @AutoConfigureOrder 执行顺序
  • 实现方式
    • 编程式 实现 ImportSelector 接口,并且将实现类声明称bean。
    • 配置文件 META-INF/spring.factories

Spring循环依赖

当bean的作用域是Prototype的是不支持循环依赖的。不然会出现套娃,

  • 可能出现的依赖场景
    • 构造器循环依赖
    • 属性注入循环依赖
  • Spring维护了三个Map,所谓的三级缓存。
    • singletonObjects:第一级缓存,里面放置的是实例化好的单例对象
    • earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象。
    • singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。
    • 创建bean的时候Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取,如果还是获取不到就从三级缓存singletonFactories中取(Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外提前暴露依赖的引用值(提前曝光),根据对象引用能定位到堆中的对象,其原理是基于Java的引用传递),取到后从三级缓存移动到了二级缓存完全初始化之后将自己放入到一级缓存中供其他使用,因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /*
     * 一级缓存
     * singletonObjects 这就是人们常说的单例池,从微观意义上来说,这就是我们的 Spring 容器,
     * 它用于存放完全初始化好的 bean,从缓存中取出的 bean 可以直接使用。
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    /*
     * 三级缓存
     * 存放 bean 工厂对象,提前曝光引用,解决循环依赖,虽然这个对象不完整,但是已经可以根据引用定位到堆内存中的对象,
     * 此对象可能是通过构造方法创建,还没装配属性。
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /*
     * 二级缓存
     * 存放原始的 bean 对象用于解决循环依赖,存到里面的对象还没有被填充属性。
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /** Names of beans that are currently in creation */
    // 标记为正在创建的 bean 的集合(A 依赖于 B,实例化 A 的过程中发现依赖 B,就去创建 B,那么 A 就会放在此集合中)
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}
  • 首先尝试去从一级缓存 singletonObjects 单例池中去获取,没有获取到并且要获取的 Bean 正在创建中,再去查找二级缓存 earlySingletonObjects,如果还是没有找到,就再去三级缓存 singletonFactories 中去找,找到了就从三级缓存 singletonFactories 中移除,放入到二级缓存 earlySingletonObjects 中去,这样做的好处就是,如果还有其他对象尝试获取这个 Bean,那么可以直接从二级缓存中取出来,就不需要再调用 getEarlyBeanReference() 方法了。最终,创建好的 Bean,全部 put 到了单例池 singletonObjects 中了。

  • 如何解决循环依赖

    • 构造器循环依赖解决办法:在构造函数中使用@Lazy注解延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象说明:一种互斥的关系而非层次递进的关系,故称为三个Map而非三级缓存的缘由 完成注入;

Spring中Filter和Interceptor的区别

  • Filter是基于函数回调(doFilter()方法)的,而Interceptor则是基于Java反射的(AOP思想)。
  • Filter依赖于Servlet容器,而Interceptor不依赖于Servlet容器。
  • Filter对几乎所有的请求起作用,而Interceptor只能对action请求起作用。
  • Interceptor可以访问Action的上下文,值栈里的对象,而Filter不能。
  • 在action的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
  • Filter在过滤是只能对request和response进行操作,而interceptor可以对request、response、handler、modelAndView、exception进行操作。
  • interceptor 的执行顺序大致为:
    • 请求到达 DispatcherServlet
    • DispatcherServlet 发送至 Interceptor ,执行 preHandle
    • 请求达到 Controller
    • 请求结束后,postHandle 执行
  • Spring 中主要通过 HandlerInterceptor 接口来实现请求的拦截,实现 HandlerInterceptor 接口需要实现下面三个方法:
    • preHandle() – 在handler执行之前,返回 boolean 值,true 表示继续执行,false 为停止执行并返回。
    • postHandle() – 在handler执行之后, 可以在返回之前对返回的结果进行修改
    • afterCompletion() – 在请求完全结束后调用,可以用来统计请求耗时等等
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{

	private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);

	//before the actual handler will be executed
	public boolean preHandle(HttpServletRequest request,
		HttpServletResponse response, Object handler)
		throws Exception {

		long startTime = System.currentTimeMillis();
		request.setAttribute("startTime", startTime);

		return true;
	}

	//after the handler is executed
	public void postHandle(
		HttpServletRequest request, HttpServletResponse response,
		Object handler, ModelAndView modelAndView)
		throws Exception {

		long startTime = (Long)request.getAttribute("startTime");

		long endTime = System.currentTimeMillis();

		long executeTime = endTime - startTime;

		//modified the exisitng modelAndView
		modelAndView.addObject("executeTime",executeTime);

		//log it
		if(logger.isDebugEnabled()){
		   logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
		}
	}
}
  • Servlet 的 Filter 接口需要实现如下方法:
    • void init(FilterConfig paramFilterConfig) – 当容器初始化 Filter 时调用,该方法在 Filter 的生命周期只会被调用一次,一般在该方法中初始化一些资源,FilterConfig 是容器提供给 Filter 的初始化参数,在该方法中可以抛出 ServletException 。init 方法必须执行成功,否则 Filter 可能不起作用,出现以下两种情况时,web 容器中 Filter 可能无效: 1)抛出 ServletException 2)超过 web 容器定义的执行时间。
    • doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) – Web 容器每一次请求都会调用该方法。该方法将容器的请求和响应作为参数传递进来, FilterChain 用来调用下一个 Filter。
    • void destroy() – 当容器销毁 Filter 实例时调用该方法,可以在方法中销毁资源,该方法在 Filter 的生命周期只会被调用一次。

Spring 事务

Spring的事物抽象

  • Spring事物的特性

    • 隔离级别
      • 处理并发事务带来的问题
    • 事物传播
      • 处理业务代码互相调用事物问题
    • 事物回滚
      • 处理业务失败会滚业务事物问题
    • 事物超时
      • 处理事物允许的执行时间问题
    • 是否只读
      • 据说可以只读提高事物执行效率
  • 事物开启方式

    • 编程式事务 代码中硬编码开启事物。
    • 声明式事务
      • 基于注解开启事物。
      • 基于xml配置文件开启事物。

Spring 事务中的隔离级别

TransactionDefinition 接⼝中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使⽤后端数据库默认的隔离级别,Mysql 默认采⽤的 REPEATABLE_READ隔离级别 Oracle 默认采⽤的 READ_COMMITTED隔离级别。

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。

  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。

  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。

  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会⽤到该级别。

Spring 事务中事务传播⾏为

  • ⽀持当前事务的情况:
    • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
    • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
    • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
  • 不⽀持当前事务的情况:
    • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_NEVER: 以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  • 其他情况:
    • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

TransactionStatus接口介绍

  • TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息.
public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
} 

Spring Boot 启动流程

Spring Boot 程序的入口是 SpringApplication.run(Application.class, args) 方法

public ConfigurableApplicationContext run(String... args) {
    // 1.创建并启动计时监控类
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 2.声明应用上下文对象和异常报告集合
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    // 3.设置系统属性 headless 的值
    this.configureHeadlessProperty();
    // 4.创建所有 Spring 运行监听器并发布应用启动事件
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();
    Collection exceptionReporters;
    try {
        // 5.处理 args 参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 6.准备环境
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        // 7.创建 Banner 的打印类
        Banner printedBanner = this.printBanner(environment);
        // 8.创建应用上下文
        context = this.createApplicationContext();
        // 9.实例化异常报告器
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        // 10.准备应用上下文
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 11.刷新应用上下文
        this.refreshContext(context);
        // 12.应用上下文刷新之后的事件的处理
        this.afterRefresh(context, applicationArguments);
        // 13.停止计时监控类
        stopWatch.stop();
        // 14.输出日志记录执行主类名、时间信息
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
        // 15.发布应用上下文启动完成事件
        listeners.started(context);
        // 16.执行所有 Runner 运行器
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }
    try {
        // 17.发布应用上下文就绪事件
        listeners.running(context);
        // 18.返回应用上下文对象
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

  • Spring Boot 的启动流程

    • 1、创建并启动计时监控类

      • 此计时器是为了监控并记录 Spring Boot 应用启动的时间的,它会记录当前任务的名称,然后开启计时器。
    • 2.声明应用上下文对象和异常报告集合

      • 此过程声明了应用上下文对象和一个异常报告的 ArrayList 集合。
    • 3.设置系统属性 headless 的值

      • 设置 Java.awt.headless = true,其中 awt(Abstract Window Toolkit)的含义是抽象窗口工具集。设置为 true 表示运行一个 headless 服务器,可以用它来作一些简单的图像处理。
    • 4.创建所有 Spring 运行监听器并发布应用启动事件

      • 此过程用于获取配置的监听器名称并实例化所有的类。
    • 5.初始化默认应用的参数类

      • 也就是说声明并创建一个应用参数对象。
    • 6.准备环境

      • 创建配置并且绑定环境(通过 property sources 和 profiles 等配置文件)。
    • 7.创建 Banner 的打印类

      • 就是出个那个logo也可以自己定义。
    • 8.创建应用上下文

      • 根据不同的应用类型来创建不同的 ApplicationContext 上下文对象。
    • 9.实例化异常报告器

      • 它调用的是 getSpringFactoriesInstances() 方法来获取配置异常类的名称,并实例化所有的异常处理类。
    • 10.准备应用上下文

      • 此方法的主要作用是把上面已经创建好的对象,传递给 prepareContext 来准备上下文,例如将环境变量 environment 对象绑定到上下文中、配置 bean 生成器以及资源加载器、记录启动日志等操作。
    • 11.刷新应用上下文

      • 此方法用于解析配置文件,加载 bean 对象,并且启动内置的 web 容器等操作。
    • 12.应用上下文刷新之后的事件处理

      • 这个方法的源码是空的,可以做一些自定义的后置处理操作。
    • 13.停止计时监控类

      • 停止此过程第一步中的程序计时器,并统计任务的执行信息。
    • 14.输出日志信息

      • 把相关的记录信息,如类名、时间等信息进行控制台输出。
    • 15.发布应用上下文启动完成事件

      • 触发所有 SpringApplicationRunListener 监听器的 started 事件方法。
    • 16.执行所有 Runner 运行器

      • 执行所有的 ApplicationRunner 和 CommandLineRunner 运行器。
    • 17.发布应用上下文就绪事件

      • 触发所有的 SpringApplicationRunListener 监听器的 running 事件。
    • 18.返回应用上下文对象

      • 到此为止 Spring Boot 的启动程序就结束了。

Spring代理

  • 动态代理
    • 在程序运行时利用反射动态创建代理对象<复用性,易用性,更加集中都调用invoke。
  • 静态代理
    • 程序运行前代理类的.class文件就存在了。

Spring中获取代理对象

spring中获取代理对象的三种方式:

  1. 直接autowire基于接口注入。
  2. @Autowire applicationcontext,通过applicationcontext.getbean获取代理对象。
  3. 通过aopcontext.currentproxy方法获取(注意:需要启动类上增加注解@enableaspectjautoproxy(exposeproxy=true));
  • 如果spring启动时循环依赖导致报错,可以通过依赖注入属性增加@lazy注解解决。

JDK Proxy 和 CGLib 有什么区别

  • JDK Proxy
    • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现。
    • JDK Proxy 是通过拦截器加反射的方式实现的;
    • JDK Proxy 只能代理继承接口的类;
  • CGLib
    • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
    • CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。
最近更新: 2025/12/27 18:51
Contributors: 庆峰
Prev
Java MyBatis
Next
Java SpringMVC