Spring学习笔记(一)——Spring体系结构、IoC容器、Bean

  • 参考文档:传送门1 传送门2
  • 说明:第一个文档是W3School中的Spring学习文档,比较粗略。第二个文档是Spring官方的文档,非常详细,对自己英语水平不自信的话使用浏览器的翻译插件查看。
  • 写在前面:本文是个人学习总结,笔记内容都来自于以上两个文档,笔记大部分采用了问答的方式来梳理文档中复杂的知识体系,属个人习惯,不喜勿喷。

Spring的体系结构

Spring是模块化的,允许你选择适用自己的模块而不必将其他模块导入。

完整的Spring框架提供了大约20个模块:
在这里插入图片描述

核心容器-Core Container

在这里插入图片描述

  1. Spring-Core

Core模块提供了Spring框架的基础组成部分,提供了控制反转和依赖注入的特性。

  1. Spring-Beans

Beans模块是所有模块都要用到的,提供 BeanFactory,工厂模式的微妙实现。
读取配置文件,创建和管理Bean以及实现控制反转和依赖注入所需要的类都在这里。

  1. Spring-Context

Context模块是在Core模块和Beans模块的基础上创建的。

Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。

Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。

ApplicationContext接口是Context模块的焦点。

  1. Spring-Exception Language

SpEL模块提供了强大的表达式语言,用于在运行时查询和操作对象图。

数据访问或集成

在这里插入图片描述

数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块。

  1. JDBC=Java Data Base Connectivity

封装了数据库连接的抽象层。

  1. ORM=Object Relational Mapping

提供了对流行的对象关系映射API的集成,通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。

  1. OXM=Object XML Mapping

提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。

  1. JMS=Java Message Service

包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。

  1. 事务处理模块

为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。

  • 编程式事务管理和声明式事务管理的区别?

编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法。
声明式事务是通过注解或配置由spring自动处理。

Web

在这里插入图片描述
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成。

  1. Web

提供面向web的基本功能和面向web的应用上下文。

  1. Web-MVC

为web应用提供了模型视图控制(MVC)和REST Web服务的实现。

  1. Web-Socket

为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。

  1. Web-Portlet

模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。

其他模块

q
在这里插入图片描述

还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和test模块。

  1. AOP

这个模块提供了面向切面的编程实现。

允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来,是Spring框架的特性之一。

  1. Aspects

提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

  1. Instrumentation

在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

  1. Messaging

为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。

它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

  1. 测试

支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。

容器

  • 什么是容器?

——容器相当于一个大工厂,在我们需要的时候为我们生产Bean,并且管理它们之间的依赖关系,掌握一个Bean创建到销毁的全过程。

IoC与Bean

  • 什么是IoC?

——IoC(inversion of Control),控制反转,指对象之间的依赖关系交给容器来创建和维护

  • 什么是DI?

——DI(Dependency Injection),依赖注入,指容器通过调用set方法或者构造方法来创建对象之间的依赖关系

  • IoC和DI有什么关系?

——二者相似,却又不是完全相同,控制反转是目标依赖注入是我们实现控制反转的一种手段

  • Bean是什么?

——Bean是POJO的一种,指简单java对象,它是数据模型的一种。有几个简单的要求:属性私有,提供无参构造,提供get和set方法,实现序列化接口

——在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化,组装和管理的对象

  • 理解:

一个java对象的成员变量可能包含一个或者多个其他java对象,这就是依赖。以往,我们的java类中,通常需要自己将依赖对象实例化。使用Spring时,把这个步骤交给容器来做,那么容器就会在新建一个bean的时候,容器就会检查它的依赖为它注入依赖关系。也就是说,是由Bean本身控制实例化的,所以我们称之为控制反转

  • 举例

Class A中用到了Class B的对象b,一般情况下,需要在A的代码中new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。

  • 原理

听起来spring似乎做了一件神奇的事情,IoC的底层原理其实就是我们JavaSE中学过的反射机制,这样也就不难想象Spring是怎么做到解耦的。

BeanFactory和ApplicationContext

  • Spring IoC容器的基础模块?
  1. org.springframework.beans
  2. org.springframework.context
  • BeanFactory是什么?

——Spring 的 BeanFactory 容器是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor中被定义。

  • ApplicationContext是什么?

