Spring入门:基本概念及基本配置

发布于 2020-03-28  134 热度


概述

简介

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。
框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring 使用基本的 JavaBean来完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不仅限于服务器端的开发。
从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring 是一个分层的 JavaSE/EEfull-stack(一站式) 轻量级开源框架。

优势

  • 方便解耦,简化开发
    Spring 就是一个大工厂,可以将所有对象创建和依赖关系维护,交给 Spring 管理

  • AOP 编程的支持
    Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

  • 声明式事务的支持
    只需要通过配置就可以完成对事务的管理,而无需手动编程

  • 方便程序的测试
    Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序

  • 方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持

  • 降低 JavaEE API 的使用难度
    Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等),都提供了封装,
    使这些 API 应用难度大大降低

环境搭建及测试

下载包

官网:http://spring.io/
下载地址:http://repo.springsource.org/libs-release-local/org/springframework/spring
解压:(Spring 目录结构:)
* docs :API 和开发规范.
* libs :jar 包和源码.
* schema :约束.

创建Web项目,引入Jar包

Spring入门:基本概念及基本配置
可以看到,Spring核心容器的组件包括Beans、Core、Context、Expression Language。
分别对应spring-beans.jar、spring-core.jar、spring-context.jar、spring-expression.jar。
这四个Jar包是Spring最基础的Jar包。
Spring使用的是apache的日志包,所以还须引入com.springsource.org.apache.commons.logging.jar。
部分老版本还要导入com.springsource.org.apache.log4j.jar。

配置文件

编写实体类

先写一个实体类User.java

package cn.itcast.bean;

public class User {

    private String name;
    private Integer age;

    public User() {
        System.out.println("User对象空参构造方法!!!!");
    }
    //get、set、toString方法省略
}

书写配置对象到容器

新建一个xml文件,
配置文件名任意(建议:applicationContext.xml)
配置文件位置任意(建议放到src下)

导入约束:
Preferences->XML Catelog->File System->找到spring的schema/beans/XXXX(最新版).xsd->打开->Key type选择Schema location->把约束文件名(XXXX.xsd)粘贴到Key输入框的最后面->OK->
输入beans元素->切换到设计视图->在beans元素上点击右键->Edit Namespaces->Add...->选中xsi->OK->Add...->Specify New Namespace->Browse->Select XML Catelog entry->找到刚才导入的XXX.xsd->OK->把Location Hint的路径(去掉文件名:/XXX.xsd)粘贴到Namespace Name中->OK。

如果在beans中输入<有提示,就说明约束导入成功了。
之后将需要Spring管理的对象放在beans元素中,用bean元素描述就可以了。
applicationContext.xml

<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">
    <!-- Spring 的入门案例================ -->
    <bean id="userDao" class="cn.itcast.bean.User"></bean>
</beans>

编写测试代码

Demo.java

package cn.itcast.c_injection;

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

import cn.itcast.bean.User;

public class Demo {
    @Test
    public void fun1(){
        //1 创建容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2 向容器"要"user对象
        User u = (User) ac.getBean("user");
        //3 打印user对象
        System.out.println(u);
    }
}

思想

IOC

Inversion of Control 控制反转. 指的是 对象的创建权反转(交给)给 Spring.由程序员自己创建反转给了Spring创建。作用是实现了程序的解耦合.

DI

Dependency Injection 依赖注入.实现IOC思想需要有DI做支持,Spring 创建这个类的过程中,Spring 将类的依赖的属性设置进去.
注入方式:
1. set方法注入
2. 构造方法注入
3. 字段注入

注入方式:
1. 值类型注入
2. 引用类型注入

工厂(容器)

BeanFactory :是在 getBean 的时候才会生成类的实例.
ApplicationContext :在加载 applicationContext.xml(容器启动)时候就会创建.

ApplicationContext:

每次容器启动时就会创建容器中配置的所有对象.并提供更多功能
ApplicatioContext 接口有两个实现类:
* ClassPathXmlApplicationContext :加载类路径下 Spring 的配置文件.
* FileSystemXmlApplicationContext :加载本地磁盘下 Spring 的配置文件.

BeanFactory(过时):

spring原始接口.针对原始接口的实现类功能较为单一
BeanFactory接口实现类的容器.特点是每次在获得对象时才会创建对象

总结

web开发中,使用applicationContext. 在资源匮乏的环境可以使用BeanFactory.

配置详解

<?xml version="1.0" encoding="UTF-8"?

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
    <!-- 将User对象交给spring容器管理 -->
    <!-- Bean元素:使用该元素描述需要spring容器管理的对象-->
    <bean name="user" class="cn.itcast.bean.User"></bean>
    <import resource="cn/itcast/b_create/otherContext.xml"/>
</beans>

import元素

可以使用import元素来引入其他配置文件,实现分模块控制。

