Java进阶之路对标阿里P6(2)——Spring深入学习(1w字)

本文深入探讨了Spring框架的核心优势,包括解耦、AOP编程和声明式事务管理。详细介绍了Spring的核心架构,强调了IOC和DI的概念,以及AOP在方法拦截中的应用。同时,阐述了Spring中单例和原型作用域,以及依赖注入的不同方式。此外,讲解了简单工厂、工厂方法和代理模式等设计模式。最后,分析了Spring的源码实现,包括BeanFactory和Bean的创建过程,以及循环依赖问题的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring笔记

任务一 手写Spring

1.1 Spring的优势

  1. 方便解耦、简化开发。管理对象间依赖关系、封装了单例、文件解析等
  2. AOP编程支持 oop不容易实现的,面向切面编程轻松实现
  3. 声明式事务支持 便捷的事务管理
  4. 程序测试
  5. 集成各种框架
  6. 降低了j2ee api调用难度
  7. 源码学习

1.2 Spring核心架构

  1. web、deta access、Aop、core container、test

描述:web层处理浏览器前端的请求及界面渲染,springmvc,与其他应用的交互,远程调用能力;data access为数据持久化提供了大量的模板,简化了操作,只需要调用api即可,配合Aop可以实现事务控制。AOP为日志、事务等面向切面编程的需求服务。核心容器,管理了对象,包括DI实现,以及上下文。test就是测试模块。

1.3 核心思想 IOC/DI、AOP

1.3.1 IOC(对象角度)

Inversion of Control,控制反转。在具体的使用位置,只需要定义变量,不需要new出对象,而是交由容器去做,用autowire标记后,容器就会注入进来。

控制:对象的创建、管理

反转:控制权交由外部容器实现

1.3.2 DI(容器角度)

Dependency Injection 依赖注入。同一问题的两种角度的描述

1.3.3 AOP

面向切面编程

oop是垂直继承体系,而横切重复问题无法解决,即使抽取了代码,也需要有一行来调用,这个重复部分无法避免。

AOP这种面向横切的,就应运而生。可以在方法的前后增强。实际即是代理模式

切:不改变原有代码逻辑,只操作横切逻辑代码,即横切逻辑

面:不单单是一个方法,多个方法的相同切点,组成一个面

1.4 手写实现IOC、AOP

  • 编写配置文件
  • 加载配置文件
  • 解析配置文件
  • 解析bean,读出id作为key,class反射实例化出对象,作为value放进map(IOC容器)中
  • 解析property,获得父对象,从上一步的map中,拿出对象,获得方法,遍历找出set方法,invoke方法,把依赖对象注入进去
  • 提供getBean出口——至此,IOC完成
  • AOP,通过动态代理实现,在方法执行的前后增强
  • servlet中从容器中获得代理工厂ProxyFactory,把service交给代理工厂实例化,由他去执行invoke,把service的方法置于事务控制中。

1.5 设计模式

1.5.1 简单工厂模式

对一个接口,通过不同的实现类,在工厂类中通过switch调用,或是重载方法调用。

比如INoodle接口,实现类有各种的Noodle类,工厂类中通过类型,switch调用不同实现类;Mybatis源码中则是通过方法重载,不同的参数列表实现。

1.5.2 工厂方法模式

在简单工厂中进一步封装,抽取一个通用的createNoodle方法,创建INoodleFactory工厂接口,编写不同的Noodle工厂类,每个工厂只生成一种。在create方法中实现特定的Noodle创建。通过使用不同的工厂类,创建实例。类似Mybatis中,SqlsessionFactory,专门创建sqlsession。

1.5.3 饿汉式单例模式

首先,通用部分,单例模式:

  • 构造方法私有化,只有该类本身可以实例化自身;

    private HungrySingleton(){}
    
  • 调用私有构造方法实例化自身,获得静态常量属性:

    private static final HungrySingleton instance = new HungrySingleton();
    
  • 通过静态公共方法返回。

public static HungrySingleton getInstance(){
    return instance;
}

不同点:定义私有属性instance时就实例化对象。

1.5.4 懒汉式单例

通用部分一样,不同的是实例化时,需要判断instance是否为空。定义静态常量时,先不实例化。