——在 Spring 中,有大量对 BeanFactory 接口的实现。ApplicationContext是BeanFactory的一个子接口,BeanFactory提供了配置框架和基本功能,ApplicationContext添加了更多特定于企业的功能一般情况下,我们使用的都是ApplicationContext而不是BeanFactory。

  • BeanFactory和ApplicationContext的继承关系?

在这里插入图片描述
每实现一个接口,ApplicationContext的功能就强大一分。

  • ApplicationContext和BeanFactory 用来干什么?

——加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean

  • 开发者如何操控容器?

——开发者通过配置元数据来对容器发送指令,容器读取配置的元数据来实例化,配置和组装对象

  • 如何配置元数据?

——以XML,Java注释或Java代码表示。

配置元数据的三种方式

上面已经提到,配置元数据表示告诉Spring容器在应用程序中实例化,配置和组装对象。

传统上,配置元数据以简单直观的XML格式提供,但不是唯一允许的配置元数据形式。

如何编写元数据不会影响容器的工作。

目前,许多开发人员为其Spring应用程序选择基于Java的配置

除了这两种方式外,还可使用java注解来配置,用到的注解有@Service,@Component,@Repository,@Controller。

  1. 基于XML的形式来配置元数据

基于XML的配置元数据将这些bean配置为顶级元素<beans/>内的<bean/>元素。

<?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">
     
    <bean id="bean的唯一标识" class="bean的完全限定名">
        <!--这里是关于这个bean的一些配置和依赖关系-->
    </bean>

</beans>
  1. 基于Java配置元数据

@Configuration对类进行注释。作用和XML文件中Beans标签类似。

@Bean用来注释在@Configuration类的方法上。作用和XML文件中的Bean标签类似。

@Configuration类允许通过调用@Bean同一类中的其他方法来定义bean间依赖关系。相当于XML中的ref。

除此之外,@DependsOn,@Primary,@Lazy,@Import,@ImportResource,@Value也是常用到的。

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
  1. 使用java注解配置

使用了@Service(标注服务层)、@Component(标注普通bean)、@Repository(标注DAO层)、@Controller(标注Controller)注解的类会自动注册为一个bean,不需要再在xml文件中配置。

使用这种方法需要在applicationContext.xml文件中进行配置用来自动扫描base-package包下的注解:

<context:component-scan base-package="这里写包名" />

以服务层的类为例:

package com.xx.service.impl;

import com.xx.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void doSomething() {
        System.out.println("i am a user server");
    }
}

实例化ApplicationContext

  • 常见的ApplicationContext 接口实现类都有哪些?
  1. FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
  2. ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
  3. XmlWebApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。

小提示:在IDEA下打开ApplicationContext类,使用Ctrl+h的组合键可以查看继承关系。

  • 实例化一个ApplicationContext

提供给ApplicationContext构造函数的位置路径是资源字符串,它允许容器从各种外部资源(如本地文件系统,Java等)加载配置元数据CLASSPATH。这里用到了Springh中的Resource 抽象概念,它提供了一种从URI语法中定义的位置读取InputStream的便捷机制。Resouce路径用于构建应用程序上下文。

ApplicationContext context = new 
	ClassPathXmlApplicationContext("Beans.xml","ServiceBeans.xml");

看到了吗?容器可以接收不止一个XML文件。有什么启发吗?让bean定义跨越多个XML文件会很有用。通常,每个单独的XML配置文件都代表架构中的逻辑层或模块

比如我在ServiceBeans.xml中配置了一个Work的bean:

<bean id="work" class="com.xx.entity.Work">
		<!--name是属性名,value是赋的值-->
        <property name="workName" value="deBug"></property>
 </bean>

那么,在Beans.xml中我们可以通过import引入外部xml解决跨xml的bean依赖问题

<import resource="ServiceBeans.xml"></import>

<bean id="user" class="com.xx.entity.User">
    <property name="name" value="Bob"></property>
    <!--ref表示另一个bean定义的id,引用-->
    <property name="work" ref="work"></property>
</bean>

元素id和ref元素之间的这种联系表达了协作对象(beans)之间的依赖关系。

ApplicationContext是高级工厂的接口,能够维护不同bean及其依赖项的注册表。通过使用该方法T getBean(String name, Class< T > requiredType)检索Bean的实例

这里name和requiredType不一定要全部传给方法,但是,二者结合通常可以保证你找到想要的bean。

package com.xx.main;

