Spring框架之Ioc容器篇(开门篇)

目录

  1. Spring简介
    1. Spring框架结构
    2. 下载Spring框架
    3. 第一个Spring项目(Hello World)
  2. IoC容器
  3. 配置Bean (3种方式:XML、Java注解、Java代码)
  4. 依赖注入

Spring是一款JavaEE轻量级开源框架(用于简化JavaEE企业级应用的开发)。

Spring分为
  1. 狭义
    Spring Framework(本篇文章介绍)
      2个核心功能:Ioc、AOP。
  2. 广义(以Spring Framework框架为核心的Spring技术栈)
    1. Spring Framework
    2. Spring MVC
    3. Spring Boot(Spring团队提供的全新框架,用来简化Spring项目的搭建和开发)
      以Spring为基础,提供了大量开箱即用的依赖包(自动管理依赖包中的依赖、提供了大量默认配置)使开发者更专注于业务逻辑。
    4. Spring Cloud(一款基于Spring Boot实现的微服务框架)
      用来开发高度可扩展、高性能、简单易懂、易部署、易维护的分布式微服务系统。
      它并不是某一门技术,而是一系列微服务解决方案或框架的集合。它将市面上成熟的、经过验证的微服务框架整合后通过Spring Boot的思想进行再封装(屏蔽掉其中复杂的配置和实现原理)。
    5. Spring Mobile(对Spring MVC进行了扩展,用来简化移动端Web应用的开发)
    6. Spring Data
      数据库访问模块。
      通过它,开发人员可以使用一种相对统一的方式,来访问位于不同类型数据库中的数据。
    7. Spring Security(前身为Acegi)
      一款可以定制化的身份验证和访问控制框架。
    8. Spring Batch(批处理)
     一款专门针对企业级系统中的日常批处理任务的轻量级框架,能够帮助开发人员方便的开发出健壮、高效的批处理应用程序。

1. Spring简介

优点
  1. 方便解耦,简化开发
    将所有对象的创建和依赖关系的维护交给SpringIoC容器来管理。
  2. 方便集成各种流行框架
    Struts2、Hibernate、MyBatis等。
  3. 降低JavaEE API的使用难度
    对JavaEE开发中一些难用的API(如:JDBC、JavaMail、远程调用等)进行了封装。
  4. 方便测试
    支持JUnit4,可通过注解对Spring程序进行测试。
  5. 支持AOP面向切面编程
    降低通用功能(通常是一些可复用的功能,如:日志功能、事务功能、权限检查、参数检查、统计信息)和业务逻辑的耦合,减少代码的重复性。
  6. 支持声明式事务处理
    只需通过配置就可完成对事务的管理,而无需通过编程来完成。
在实际开发中,服务器端应用程序通常采用三层体系架构,分别为
  1. 表现层(web)
    Spring在表现层提供了对 Spring MVC、Struts2 等框架的整合;
  2. 业务逻辑层(service)
    Spring在业务逻辑层提供了管理事务和记录日志的功能;
  3. 持久层(dao)
    Spring在持久层还可以整合 MyBatis、Hibernate 和 JdbcTemplate 等技术,对数据库进行访问。
  1. Spring框架结构
Spring框架结构
说明:
  1. 核心容器(CoreContainer)由4个模块组成:
      1. Core核心模块(其他模块都依赖该模块)
        提供了Spring框架基本的核心工具类(资源访问、类型转换、常用工具类等)。
      2. Beans模块
        提供了BeanFactory容器(一个工厂模式的复杂实现)实现基础IoC功能(访问配置文件、创建和管理bean、DI依赖注入等)
      3. Context上下文模块(ApplicationContext接口可用于获取配置中的任何Java对象)
        基于Core和Beans模块,添加了 资源绑定、数据验证、国际化、JavaEE支持、容器生命周期、事件传播等功能。
      4. SpEL表达式语言模块
        支持:访问和修改 属性值、数组、容器、索引器;方法调用;命名变量;算数和逻辑运算;从Spring容器获取Bean;列表投影、选择和一般的列表聚合等。
  
  2. 数据访问/集成(DataAccess/Integration)由5个模块组成:
      1. JDBC模块
        提供了JDBC抽象层(对JDBC、事务等操作进行了封装)。
      2. ORM模块
        方便集成 对象/关系映射框架(JPA、JDO、Hibernate、MyBatis)。
      3. OXM模块
        方便集成 Object/XML映射(JAXB、Castor、XMLBeans、JiBX、XStream) 。
       4. JMS模块(Java消息服务) 
        提供了一套“消息生产者、消息消费者”模板来方便使用JMS。
        JMS用于在两个应用之间或分布式系统中发送消息,进行异步通信。
      5. Transactions模块
        支持编程和声明式事务管理。

  3. Web(MVC/Remoting)由4个模块组成:
      1. Web模块
        提供了基本的Web开发功能(如:多文件上传功能、使用Servlet监听器的IoC容器初始化、Web应用上下文)。
      2. Servlet模块
        提供了Spring MVC Web框架实现(提供了基于注解的请求资源注入、更简单的数据绑定、数据验证、JSP标签等)。
      3. WebSocket模块
        提供了快速搭建WebSocket Server实现双向通讯的接口。
      4. Portlet模块
        提供了在Portlet环境中使用MVC实现类似Web-Servlet模块的功能。

  4. 其他
      1. AOP模块
        提供了面向切面编程实现,降低通用功能和业务逻辑的耦合(提供 日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术)。
      2. Aspects模块
        提供与AspectJ(一个功能强大且成熟的面向切面编程AOP框架)的集成。
      3. Instrumentation模块
        提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
      4. Messaging模块
        为STOMP提供了支持。
      5. Test测试模块
        支持JUnit、TestNG测试框架。
  1. 下载Spring框架