// 构造方法私有化
private LazySingleton() {}

// 将自身实例化对象设置为一个属性,并用static修饰
private static LazySingleton instance;

// 静态方法返回该实例,加synchronized关键字实现同步
public static synchronized LazySingleton getInstance() {
    if(instance == null) {
        instance = new LazySingleton();
    }
    return instance;
}
1.5.5 代理模式
1.5.5.1 静态代理

针对一个实现类,编写代理类,且其只能接收该实现类作为入参。

代理,其实就是把执行方法的工作交给了代理类。如下:

IRentingHouse rentingHouse = new RentingHouseImpl();
// 自己要租用一个一室一厅的房子
// rentingHouse.rentHosue();

RentingHouseProxy rentingHouseProxy = new RentingHouseProxy(rentingHouse);
rentingHouseProxy.rentHosue();

静态跟动态的区别就是:每个代理类只能对应一个场景。动态代理是由jdk或cglib封装了的,通过反射拿到传入的对象,去动态实例化。获得其方法等内容。

为了在代理类中附加一些业务处理,比如在执行原方法的前后。做横向逻辑切入。

1.5.5.2 动态代理

jdk、cglib两种方式实现。

在ProxyFactory中,其实也是一个工厂方法模式,专门用来生成Proxy的。

public Object getJdkProxy(Object obj) {

    // 获取代理对象
    return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result = null;

                    // 写增强逻辑
                    System.out.println("中介(代理)收取服务费3000元");
                    // 调用原有业务逻辑
                    result = method.invoke(obj,args);

                    System.out.println("客户信息卖了3毛钱");

                    return result;
                }
            });

}

调用时,从代理对象中生成对象,调用具体的方法。(故method能匹配上。)

cglib代理:

public Object getCglibProxy(Object obj) {
    return  Enhancer.create(obj.getClass(), new MethodInterceptor() {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object result = null;
            System.out.println("中介(代理)收取服务费3000元");
            result = method.invoke(obj,objects);
            System.out.println("客户信息卖了3毛钱");
            return result;
        }
    });
}

jdk面向接口,需要有接口才能实现(能通过getInterface拿到接口)。调用时:

IRentingHouse rentingHouse = new RentingHouseImpl();
// JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的(实现类)。传入接口也行
IRentingHouse jdkProxy= (IRentingHouse) ProxyFactory.getInstance().getJdkProxy(rentingHouse);
jdkProxy.rentHouse();

RentingHouseImpl rentingHouse2 = new RentingHouseImpl();
RentingHouseImpl cglibProxy = (RentingHouseImpl) ProxyFactory.getInstance().getCglibProxy(rentingHouse2);
cglibProxy.rentHouse();

任务二 Spring特性及源码

回顾Spring

BeanFactory是顶级接口,ApplicationContext是子接口,常用的,里面加了更多的功能。常用他下面的实现类

ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器

启动方式

web.xml中配置,配置类启动、xml启动两种。

实例化bean(3种)
  • 无参构造
  • 静态方法
  • 实例方法
bean作用范围@scope、生命周期
  • singleton
  • prototype
依赖注入方式
  • set注入(使用最多)@autowired
  • 构造函数注入
注解详解

@autowired 按照类型注入,当一个类型有多个bean时,如IDAO类型(接口),有多个impl(Mybatis、jdbc等),配合@Qualifier(name=“jdbcDaoImpl”),加个名称做定位即可。

@Resource默认是byname,可以指定name,type

@configuration配置类

@componentscan扫描

@propertysource

@Import

@Value,赋值,读取配置文件信息${}

@Bean 注入容器

XML方式

  1. 去掉手写的bean加载,及配置文件引入applicationcontext.xml
  2. getbean由ClassPathXmlApplicationContext这个类加载xml配置文件读取

加上注解

  1. 配上@autowired
  2. @configuration加载连接信息SpringConfig类
  3. 事务控制@transactional,注解扫描,@service,@Repository

延迟加载

lazy-init

BeanFactory和FactoryBean

BeanFactory是容器顶级接口,生产bean的工厂;FactoryBean是bean的一种,工厂bean

后置处理器