import com.xx.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test_001 {
    public static void main(String[] args) {
        
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml", "ServiceBeans.xml");
		
// 实例化User对象,有byName和byType两种方式	
        // User user = (User)context.getBean("user");
        // User user1 = (User) context.getBean(com.xx.entity.User.class);
        User user2 = (User) context.getBean("user",com.xx.entity.User.class);
		
		// User类和Work类我已经重写了toString方法
        System.out.println(user2);
    }
}

程序执行结果:
在这里插入图片描述
getBean() 不应该是由我们(开发者)来调用,这是容器该做的事情。Spring还提供了其他的检索bean对象的方法,但是我们一般不会用到它们。

Bean

上面已经提到,Bean是构成我们整个应用程序的支柱,也是我们Spring的IoC容器的管理对象。Bean以开发人员配置元数据的方法提供给容器来管理。元数据配置可以选择XML方式或者java配置。

Bean的定义

一个完整的Bean的定义包括:

属性描述
Class这个属性是强制性的,并且指定用来创建 bean 的类。
Name这个属性指定唯一的 bean 标识符。name = id
Scopebean创建对象的作用域
Constructor arguments用来注入依赖关系
Properties用来注入依赖关系,对象的属性
Autowiring mode用来注入依赖关系
Lazy initialization mode延迟初始化,请求时再创建,否则不创建对象
Initialization method在 bean 的所有必需的属性被容器设置之后,调用回调方法
Destruction method当包含该 bean 的容器被销毁时,使用回调方法

Bean和容器的关系

在这里插入图片描述

Bean的命名

每个bean都有一个或多个标识符。这些标识符在托管bean的容器中必须是唯一的。bean通常只有一个标识符。但是,如果它需要多个,则额外的可以被视为别名(非常不推荐是用别名)。

  • 为什么要给Bean命名?

——Bean的名字是它的唯一标识,实例化Bean和DI都需要用到它。

  • 如果没有命名会发生什么?

——如果你没有自己命名,那么容器会自己给Bean使用驼峰命名为Bean生成唯一名称。如果要引用这个Bean,可以通过ref或者Service Locator样式查找。

  • XML文件中配置的bean的属性有idname,二者有什么区别?

——二者没有区别,id和name属性都是用来给Bean命名的。

  • 命名有什么规范吗?

——Bean使用标准Java约定作为实例字段名称。Bean的首字母小写,并开始采用驼峰命名的方式。特殊情况比如ABean这种则保持原有的类名,这java.beans.Introspector.decapitalize的要求一致。

  • 怎么给Bean其别名?

——可以在bean定义时为id或者name赋多值,不同名字之间使用逗号(,),分号(;)或空格分隔。也可以通过alias标签在bean的定义之外起别名。

给user另起三个别名:aUser,oneUser,mine

<bean id="user,aUser;oneUser mine" class="com.xx.entity.User">
   <property name="name" value="Bob"></property>
   <property name="work" ref="Work"></property>
</bean>

等同于:

<alias name="user" alias="aUser"/>
<alias name="user" alias="oneUser"/>
<alias name="user" alias="mine"/>

那么,在别处使用别名引用Bean时,它们使用的其实是同一个对象。

Bean的实例化

  • Bean的实例化有几种方式?分别是什么?

——Bean的实例化有三种方式:使用构造函数实例化,使用静态工厂方法实例化,使用实例工厂方法实例化。

Bean的作用域

  • 什么是Bean的作用域?

——指实例化的Bean对象的使用范围。

  • Spring支持的作用域有哪些?

—— Spring支持六个不同的作用域,其中有四个仅适用WebApplicationContext环境。

作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
application仅适用于WebApplicationContext环境
websocket仅适用于WebApplicationContext环境
  • 如何选择作用域?

——根据不同的场景选择,例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

  • 当我选择singleton作为一个Bean的作用域是会发生什么?

——singletons是单例的,Spring IoC会为之创建bean对应唯一的实例以供使用。也就是说,创建容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。

  • 当我选择Prototype作为一个Bean的作用域是会发生什么?

——当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

  • request,session,application,和websocket这四个作用域范围有何特殊之处?

——这四个作用域都是适用于WebApplicationContext环境的作用域,如果不经过处理将这些作用域与常规的IoC容器一起使用,就会引发未知bean范围的问题。使用Spring MVC则无需配置,如果使用Servlet 2.5 Web容器,并且在Spring之外处理请求 DispatcherServlet(例如,使用JSF或Struts时),则需要注册 org.springframework.web.context.request.RequestContextListener ServletRequestListener。将以下声明添加到Web应用程序的web.xml文件中:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