官网下载Spring框架commons-logging-xxx.jar

Spring框架目录

目录说明:
  1. docs目录
    存放:Spring的API文档、开发规范。
  2. libs目录
    存放:开发需要的jar。
  3. schema目录
    存放:开发所需要的schema文件(定义了Spring相关配置文件的约束)。

libs目录下的jar说明
  其中1、2、3、4分别对应Spring框架的核心容器的4个模块。
  1. spring-core.jar(必须)
    提供了基本的核心工具类(其它jar包的核心)。
    依赖第三方jar包:commons-logging-xxx.jar。
  2. spring-beans.jar(必须)
    提供了基础IoC功能(提供了进行IoC/DI操作相关的所有类:访问配置文件、创建和管理bean、其他相关的类)。
  3. spring-context.jar(必须)
    在基础IoC功能上进行了扩展(邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存、各种视图框架的封装等企业级功能)。提供了使用ApplicationContext时所需的全部类,JDNI所需的全部类,instrumentation组件以及校验Validation方面的相关类。
  4. spring-expression.jar(必须)
    提供了Spring表达式语言相关的所有类。
  5. spring-aop.jar
    提供了AOP特性相关的所有类。
  6. spring-jdbc.jar
    提供了SpringJDBC相关的所有类。
  7. spring-tx.jar
    提供了事务和异常处理相关的所有类(为JDBC、Hibernate、JDO、JPA等提供一致的声明式和编程式的事务管理)。
  7. spring-web.jar
    包含Web 应用开发时,用到Spring 框架时所需的核心类,包括自动载入Web Application Context 特性的类、Struts 与JSF 集成类、文件上传的支持类、Filter 类和大量工具辅助类。
    外部依赖spring-context, Servlet API, (JSP API, JSTL, Commons FileUpload, COS)。
  8. spring-webmvc.jar 
    包含Spring MVC 框架相关的所有类。包含国际化、标签、Theme、视图展现的FreeMarker、JasperReports、Tiles、Velocity、XSLT相关类。包括框架的Servlets,Web MVC框架,控制器和视图支持。
    如果应用使用了独立的MVC 框架,则无需这个JAR 文件里的任何类。
    外部依赖spring-web, (spring-support,Tiles,iText,POI)。
  9. spring-aspects.jar 
    提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中,比如Eclipse AJDT。
  10. spring-context-support.jar
    Spring context的扩展支持,用于MVC方面。
  11. spring-instrument.jar
    Spring对服务器的代理接口
  12. spring-instrument-tomcat.jar
    Spring对tomcat连接池的集成
  13. spring-jms.jar
    为简化jms api的使用而做的简单封装。
    外部依赖spring-beans,spring-dao,JMS API。
  14. spring-orm.jar
    整合第三方的orm实现,如hibernate,ibatis,jdo以及spring 的jpa实现
  15. spring-oxm.jar
    Spring对于object/xml映射的支持,可以让JAVA与XML之间来回切换
  16. spring-messaging.jar:
  17. spring-test.jar
    对JUNIT等测试框架的简单封装
  19. spring-webmvc-portlet.jar
    Spring MVC的增强
  20. spring-websocket.jar:
  1. 第一个Spring项目(Hello World)
===》1. 导入依赖包
MyEclipse项目栏右击新建Java项目 | 点击项目右键 | Build Path | Config Build Path | Libraries | add External JARs |  1. 导入核心容器模块对应的4个jar(后期看需要再导入其他jar)或者 直接把spring的lib中所有的jar全部导入 ; 2. 导入commons-logging-xxx.jar

===》2. 新建HelloWorld.java文件(com.sst.cx包下)
  package com.sst.cx;
  public class HelloWorld {
       private String message;
       public void setMessage(String message){
          this.message  = message;
       }
       public void getMessage(){
          System.out.println("Your Message : " + message);
       }
       public void init(){
          System.out.println("Bean is going through init.");
       }
       public void destroy(){
          System.out.println("Bean will destroy now.");
       }
  }

