Spring基础应用

1.Spring简介

1.1 Spring介绍

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring是一个非常活跃的开源框架, 基于IOC和AOP来构架多层JavaEE系统,以帮助分离项目组件之间的依赖关系。它的主要目地是简化企业开发。
简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

1.2 Spring起源

Rod Johnson在2002年编著的《Expert one on one J2EE design and development》一书中,对Java EE 系统框架臃肿、低效、脱离现实的种种现状提出了质疑,并积极寻求探索革新Spring Logo之道。以此书为指导思想,他编写了interface21框架,这是一个力图冲破J2EE传统开发的困境,从实际需求出发,着眼于轻便、灵巧,易于开发、测试和部署的轻量级开发框架。Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。同年他又推出了一部堪称经典的力作《Expert one-on-one J2EE Development without EJB》,该书在Java世界掀起了轩然大波,不断改变着Java开发者程序设计和开发的思考方式。在该书中,作者根据自己多年丰富的实践经验,对EJB的各种笨重臃肿的结构进行了逐一的分析和否定,并分别以简洁实用的方式替换之。至此一战功成,Rod Johnson成为一个改变Java世界的大师级人物。

1.3 Spring作用

  • 方便解耦,简化开发:Spring 就是一个大工厂,可以将所有对象创建和依赖关系维护,交给 Spring容器管理
  • AOP 编程(面向切面编程)的支持:Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
  • 声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程
  • 方便程序的测试:Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序
  • 方便集成各种优秀框架:Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz(定时任务)等)的直接支持
    Spring可以无缝整合其他的框架
  • 降低 JavaEE API 的使用难度:Spring对 JavaEE 开发中非常难用的API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低

1.4 Spring的组成

Spring框架包含的功能大约由20个模块组成。这些模块按组可分为核心容器、数据访问/集成,Web,AOP(面向切面编程)、设备、消息和测试。
Spring基础应用
Spring基础应用
Spring基础应用

1.4.1 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容器检索对象,还支持列表的投影、选择以及聚合等。

2.Spring之IOC

依赖注入或控制反转的定义中,调用者不负责被调用者的实例创建工作,该工作由Spring框架中的容器来负责,它通过开发者的配置来判断实例类型,创建后再注入调用者。由于Spring容器负责被调用者实例,实例创建后又负责将该实例注入调用者,因此称为依赖注入。而被调用者的实例创建工作不再由调用者来创建而是由Spring来创建,控制权由应用代码转移到了外部容器,控制权发生了反转,因此称为控制反转.

2.1 IOC-控制反转

IOC-Inversion of Control,即控制反转。它不是什么技术,而是一种设计思想。
传统的创建对象的方法是直接通过 new 关键字,而 spring 则是通过 IOC 容器来创建对象,也就是说我们将创建对象的控制权交给了 IOC 容器。我们可以用一句话来概括 IOC:
IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建、初始化、销毁等工作交给spring容器来做。
IOC概念:程序中开发者不需要人工的创建对象,而是把创建的对象权利或者工作转交给Spring容器,让Spring容器去创建并管理这些对象,以达到程序的高度解耦!=

2.2 DI-依赖注入

Dependency Injection,说的是创建对象实例时,同时为这个对象注入它所依赖的属性。相当于把每个bean与bean之间的关系交给容器管理。而这个容器就是spring。
例如我们通常在 Service 层注入它所依赖的 Dao 层的实例;在 Controller层注入 Service层的实例。

2.3对IoC与DI浅显易懂的讲解

IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、微信号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

2.5 IOC使用

  • 创建web项目
    项目名: spring-IOC
    引入jar包
    如果练习IOC ,需要引入 org.springframework.beans , org.springframework.context
    jar包位置!
    Spring下载文件中/libs文件下!: beans,context,core,context-support,spring-expression.
    Maven项目,pom.xml添加一下内容:
    pom.xml:
<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.2</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>

</dependencies>
  • 引入log4j.properties
    文件位置:src目录下
    maven项目:resources目录下
log4j.rootLogger=ERROR,A1
log4j.logger.org.mybatis = ERROR
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
  • 创建一个操作类
/**
 * @ClassName: Person
 * @author: BRUCELIU
 * @date: 2018年9月17日 下午9:47:54
 * @Description:TODO
 */
public class Person {

	private String name;
	private Integer age;

	public Person() {
		System.out.println("------------> Person.Person");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}
}
  • 创建配置文件
    建议命名: applicationContext.xml
    建议位置: 普通项目src下 / maven项目 resources下
<?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">

	<!-- id 和 name 可以同时存在,作为bean的标识 class添加的应该是class的全路径 -->
	<bean id="personId" name="personName" class="com.bruceliu.bean.Person" />

</beans>
  • 测试
public class TestSpring {