Bean的生命周期

完整的bean的声明周期大致可分为四个部分:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁 。
在这里插入图片描述
Spring Framework提供了许多可用于自定义bean特性的接口。生命周期回调是其中的一种,最重要的两个是初始化回调销毁回调

当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。初始化回调和销毁回调与之对应。

也就是说,我们人为地可以介入bean的生命周期(有AOP的韵味)。

初始化回调

初始化回调是指设置bean的所有必要属性后调用的方法

初始化回调用来完成对一个对象初始化的工作。

实现初始化回调的方式有三种:

  • 实现InitializingBean接口进行初始化回调

org.springframework.beans.factory.InitializingBean这个接口指定了唯一的一个方法:

void afterPropertiesSet() throws Exception;

实现这个接口并重写这个方法就可以在该方法中进行初始化工作,Spring在完成对象的定以后会自动调用该方法:

package com.xx.beanTest;

import org.springframework.beans.factory.InitializingBean;

public class ExampleBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化工作
        System.out.println("初始回调方法执行了...");
    }

    public void show(){
        System.out.println("show方法执行了...");
    }
}

Beans.xml:

<bean id="exampleBean" class="com.xx.beanTest.ExampleBean"></bean>

测试类:

package com.xx.main;

import com.xx.beanTest.ExampleBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test_002_init {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

        ExampleBean exampleBean = context.getBean(ExampleBean.class);

        exampleBean.show();
		// 初始回调方法执行了...
		// show方法执行了...
    }
}

可以看到我们只调用了show方法,但是afterPropertiesSet()方法被自动调用了。

不过建议不要使用该InitializingBean接口,因为它会将不必要地代码耦合到Spring中

推荐在基于 XML 的配置元数据的情况下使用 init-method 属性来指定带有 void 无参数方法:

 <bean id="exampleBeanB" class="com.xx.beanTest.ExampleBeanB" init-method="myInit"></bean>
public class ExampleBeanB {

    private void myInit() {
        System.out.println("xml形式的初始化回调");
    }
}

除了这两种方法,还可以使用基于Java配置的带有initMethod属性@Bean

销毁回调

有了初始化回调的概念,销毁回调就很容易理解了。

销毁回调就是在Bean的实例化对象被Spring销毁后调用的方法。

org.springframework.beans.factory.DisposableBean 接口指定一个单一的方法:

void destroy() throws Exception;

实现这个接口并重写destroy()方法就可以做一些对象销毁后的清除工作。同样的,实现DisposableBean接口的方式会带来不必要的代码耦合。

package com.xx.beanTest;

import org.springframework.beans.factory.DisposableBean;

public class ExampleBean implements DisposableBean {

	@Override
    public void destroy() throws Exception {
        System.out.println("销毁回调方法执行了...");
    }
  
    public void show(){
        System.out.println("show方法执行了...");
    }
 
}
<bean id="exampleBean" class="com.xx.beanTest.ExampleBean"></bean>

由于这种方法带来了不必要的代码耦合,我们更推荐基于 XML 的配置元数据的情况下,使用 destroy-method 属性来指定带有 void 无参数方法的名称来实现销毁方法回调:

public class ExampleBean {
    
    private void myDestroy() {
        System.out.println("基于XML形式的销毁方法回调...");
    }
}
<bean id="exampleBean" class="com.xx.beanTest.ExampleBean" destroy-method="myDestroy"></bean>

默认的初始化和销毁方法

  • 默认的初始化和销毁方法有什么用?

如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。使用默认的初始化和销毁方法可以完成对所有bean的初始化和销毁方法的定义。

  • 如何启用默认的初始化和销毁方法?

——在Beans标签上添加属性 default-init-methoddefault-destroy-method 就可以完成对所有bean的初始化回调和销毁回调。

示例:

<beans 
    default-init-method="init" 
    default-destroy-method="destroy">

   <bean id="..." class="...">
       <!-- collaborators and configuration for this bean go here -->
   </bean>

   <bean id="..." class="...">
       <!-- collaborators and configuration for this bean go here -->
   </bean>

</beans>

这样在编写Bean时只需要给它们的初始化回调方法和销毁方法起统一的名字就可以 。

如果在使用了默认初始化方法和回调方法情况下,bean自身还配置了init-method或者destroy-method,那么默认方法会被覆盖掉

在非Web应用程序中正确关闭Spring IoC容器

  • Spring Web需要注意这个问题吗?