<import resource="cn/itcast/b_create/otherContext.xml"/>

bean元素

属性

class属性

被管理对象的完整类名

name属性和id属性

  • name属性:给被管理的对象起个名字.获得对象时根据该名称获得对象.
    可以重复.可以使用特殊字符.

  • id属性: 与name属性一模一样.
    名称不可重复.不能使用特殊字符.

结论: 尽量使用name属性.

scope属性

指定bean的作用范围,其取值有以下几个:
* singleton
单例对象.被标识为单例的对象在spring容器中只会存在一个实例。

  • prototype
    多例原型.被标识为多例的对象,每次再获得才会创建.每次创建都是新的对象.
    整合struts2时,ActionBean必须配置为多例的.

  • request
    web环境下.对象与request生命周期一致.

  • session
    web环境下,对象与session生命周期一致.

  • globalSession
    WEB 项目中,应用在 Porlet 环境.如果没有 Porlet 环境那么 globalSession 相当于 session.

init-method

配置一个方法作为生命周期初始化方法.spring会在对象创建之后立即调用.

destory-method

配置一个方法作为生命周期的销毁方法.spring容器在关闭并销毁所有容器中的对象之前调用.

创建对象的方式

空参构造方式

<bean  name="user" class="cn.itcast.bean.User" ></bean>

静态工厂

package cn.itcast.b_create;
import cn.itcast.bean.User;
public class UserFactory {
    public static User createUser(){
        System.out.println("静态工厂创建User");
        return new User();
    }
}
<bean name="user" class="cn.itcast.bean.UserFactory" factory-method="createUser"></bean>

实例工厂

package cn.itcast.b_create;
import cn.itcast.bean.User;
public class UserFactory {
    public User createUser(){
        System.out.println("静态工厂创建User");
        return new User();
    }
}
<bean name="user" factory-bean="userFactory" factory-method="createUser"></bean>
<bean name="userFactory" class="cn.itcast.bean.UserFactory"></bean>

属性注入方式

假定存在:
User.java

package cn.itcast.bean;
public class User {
    private String name;
    private Integer age;
    private Car car;
    public User() {
            System.out.println("User对象空参构造方法!!!!");
    }

    public User(String name, Car car) {
        System.out.println("User(String name, Car car)!!");
        this.name = name;
        this.car = car;
    }

    public User(Car car,String name) {
        System.out.println("User(Car car,String name)!!");
        this.name = name;
        this.car = car;
    }

    public User(Integer name, Car car) {
        System.out.println("User(Integer name, Car car)!!");
        this.name = name+"";
        this.car = car;
    }
    //get、set、toString方法省略
}

Car.java

package cn.itcast.bean;

public class Car {
    private String  name;
    private String color;
    //get、set、toString方法省略
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
    <!-- 将car对象配置到容器中 -->
    <bean name="car" class="cn.itcast.bean.Car" >
        <property name="name" value="兰博基尼" ></property>
        <property name="color" value="黄色" ></property>
    </bean>
</beans>

set方法注入

<bean  name="user" class="cn.itcast.bean.User" >
    <!--值类型注入: 为User对象中名为name的属性注入tom作为值 -->
    <property name="name" value="tom" ></property>
    <property name="age"  value="18" ></property>
    <!-- 引用类型注入: 为car属性注入下方配置的car对象 -->
    <property name="car"  ref="car" ></property>
</bean>

构造函数注入

<bean name="user2" class="cn.itcast.bean.User" >
    <!-- name属性: 构造函数的参数名 -->
    <!-- index属性: 构造函数的参数索引 -->
    <!-- type属性: 构造函数的参数类型-->
    <constructor-arg name="name" index="0" type="java.lang.Integer" value="999"></constructor-arg>
    <constructor-arg name="car" ref="car" index="1" ></constructor-arg>
</bean>

因为存在两个构造参数:User(String name, Car car)h和User(Integer name, Car car),
只根据name属性无法断定应执行哪个,需要type属性指明参数类型,如果不存在此问题可以省略type属性。
因为存在两个构造参数:User(String name, Car car)h和User(Car car,String name),
只根据name属性无法断定应执行哪个,需要index属性指明参数位置,如果不存在此问题可以省略index属性。

p名称空间注入

使用该方法需要在beans元素中导入p名称空间:
xmlns:p="http://www.springframework.org/schema/p"
使用p:属性完成注入
* 值类型: p:属性名="值"
* 对象类型 :p:属性名-ref="bean名称"

<bean  name="user3" class="cn.itcast.bean.User" p:name="jack" p:age="20" p:car-ref="car" ></bean>

spel语言注入

Spring Expression Language sping表达式语言。
可以实现从别的bean取值过来。

<bean name="user4" class="cn.itcast.bean.User" >
        <property name="name" value="#{user.name}" ></property>
        <property name="age" value="#{user3.age}" ></property>
        <property name="car" ref="car" ></property>
</bean>

复杂类型注入

<bean name="cb" class="cn.itcast.c_injection.CollectionBean" >
    <!-- 如果数组中只准备注入一个值(对象),直接使用value|ref即可 
    <property name="arr" value="tom" ></property>
    -->
    <!-- array注入,多个元素注入 -->
    <property name="arr">
        <array>
            <value>tom</value>
            <value>jerry</value>
            <ref bean="user4" />
        </array>
    </property>