===》3. 新建HelloBeans.xml(src下)
  定义各对象(分配唯一ID、属性赋值、依赖关系)。
<?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
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="helloWorld" class="com.sst.cx.HelloWorld">
       <property name="message" value="Hello World!"/>
   </bean>
</beans>

===》4. 新建Main.java
  获取配置文件中的HelloWorld类实例,并调用其getMessage方法。 
  package com.sst.cx;
  import org.springframework.context.ApplicationContext;
  import org.springframework.context.support.ClassPathXmlApplicationContext;
  public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("HelloBeans.xml");
            // HelloWorld obj = context.getBean("helloWorld", HelloWorld.class);
            HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
            obj.getMessage();
        }
  }
说明:
  1. ClassPathXmlApplicationContext类用于加载Spring配置文件,创建并初始化所有Bean对象。
  2. ApplicationContext.getBean()方法用于获取Bean,返回类型为Object。

===》5. 运行
Main.java文件 右键 | Run As Java Application运行程序,控制台会输出:
Your Message : Hello World!
添加jar依赖包

运行结果

2. IoC容器

Spring通过IoC容器来管理(创建、初始化、依赖关系)所有Java对象(被称为SpringBean)。

1. IoC(Inversion of Control)控制反转
  是一种设计思想,用来最大限度地降低耦合度。
  传统Java应用中,A类中想调用B类的属性或方法时,需要在代码中通过new创建B类的对象然后才能调用其属性或方法。A称为调用者,B称为被调用者,调用者掌握着被调用者的创建控制权。
  在Spring应用中,所有Java对象的创建控制权都由Ioc容器掌握。首先对各个对象及依赖关系进行配置(配置Bean的3种方式:XML配置文件、注解、Java代码,配置依赖注入的2种方式:基于构造函数、基于setter);Spring启动时,IoC容器会加载并解析这些配置文件,根据对象定义及依赖关系利用Java的反射机制创建并管理对象(被IoC容器创建并管理的对象被称为SpringBean);当需要使用某个Bean时直接从IoC容器中获取,而不是通过new方式创建。Ioc容器实现解耦的原理:对象的信息及依赖关系都是在配置文件中定义的,而不是在代码中紧密耦合,当对象发生改变后只需修改配置文件,而无需对Java代码进行修改。
  原本调用者为主动的一方,需要什么资源就自己去创建,在Spring中由IoC容器掌握主动权,原本的调用者需要被动的等待IoC容器创建自己所需的对象,即所谓的控制反转(控制权反转)。
  
2. DI(Denpendency Injection)依赖注入
  A类中有一个类型为B类的属性(A的对象依赖于B的对象),Spring的IoC容器在创建对象时会自动根据依赖关系,将依赖对象B注入到当前对象A中,即所谓的依赖注入。
IoC容器的2种实现:
  1. BeanFactory容器
    在org.springframework.beans.factory.BeanFactory接口中定义。BeanFactory相关的接口(BeanFactoryAware,InitializingBean,DisposableBean)向后兼容其他三方框架。
    提供了IoC容器的基本功能(功能最简单的容器)。采用懒加载机制,Ioc容器在加载配置文件时并不会立即创建Java对象,只有在程序中使用到时才会创建。 移动设备或基于applet的应用的资源非常宝贵, 优先选择BeanFactory。
    例:
      BeanFactory factory = new ClassPathXmlApplicationContext("HelloBeans.xml");  
      HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");   
      obj.getMessage();
  2. ApplicationContext容器
    在org.springframework.context.ApplicationContext接口(BeanFactory接口的子接口)中定义。对BeanFactory容器进行了扩展,增加了许多企业级功能(AOP、国际化、事务)。
    常用的实现类:
      1. FileSystemXmlApplicationContext
        需提供XML文件的完整路径("/"代表项目根目录)。
      2. ClassPathXmlApplicationContext
        不需要提供XML文件的完整路径。
        会在ClassPath下搜索XML文件(需正确配置ClassPath)。
      3. WebXmlApplicationContext
        在应用范围内搜索XML文件。
    例:
      // 创建和初始化所有对象(需提供XML文件的完整路径, /代表项目根目录)
      ApplicationContext context = new FileSystemXmlApplicationContext("/src/HelloBeans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");  
      obj.getMessage();   
      // 容器销毁 
      // context.registerShutdownHook();

3. 配置Bean(3种方式:XML、Java注解、Java代码)

说明:
  1. 当无法在类中添加注解时(如:DataSource、JdbcTemplate等第三方库中的类),使用xml方式配置。
  2. 当前项目中的类使用注解方式配置更好。
  3. 可一起联合使用。
  1. XML方式配置
根元素为beans,可包含多个子元素bean(每一个bean元素对应一个Bean对象)。
   <bean id="唯一标识" class="包路径.类名"></bean>