——不需要,Spring的基于Web的 ApplicationContext实现已经具有代码实现,可以在相关Web应用程序关闭时正常关闭Spring IoC容器。

  • 如何在非Web应用程序中正确关闭Spring IoC容器?

——如果在非Web应用程序环境中使用Spring的IoC容器,需要调用接口ConfigurableApplicationContext上声明的方法registerShutdownHook(),这样做可确保正常关闭并在单例bean上调用相关的destroy方法,以便释放所有资源。

示例:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...
	
        // main method exits, hook is called prior to the app shutting down...
    }
}

Bean的前置处理器和后置处理器

在这里插入图片描述

  • 前置处理器和后置处理器有什么用?

——很显然,前置处理器和后置处理器可以在我们使用初始化回调的前后做一些额外处理

  • 在Spring中是怎样定义前置处理器和后置处理器的?

——BeanPostProcessor 接口定义了postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法,对应的就是前置处理器和后置处理器。

  • 前置处理器和后置处理器可以有多个吗?

——可以,你可以配置多个 BeanPostProcessor 接口,通过设置 BeanPostProcessor 实现的 Ordered 接口提供的 order 属性来控制这些 BeanPostProcessor 接口的执行顺序。ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器,然后通过在容器中创建 bean,在适当的时候调用它。

应用举例:

前置处理和后置处理:

package com.xx.beanTest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class InitExample implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeforeInitialization..." + beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("AfterInitialization..." + beanName);
        return null;
    }
}

bean:

package com.xx.beanTest;

public class ExampleBean {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void getMessage() {
        System.out.println("Your Message : " + message);
    }

    public void myInit() {
        System.out.println("init...");
    }

    public void myDestroy() {
        System.out.println("destroy...");
    }
}

Beans.xml:注册ExampleBean和InitExample

<bean id="exampleBean" class="com.xx.beanTest.ExampleBean" 
	init-method="myInit" destroy-method="myDestroy">
	
        <property name="message" value="hello world!"></property>
</bean>

<bean class="com.xx.beanTest.InitExample"></bean>

测试类:

package com.xx.main;

import com.xx.beanTest.ExampleBean;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test_init {
    public static void main(String[] args) {

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

        ExampleBean exampleBean = context.getBean(ExampleBean.class);

        exampleBean.getMessage();
		
		// 关闭容器,调用destroy方法
        context.registerShutdownHook();

    }
}

运行结果:

BeforeInitialization...exampleBean		# 前置处理
init...									# 初始化
AfterInitialization...exampleBean		# 后置处理
Your Message : hello world!				# getMessage()方法
destroy... 								# 销毁方法

Bean的定义继承

  • 为什么要使用定义继承?

——Bean的定义包含了非常有用的一些信息,包括构造函数参数,属性值和特定于容器的信息。如果这些定义可以被继承的话那么可以帮助我们快速完成元数据的配置。

  • 子Bean和父Bean有什么关系?

——子bean的定义将继承父bean的定义配置数据。子bean的定义还可以覆盖某些值或根据需要添加其他值。

  • 如何继承?

——基于XML的配置元数据时,子bean的定义可以使用parent属性指定父Bean

示例:

<bean id="fatherBean" abstract="true">
    <property name="name" value="fatherName"/>
    <property name="age" value="50"/>
</bean>

<bean id="sonBean"
        class="org.xx.beans.TestBean2"
        parent="fatherBean" init-method="init">  
    <property name="age" value="20"/>
    <!-- the name property value of fatherName will be inherited from parent -->
</bean>
  • 为什么父 Bean没有 class属性,abstract属性又是干嘛的?

——这表示这个bean将作为一个模板,class属性对于父Bean来说并不是必须的。但是必须指定abstract为true。说明这个bean是抽象的,不能被实例化,它是不完整的。但是它可以作为一个模板被其他bean所继承。

  • 忘记指定abstract属性为true会发生什么?

——ApplicationContext默认情况下预先实例化所有单例,如果你有一个(父)bean定义,你只打算用作模板,并且这个定义指定了一个类,你必须确保将abstract属性设置为true否则应用程序上下文将实际(尝试)预先实例化abstractbean。

  • 抽象的 Bean 可以使用 ref 被其他 Bean引用吗?

——不可以。

  • 子Bean的 name属性和 age属性的值是多少?

——name属性继承自父Bean,是fatherName;age属性是自己的(父Bean的age被覆盖),是20。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值