目录
Spring 学习笔记
一、Spring 概述
1、简化 Java 开发
Spring 是一个开源的框架,通过使用Bean或者JavaBean来表示应用程序的组件,但是Spring中的Bean或者JavaBean并不需要完全遵守JavaBean的规范,使用Spring框架搭建的应用程序组件可以是任何形式的POJO。
Spring通过以下四个策略,简化了Java程序的开发:
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;
- 通过切面和模板减少样板式代码;
1.1、基于POJO的轻量级和最小侵入性编程
很多框架通过强迫应用中的组件继承框架提供的类或者实现框架提供的接口,这样容易导致应用程序和框架绑死,这种侵入性的编程方式,容易导致更换框架时候,应用程序将无法运行。
Spring框架不强迫应用程序实现Spring提供的规范或者继承Spring的规范类。Spring通过依赖注入DI的方式来装配POJO。
1.2、依赖注入
任何一个有实际意义的Java应用程序,都会有两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作的对象(即它所依赖的对象)的引用,这将会导致程序的高度耦合。
Class A中用到了Class B的对象b,一般情况下,在传统的Java应用程序中,需要在A的代码中显式的new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
1.3、应用切面编程
面向切面编程能够允许在遍布应用各处的功能分离出来形成可重用的组件。应用程序由许多不同的组件组成,每一个组件负责一块特定的功能。但是程序中会存在如日志、事务管理和安全等系统服务,这些服务会经常跨越系统的多个组件,融入到其他的组件中。
通过AOP编程,能够使上述的服务模块化,并以声明的方式将他们应用到需要的组件中去。
我们可以把切面想象为覆盖在很多组件上的一个外壳。借助AOP编程,可以使各个功能层去包裹核心业务层,这些层以声明的方式灵活的应用到系统中。
2、容纳 Bean
在基于Spring的应用中,应用对象存在于Spring容器中,Spring容器负责创建对象,装配对象并管理对象的整个生命周期。容器是Spring的核心,Spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。
在Spring中自带了多个容器的声线,可以归纳为两种类型
- BeanFactory:bean工厂,最简单的容器,提供基本的DI支持;
- ApplicationContext:应用上下文,基于BeanFactory构建,提供应用框架级别的服务。
在实际的开发过程中,一般使用ApplicationContext来作为Spring容器进行操作。
1.2.1、使用应用上下文
Spring自带了多种类型的应用上下文,常用的如下:
- AnnotationConfigApplicationContext:从一个或多个基于Java配置类中加载Spring应用上下文;
- AnnotationConfigWebApplicationContext:从一个或多个基于Java配置类中加载Spring Web应用上下文;
- ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件加载Spring应用上下文;
- FileSystemXmlApplicationContext:从文件系统下一个或多个XML配置文件加载Spring应用上下文;
- XxmlWebApplicationContext:从web应用下一个或多个XML配置文件加载Spring应用上下文;
1.2.2、Bean 的生命周期
在传统的Java项目中,bean的生命周期开始于new关键字进行bean的实例化,一旦该bean的实例化对象不再被使用,则由Java自动进行垃圾回收。
在Spring中,bean的生命周期如下:
- Spring对bean进行实例化;
- Spring将属性和bean的引用注入到bean对应的属性中;
- 调用bean实现接口的方法,(如果bean声明了init-method的初始化方法,调用该方法);
- bean已经准备就绪,Spring的应用上下文可以使用bean实例化对象,并且该对象会驻留在应用上下文中,直到应用上下文被销毁。
- 调用实现接口方法,(如果bean声明了destory-method的销毁方法,调用该方法);
二、装配 Bean
在Spring中,对象无需自己查找或创建与其所管理啊的其他对象。相反,Spring容器负责把需要相互协作的对象引用赋予各个对象。
2.1、Spring装配Bean的可选方案
Spring容器负责创建应用程序中的bean并通过依赖注入来协调这些对象之间的关系,Spring提供了三种主要的装配方式:
- 在XML中进行显示的配置;
- 在Java中进行显示的配置;
- 隐式的bean发现机制和自动装配;
2.2、通过XML装配Bean
2.2.1、创建XML配置规范
在使用XML装配Bean之前,需要创建一个具有特定配置规范的xml文件,最简单的Spring XML配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
</beans>
很容易看出,用来装配bean的最基本的XML元素包含在beans标签中,在Spring XML文件中,beans为文件的根名称空间,是所有Spring配置的根元素。
2.2.2、声明一个简单的<
bean>
要在基于XML的Spring配置中声明一个bean,我们要使用bean元素:
<bean id="" class="">
</bean>
- class属性:通过class属性来指定bean的类,属性名称为bean的全限定类名;
- ID属性:为每个bean设置一个单独的名称;
例,如一下的一个普通Java类,在Spring的XML文件中进行配置:
package com.spring.di;
public class Teacher {
private String name;
private Integer age;
// 省略 setter 和 getter 方法
public Teacher() {}
public Teacher(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
}
一般情况下,Spring的XML配置文件一般放在项目的src目录下创建applicationContext.xml文件来表示。
<bean id="teacher" class="com.spring.di.Teacher"></bean>
2.2.3、借助构造器注入初始化bean
在Spring的XML中声明DI时,使用构造器进行依赖注入可以有以下两种基本的方式进行声明:
- 使用元素声明;
- 使用c-命名空间进行声明;
2.2.3.1、构造器注入字面量
package com.spring.di;
public class Course {
private String courseName;
public String getCourseName() {
return courseName;
}
public Course(String courseName) {
this.setCourseName(courseName);
}
// 默认构造方法
public Course() {
}
// 带参构造函数
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
使用元素进行构造器参数的注入,通过使用元素中的name和value属性来对构造器中的参数进行字面量的注入。如,将构造方法的courseName参数注入字面量“语文课”:
<bean id="course" class="com.spring.di.Course">
<constructor-arg name="courseName" value="语文课"/>
</bean>
同样可以使用c-命名空间进行字面量的注入:
<bean id="course" class="com.spring.di.Course" c:courseName="语文课"></bean>
2.2.3.2、构造器注入bean的引用
如果一个bean中的构造方法含有对另外一个bean的引用,如创建一个ChineseTeacher类,该类中构造函数有对Course类的引用,此时需要告知Spring要将Course类的引用传递到ChineseTeacher的构造器中。
package com.spring.di;
public class ChineseTeacher {
private String name;
private Course course;
// 默认构造方法
public ChineseTeacher() {}
// 带参构造函数
public ChineseTeacher(String name,Course course) {
this.name = name;
this.course = course;
}
}
注入字面量时,使用value来注入字面量的值,此时ChineseTeacher中构造方法有对Course类的引用,此时使用 ref 来表示对注入bean的引用,ref 属性的值就是对应bean在Spring XML中的ID。
使用元素进行bean引用参数的注入:
<bean id="course" class="com.spring.di.Course">
<constructor-arg name="courseName" value="语文课"/>
</bean>
<bean id="chineseTeacher" class="com.spring.di.ChineseTeacher">
<constructor-arg name="name" value="孟祥杰" />
<constructor-arg name="course" ref="course" />
</bean>
使用c-命名空间进行bean引用参数的注入:
<bean id="course" class="com.spring.di.Course" c:courseName="语文课"/>
<bean id="chineseTeacher" class="com.spring.di.ChineseTeacher" c:name="孟祥杰" c:course-ref="course"/>
2.2.3.3、构造器注入集合
假设存在一个Disc(唱片类),在定义该类时,类中需要有唱片的名称和唱片中歌曲的名称,此时我们需要在唱片类中引入集合。
package com.spring.di;
import java.util.List;
public class Disc {
private String discName;
private List<String> songName;
public Disc() {}
public Disc(String discName, List<String> songName) {
this.discName = discName;
this.songName = songName;
}
}
此时可以在中使用元素来实现对集合的声明,如下案例所示:
<bean id="disc" class="com.spring.di.Disc">
<constructor-arg name="discName" value="张学友金曲" />
<constructor-arg name="songName">
<list>
<value>结束不是我要的结果</value>
<value>离开以后</value>
<value>饿狼传说</value>
<value>吻别</value>
<value>寂寞的男人</value>
</list>
</constructor-arg>
</bean>
除了使用List类型的集合以外,同样可以使用Set和Map类型的集合。如果集合中所保存的是对象的引用,此时用<ref bean="">
来代替 <value>
标签。
2.2.4、借助 setter 方法来实现属性的注入
2.2.4.1、使用 <property>
标签实现字面量、bean引用和集合的注入
<!-- 借助setter方法实现属性的注入 -->
<!-- 使用 property注入字面量,bean引用和集合类型 -->
<bean id="classroom" class="com.spring.di.setter.Classroom">
<!-- 注入常量 -->
<property name="cname" value="通信102" />
</bean>
<bean id="student" class="com.spring.di.setter.Student">
<!-- 注入常量 -->
<property name="sname" value="孟祥杰" />
<!-- 注入bean引用 -->
<property name="room" ref="classroom"/>
<!-- 注入集合 -->
<property name="courses">
<list>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</list>
</property>
</bean>
2.2.4.2、使用p名称空间实现字面量、bean引用和集合的注入
<!-- 借助setter方法实现属性的注入 -->
<!-- 使用 p名称空间注入字面量 -->
<bean id="classroom" class="com.spring.di.setter.Classroom" p:cname="通信102" />
<!-- 使用 p名称空间注入字面量和bean对象的引用 -->
<bean id="student" class="com.spring.di.setter.Student" p:sname="孟祥杰" p:room-ref="classroom">
<property name="courses">
<list>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</list>
</property>
</bean>
2.3、IOC的开发注解
在类上使用@Componenet注解,这个注解表明该类为组件类,并告知Spring要为这个类创建bean,但是默认的组件扫描是不启用的,需要在XML文件中进行开启,从而命令Spring去寻找带有@Component注解的类。配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.soundsystem"></context:component-scan>
</beans>
base-package
属性的值为带有@Component注解类所在包的全限定名。
2.3.1、@Component
使用@Component会将类变为组件类,并会让Spring创建这个类的bean。通过在@Component之后添加("value")
来为该组件类创建如XML配置中ID属性。@Component有三个衍生的注解:
- @Controller:修饰web层
- @Service:修饰service层
- @Repository:修饰dao层
以上三个衍生的注解用法和@Component一样。
Dao层使用@Repository注解
package com.dao;
import org.springframework.stereotype.Repository;
import com.domain.Customer;
@Repository("customerDao")
public class CustomerDao {
}
Service层使用@Service注解
package com.service;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service("customerService")
public class CustomerService {
}
2.3.2、属性注入的注解方式
- 普通属性注入注解:通过使用@Value来设置普通属性的注入值;
- 对象属性注入注解:@Autowired,通过对象引用的类型完成属性的注入,一般情况下还会使用
@Qualifier(value="")
一起使用,其中value的属性值为引用对象的@Component注解的值。但是在实际的开发过程中,我们通常使用@Resource(name="")
方式来完成以名称的方式进行bean引用的注解。
package com.service;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.dao.CustomerDao;
import com.domain.Customer;
@Service("customerService")
public class CustomerService {
@Value("Martin")
private String username;
public String getUsername() {
return username;
}
@Resource(name="customerDao")
private CustomerDao customerDao;
public void doAddCustomer(Customer customer) {
customerDao.doAddCustomer(customer);
}
}
2.3.3、bean其他注解方式
2.3.3.1、生命周期注解
- 初始化方法注解:@PostConstruct,使用@PostConstruct注解就相当于在XML文件中配置了
init-method="init"
; - 销毁方法注解:@PreDestory,使用@PreDestroy注解就相当于在XML文件中配置了
destory-method="destory"
;
package com.domain;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component("lifeCircle")
public class LifeCircle {
@PostConstruct
public void init() {
System.out.println("我是初始化方法....");
}
@PreDestroy
public void destory() {
System.out.println("我是销毁方法....");
}
}
2.3.3.2、Bean作用范围注解
@Scope注解,表示Bean的作用范围,其中有singleton
、prototype
、request
、session
和globalsession
- singleton:单例模式,默认
- prototype:多例模式
- request:在request作用域中
- session:在session域中
- globalsession:全局,通常使用再拥有子域名的站点中