Spring基础
一. Spring简介
1. Spring介绍
Spring框架主页: Spring官网
Spring资源地址:下载地址
-
Spring框架,由Rod Johnson开发
-
Spring是一个非常活跃的开源框架, 基于IOC和AOP来构架多层JavaEE系统,以帮助分离项目组件之间的依赖关系
-
它的主要目地是简化企业开发
2. Spring解决的问题
-
方便解耦,简化开发:Spring 就是一个大工厂,可以将所有对象创建和依赖关系维护,交给 Spring 管理
-
AOP 编程的支持:Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
-
声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程
-
方便程序的测试:Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序
-
方便集成各种优秀框架:Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持
-
降低 JavaEE API 的使用难度:Spring对 JavaEE 开发中非常难用的API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低
3. Spring的组成
Spring框架包含的功能大约由20个模块组成。这些模块按组可分为核心容器、数据访问/集成,Web,AOP(面向切面编程)、设备、消息和测试
3.1 Spring组成图
4.core – 核心模块
-
spring-core:依赖注入IoC与DI的最基本实现
-
spring-beans:Bean工厂与bean的装配
-
spring-context:spring的context上下文即IoC容器
-
spring-context-support
-
spring-expression:spring表达式语言
详细说明
(1)spring-core
这个jar文件包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类
(2)spring-beans
这个jar文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean以及进行Inversion of Control / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI支持,引入spring-core.jar及spring- beans.jar文件就可以了
(3)spring-context
Spring核心提供了大量扩展,这样使得由 Core 和 Beans 提供的基础功能增强:这意味着Spring 工程能以框架模式访问对象。Context 模块继承了Beans 模块的特性并增加了对国际化(例如资源绑定)、事件传播、资源加载和context 透明化(例如 Servlet container)。同时,也支持JAVA EE 特性,例如 EJB、 JMX 和 基本的远程访问。Context 模块的关键是 ApplicationContext 接口。spring-context-support 则提供了对第三方库集成到 Spring-context 的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
(4)spring-expression
为在运行时查询和操作对象图提供了强大的表达式语言。它是JSP2.1规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。
数据访问与集成层包含 JDBC、ORM、OXM、JMS和事务模块。
(1)spring-jdbc
提供了 JDBC抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
(2)spring-tx
支持编程式事务和声明式事务,可用于实现了特定接口的类和所有的 POJO 对象。编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细。
(3)spring-orm
提供了对流行的对象关系映射 API的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring 的其它功能整合,比如前面提及的事务管理。
(4)spring-oxm
模块提供了对 OXM 实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
(5)spring-jms
模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了 spring-messaging 模块
Spring 处理Web层jar
Web 层包括 spring-web、spring-webmvc、spring-websocket、spring-webmvc-portlet 等模块。
详细说明
(1)spring-web
提供面向 web 的基本功能和面向 web 的应用上下文,比如 multipart 文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分
(2)spring-webmvc
为 web 应用提供了模型视图控制(MVC)和 REST Web 服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成
(3)spring-webmvc-portlet
(即Web-Portlet模块)提供了用于 Portlet 环境的 MVC 实现,并反映了 pring-webmvc 模块的功能
Spring AOP涉及jar
(1)spring-aop
提供了面向切面编程(AOP)的实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦。使用源码级的元数据。
(2)spring-aspects
提供了对 AspectJ 的集成
Instrumentation 模块涉及jar
(1)spring-instrument
模块提供了对检测类的支持和用于特定的应用服务器的类加载器的实现。
(2)spring-instrument-tomcat
模块包含了用于 Tomcat 的Spring 检测代理。
Messaging消息处理 涉及jar
spring-messaging 模块
从 Spring 4 开始集成,从一些 Spring 集成项目的关键抽象中提取出来的。这些项目包括 Message、MessageChannel、MessageHandler 和其它服务于消息处理的项目。这个模块也包含一系列的注解用于映射消息到方法
Test模块涉及jar
spring-test 模块
通过 JUnit 和 TestNG 组件支持单元测试和集成测试。它提供了一致性地加载和缓存 Spring 上下文,也提供了用于单独测试代码的模拟对象(mock object)
二. 入门程序
目标:理解spring框架创建对象的过程。
注意:在导入依赖时,spring的所有依赖版本应该一致。
1、导入相关依赖
junit junit4.12 test org.springframework spring-context4.3.18.RELEASE
2、编写相关代码
public interface UserDAO { void save(); } public class UserDAOImpl implements UserDAO { public void save() { System.out.println("UserDAOImpl====save"); } } public interface UserService { void setUserDAO(UserDAO userDAO); void add(); } public class UserServiceImpl implements UserService { private UserDAO userDAO; public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } public void add() { System.out.println("UserServiceImpl====add"); userDAO.save(); } } public class UserController { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public void doAdd(){ System.out.println("UserController===doAdd"); userService.add(); } }
3、配置相关对象创建以及依赖
spring.xml
第一个name是为了spring能在类中找到该名字的set方法,ref是引用别的bean 标签的id,从而进行注入。
4、测试
public class MyTest { @Test public void testAdd(){ // 读取配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); final UserController userController = context.getBean("userController", UserController.class); userController.doAdd(); } }
三. 对象创建的细节
1.bean标签和属性讲解
bean标签
bean标签,是根标签beans内部必须包含的标签,它是用于声明具体的类 的对象!
bean标签对应属性
Property | 属性解释 |
---|---|
class | 指定bean对应类的全路径 |
name | name是bean对应对象的一个标识 |
scope | 执行bean对象创建模式和生命周期 |
id | id是bean对象的唯一标识,不能添加特别字符 |
lazy-init | 是否延时加载 默认值:false |
init-method | 对象初始化方法 |
destory | 对象销毁方法 |
2.创建对象工厂
2.1. BeanFactory
使用bean工厂来创建对象
@Test public void testBeanFactory1(){ // 读取配置文件 // 1、使用bean工厂来创建(已过时,不推荐使用) // a、通过类路径读取资源文件, Resource resource = new ClassPathResource("spring.xml"); // b、通过文件路径读取资源文件 //Resource resource = new FileSystemResource("src/main/resources/spring.xml"); BeanFactory factory = new XmlBeanFactory(resource); final UserController userController = factory.getBean("userController", UserController.class); userController.doAdd(); }
2.2. FileSystemXmlApplicationContext
从硬盘绝对路径下加载配置文件
@Test public void testBeanFactory2(){ //通过绝对路径加载配置文件 ApplicationContext context = new FileSystemXmlApplicationContext("src/main/resources/spring.xml"); // 从系统目录中加载配置文件 ApplicationContext context1 = new FileSystemXmlApplicationContext("C:\Users\wangliang\Desktop\spring.xml"); }
2.3. ClassPathXmlApplicationContext
从类路径下加载配置文件
普通项目: src目录下
maven项目: resources目录下
@Test public void testBeanFactory3(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); }
经典面试题:
BeanFactory与ApplicationContext区别:
1、ApplicationContext继承自BeanFactory
2、BeanFactory是一个轻量级的对象工厂,作用是为了创建spring中管理的对象。而applicationContext作为上下文,有着更强大的功能。
3、BeanFactory在加载配置文件时并不会创建对象和设置依赖关系,直到获取对象并使用时才会创建对象并设置依赖。而applicationContext在加载配置文件时就会创建所有的上下文关联对象和设置其依赖关系(单例对象才会创建,原型不会)。
在加载spring框架的配置文件时ClassPath和FileSystem的区别:
ClassPath通过当前项目类路径加载。
FileSystem是通过系统文件路径。
3. 练习bean标签属性
3.1 name属性
可以重复,可以使用特殊字符
3.2 id属性
id属性作用和name几乎相同,但是也有细微的差别,id不可重复,且不能使用特殊字符
Java代码
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-bean.xml"); /** * 参数1: name/id * 参数2(可选): 可以指定生成对象类型,如果不填此参数,需进行强转 * 两种方式都可以获取! */ TestBean testBeanName = applicationContext.getBean("testBeanName", TestBean.class); TestBean testBeanId = applicationContext.getBean("testBeanId", TestBean.class);
3.3 scope属性
bean标签中添加scope属性,设置bean对应对象生成规则.
singleton的设置在通过上下文加载时就会创建对象。而prototype则不会创建。
3.3.1 scope = “singleton”
单例,默认值,适用于实际开发中的绝大部分情况.
配置:
测试:
@Test public void testPerson(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Person person1 = context.getBean("person", Person.class); Person person2 = context.getBean("person", Person.class); System.out.println(person1 == person2); }
3.3.2 scope=”prototype”
多例,适用于struts2中的action配置
配置:
测试:
@Test public void testPerson(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Person person1 = context.getBean("person", Person.class); Person person2 = context.getBean("person", Person.class); System.out.println(person1 == person2); }
注意:在普通项目中,scope就两种值,单例和原型(多例),但是在web项目中,还有几个值:
request:在请求范围
session:在会话范围
global-session:在全局范围
3.4 lazy-init属性
注意: 只对单例有效,设置scope=”singleton”时测试
延时创建属性.
lazy-init=”false” 默认值,不延迟创建,即在启动时候就创建对象.
lazy-init=”true” 延迟初始化,在用到对象的时候才会创建对象.
配置:
测试1: lazy-init=”false”
@Test public void test2(){ //TODO 测试bean标签中的 lazy-init="false" 默认值 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-bean.xml"); System.out.println("获取数据之前!"); /** * 参数1: name/id * 参数2(可选): 可以指定生成对象类型,如果不填此参数,需进行强转 * 两种方式都可以获取! */ TestBean testBeanName = applicationContext.getBean("testBeanName", TestBean.class); TestBean testBeanId = applicationContext.getBean("testBeanId", TestBean.class); System.out.println("获取数据之后!"); //测试结果: 先输出 实体类的构造方法 --> 获取数据之前 --> 获取数据之后 //证明: false 不延迟创建,在创建ApplicationContext的时候就创建了对象! }
测试2:lazy-init=”true”
@Test public void test2(){ //TODO 测试bean标签中的 lazy-init="true" 默认值 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-bean.xml"); System.out.println("获取数据之前!"); /** * 参数1: name/id * 参数2(可选): 可以指定生成对象类型,如果不填此参数,需进行强转 * 两种方式都可以获取! */ TestBean testBeanName = applicationContext.getBean("testBeanName", TestBean.class); TestBean testBeanId = applicationContext.getBean("testBeanId", TestBean.class); System.out.println("获取数据之后!"); //测试结果: 先输出 获取数据之前 ---> 实体类的构造方法 --> 获取数据之后 //证明: true 延迟创建, 只有在获取的时候创建. }
3.5 初始化/销毁
在TestBean类中添加初始化方法和销毁方法(名称自定义):
public void init() { System.out.println("TestBean的初始化方法"); } public void destroy() { System.out.println("TestBean的销毁方法"); }
@Test public void test3(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); TestBean testBean = context.getBean("testBeanId", TestBean.class); // 关闭上下文 ((ClassPathXmlApplicationContext)context).close(); }
注意:只有当scope=”singleton”或者不写scope属性(不写的话默认是singleton),并且关闭了applicationContext对象时destory方法才会执行。
四. 对象创建的几种方式
1. 无参构造函数
之前使用的方式调用了类的无参构造函数.
2. 有参数构造函数
后面章节:对象的依赖-属性注入 (注入属性) 后面章节进行讲解.
@Data public class Person { public Person(String name, String id) { System.out.println("person被有参创建"); this.name = name; this.id = id; } private String name; private String id; }
constructor-arg标签有如下属性:
name:属性名称
index:参数的位置(下标),从0开始
type:参数的类型
value:参数的值,只能应用于基本类型
ref:引用,引用其他对象
3. 静态工厂模式
创建工厂类:
/** * Person工厂类 */ public class PersonFactory { public static Person createPerson(){ System.out.println("静态工厂创建Person"); return new Person(); } }
配置文件:spring.xml
测试代码
ApplicationContext context =new ClassPathXmlApplicationContext("spring.xml"); //获取工场bean对应的name Person person = context.getBean("person", Person.class); System.out.println("person = " + person);
4. 非静态工厂
创建工场类
/** * Person工厂类 */ public class PersonFactory { /** * 非静态创建对象 * @return Person */ public Person createPerson1(){ System.out.println("非静态工厂创建Person"); return new Person(); } }
配置文件:spring.xml
测试代码
//TODO 测试非静态工厂模式 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); //测试结果:会触发PersonFactory createPerson1方法 输出 System.out.println("非静态工厂创建Person");
五、IOC和DI
inverse of control 控制反转。将控制权反转给容器(spring),将对象的创建和依赖关系的设置交给容器来完成。
DI依赖注入。依赖注入是控制反转的一种实现方式。
5.1 注入的两种方式
1、使用set方法
注意:
property的name属性是指定的set方法的名称,按照javabean的setter生成规则
必须要set方法
2、使用构造方法
参考有参构造的使用
5.2 简化过程
一般情况下,使用set方法注入,可以采用自动注入,避免大量的set编写。
autowire="byType"byType:根据类型注入。一个类型有多个对象则无法注入。
byName:根据名称注入。名称不一致就无法注入。
如果在每一个bean标签的配置上都添加一个autowire比较麻烦,可以直接在最上面的beans标签上配置默认的自动注入。
注意:byName需要名字与set方法的名称对应。byType需要该类型的对象唯一。
六、使用注解方式
问题:
使用配置文件来配置,每次创建一个类就需要配置一次,太过于麻烦,可以使用注解来将配置简化。
注意:由于注解需要写在类上,所以只有当该类是自定义的类,才可以使用注解,如果是第三方框架提供的类,还是需要使用配置文件配置。
@Repository("userDAO") // 可以在后面的括号中指定创建的id,如果不指定,默认为当前类名第一个字母小写 public class UserDAO { public void save(){ System.out.println("UserDAO === save"); } } @Service public class UserService { @Autowired @Qualifier("userDAO") // 指定名称注入 private UserDAO userDAO; public void save(){ System.out.println("UserService===save"); userDAO.save(); } } @Controller public class UserController { @Autowired // 自动注入 private UserService userService; public void save(){ System.out.println("UserController====save"); userService.save(); } }
如果在注入的过程中使用@Autowired(spring框架中的),默认使用类型注入。如果相同类型对象有两个,那么可以指定名称来注入,需用使用@Qualifier(“userDAO”)。
也可以使用@Resource注解(javax包中的)来注入。默认也是根据类型注入,如果要根据名称注入,那么需要指定name属性。
@Resource(name = "userService")
经典面试题:
@Resource和@Autowired的区别。
面试题
1. Spring加载配置文件的方式的区别(ClassPath和FileSystem) 2. Spring加载bean的两种方式的区别(BeanFactory和ApplicationContext) 3. Spring创建的bean的生命周期(Singleton、Prototype、request、session、global-session) 注:request、session、global-session仅在web应用程序中才有效