bean元素的常用属性
  1. id
    指定 Bean的唯一标识符(必须以字母开始,由字母、数字、下划线组成)。
  2. name
    指定 Bean的名称(同一个Bean可指定多个名称,以,分隔)。
  3. class
    指定 Bean对应的类(Bean对应的具体实现类的完全限定名)。
  4. scope  
    指定 Bean的作用域(5种)。
      1. singleton(默认)
        单例,每次获取时都是同一个实例(仅对于该容器 即该容器共享同一个实例,一个Web应用会有多个容器)。
      2. prototype  
        每次获取时都创建新的实例。
      // ---------以下只在web的ApplicationContext的上下文中有效(XmlWebApplicationContext) ,否则就会抛出一个 IllegalStateException 的异常---------
      3. request    
        每次HTTP请求都会创建一个实例(其作用域为本次request)。
      4. session    
        同一个会话共享同一个实例(其作用域为本次session)。
      5. application
        同一个Web应用共享同一个实例(其作用域为本应用application)。
      6. websocket
        在整个WebSocket中有效。
    5. lazy-init(只在scope=singleton时有效)
      延迟加载。
      设置为true则首次使用时才会创建Bean实例;设置为false则容器启动时就创建Bean实例。
    6. init-method
      容器创建Bean时(在所有必需的属性被容器设置之后)会调用init-method属性中指定的方法(对应Bean类中创建的自定义初始化方法)。
    7. destroy-method(只在scope=singleton时有效)
      容器销毁Bean时(容器被销毁时会删除容器中的Bean)会调用destroy-method属性中指定的方法(对应Bean类中创建的自定义销毁方法)。
      对于初始化方法和销毁非法,分为全局指定和单个类指定:
        1. 全局指定:在beans元素中加 default-init-method="init" default-destroy-method="destroy"。
        2. 单个类指定:在bean元素中加 init-method="init" destroy-method="destroy"。
    8. parent 
      指定父Bean,与Java类的继承无关(子Bean和父Bean对应的实例不需要有继承关系)。子Bean可以继承父Bean的配置数据(构造方法参数值、属性值),也可以根据需要重写或添加属于自己的配置信息。
      例:
       <bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
        <property name="message1" value="Hello"/>
        <property name="message2" value="World"/>
       </bean>
       <bean id="helloIndia" class="com.sst.cx.HelloIndia" parent="helloWorld">
        <property name="message1" value="Hi"/>
       </bean>
    9. abstract
      设置为true则表示将Bean定义为抽象模版(表明这个Bean是抽象的,不能被实例化,也不能被其他Bean引用,只能在子Bean的parent中引用)。
      例:
       <bean id="beanTeamplate" abstract="true">
        <property name="message1" value="Hello World!"/>
        <property name="message2" value="Hello Second World!"/>
        <property name="message3" value="Namaste India!"/>
       </bean>
    10. autowire(自动装配)
      把Bean与Bean之间建立依赖关系的行为称为装配。
      Spring容器可以在不使用<constructor-arg>和<property>的ref属性手动装配(但随着应用的不断发展,容器中包含的Bean会越来越多,Bean和Bean之间的依赖关系也越来越复杂,这就使得我们所编写的XML配置也越来越复杂繁琐,从而导致可读性差、代码极易出错、严重降低开发效率。可通过自动装配来解决)的情况下,通过bean元素的autowire自动装配属性(根据装配规则为Bean从应用上下文中查找它所依赖的Bean并建立依赖关系)来简化XML内容。仍然可以使用ref属性进行“重写”。
       自动装配的局限性:不能自动装配基础数据类型;没有显式装配精确。
       装配规则(5种)
         1. no(默认)
           不使用自动装配。
           Bean的依赖关系必须通过<constructor-arg>和<property>元素的ref属性来定义。
         2. byName
           按属性名自动装配。
           从所有IoC容器中寻找id/name和(使用自动装配bean对应实例的)属性名相同的bean,并建立依赖关系。
         3. byType
           按属性数据类型自动装配。
           从所有IoC容器中寻找class和(使用自动装配bean对应实例的)属性类型相同的bean,并建立依赖关系(如果同时存在多个对应的bean则异常)。
         4. constructor
           类似于byType,按构造函数的参数类型自动装配。
           从所有IoC容器中寻找class和(使用自动装配bean对应实例的)构造参数类型相同的bean,并建立依赖关系(如果不存在对应的构造函数参数类型的bean则异常)。
         5. default
           使用上一级元素beans中的自动装配规则(即 beans元素的default-autowire属性)。

bean元素的子元素(分别对应属性注入的2种方式)
  1. constructor-arg    
    通过Bean实例的带参构造函数实现Bean的属性注入(一个constructor-arg元素对应一个需要注入的属性)。
    name属性指定对应的构造参数名,value属性指定参数值。
    index属性指定构造参数的序号(从0开始),type属性指定构造参数的类型。
  2. property   
    通过Bean实例的setter方法实现Bean的属性注入(一个property元素对应一个需要注入的属性)。
    name属性指定Bean实例相应的属性名,value属性指定属性值。

