Spring架构及核心模块总结

1、Spring及其优点

Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架,主要是为了解决企业应用开发的复杂性而诞生的。它采用模块化分层设计,具有以下特点:

  • 轻量低侵入式设计,代码污染极低;
  • 控制反转和依赖注入实现了松散耦合;
  • 切面编程降低业务耦合度,提高程序的可重用性及开发效率;
  • ORM和DAO简化了底层的数据库访问;
  • 方便集成各种优秀框架等。

2、Spring模块组成

Spring 框架是一个分层架构,每个模块既可单独存在,又可与其他模块联合实现,其架构如下图所示:


Spring架构及核心模块总结_第1张图片
Spring架构图
  • 核心容器:提供依赖注入和Bean管理功能,主要组件是 BeanFactory,它使用控制反转模式将应用程序配置和依赖规范与实际的应用代码分开;
  • Context:扩展了BeanFactory的概念,增加了对国际化、事件传播,以及验证等的支持,此外还提供了许多企业服务及对模版框架集成的支持;
  • Web:建立于Context模块之上,提供了一个适合于Web应用的上下文。另外,这个模块还提供了一些面向服务支持,也提供了Spring和其它Web框架的集成;
  • MVC:是一个全功能的构建 Web 应用程序的 MVC 实现,容纳了大量视图技术,如 JSP、Velocity、POI等;
  • AOP:为Spring容器管理的对象提供了对面向切面编程的支持;
  • DAO:该层封装了对数据库的访问,并且处理了其抛出的错误消息,同时还基于AOP模块提供了事务管理;
  • ORM:Spring支持多种ORM框架,简化了数据库操作。

3、Spring控制反转

控制反转(Inversion of Control)是一种设计思想,即由传统的在Bean对象内部直接获取依赖对象改为由容器创建并在Bean需要时注入依赖对象。对于Spring框架来说,就是由Spring容器来负责实例化、配置以及装载Bean对象并控制其生命周期。这样,有利于设计出松散耦合、方便灵活、便于复用的程序。
为了实现控制反转,Spring容器需要通过依赖注入(Dependency Injection)维护Bean之间的依赖关系。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

4、Bean的元数据配置

Bean以及Bean的那些依赖对象,都是容器使用元数据通过反射生成的。Bean的元数据主要包括实现类、属性信息、依赖关系、行为配置及创建方式,这些配置可以写到XML、Java注解或Java代码中。为了简化配置配置文件,让程序变得简洁并指明组件所在的层次,可以优先使用基于注解的Bean配置。但是对应第三方组件,无法在这些组件里加如注解使之成为Spring容器管理的Bean,这种情况下就需要基于XML或Java类进行配置。此外,配置比较复杂的Bean也可以使用基于Java类的配置。

  • 基于XML的Bean配置:在XML配置文件中配置元数据。

    
     
    
    
    
        
        
    
    
    

  • 基于注解的Bean配置:在Bean的实现类上标注注解实现。
// @Controller和@RestController表示控制层组件
// @Service表示业务逻辑层组件
// @Repository表示数据访问层组件
// @Component表示通用组件
@Component
@Scope
@Lazy
public class Bean{
    // @Qualifier、@Autowired和@Resource配合注入依赖
    // @PostConstruct和@PreDestroy指定初始化及清理方法
}
  • 基于Java的Bean配置:使用@Configration@Bean@Import@DependsOn注解配置Bean,也可以通过AnnotationConfigApplicationContext及其子类加载基于java类的配置。
// @Configuration表示配置类并将配置类声明为一个Bean
// @Bean声明一个Bean,默认以方法名为名字,通过initMethod和destroyMethod指定初始化及清理方法
// @Import引入另一个配置类
// @DependsOn控制Bean初始化顺序
@Configuration
public class Config{
    @Bean
    @Scope
    @Lazy
    public Bean bean(){
        Bean bean = new Bean();
        // 配置bean
        return bean;
    }
}

