上面一节我们说了spring的体系和jar包的依赖关系,这节笔者主要详解一下spring3.x
一、Spring概述
1、Spring是什么?
Spring是分层的Java SE/EE应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。2、Spring3.X的体系结构
4、Spring带来的好处
方便解耦,简化开发通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
降低JavaEE API的使用难度
Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
5、Spring的资源
5.1 下载开发资源包
官方网站: http://spring.io/5.2、Spring的资源包介绍
spring Framework的官方发行包内部结构为:
5.3、关于资源包的说明
Spring Framework官方最新版本是4.3.2。但是能下载到的就是3.2,这是Spring3这个版本的最后一个官方资源包。之后的版本全用MAVEN整合了。Spring依赖的第三方资源包能下载的版本就是3.0.2。后面的也都整合到MAVEN里面去了。
6、搭建Spring核心开发环境
6.1、拷贝最基本的核心jar包
根据第一节。添加这4个jar包以及它的依赖包(spring-Context其实还依赖了aop但是这里因为讲解IOC所以不用依赖也是可以的)。所以上图为他们的4个jar包名称。
6.2、核心依赖的jar包(日志输出)
commons-logging或log4j:用哪个都行。
6.3、Spring的配置文件:基于xml或注解的方式
(下面搭建项目时在讲解)
概述部分讲解完毕。下面开始spring的核心之一IOC部分
二、Spring的核心之IoC(Inversion of Control 控制反转)
1、IoC是什么?
注意:这里需要理解什么是注入。(举例,比如普通的set方法就是一种注入等等。)
2、Spring容器
Spring容器简单理解就是用于存放对象的一个boss那么一个东西。3、Spring的IoC入门(注意:这里只是IOC的入门案例,了解ioc是怎么个过程)
3.1、建立一个java工程(没有整合web项目,所以jar工程就够用了)
3.2、导入Spring的核心jar包(5个jar包)
3.3、建立一个XML配置文件
注意:现在xml文件用什么名字无所谓。
3.4、资源交给Spring容器管理(当你写好了controller层,service层,以及dao层时需要交给spring来管理他们的关系)
比如:写好了dao层之后:
3.5、Spring容器的初始化及资源的获取
配置依赖注入:service与dao层之间的关系:
在service层中写一个register方法,调用dao层的方法(注入的dao中有save方法)如下:
package springIoc.service;
import springIoc.dao.XXDao1;
public class XXService1 {
private XXDao1 xxDao1;
public void setXxDao1(XXDao1 xxDao1) {
this.xxDao1 = xxDao1;
}
public void rgeister(){
xxDao1.save();
}
}
初始化和资源的获取测试:
package springIoc.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIoc.service.XXService1;
public class Client {
public static void main(String[] args) {
/* 注意这是直接new对象获取,耦合度很高,没有调用spring来管理的bean == 不采用
* XXService1 xxService1 = new XXService1();
xxService1.rgeister();
*/
//Spring容器的初始化
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
//获取资源
//XXDao1 xxdao1 = (XXDao1) ac.getBean("xxDao1");
//xxdao1.save();
XXService1 xxService1 = (XXService1) ac.getBean("xxService1");
xxService1.rgeister();
}
}
结果成功!
4、Spring中API的体系结构(了解)
4.1、核心接口和类
BeanFactory:它使用的是延迟加载思想。当我们需要用bean时,才去创建对象。ApplicationContext:它继承自BeanFactory接口,还继承了很多其他接口。功能比BeanFactory强大。它用的不是延迟加载思想,而是在加载配置文件时就创建了。(推荐)
FileSystemXmlApplicationContext:配置文件在本地磁盘上,使用该类加载。
ClassPathXmlApplicationContext :配置文件在classpath中,使用此类加载。
特别注意:以上主要ioc已经讲解完毕!下面的Ioc配置部分很重要,两种方式在实际开发中用到的都很多!
5、基于XML的spring的IoC配置
5.1、Spring实例化bean的方式
a、调用默认的构造方法 (推荐)
<!-- dao层 -->
<bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
<bean id="xxDao2" class="springIoc.dao.XXDao2"></bean>
b、静态工厂方法初始化:老系统才有可能用到(现在已经完全不用了)
c、实例工厂方法初始化:老系统才有可能用到(现在已经完全不用了)
5.2、Bean的作用范围(Bean下的scope标签)
* scope属性的取值:
* singleton :默认值.单例的.Spring生成Bean的时候采用的是单例的模式.
* prototype :多例的.Spring生成Bean的时候采用的是多例的模式.每次getBean的时候都会得到一个新的Bean的实例.(与Struts2整合时要使用此值)
* request :应用在web应用中.创建一个Bean的实例将Bean实例存入到request域中.
* session :应用在web应用中.创建一个Bean的实例将Bean的实例存入session域中.
* globalsession :应用在web应用中.全局session.一般用于Porlet应用环境.如果没有Porlet环境.配置全局session等价于session.
5.3、Bean的生命周期方法
init-method:Bean初始化的时候执行的方法.
destroy-method:
Bean销毁的时候执行的方法.必须是在scope=”singleton”情况下使用.
<bean id="xxDao1" class="springIoc.dao.XXDao1" init-method="init" destroy-method="destory"></bean>
拓展:
Bean的完整的生命周期:
Spring框架管理Bean的时候,非常精确.Bean的生成过程中有11个步骤.11个步骤才构成了Bean的完整的生命周期.
1.instantiate bean对象实例化
2.populate properties 封装属性
3.如果Bean实现BeanNameAware 执行 setBeanName
4.如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext
5.如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization
6.如果Bean实现InitializingBean 执行 afterPropertiesSet
7.调用<bean init-method="init"> 指定初始化方法 init
8.如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization
9.执行业务处理
10.如果Bean实现 DisposableBean 执行 destroy
11.调用<bean destroy-method="customerDestroy"> 指定销毁方法 customerDestroy
* 第三步和第四部为了让Bean了解Spring的环境的..
* 第五步和第八部是实现了一个接口BeanPostProcessor.
注意:当非单例时,初始化方法正常执行,但是销毁方法就不会执行了。
5.4、依赖注入(DI)
a、构造器注入(调用默认构造方法):通过传参数
0 先定义一个bean类,只提供有参的构造方法(并给每个参数提供get方法,方便测试时判断是否注入进去值)
package springIoc.bean;
import java.util.Date;
public class Bean1 {
private String name;
private int age;
private Date birthday;
//提供了有参构造方法,没有提供默认的构造方法
public Bean1(String name, int age, Date birthday) {
super();
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Date getBirthday() {
return birthday;
}
}
1、编写bean.xml交给spring管理这个类。
2.编写测试类,判断是否注入成功
package springIoc.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIoc.bean.Bean1;
public class Client {
public static void main(String[] args) {
//Spring容器的初始化
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
//获取资源
Bean1 bean1 = (Bean1) ac.getBean("bean1");
System.out.println("姓名:"+bean1.getName()+"年龄:"+bean1.getAge()+"现在时间:"+bean1.getBirthday());
}
}
打印出:姓名:小马同志年龄:26现在时间:Fri Oct 27 09:47:20 CST 2017
b、属性注入:推荐(也是常用的)
0 先定义一个bean类,定义的字段需要提供setter方法(不然注入不进去)并提供默认的构造方法(必须)
package springIoc.bean;
import java.util.Date;
public class Bean1 {
private String name;
private int age;
private Date birthday;
public Bean1() {
super();
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Date getBirthday() {
return birthday;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
1、编写bean.xml交给spring管理这个类。
<bean id="now" class="java.util.Date"></bean>
<bean name="bean1" class="springIoc.bean.Bean1">
<!-- 属性注入 -->
<property name="name" value="小马"></property>
<property name="age" value="26"></property>
<property name="birthday" ref="now"></property>
</bean>
2.编写测试类
package springIoc.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIoc.bean.Bean1;
public class Client {
public static void main(String[] args) {
//Spring容器的初始化
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/bean.xml");
//获取资源
Bean1 bean1 = (Bean1) ac.getBean("bean1");
System.out.println("姓名:"+bean1.getName()+"年龄:"+bean1.getAge()+"现在时间:"+bean1.getBirthday());
}
}
测试成功。
c、p名称空间(不做细讲)
d、SpEL(Spring Expression Lanaguage)
e、注入集合数据
5.5、团队开发:多个Spring配置文件
两种形式:
* 加载配置文件的时候一并引入.
* ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml", "applicationContext2.xml");
* 总的配置文件中引入其他的配置文件.
* <import resource="applicationContext2.xml"/>
基于xml的配置文件就讲解到这。需要会。下面说一下基于注解的配置也是同样重要,
6、基于注解的spring的IoC配置
6.1、注解的使用前提(3个步骤必须有的!)
a、引入context的名称空间b、指定要扫描的包
c、在Bean上面加入@Component注解
0、配置a和b的步骤
1 之后执行c在指定的类中添加注解(control层和service层以及dao层都需要添加各自的注解)(稍后会有详细的注解介绍)
package springIoc.dao;
import org.springframework.stereotype.Component;
/**
* @Component是spring的一个注解,它是把XXDao1看做一个组件。
* 就相当于xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
* @author mch
*
*/
@Component
public class XXDao1 {
public void save() {
System.out.println("SPRING3.XIOC成功了");
}
}
注意:@Component(value="xxDao1") 也可以写成这种注解而value值相当于bean的id
2、编写测试类运行结果
package springIoc.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIoc.dao.XXDao1;
public class Client {
public static void main(String[] args) {
//Spring容器的初始化
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/springxml.xml");
//获取资源
XXDao1 xxDao1 = (XXDao1) ac.getBean("xxDao1");
xxDao1.save();
}
}
6.2、常用注解
a、@Component作用:让Spring容器管理当前的Bean(实例化)
属性:
value:指定bean的名称。默认值是当前类简单名称(不是全类名)首字母小写。
b、@Component的衍生注解@Controller @Service @Repository
作用:和@Component作用一样,让Spring容器管理(实例化)当前的bean。
属性: value:指定bean的名称。默认值是当前类的简单类名首字母小写
特点:在三成架构中,每个注解对应一层,使语义更加明确。
@Controller:一般用在表现层,比如struts2的action上
@Service:一般用在业务层,比如Service实现
@Repository:一般用在持久层,比如Dao实现
c、@Autowired
作用:自动按类型注入需要的对象。当使用了该注解时,setter就不是必须的了。
属性:
required:是否必须注入成功。默认值是true。
true:必须注入成功,如果出现注入失败,抛出异常。
false:不一定注入成功,不抛异常。
注意事项:
一个Service接口:IBookService
两个Service实现:BookServiceImpl1 BookServiceImpl2
由于@Autowired是自动按类型注入,当使用接口类型时,就看变量的名称,如果变量名称是bookServiceImpl1,则使用BookServiceImp1这个实现类,
如果变量名是bookServiceImpl2,则使用BookServiceImpl2这个实现类。如果没有符合规范的名称(类名首字母小写),则报错。
到底注入哪个实现类:
@Autowried
private BookService bookServiceImpl1;//注入BookServiceImpl1
@Autowried
private BookService bookServiceImpl2;//注入BookServiceImpl2
@Autowried
private BookService bookService;//注入失败
d、@Value
作用:注入基本类型和String。
属性:value:SpEL表达式,要注入的值
package springIoc.dao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @Component是spring的一个注解,它是把XXDao1看做一个组件。
* 就相当于xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
* @author mch
*
*/
@Component(value="xxDao1")
public class XXDao1 {
@Value("小码")
private String name;
public void save() {
System.out.println("SPRING3.XIOC成功了");
}
public String getName() {
return name;
}
}
提供get方法查看是否注入成功
package springIoc.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIoc.dao.XXDao1;
public class Client {
public static void main(String[] args) {
//Spring容器的初始化
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("springIoc/springxml.xml");
//获取资源
XXDao1 xxDao1 = (XXDao1) ac.getBean("xxDao1");
xxDao1.save();
System.out.println(xxDao1.getName());
}
}
结果为成功
e、@Qualifier
作用:要配合@Autowried来一起使用,通过它指定要注入的bean的名称。按类型注入失效了。
属性:value:要注入的bean的名称
f、@Resource
作用:如同@Autowire和@Qualifier,是规范JSR-250中定义的(JCP)。通过指定bean的名称注入对象。
属性: name:要注入的bean的名称
g、@PostConstruct(了解)
作用:用在初始化方法上。生命周期有关
h、@PreDestroy(了解)
作用:用在销毁方法上。生命周期有关
i、@Configuration和@Bean(了解)
作用:@Configuration指定当前类为配置类,用于加载@Bean的定义。@Bean用于定义bean的名称,用法是@Bean(name="beanName")
注意:该类要被设置在注解自动扫描对应的包下。
6.3、Spring中使用单元测试
a、导入jar包:
spring-test-3.2.0.RELEASE.jar
b、设置Junit运行器和Spring配置文件
步骤:
0、编写一个类(dao层)
package springIoc.dao;
import org.springframework.stereotype.Component;
/**
* @Component是spring的一个注解,它是把XXDao1看做一个组件。
* 就相当于xml中 <bean id="xxDao1" class="springIoc.dao.XXDao1"></bean>
* @author mch
*
*/
@Component(value="xxDao1")
public class XXDao1 {
public void save() {
System.out.println("SPRING3.XIOC成功了");
}
}
1、设置是spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
<!-- 指定Spring要扫描的包
注意:它会扫描当前包和当前包的子包下的所有类(base-package:以点分割包名)
-->
<context:component-scan base-package="springIoc"></context:component-scan>
</beans>
2、进行编写测试类。进行测试
package springIoc.dao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)//指定新的运行器,该运行前会初始化Spring容器,并注入资源,执行Junit单元测试
@ContextConfiguration(locations={"classpath:springIoc/springxml.xml"})//指定spring容器要加载的配置文件
public class XXDao1Test {
@Autowired
private XXDao1 xxDao1;
@Test
public void test1(){
xxDao1.save();
}
}
结果成功!
总结:
其实spring的Ioc原理是 IOC原理:工厂 + 反射 + 配置文件.
那么Spring的Ioc就说到这,后面在继续了解它。