constructor-arg元素、property元素的常用属性
  1. name
    用于指定构造函数的参数名 或 属性名。
  2. ref
    用于注入 指定Bean实例(根据填入的 Bean元素的id或name) 
  3. value
    用于注入 一个常量值。

constructor-arg元素、property元素的子元素
  1. ref
    用于注入 指定Bean实例(根据填入的 Bean元素的id或name) 
  2. value
    用于注入 一个常量值。
  3. list(允许重复)
    用于注入 List或数组类型的属性。
  4. set(不能重复)
    用于注入 Set类型的属性。
  5. map
    用于注入 Map类型的属性。
    子元素:entry(用于设置一个键值对。key属性指定字符串类型的键,ref或value子元素指定其值)
  6. props元素
    用于注入 键值对
  7. array元素
    用于注入

示例

<?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
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <!-- 
      懒加载:lazy-init="true"
      初始化方法:init-method
      销毁方法:destroy-method
    -->
   <bean id="..." class="..." lazy-init="true" init-method="..." destroy-method="...">
   </bean>
</beans>
  1. 注解方式配置(简化xml文件内容)

通过注解可以在不改变原有代码逻辑的情况下,在源代码中嵌入补充信息。
从Spring2.5开始,可以对类、方法、属性添加注解来实现:配置Bean,实现依赖注入(会在xml配置的注入之前执行,所以会被覆盖)、自动装配。

===》1. 首先需要在xml配置文件中,开启组件扫描功能。
  <!-- 开启组件扫描功能。会扫描包下的类,从类的注解中获取Bean的定义信息(如:如果类使用了@Component注解,则将该类配置到容器中)。 -->
  <context:component-scan base-package="com.sst.cx"></context:component-scan>
  <!-- 开启注解功能 -->
  <context:annotation-config/>

===》2. 配置Bean的注解(在类上添加如下注解,会将该类配置到IoC容器中)
  1. @Component注解
    将类标识为Bean。
    可以作用在应用的任何层次(如:Service层、Dao层等)。
  2. @Repository注解
    将Dao层(数据访问层)的类标识为Bean。
  3. @Service注解
    将Service层(业务层)的类标识为Bean。
  4. @Controller注解
    将Controller层(控制层)的类标识为Bean(如:Struts2的Action、SpringMVC的Controller)。
    例:@Component("helloBean")、@Repository("userDaoImpl")
  5. @Scope注解
    指定Bean的作用域

===》3. 实现依赖注入的注解
  1. @Autowired注解(自动装配,默认使用byType自动装配规则)
    可用在setter方法、非setter方法、构造函数、属性。
    1. 用在setter方法上(会自动使用byType,不用设置property)
      @Autowired
      public void setHelloB(HelloB helloB) {
          this.helloB = helloB;
      }
    2. 用在属性上(会自动使用byType,不用设置set方法)
      @Autowired
      HelloB helloB;
    3. 用在构造函数上(会自动使用byType,不用设置property)
      @Autowired
      public HelloWorld(HelloB helloB) {
          this.helloB = helloB;
      }
    4. 当没有匹配到时会报NoSuchBeanDefinitionException异常,可设置required属性为false来避免。
      @Autowired(required=false)  设为非必须(默认是必须的)
  2. @Qualifier注解(与@Autowired注解配合使用:@Autowired + @Qualifier)
    当匹配到多个相同类型的bean时使用,将自动装配规则由默认的byType修改为byName。
    例:
      @Autowired
      @Qualifier("helloB1")
      private HelloB helloB;
  3. @Resource注解(自动装配,默认使用byName自动装配规则)
    如果指定name属性,则按byName进行装配;
    如果指定type属性,则按byType进行装配;
    如果都不指定,则先按byName进行装配,如果没有找到则再按byType进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。
  4. @Value注解(注入普通属性值)
    @Value("张三")
  5. @Nullable注解(注入null)
4. 生命周期相关的注解
  1. @PostConstruct注解 
    标识为初始化方法(指定为init-method)
  2. @PreDestroy注解
    标识为销毁方法(指定为destroy-method)
5. 其他
  1. @Required注解
    用于setter方法上。表示相应的bean属性必须进行注入,否则BeanInitializationException异常。
    例:
      @Required
      public void setName(String name) {
          this.name = name;
      }
      <bean id="helloWorld" class="com.sst.cx.HelloWorld">
          <property name="name" value="cx"/>
      </bean>
  2. @Test注解
  1. Java代码方式配置

可以完全摆脱xml配置文件。