基于Java方式的配置方式不是为了完全替代基于XML方式的配置,两者可以结合使用: 在基于Java方式的配置类中使用@ImportResource引入基于XML方式的配置文件;在基于XML方式的配置文件中使用引入基于Java方式的配置。

5、Bean的依赖注入

依赖注入是指调用类对其他类的依赖关系由第三方注入,以移除调用类对其他类的依赖。在Spring中,对象依赖注入的方法有一下三种:

  • 基于构造器注入:保证重要属性预先设置,无需提供每个属性Setter方法,适合实现强制依赖,必须提供带参的构造函数。


    
        
    
     
      
  
  • 基于属性注入:无循环依赖的问题,具有可选择性和灵活高的优点,利于类的继承和扩展,必须提供无参构造方法并为需要注入的属性提供set方法。适合实现可选依赖,使用@Required注解可用来构造必要的依赖。
 
    
      
      

  • 基于工厂方法注入:工厂类方法一般以接口或抽象类变量的形式返回目标类实例,分为静态和非静态两种,不推荐这种方法。


    
     
      


  

    
     
      
  

标签内,还可以使用标签定义内部Bean,使用,,标签定义相应的Java集合类型List,Set,Map以及Properties的属性和参数。

6、Bean的自动装配

Spring容器可以根据Bean之间的依赖关系自动装配,自动装载能够减少指定的属性或者是构造参数配置,但是也有不能够自动装配简单属性,装配的信息对开发者可见性不好,存在多个符合的Bean时会抛出异常等缺点。Spring容器提供了如下五种自动装配类型:

装配方式 装配方式特点
no 默认方式是不进行自动装配,通过显式设置ref 属性来进行装配。
byName 通过参数名自动装配。
byType 通过参数类型自动装配。
constructor 类似于byType, 但是要提供给构造器参数,否则会抛出异常。
autodetect 首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式

Spring中自动装配也可以使用XML或java配置:

  • 使用XML配置:




  • 使用Java配置:可以使用@Autowired@Resource注解方式进行Spring的依赖注入。@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的Bean时,才会按类型装配。
@Component
public class Bean {
    private final OtherBean otherBean;

    @Autowired
    public UserResource(@Qualifier("otherBean")OtherBean otherBean) {
        this.otherBean = otherBean;
    }
    
    @Bean// 声明一个bean,可以通过autowire属性指定该Bean的自动装配类型
    @Primary// 指定默认的Bean
    @Qualifier("secondBean")// 不存在时使用bean的名字作为qualifier
    public SecondBean secondBean(){
        return new SecondBean();
    }
}

7、Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下作用域:

作用域 作用域范围
singleton 单例模式,在整个应用ApplicationContext只有一个实例,是默认的作用域。
prototype 原型模式,每次获取Bean时,都将产生一个新的实例,之后容器不再维护实例状态,即在原型Bean生命周期中销毁方法是不会调用的。
request 每次HTTP请求都将产生一个新实例,只在基于web的ApplicationContext中可用。
session 每次HTTP Session都将产生一个新实例,只在基于web的ApplicationContext中可用。
globalsession 每个全局的HTTP Session都将产生一个新实例,只在基于门户web的ApplicationContext中可用。
application 每个ServletContext 而不是每个ApplicationContext中包含一个。只在基于web的ApplicationContext中可用。

