深入学习Spring框架(一)- 入门

1.Spring是什么?

  Spring是一个JavaEE轻量级的一站式开发框架。
    JavaEE: 就是用于开发B/S的程序。(企业级)
    轻量级:使用最少代码启动框架,然后根据你的需求选择,选择你喜欢的模块使用。
    重量级:早期有的EJB,开发一个HelloWorld程序都需要引入EBJ的全部模块
    一站式:Spring框架提供涵盖了JavaEE开发的表示层,服务层,持久层的所有组件功能。也就是说,原则上,学完一套Spring框架,不用其他框架就可以完成网站一条流程的开发。但是Spring仍然可以和其他的框架无缝整合。

2.Spring的优点

  轻量:Spring 是轻量的,就是除内核模块(4个jar),其他模块由开发者自由选择使用,同时支持整合其他框架。也可以称为就是可插拔式开发框架,像插头和插座一样,插上就用。这就是Spring框架核心理念(Ioc)。
  控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
  面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
  容器:Spring 包含并管理应用中对象的生命周期和配置。
  MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
  事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
  异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

3.Spring的作用

  Spring强调的理念是,轻量级。Spring的轻量级主要体现在模块的可插拔,Spring提供的功能模块,除了内核模块以外,开发人员可以选择性使用。所以,Spring框架在现实开发中,主要的功能用于整合各种开发框架开发项目。

 

4.Spring框架包

  Spring官方网站:https://spring.io/

  4.1 下载

  Spring官方提供的Maven方式的项目下载:https://start.spring.io/
  但是基于简单入门的原则,我们要通过导入包的方式来学习。需要下载框架的zip包
  地址为:http://repo.springsource.org/libs-release-local/org/springframework/spring/

  4.2 目录说明

  根目录:

  

  jar包:

  

  包说明:

包名 说明
spring-aop-4.3.2.RELEASE.jar 实现了AOP的支持
spring-aspects-4.3.2.RELEASE.jar AOP框架aspects支持包
spring-beans-4.3.2.RELEASE.jar 内核支撑包,实现了处理基于xml对象存取
spring-context-4.3.2.RELEASE.jar  内核支撑包,实现了Spring对象容器
spring-context-support-4.3.2.RELEASE.jar  容器操作扩展包,扩展了一些常用的容器对象的设置功能
spring-core-4.3.2.RELEASE.jar  内核支撑包,Spring的内核
spring-expression-4.3.2.RELEASE.jar  内核支撑包,实现了xml对Spring表达式的支持
spring-instrument-4.3.2.RELEASE.jar  提供了一些类加载的的工具类
spring-instrument-tomcat-4.3.2.RELEASE.jar  提供了一些tomcat类加载的的工具类,实现对应Tomcat服务的调用
spring-jdbc-4.3.2.RELEASE.jar  SpringJDBC实现包,一个操作数据库持久层的子框架
spring-jms-4.3.2.RELEASE.jar  集成jms的支持,jms:Java消息服务。
spring-messaging-4.3.2.RELEASE.jar  集成messaging api和消息协议提供支持
spring-orm-4.3.2.RELEASE.jar  ORM框架集成包,实现了Hibernate,IBatis,JDO的集成。
spring-oxm-4.3.2.RELEASE.jar  Spring OXM对主流O/X Mapping框架做了一个统一的抽象和封装。就是对应XML读写框架的支持
spring-test-4.3.2.RELEASE.jar  Spring集成JUnit测试
spring-tx-4.3.2.RELEASE.jar  事务代理的支持
spring-web-4.3.2.RELEASE.jar  SpringWeb通用模块
spring-webmvc-4.3.2.RELEASE.jar  SpringMVC子框架
spring-webmvc-portlet-4.3.2.RELEASE.jar  Spring对门户技术(portlet)的支持
spring-websocket-4.3.2.RELEASE.jar  Spring对websocket的支持

注:红色部分为基础核心包,使用Spring必须导入

 

5.入门实例

  Spring之所以可以实现模块的可插拔是支持依赖注入,所谓的依赖注入/控制反转就是不用new就可以创建对象。

  使用Spring框架不用new创建一个对象:

  1.创建项目并导入jar包:将Spring的基础支撑包和依赖的日志包复制到lib文件下,并且加入项目中

  

  2.创建配置文件
  在项目的src下面创建配置文件applicationContext.xml中并完成配置文件的约束

<?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">
 
 
</beans>

  3.创建对象到Spring容器中
  创建一个类