	@Test
	public void test1() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		// getBean 可以使用配置文件中的id值,也可以使用配置文件的name值.
		Person person = (Person) applicationContext.getBean("personId");
		System.out.println("person = " + person);
	}
}

3. 配置文件详解

准备工作: 创建配置文件:
applicationContext-bean.xml
创建测试代码: BeanTest.java

3.1 bean标签和属性

bean标签
bean标签,是根标签beans内部必须包含的标签,它是用于声明具体的类的对象!
bean标签对应属性
Spring基础应用

3.2 创建对象工厂

  • FileSystemXmlApplicationContext
    从硬盘绝对路径下加载配置文件
	@Test
	public void testBeanFactory1(){
      //通过绝对路径加载配置文件
	  ApplicationContext context = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring-01-IOC\\src\\applicationContext-bean.xml");
	}
  • ClassPathXmlApplicationContext
    从类路径下加载配置文件
    普通项目: src目录下
    maven项目: resources目录下
@Test
public void testBeanFactory2(){
	ApplicationContext context2 = new ClassPathXmlApplicationContext("applicationContext-bean.xml");
} 

3.3 bean标签使用

name属性
可以重复,可以使用特殊字符
Spring基础应用
id属性
id属性作用和name几乎相同,但是也有细微的差别,id不可重复,且不能使用特殊字符

  <!--同时添加name和id -->
  <bean name="testBeanName"  id="testBeanId"  class="com.bruceliu.spring.bean.TestBean" />
  • 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);

scope属性
bean标签中添加scope属性,设置bean对应对象生成规则.

  • singleton
    scope = "singleton"单例,默认值,适用于实际开发中的绝大部分情况.
    配置:
<bean name="testBeanName" scope="singleton" id="testBeanId"  class="com.bruceliu.spring.bean.TestBean" />

测试:

@Test
public void test2(){
        //TODO 测试bean标签中 scope = singleton
        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);

        System.out.println(testBeanName == testBeanId); //打印 true

    }

prototype
scope=“prototype”
多例,适用于struts2中的action配置
配置:

<bean name="testBeanName" scope="prototype" id="testBeanId"  class="com.bruceliu.spring.bean.TestBean" />

测试:

@Test
public void test2(){
        //TODO 测试bean标签中 scope = prototype

        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);

        System.out.println(testBeanName == testBeanId); //打印 false
}

Spring基础应用
lazy-init属性
注意: 只对单例有效,设置scope="singleton"时测试
延时创建属性.
lazy-init=“false” 默认值,不延迟创建,即在启动时候就创建对象.
lazy-init=“true” 延迟初始化,在用到对象的时候才会创建对象.

  • 配置:
<bean name="testBeanName" id="testBeanId"  scope="singleton" lazy-init="false" class="com.bruceliu.spring.bean.TestBean" />
  • 测试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 延迟创建, 只有在获取的时候创建.
}

初始化/销毁
在TestBean类中添加初始化方法和销毁方法(名称自定义):

public void init() {
   System.out.println("TestBean的初始化方法");
}
    
public void destroy() {
   System.out.println("TestBean的销毁方法");
}

配置
Spring基础应用

4.对象创建方式

创建配置文件:applicationContext-create.xml
创建测试代码:CreateTest.java

4.1 无参构造函数 默认调用无参数

之前使用的方式调用了类的无参构造函数.
Spring基础应用

4.2 有参数构造函数

后面章节:对象的依赖-属性注入 (注入属性) 后面章节进行讲解.
Spring基础应用
在调用有参数的构造方法的时候,推荐 name和index一起用 或者 type和index一起用,以防歧义问题!

4.3 静态工厂模式

  • 创建工厂类:
/**
 * Person工厂类
 */
public class PersonFactory {

    public static Person  createPerson(){

        System.out.println("静态工厂创建Person");

        return  new Person();
    }
}
  • 配置文件:applicationContext-create.xml
<bean name="personFactory" class="com.bruceliu.spring.utils.PersonFactory" factory-method="createPerson" />
  • 测试代码
 ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext-create.xml");
 //获取工场bean对应的name
 Person person = (Person) context.getBean("personFactory");
 System.out.println("person = " + person);

4.4 非静态工厂

  • 创建工厂类
/**
  * Person工厂类
  */
public class PersonFactory {
   /**
     * 非静态创建对象
     * @return Person
     */
 public  Person createPerson1(){
    System.out.println("非静态工厂创建Person");
    return new Person();
 } }  
  • 配置文件:applicationContext-create.xml
<bean name="personFactory1" class="com.itqf.spring.utils.PersonFactory" />
<bean name="personFactory2"  factory-bean="personFactory1" factory-method="createPerson1" />
  • 测试代码
 //测试非静态工厂模式
 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-create.xml");
//测试结果:会触发PersonFactory createPerson1方法 输出 System.out.println("非静态工厂创建Person");