    <!-- 如果List中只准备注入一个值(对象),直接使用value|ref即可 
    <property name="list" value="jack" ></property>-->
    <property name="list"  >
        <list>
            <value>jack</value>
            <value>rose</value>
            <ref bean="user3" />
        </list>
    </property>
    <!-- map类型注入 -->
    <property name="map"  >
        <map>
            <entry key="url" value="jdbc:mysql:///crm" ></entry>
            <entry key="user" value-ref="user4"  ></entry>
            <entry key-ref="user3" value-ref="user2"  ></entry>
        </map>
    </property>
    <!-- prperties 类型注入 -->
    <property name="prop"  >
        <props>
            <prop key="driverClass">com.jdbc.mysql.Driver</prop>
            <prop key="userName">root</prop>
            <prop key="password">1234</prop>
        </props>
    </property>
</bean>

使用@注释代替bean元素

Spring支持使用@注解代替xml配置文件中的bean元素。

导包

Spring4个核心包+2个apache日志包+spring-aop

配置Spring配置文件

引入命名空间

打开Spring配置文件,按如下步骤操作。
Preferences->XML Catelog->File System->找到spring的schema/context/XXXX(最新版).xsd->打开->Key type选择Schema location->把约束文件名(XXXX.xsd)粘贴到Key输入框的最后面->OK->
输入beans元素->切换到设计视图->在beans元素上点击右键->Edit Namespaces->->Add...->Specify New Namespace->Browse->Select XML Catelog entry->找到刚才导入的XXX.xsd->OK->把Location Hint的路径(去掉文件名:/XXX.xsd)粘贴到Namespace Name中->在Prefix中输入“context”->OK。

如果在beans中输入<context:有提示,就说明约束导入成功了。

配置Spring配置文件

在Spring配置文件的beans元素中插入context:component-scan元素,Spring会自动扫描指定包中所有类的注解配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
    <!-- 指定扫描cn.itcast.bean报下的所有类中的注解.
         注意:扫描包时.会扫描指定报下的所有子孙包
     -->
    <context:component-scan base-package="cn.itcast.bean"></context:component-scan>
</beans>

配置需要Spring管理的类

打开需要Spring管理的类,在类中加@注释实现由Spring创建管理。
在类的上一行加入@Component("名字")注解即可,也可以使用以下几种注释代替:

  • @Service("名字")
    service层

  • @Controller("名字")
    web层

  • @Repository("名字")
    dao层

几种注释之间功能没有任何区别,只是为了体现出类的分层。

值的注入

  • @Value("值")

可以在成员变量前面或set方法前面使用@Value("值")来注入值。

public class Test{
    @Value(123)//相当于a = 123
    int a;

    @Value(123)//相当于调用了setA(123)
    public setA(int a){
        this.a = a;
        System.out.println("调用了set!");
    }
}

在成员变量前面使用更直观清晰,但是没有经过set方法,失去了封闭性。
在set方法前面使用按逻辑来说是正确的做法,推荐使用,但是不那么直观清晰。

引用的注入

  • @Autowired
    自动装配,会自动找到类型一致的对象装配给他。
    问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.

  • @Qualifier("名字")
    根据名字自动装配。

  • @Resource(name="名字")
    手动注入,推荐。

初始化、销毁方法

  • @PostConstruct
    在对象被创建后会调用

  • @PreDestroy
    在销毁之前调用

设定范围

@Scope(scopeName="singleton")
singleton:单例
prototype:多例

代码实例

User.java

package cn.itcast.bean;

//导包代码省略
//  @Service("user") // service层
//  @Controller("user") // web层
//  @Repository("user")// dao层
//指定对象的作用范围
@Component("user")
@Scope(scopeName="singleton")//设定作用范围
public class User {
    private String name;
    @Value("18")
    private Integer age;

    //@Autowired //自动装配
    //@Qualifier("car2")//指定名称注入
    @Resource(name="car")//手动注入
    private Car car;

    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    public String getName() {
        return name;
    }
    @Value("tom")//通过set方法注入值
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @PostConstruct //在对象被创建后调用
    public void init(){
        System.out.println("我是初始化方法!");
    }
    @PreDestroy //在销毁之前调用
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
}

我一直在开辟我的天空