package com.gjs.service;

public class HelloSpringService {
    public void say() {
        System.out.println("你好!Spring");
    }
}

  往applicationContext.xml配置文件中加入配置

<?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">
         <!-- <bean id="" class = ""> 
          配置让spring管理类的对象的创建
          id : 唯一标识
          class :被管理类的全限定名
          -->  
        <bean id="HelloSpringService" class="com.gjs.service.HelloSpringService"/>
            
</beans>

  测试类, 使用getBean方法获得容器中的对象。

package com.gjs.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.gjs.service.HelloSpringService;

public class TestSpring {
    @Test
    public void testName() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloSpringService helloSpringService = context.getBean("HelloSpringService",HelloSpringService.class);
        helloSpringService.say();
    }
}

6.Spring容器的两个实现

ClassPathXmlApplicationContext:通过classpath路径(相对路径)直接获得加载的xml文件(推荐使用)

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

FileSystemXmlApplicationContext:通过文件路径(绝对路径)来获得加载的xml文件。

ApplicationContext context = new FileSystemXmlApplicationContext("D:\\java\\eclipse-workspace\\spring1\\src\\applicationContext.xml");

8.控制反转(IOC)和依赖注入(DI)(A)

  DI:Dependency Injection(依赖注入):

  依赖注入是指Spring创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。

  IoC:Inverse of Control(控制反转):

  Spring号称是一个可以实现模块可插拔的JavaEE开发框架。而实现程序可插拔的核心理念就是控制反转。所谓的控制反转,就是将代码的调用权从调用方转移给被调用方(服务提供方)。不用修改调用方的的代码,只要修改配置文件就实现对象的切换。
  读作“反转控制”,更好理解,它不是什么技术,而是一种设计思想,好比于MVC。就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
  正控:若调用者需要使用某个对象,其自身就得负责该对象的创建。
  反控:调用者只管负责从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架。
  控制反转(Ioc),就是依赖注入加上面向接口的编程思想的实现

  示例:

  整体结构:

  

  CustomService接口:

package com.gjs.service;

public interface CustomeService {
    public void say();
}

  实现类CustomServiceImpl1:

package com.gjs.service.impl;

import com.gjs.service.CustomeService;

public class CustomServiceImpl1 implements CustomeService {

    @Override
    public void say() {
        System.out.println("CustomerServiceImpl1.say()");
    }
}

  实现类CustomServiceImpl2:

package com.gjs.service.impl;

import com.gjs.service.CustomeService;

public class CustomServiceImpl2 implements CustomeService {

    @Override
    public void say() {
        System.out.println("CustomerServiceImpl2.say()");
    }
}

  调用方 CustomClient:

package com.gjs.client;

import com.gjs.service.CustomeService;

public class CustomClient {
    //1.声明一个接口引用类型
    private CustomeService customeService;
    //2.spring的依赖注入(DI)需要有一个set方法
    public void setCustomeService(CustomeService customeService) {
        this.customeService = customeService;
    }
    
    public void say() {
        customeService.say();
    }
}

  配置文件 applicationContext.xml:

<?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">
         <!-- 
        <bean id="" class = ""> 
          配置让spring管理类的对象的创建
          id : 唯一标识
          class :被管理类的全限定名
          -->  
        <bean id="client" class="com.gjs.client.CustomClient">
            <!-- 依赖注入(DI) :注入被调用方 
              <property name="" value/ref=""/>
                         位置:
                  name : 是set方法确定的属性,不是成员变量
                      确定属性 : setXxx 方法去掉set前缀 ,剩余部分首字母小写
                         参数:        
                  value : 值类型注入(字符串,基本数据类型)
                  ref :引用数据类型(对象), 对应bean的id
           -->
            <property name="customeService" ref="ServiceImpl1"/>
        </bean>
        
        <bean id="ServiceImpl1" class="com.gjs.service.impl.CustomServiceImpl1"/>
        <bean id="ServiceImpl2" class="com.gjs.service.impl.CustomServiceImpl2"/>    
</beans>

  测试类:

package com.gjs.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.gjs.client.CustomClient;
import com.gjs.service.CustomeService;

public class TestSpring {
    @Test
    public void testName() throws Exception {
        //1.读取配置文件,创建Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取调用方 CustomClient对象
        CustomClient client = context.getBean("client", CustomClient.class);
        //调用CustomClient对象的say()方法
        client.say();
    }
}

  应用:

  Spring框架又被称为容器框架,即通过控制反转将各个框架的对象创建交给Spring管理,然后通过依赖注入在层之间传递参数整合框架,并达到低耦合的目的。

  