BeanPostProcessor,BeanFactoryPostProcessor

工厂初始化后可以用后者

bean实例化后可以使用前者。

任务三 Spring源码部分

代码架构思维、理解框架

原则:定焦原则,抓主线

宏观原则:关注架构、业务流程

源码方法和技巧:

  • 断点(观察调用栈)
  • 反调(find usage)
  • 经验(doXXX真正开始处理)

IOC源码

3.1 bean创建的切入

利用debug,在bean的关键调用位置,查看线程调用栈。

开发工具在断点部分,会显示当前执行代码所处的调用栈,即可知道程序调用了哪些代码。

关键的bean创建,放在构造方法中,调用栈为:AbstractApplicationContext 类 的 refresh ⽅法

debug循环中条件控制

3.2 refresh方法总览创建流程
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.预处理
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      // 获取BeanFactory,默认实现为DefaultListableBeanFactory
       // 加载beandefinition,注册到beandefinitionregistry
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.BeanFactory的预准备工作,类加载器等
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.后置处理工作
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.(国际化功能;消息绑定,解析)
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
          /*第⼗⼀步:
            初始化所有剩下的⾮懒加载的单例bean
            初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
            填充属性
            初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、 init-method⽅法)
            调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
            */
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
3.3 BeanFactory创建子流程
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

在这里插入图片描述

3.4 BeanDefinition加载、解析、注册子流程

在obtainBeanFactory流程中

在这里插入图片描述

3.5 Bean创建流程

AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中

在这里插入图片描述

3.6 lazy-init延迟加载机制

AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中

// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

DefaultListableBeanFactory.preInstantiateSingletons()

public void preInstantiateSingletons() throws BeansException {
  // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               final FactoryBean<?> factory = (FactoryBean<?>) bean;
               boolean isEagerInit;
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                  isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                              ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
               }
               else {
                  isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
               }
               if (isEagerInit) {
                  getBean(beanName);
               }
            }
         }
3.7 Spring IOC循环依赖问题
3.7.1 构造器注入

(无法解决)BeanCurrentlyInCreationException

3.7.2 Field属性,set注入

spring采⽤的是提前暴露对象的⽅法

单例的可以解决

prototype的无法解决

基于java的引用传递(类似指针?)定义后,可以延后设值。

3.7.3 源码

AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中->DefaultListableBeanFactory.preInstantiateSingletons()

->AbstractBeanFactory.doGetBean()

作业

思路:

改造前现状

通过bean.xml配置需要管理的属性、及依赖关系,在BeanFactory中实现对bean的管理,以及依赖注入

需要实现的重点

  1. 采用xml+anno的形式,需要实现@Service、@Repository前面两者可以合一起,bean对象发现及创建,@Autowired维护依赖、@Transactional,Aop的实现,注解的value处理,以及aop实现的方式,jdk、cglib
  2. 加载配置文件(base-package)->解析配置文件,解析scan包,拼装获得全类名集合->实例化对象->id+对象放到map中->依赖注入->启动调用
  3. 实现,编写注解类->编写解析处理类->改造原有业务代码,加入注解
  4. 事务注解开发,区分是jdk还是cglib
  5. 判断条件:TestStringCal.class.getInterfaces().length==0,在实例化对象的时候判断有无事务注解

…(img-ZpflD8Ic-1631316525590)]

思路:

改造前现状

通过bean.xml配置需要管理的属性、及依赖关系,在BeanFactory中实现对bean的管理,以及依赖注入

需要实现的重点

  1. 采用xml+anno的形式,需要实现@Service、@Repository前面两者可以合一起,bean对象发现及创建,@Autowired维护依赖、@Transactional,Aop的实现,注解的value处理,以及aop实现的方式,jdk、cglib
  2. 加载配置文件(base-package)->解析配置文件,解析scan包,拼装获得全类名集合->实例化对象->id+对象放到map中->依赖注入->启动调用
  3. 实现,编写注解类->编写解析处理类->改造原有业务代码,加入注解
  4. 事务注解开发,区分是jdk还是cglib
  5. 判断条件:TestStringCal.class.getInterfaces().length==0,在实例化对象的时候判断有无事务注解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值