配置类相关的注解
  1. @Configuration注解
    标识该类为配置类(一个配置类等价于一个xml配置文件)。
    获取容器:ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    当有多个配置类时:
     public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class, OtherConfig.class);
        ctx.register(AdditionalConfig.class);
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
     }
  2. @Bean注解
    将该类注册到IoC容器中(所修饰的方法相当于Bean的id属性,方法返回类型相当于Bean的class属性)。
    获取容器中的对象:Foo foo = ctx.getBean(Foo.class);
  3. @Import注解
    导入其他配置类(从其他配置类中加载@Bean定义)。

  例:
    @Import(ConfigA.class)
    @Configuration
    public class AppConfig {
        @Bean
        public Foo foo() {
            return new Foo(bar());  // 依赖注入
        }
        @Bean
        public Bar bar() {
            return new Bar();
        }
        @Bean(initMethod = "init", destroyMethod = "cleanup" )
        public HelloB helloB(){ // HelloB类中有init、cleanup方法
          return new HelloB();
        }
        @Bean
        @Scope("prototype")
        public HelloB helloB(){ 
          return new HelloB();
        }
    }

Bean生命周期

主要分为
  1. 实例化阶段
    向容器请求一个尚未初始化的bean时 或 初始化bean时需要注入另一个尚末初始化的依赖时,容器就会调用doCreateBean()方法创建Bean并进行实例化(给属性赋值属性)。
  2. 初始化阶段
    1. 执行Aware接口的方法(如果Bean实现了xxxAware接口)。通过Aware类型的接口可以获取到Spring容器的一些资源。如:实现BeanNameAware接口可以获取到BeanName,实现BeanFactoryAware接口可以获取到工厂对象BeanFactory等。
    2. 执行BeanPostProcessor的前置处理方法postProcessBeforelnitialization()方法(如果Bean实现了BeanPostProcessor接口),此时Bean已经创建但未初始化。
    3. 执行afeterPropertiesSet()初始化方法(如果Bean实现了InitializingBean接口)
    4. 执行自定义的初始化方法(如果在bean元素的init-method属性中进行了配置)。
    5. 执行BeanPostProcessor的后置处理方法postProcessAfterinitialization()方法(如果Bean实现了BeanPostProcessor接口)此时Bean已经初始化。
  3. 销毁阶段
    1. 执行DestructionAwareBeanPostProcessor后置处理方法postProcessBeforeDestruction()方法(如果Bean实现了DestructionAwareBeanPostProcessor接口)。
    2. 执行DisposableBean的destroy()方法(如果Bean实现了DisposableBean接口)。
    3. 执行自定义的销毁方法(如果在bean元素的destroy-method属性中进行了配置)。
Bean的生命周期

后置处理器

Spring提供了如下后置处理器
  1. BeanFactoryPostProcessor
  2. BeanPostProcessor
  3. InitializingBean

如果bean实现了以上接口中的方法,这些方法会在bean初始化时调用,可以做一些前置、后置处理。
例(BeanPostProcessor)

===》InitHelloWorld.java
package com.sst.cx;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InitHelloWorld implements BeanPostProcessor {
     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
          System.out.println("BeforeInitialization : " + beanName);
          return bean;  
       }
       public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
          System.out.println("AfterInitialization : " + beanName);
          return bean;  
       }
}

4. 依赖注入(即给属性赋值)

分为2种:
  1. 基于构造函数(通过Bean实例的带参构造函数实现Bean的属性注入)2种方式
    方式1. 根据构造函数的参数名
      <bean id="student" class="com.sst.cx.Student">
        <constructor-arg name="id" value="2"></constructor-arg>
      </bean>
    方式2. 根据构造函数的参数顺序
      <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg type="int" value="2001"/>
        <constructor-arg type="java.lang.String" value="Zara"/>
      </bean>
      或
      <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg index="0" value="2001"/>
        <constructor-arg index="1" value="Zara"/>
      </bean>
    使用步骤:
      1. 在Bean中创建一个带参构造函数(一个构造参数对应一个需要注入的属性)。
      2. 在xml配置文件中使用<bean>元素对Bean进行定义,在<bean>元素内使用<constructor-arg>子元素对构造函数内的参数进行赋值(Bean的构造函数内有多少参数,就需要使用多少个 <constructor-arg> 元素)。
  2. 基于setter(通过Bean实例的setter方法实现Bean的属性注入)
    根据属性名
    <bean id="student" class="com.sst.cx.Student">
        <property name="id" value="1"></property>
        <property name="grade" ref="grade"></property>
    </bean>
    使用步骤:
      1. 在Bean中创建一个无参构造函数(若有其他带参构造函数时则需创建,若没有其他带参构造函数时则无需创建),并为所有需要注入的属性提供一个settter方法。
        在Spring实例化Bean的过程中,IoC容器首先会调用默认的构造方法(无参构造方法)实例化Bean(Java对象),然后通过Java的反射机制根据属性名调用Bean的setter方法 将属性值注入到Bean中。
      2. 在xml配置文件中使用<bean>元素对Bean进行定义,在<bean>元素内使用<property>子元素对各个属性进行赋值。