9.标签说明

  9.1 bean标签

  作用:用于声明一个类,在启动Spring框架的时候根据该配置的类创建对象到容器里面

<bean id="someBean" class="com.gjs.pojo.SomeBean" scope="prototype"></bean>

id:设置对象名(唯一标识符)(推荐使用)
name:设置对象名(唯一标识符),与id的区别是可以有多个名称,每个名称用逗号隔开。
class:指定对象对应的类
scope:用于设置的对象的作用范围,可选参数如下:

  

在Web开发的三层架构中的使用
  Web层:一般都是多例
  Service层 :单例
  DAO层 :单例

  9.2 alias 别名标签  

  作用:为已配置的bean设置别名

bean id="user" name="test" class="com.gjs.pojo.User"/>
<alias name="user" alias="user1"/> 

name:必要属性, 代表为哪一个bean配置别名, 此属性的值为其他bean标签的id或name属性值
alias: 必要属性,命名的别名

10.实例化Bean的四种方式

  1.构造器实例化(无参数构造器):(最常用)

public class SomeBean {
    public SomeBean() {
        System.out.println("SomeBean.SomeBean()");
    }
}
<bean id="someBean" class="com.gjs.pojo.SomeBean"/>

  2.通过静态方法工厂创建(C)

public class SomeBean {
    public SomeBean() {
        System.out.println("SomeBean.SomeBean()");
    }
}
public class SomeBeanFacotry {
    //静态工厂方法
    public static SomeBean getSomeBean() {
        System.out.println("执行静态工厂方法");
        return new SomeBean();
    }
}
<bean id="someBean" class="com.gjs.pojo.SomeBeanFacotry" factory-method="getSomeBean"/>

  3.通过实体工厂创建(C)

public class SomeBean {
    public SomeBean() {
        System.out.println("SomeBean.SomeBean()");
    }
}
public class SomeBeanFacotry {
    //实例工厂方法
    public SomeBean getSomeBean() {
        System.out.println("执行实例工厂方法");
        return new SomeBean();
    }
}
<bean id="someBeanfactory" class="com.gjs.pojo.SomeBeanFactory"/>

<bean id="someBean" factory-bean="someBeanfactory" factory-method="getSomeBean"/>

  4.实现FactoryBean接口实例化:实例工厂变种
  实现FactoryBean接口,MyBatis和Spring集成就是使用的这种方式。此种方式,如果没有使用Bean对应的对象,Spring就不会自动创建,只有在使用的时候Spring才会创建对应的对象

public class SomeBean {
    public SomeBean() {
        System.out.println("SomeBean4.SomeBean4()");
    }
}
public class SomeBeanObjectFactory implements FactoryBean<SomeBean>{
    //返回的泛型类型对应的对象
    @Override
    public SomeBean getObject() throws Exception {
        SomeBean bean = new SomeBean();
        return bean;
    }
}
<bean id="someBean" class="cn.zj.domian.SomeBeanObjectFactory"/>

11.初始化和销毁方法

<bean id="someBean" class="......" 
   <init-method="该类中初始化方法名"/> 
  <destroy-method="该类中销毁方法名"/>
</bean>

  init-method:bean生命周期初始化方法,对象创建后就进行调用
  destroy-method:容器被销毁的时候,如果bean被容器管理,会调用该方法。

12.依赖注入的四种方法

  1.setter注入,(属性注入)(常用)

使用setter注入:

1,使用bean元素的<property>子元素设置;

1.简单类型值,直接使用value赋值;
2.引用类型,使用ref赋值;
3.集合类型,直接使用对应的集合类型元素即可。

2,spring通过属性的set方法注入值;
3,在配置文件中配置的值都是string,spring可以自动的完成类型的转换

示例:

员工类

public class Employee {
    private Integer age;
    private String name;
    private Department dept;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Department getDept() {
        return dept;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Employee [age=" + age + ", name=" + name + ", dept=" + dept + "]";
    }    
}

  部门类

public class Department {
    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

  配置文件

<?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">
    
