1. 概述
Spring是一个开源框架,它为简化企业级应用开发而生,可以使简单的JavaBean实现以前只有EJB才能实现的功能。
1.1. 特点
- 轻量级(非侵入性):基于Spring开发的时候,我们不需要实现Spring提供的任何接口,也不需要继承Spring的任何类,然后我们就可以使用Spring给我们提供的功能
- 依赖注入(DI-Dependency Injection、IOC)
- 面向切面编程(AOP-Aspect Oriented Programming)
- 容器:Spring是一个容器,它可以管理应用对象的生命周期
- 一站式框架:在IOC和AOP的基础上可以整合各种企业级的开源框架和优秀的第三方类库(实际上Spring自身也提供了展现层的Spring MVC和持久层的Spring JDBC)
- 组件组合:Spring实现了使用简单的组件组合成一个复杂的应用,在Spring中可以使用XML配置和Java注解组合这些组件
1.2. 模块
1.3. 配置文件
一个典型的Spring项目需要创建一个或多个Bean配置文件,这些配置文件用于配置Spring IOC容器管理的Bean。Bean的配置文件可以放在Classpath下,也可以放在其它目录下。
2. Hello World
HelloWorld.java
package org.lin.stu.spring; public class HelloWorld { private String name; public void setName(String name) { this.name = name; } public void say() { System.out.println("Hello " + name); } }
2.1. 传统方式
//创建HelloWorld的一个对象 HelloWorld helloWorld = new HelloWorld(); //为name属性赋值 helloWorld.setName("World"); //调用方法 helloWorld.say();//Hello World
2.2. 使用Spring
<?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.xsd"> <!--初始化IOC容器时会调用HelloWorld.java的无参构造器创建名为helloWorld的对象,然后调用setName(String name)方法为name属性赋值--> <bean id="helloWorld" class="org.lin.stu.spring.HelloWorld"> <!--name属性值为setter风格的属性名--> <property name="name" value="World"/> </bean> </beans>
//创建Spring的IOC容器,ClassPathXmlApplicationContext表示配置以XML文件的形式存放在类路径下 //创建后会立即初始化IOC容器(即创建并初始化配置的Bean) ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //从IOC容器中获取Bean HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld"); //调用方法 helloWorld.say();//Hello World
3. Spring中的Bean配置
3.1. 在XML文件中通过bean节点来配置Bean
//class:全类名,通过反射的方式创建Bean <bean id="helloWorld" class="org.lin.stu.spring.HelloWorld"> <property name="name" value="World"/> </bean>
id:Bean的名称:
- 在IOC容器中必须是唯一的
- 若id没有指定,Spring自动将非限定性类名首字母小写作为Bean的名称
- 可以给Bean指定多个名称,多个名称可用逗号、分号或空格分隔
3.2. BeanFactory&ApplicationContext
- BeanFactory:IOC容器的基本实现
- ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口
- BeanFactory是Spring框架的基础设施,面向Spring本身,ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory
- 无论使用何种方式,配置文件是相同的
3.3. ApplicationContext
- ApplicationContext的主要实现类:
- ClassPathXmlApplicationContext:从类路径下加载配置文件
- FileSystemXmlApplicationContext:从文件系统中加载配置文件
- ConfigurableApplicationContext扩展于ApplicationContext,新增加两个主要方法:refresh()和close(),让ApplicationContext具有刷新和关闭能力
- ApplicationContext在初始化时就实例化所有单例的Bean
- WebApplicationContext是专门为WEB应用而准备的
3.4. 通过类型获取Bean
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
如果IOC容器中有多个同类型的Bean,则抛出NoUniqueBeanDefinitionException异常
3.5. 依赖注入
Spring支持3种依赖注入方式
- 属性注入
- 构造器注入
- 工厂方法注入(很少使用,不推荐)
3.5.1. 属性注入
- 属性注入即通过set方法注入Bean的属性值或依赖的对象
- 属性注入使用<property>元素,使用name属性指定Bean的属性名,value属性或<value>子节点指定属性值
- 属性注入是实际应用中最常用的注入方式
3.5.2. 构造器注入
通过构造器注入Bean的属性值或依赖的对象
Car.java
package org.lin.stu.spring; public class Car { private String brand;//品牌 private Double price; private Integer maxSpeed; public Car() { } public Car(String brand, Double price) { this.brand = brand; this.price = price; } public Car(String brand, Integer maxSpeed) { this.brand = brand; this.maxSpeed = maxSpeed; } //getters and setters are omitted }
<bean id="car1" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi"/> <constructor-arg value="300000"/> </bean> <bean id="car2" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi"/> <constructor-arg value="240"/> </bean>
以上代码等价于:
<bean id="car1" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi" index="0"/> <constructor-arg value="300000" index="1"/> </bean> <bean id="car2" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi" index="0"/> <constructor-arg value="240" index="1"/> </bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Car car1 = (Car) ctx.getBean("car1"); Car car2 = (Car) ctx.getBean("car2"); System.out.println(car1);//Car{brand='Audi', price=null, maxSpeed=300000} System.out.println(car2);//Car{brand='Audi', price=null, maxSpeed=240}
因为300000和240既可以匹配Double也可以匹配Integer,此时仅靠顺序无法达到目的。
可以通过type属性避免上述问题:
<bean id="car1" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi"/> <constructor-arg value="300000" type="java.lang.Double"/> </bean> <bean id="car2" class="org.lin.stu.spring.Car"> <constructor-arg value="Audi" index="0"/> <constructor-arg type="java.lang.Integer"> <value>240</value> </constructor-arg > </bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Car car1 = (Car) ctx.getBean("car1"); Car car2 = (Car) ctx.getBean("car2"); System.out.println(car1);//Car{brand='Audi', price=300000.0, maxSpeed=null} System.out.println(car2);//Car{brand='Audi', price=null, maxSpeed=240}
3.6. 字面值
- 字面值:可用字符串表示的值,可以通过<value>标签或value属性进行注入
- 基本数据类型及其封装类型、String等类型都可以采用字面值注入的方式
- 若字面值中包含特殊字符(例如<、>等字符),可以使用<![CDATA[]]>把字面值包裹起来,但只能通过<value>标签方式注入,例如:<value><![CDATA[<Audi>]]></value>