短命名空间注入(对上述2种方式进行简化)
  1. c命名空间(构造函数方式注入的快捷实现)  
    需要在beans元素中引入XML约束xmlns:c="http://www.springframework.org/schema/c"
    以bean属性的形式(c:普通属性="属性值" c:对象属性-ref="对象的引用")代替<constructor-arg> 元素。
  2. p命名空间(setter方式注入的快捷实现)
    需要在beans元素中引入XML约束xmlns:p="http://www.springframework.org/schema/p"
    以bean属性的形式(p:普通属性="属性值" p:对象属性-ref="对象的引用")代替<property> 元素。

示例(基于构造函数的属性注入)

===》1. Grade.java 年级类
package com.sst.cx;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Grade {
    private static final Log LOGGER = LogFactory.getLog(Grade.class);
    private Integer gradeId;
    private String gradeName;
    public Grade(Integer gradeId, String gradeName) {
        LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:gradeId=" + gradeId + ",gradeName=" + gradeName);
        this.gradeId = gradeId;
        this.gradeName = gradeName;
    }
    @Override
    public String toString() {
        return "Grade{" +
                "gradeId=" + gradeId +
                ", gradeName='" + gradeName + '\'' +
                '}';
    }
}

===》2. Student.java 学生类
package com.sst.cx;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Student {
    private static final Log LOGGER = LogFactory.getLog(Student.class);
    private int id;
    private String name;
    private Grade grade;
    public Student(int id, String name, Grade grade) {
        LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:id=" + id + ",name=" + name + ",grade=" + grade);
        this.id = id;
        this.name = name;
        this.grade = grade;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

===》3. 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
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="student" class="com.sst.cx.Student">
        <constructor-arg name="id" value="2"></constructor-arg>
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="grade" ref="grade"></constructor-arg>
    </bean>
    <bean id="grade" class="com.sst.cx.Grade">
        <constructor-arg name="gradeId" value="4"></constructor-arg>
        <constructor-arg name="gradeName" value="四年级"></constructor-arg>
    </bean>
</beans>

===》4. 测试
package com.sst.cx;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
    private static final Log LOGGER = LogFactory.getLog(MainApp.class);
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        Student student = context.getBean("student");
        LOGGER.info(student.toString());
    }
}

示例(基于setter的属性注入)

===》1. Grade.java 年级类
package com.sst.cx;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Grade {
    private static final Log LOGGER = LogFactory.getLog(Grade.class);
    private Integer gradeId;
    private String gradeName;
    // 无参构造方法。在没有其他带参构造方法时可以省略。
    public Grade() {
    }
    public void setGradeId(Integer gradeId) {
        LOGGER.info("正在执行 Grade 类的 setGradeId() 方法…… ");
        this.gradeId = gradeId;
    }
    public void setGradeName(String gradeName) {
        LOGGER.info("正在执行 Grade 类的 setGradeName() 方法…… ");
        this.gradeName = gradeName;
    }
    @Override
    public String toString() {
        return "Grade{" +
                "gradeId=" + gradeId +
                ", gradeName='" + gradeName + '\'' +
                '}';
    }
}