为了使用基于Web的ApplicationContext的作用域,需要将Request和Bean绑定到相应线程上,使Bean在Request调用链上仅在相应作用域内可见,否则会抛出IllegalStateException异常。Request和线程的绑定需要进行Web初始化配置,这取决于Servlet环境:

  • 如果Web应用使用SpringMVC框架即使用DispatcherServlet或者DispatcherPortlet来处理请求,则不需要多余配置即可绑定Request;

  • 对于Servlet2.4及之上规范的Web容器,可以增加RequestContextListenerRequestContextFilter实现绑定;

    
        
        
            
                org.springframework.web.context.request.RequestContextListener
            
        
        
        
            requestContextFilter
            org.springframework.web.filter.RequestContextFilter
        
        
            requestContextFilter
            /*
        
    
    
  • 对于Servlet3.0及之后,还可以通过WebApplicationInitializer接口来实现绑定;

  • 对于Servlet2.4之前规范的Web容器,只能使用RequestContextFilter配置方式。

而Bean和线程的绑定可以通过方法参数注入或者代理对象等实现。所谓对象代理是指由于依赖只能注入一次,如果想装载一个Bean到作用域更广的Bean中,那么需要通过XML或注解向广作用域Bean注入短作用域Bean的代理对象。这个代理对象拥有与短作用域Bean相同的public方法,可以通过调用代理对象方法间接调用短作用域Bean的方法和状态。

  • 通过方法参数注入:
    // 仅适用于部分作用域如request
    @Component
    @Scope(scopeName = "request")
    public class StateBean {
        private boolean first = true;
    
        public boolean isFirst() {
            if (first) {
                first = false;
                return true;
            }
            return false;
        }
    }
    // 方法参数注入测试
    @RestController
    @RequestMapping(path = "/state")
    public class StateResource {
        @GetMapping
        public boolean list(@Autowired StateBean stateBean) {
            return stateBean.isFirst();
        }
    }
    
  • 通过XML配置代理对象:
    
      
    
    
  • 通过注解配置代理对象:
    // request作用域代理Bean
    @Component
    @Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class StateBean {
        private boolean first = true;
    
        public boolean isFirst() {
            if (first) {
                first = false;
                return true;
            }
            return false;
        }
    }
    // 代理对象注入测试
    @RestController
    @RequestMapping(path = "/state")
    public class StateResource {
        private final StateBean stateBean;
    
        @Autowired
        public StateResource(StateBean stateBean) {
            this.stateBean = stateBean;
        }
    
        @GetMapping
        public boolean list() {
            return stateBean.isFirst();
        }
    }
    

8、Bean的生命周期

一个对象的生命周期包括创建、使用、销毁,在Spring中,Bean对象周期也遵从这一过程。但是Spring提供了许多对外接口及注解方法,允许开发者对这三个过程的前后做一些操作。下图以单例Bean为例说明了Bean自身的生命周期:


Spring架构及核心模块总结_第2张图片
Bean生命周期

只有通过ApplicationContextclose方法关闭服务才会调用销毁方法,并且在非web应用环境使用Spring容器的话,还需要调用ConfigurableApplicationContextregisterShutdownHook方法注册关闭的钩子,以确保能够调用相关的销毁方法来释放掉对应的资源。

除了上面描述的几种Aware接口,Spring还提供了其他Aware接口告诉Bean容器的基础信息:

  • BeanClassLoaderAware:加载Bean使用的类加载器;
  • LoadTimeWeaverAware:加载期间处理类定义的weaver;
  • MessageSourceAware:解析消息的配置策略;
  • NotificationPublisherAware:JMX通知发布器;
  • ResourceLoaderAware:配置的资源加载器;
  • ServletConfigAware:容器当前运行的ServletConfig
  • ServletContextAware:容器当前运行的ServletContext

在Bean自身生命周期以外,还有容器级生命周期接口方法,包括BeanFactoryPostProcessorCustomAutowireConfigurer等工厂后处理器接口方法接口(对BeanDefinition进行处理修改各个Bean的配置,在容器初始化时调用),以及InstantiationAwareBeanPostProcessorBeanPostProcessor 这两个后处理器接口实现(前者继承自后者,每个bean实例化和初始化前后都会调用)。

参考

  • https://www.ibm.com/developerworks/cn/java/wa-spring1
  • https://blog.csdn.net/icarus_wang/article/details/51588284
  • https://blog.csdn.net/ethanwhite/article/details/51476238
  • https://blog.csdn.net/a327369238/article/details/52193822

你可能感兴趣的:(Spring架构及核心模块总结)