    <!-- 员工 -->
    <bean id="employee" class="com.gjs.pojo.Employee" >
        <!-- setter方法注入: 属性注入 
            <property name="" value="">
            name : 属性名称
            value : 基本数据类型+String类型的值注入
            ref : 引用类型(对象类型)的注入
            value 和ref 只能二选一
        -->
        <property name="age" value="18"></property>
        <property name="name" value="张三"></property>
        <property name="dept" ref="dept"></property>
    </bean>
    <!-- 部门 -->
    <bean id="dept" class="com.gjs.pojo.Department" >
           <property name="id" value="1"/>
           <property name="name" value="开发部"/>
   </bean>
</beans>

2.构造器注入

使用bean元素的<constructor-arg>子元素设置:

1.默认情况下,constructor-arg的顺序就是构造器参数的顺序
2. constructor-arg的属性

name : 构造方法参数的名称
index :参数的位置从 0 开始
value :值类型注入
ref :引用类型注入
type : 参数的数据类型

3.一般在一个类必须依赖另一个类才能正常运行时,才用构造器注入

 

<?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">
 
     <!-- 配置部门 -->
     <bean id="department" class="com.gjs.pojo.Department">
         
         <constructor-arg name="id"  value="2"/>
         <constructor-arg name="name"  value="研发部"/>
     </bean>
     
     <!-- 配置员工 -->
     <bean id="emp" class="com.gjs.pojo.Employee">
         <!-- 依赖注入 :构造器注入 -->
         <constructor-arg name="id" value="1"/>
         <constructor-arg name="name" value="张三"/>
         <constructor-arg name="dept" ref="department"/>
     </bean>
</beans>

  3.p命名空间注入

  使用p命名空间注入先在约束上面引入 p标签
  xmlns:p=”http://www.springframework.org/schema/p”

<?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">
        
     <!-- 配置部门 -->
     <bean id="department" class="com.gjs.pojo.Department" 
     p:id="3"
     p:name="销售部"/>
     
     <!-- 配置员工 -->
     <bean id="emp" class="com.gjs.pojo.Employee" 
     p:id="1"
     p:name="张三"
     p:dept-ref="department"/>
</beans>

4.集合类型值注入
用于处理:
  1.键值对 Map 、Properties
  2.数组
  3.集合Set、List

public class CollectionBean {
    private Set<String> set;
    private List<String> list;
    private String[] array;
    private Map<String, String> map;
    private Properties prop; //读取本地 xxx.properties文件(本质就是一个Map集合)
}
<?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">
        
     <bean id="collectionBean" class="cn.zj.spring.pojo.CollectionBean">
         
         <!-- setter(属性)方法注入 -->
         <!-- 1.数组 -->
         <property name="arr">
             <array>
                 <value>AAA</value>
                 <value>BBB</value>
             </array>
         </property>
         
         <!-- 2.set集合 -->
         <property name="set" >
             <set>
                 <value>AAA</value>
                 <value>AAA</value>
                 <value>BBB</value>
             </set>
         </property>
         
         <!-- 3.list集合 -->
         <property name="lsit">
             <list>
                 <value>list1</value>
                 <value>list1</value>
                 <value>list2</value>
             </list>
         </property>
         <!-- 4. map集合 -->
         <property name="map">
             <map>
                 <entry key="key1" value="value1"/>
                 <entry key="key2" value="value2"/>
             </map>
         </property>
         <!-- 5.properties 集合 -->
         <property name="prop">
             <props>
                 <prop key="propKey1">propValue1</prop>
                 <prop key="propKey2">propValue2</prop>
             </props>
         </property>
     </bean> 
</beans>

13.获得properties文件的值

  1.导入命名空间方法
  将命名空间和约束重新拷贝一份,将对于的全部替换成 context,然后关联context本地schema约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    ">
    
</beans>

  2.导入Mysql驱动包和druid连接池jar包

  

  3.applicationContext.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: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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        ">
        
      <!-- 使用context 读取配置文件到spring容器中
         <context:property-placeholder location=""/>
         location : db.properties文件的位置,必须加上 classpath: 作为前缀
    -->
      <context:property-placeholder location="classpath:db.properties"/>
          <!-- 创建阿里巴巴druid连接池 -->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
              init-method="init" destroy-method="close"
          >
              <!-- 属性注入 setter方法注入-->
              <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <!-- 最大连接数 -->
            <property name="maxActive" value="${jdbc.maxActive}"/>
      </bean>
  
</beans>

  测试代码

@Test
    public void testSave() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        DataSource dataSource = context.getBean(DataSource.class, "dataSource");
        Connection conn = dataSource.getConnection();
        System.out.println("数据库连接对象:"+conn);
    }