===》2. Student.java 学生类
package com.sst.cx;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Student {
    private static final Log LOGGER = LogFactory.getLog(Student.class);
    private int id;
    private String name;
    private Grade grade;
    // 无参构造方法。在没有其他带参构造方法时可以省略。
    public Student() {
    }
    public void setId(int id) {
        LOGGER.info("正在执行 Student 类的 setId() 方法…… ");
        this.id = id;
    }
    public void setName(String name) {
        LOGGER.info("正在执行 Student 类的 setName() 方法…… ");
        this.name = name;
    }
    public void setGrade(Grade grade) {
        LOGGER.info("正在执行 Student 类的 setGrade() 方法…… ");
        this.grade = grade;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

===》3. 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
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="student" class="com.sst.cx.Student">
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="grade" ref="grade"></property>
    </bean>
    <bean id="grade" class="com.sst.cx.Grade">
        <property name="gradeId" value="3"></property>
        <property name="gradeName" value="三年级"></property>
    </bean>
</beans>

示例(短命名空间注入)

c命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="student" class="com.sst.cx.Student" c:id="1" c:name="张三" c:grade-ref="grade" />
    <bean id="grade" class="com.sst.cx.Grade" c:gradeId="3" c:gradeName="三年级" />
</beans>

p命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="student" class="com.sst.cx.Student" p:id="1" p:name="张三" p:grade-ref="grade" />
    <bean id="grade" class="com.sst.cx.Grade" p:gradeId="3" p:gradeName="三年级" />
</beans>
  1. 注入内部Bean(属性类型为类类型)

将定义在property元素、constructor-args元素内部的bean称为内部bean。

例(HelloWorld类有一个HelloB类型属性helloB,HelloB类有个name属性)
  方式1. setter方式注入(需要一个无参构造函数、一个set方法)
   <bean id="helloWorld" class="com.sst.cx.HelloWorld">
      <property name="helloB">
         <bean class="com.sst.cx.HelloB">
           <property name="name" value="张三"/>
         </bean>
       </property>
   </bean>
  方式2. 构造函数方式注入(构造函数需要一个HelloB类型的参数helloB)
   <bean id="helloWorld" class="com.sst.cx.HelloWorld">
      <constructor-args name="helloB">
         <bean class="com.sst.cx.HelloB">
           <constructor-args name="name" value="张三"/>
         </bean>
      </constructor-args>
   </bean>
  1. 注入集合(属性类型为集合)
在property元素中添加如下子元素来配置集合值:
  1. list元素
    注入一列值(允许重复)
  2. set元素  
    注入一列值(不能重复)
  3. map元素
    注入键值对(键值可以是任何类型)
  4. props元素
    注入键值对(键值都为字符串类型)
  5. array元素
    注入

注入引用(集合中的元素为类类型时)
      <property name="addressSet或addressList">
         <list>
           <value>A</value>
            <ref bean="helloBean"/>
         </list>
      </property>
      <property name="addressMap">
         <map>
            <entry key="one" value="A"/>
            <entry key ="two" value-ref="helloBean"/>
         </map>
      </property>
      <property name="addressArray">
         <array>
            <ref bean="helloBean"/>
         </array>
      </property>

示例

===》1. HelloWorld.java
  package com.sst.cx;
  import java.util.*;
  public class HelloWorld {
    private List<String> addressList;
    private Set<String>  addressSet;
    private Map<String,String>  addressMap;
    private Properties addressProp;
    private String[] addressArray;
        //
    public List getAddressList() {
        System.out.println("List Elements :"  + addressList);
        return addressList;
    }
    public void setAddressList(List<String> addressList) {
        this.addressList = addressList;
    }
    public Set getAddressSet() {
        System.out.println("Set Elements :"  + addressSet);
        return addressSet;
    }
    public void setAddressSet(Set<String> addressSet) {
        this.addressSet = addressSet;
    }
    public Map getAddressMap() {
        System.out.println("Map Elements :"  + addressMap);
        return addressMap;
    }
    public void setAddressMap(Map<String,String> addressMap) {
        this.addressMap = addressMap;
    }
    public Properties getAddressProp() {
        System.out.println("Property Elements :"  + addressProp);
        return addressProp;
    }
    public void setAddressProp(Properties addressProp) {
        this.addressProp = addressProp;
    }
    public String[] getAddressArray() {
        System.out.println("Array Elements :"  + addressArray);
        return addressArray;
    }
    public void setAddressArray(String[] addressArray) {
        this.addressArray = addressArray;
    }
  }

===》2. Beans.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
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="helloWorld" class="com.sst.cx.HelloWorld">
      <property name="addressList">
         <list>
            <value>A</value>
            <value>B</value>
            <value>C</value>
            <value>C</value>
         </list>
      </property>
      <property name="addressSet">
         <set>
            <value>A</value>
            <value>B</value>
            <value>C</value>
        </set>
      </property>
      <property name="addressMap">
         <map>
            <entry key="1" value="A"/>
            <entry key="2" value="B"/>
            <entry key="3" value="C"/>
            <entry key="4" value="C"/>
         </map>
      </property>
      <property name="addressProp">
         <props>
            <prop key="one">A</prop>
            <prop key="two">B</prop>
            <prop key="three">C</prop>
            <prop key="four">C</prop>
         </props>
      </property>
      <property name="addressArray">
         <array>
            <value>A</value>
            <value>B</value>
            <value>C</value>
            <value>C</value>
         </array>
      </property>
   </bean>
</beans>

===》3. Main.java
  package com.sst.cx;
  import org.springframework.context.ApplicationContext;
  import org.springframework.context.support.ClassPathXmlApplicationContext;
  public class Main {
       public static void main(String[] args) {
               ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
               HelloWorld helloW = (HelloWorld)context.getBean("helloWorld");
               helloW.getAddressList();
               helloW.getAddressSet();
               helloW.getAddressMap();
               helloW.getAddressProp();
       }
  }
  1. 注入其他类型

除了基础数据类型属性、类类型属性、集合外,也支持注入其他类型:Null值、特殊符号。

null值
  <property name="nullValue">
    <null/>
  </property>

特殊符号
  在XML配置文件中"<、>、"、'、&"等特殊字符是不能直接保存的,否则XML语法检查时就会报错。
  解决:
  方式1. 转义后保存。
    1. 转义序列字符之间不能有空格;
    2. 转义序列必须以“;”结束;
    3. 单独出现的“&”不会被认为是转义的开始;
    4. 区分大小写;
  方式2. 将特殊符号包含在<![CDATA[特殊符号写在这里]]>中。
    1. 不允许嵌套;
    2. ]]>不能有空格或换行;
    例:
    <property name="num">
      <![CDATA[<<12345678>>]]>
    </property>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容