当前位置: 移动技术网 > IT编程>开发语言>Java > 【Spring】Spring 学习

【Spring】Spring 学习

2020年07月17日  | 移动技术网IT编程  | 我要评论

一、什么是 Spring

Spring 是一个分层的一站式轻量级开源框架,提供了多个模块给用户自由组合,能有效解决复杂的企业级应用

 

二、Spring 的特点

  • 解耦,简化开发:使用 IOC 容器,将对象的创建和管理统一交给 IOC 来管理,降低各模块之间的依赖性,使开发者只需要着重逻辑业务的开发
  • 统一管理:由 IOC 容器统一管理对象,维护对象之间的依赖关系
  • 扩展性强:支持兼容主流框架,如 MyBatis、Hibernate等
  • 面向切面编程:AOP使的开发者可以很方便地对程序进行权限拦截、运行监控等
  • 高度开放性:开发者可以自由地选择使用部分 Spring 模块或是全部 Spring 模块

(1)spring属于低侵入式设计,代码的污染极低;

(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。

(4)spring对于主流的应用框架提供了集成支持。

 

三、Spring核心概念

3.1 IOC(控制反转)

3.1.1 概念

通俗地说,所谓的控制反转就是用工厂模式将模块包装起来,其他模块要使用时由工厂统一管理和处理。IOC 则是由一个个这样的工程组合而成的。

 

3.1.2 为什么叫控制反转

假设有 A、B 两个模块,若 A 模块需要使用到 B 模块的对象

没有IOC容器的时候:

没有 IOC 容器时,A模块需要在初始化或某个节点时 new 一个 B 对象或使用已有的 B 对象,而这个主动权在 A 模块中,A 模块能选择使用哪种 B 模块对象,是 new 一个新的,还是使用已有的

使用IOC容器后:

使用 IOC 容器后,所有的 B  对象都由 IOC 容器统一管理,在项目运行后,IOC容器发现A需要使用B,就从B工厂中生成一个B并发送给A

控制 -- 反转

控制:结合例子,可以发现对象的创建交给了 IOC 容器实现,即对象的创建和控制权交给了 IOC 容器,这就是Spring中的控制

反转:结合例子,未使用 IOC 容器时,对象的创建权和控制权是在他的调用对象手中的。

           使用了 IOC 容器后,对象的创建权和控制权都交给了 IOC 容器,而调用他的对象模块只能使用 IOC 容器返回的对象。这

           也就是依赖注入

 

3.1.3 依赖注入

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。

依赖 -- 注入

依赖:组件依赖于 IOC 容器,通过 IOC 容器来提供对象所需要的资源,实现组件间的解耦

注入:调用组件的模块,通过向 IOC 容器获取,得到所需对象的过程,叫做注入

 

3.1.4 IOC 和 DI 有什么好处

  • 提高组件重用频率,保证代码简洁,提高代码质量
  • 通过依赖注入机制,让开发者无需关系指定目标是如何创建、来自哪里、如何实现,而是只需要即拿即用,专注于自身的业务逻辑

 

四、Spring 模块

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式

Spring 框架图示

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器 :核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。 BeanFactory 使用 控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文 :Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP :通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO :JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM :Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块 :Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架 :MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

 

五、Spring 使用

5.1 ApplicationContext 的三个实现类及简单的demo

三种实现类:

  • ClassPathXmlApplicationContext:加载类路径下的配置文件,即resources下的配置文件
  • FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件(需要有访问权限)
  • AnnotationConfigApplicationContext:根据注解创建容器

配置方式:

public static void main(String[] args) {
        // 1. 获取核心容器对象
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac  = new FileSystemXmlApplicationContext("E:\\study\\project\\spring\\spring-demo03\\src\\main\\resources\\bean.xml");

        // 2. 根据ID获取Bean对象
        IAccountService accountService = ac.getBean("accountService", IAccountService.class);
        accountService.saveAccount();

        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);
        accountDao.saveAccount();
    }

 

5.2 ApplicationContext 和 BeanFactory 的区别

5.2.1 比较

  ApplicationContext BeanFactory
区别 在构建核心容器时,创建对象采取的策略是立即加载的方式,即已读取完配置文件马上就会创建文件中配置的对象 在构建核心容器时,采用的是延迟加载的方式。即什么时候根据 id 获取对象了,什么时候才真正创建对象
使用难度 简单 复杂
功能 由 BeanFactory 派生,且支持更多扩展功能 古老的接口,功能较少
推荐使用 单例模式下,更推荐使用 非单例模式,可以使用

 

5.2.2 为什么推荐使用 ApplicationContext

推荐原因:

  • 由于 BeanFactory 采用的是延时加载的方式注入,如果 Bean 注入异常,则只有在你第一次调用 GetBean 方法时才会抛出异常,而 ApplicationContext 会在初始化时进行检查,及时检查历来是否完全注入
  • 原始的 BeanFactory 无法支持 APO、Web 应用等许多插件
  • ApplicationContext 是由 BeanFactory 派生出来的,故拥有 BeanFactory 的所有功能。且又扩展了其他的功能,如资源访问、事件传播、载入多个上下文等

 

5.2.3 使用

依赖:

<packaging>jar</packaging>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
</dependencies>

ApplicationContext

public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    // 2. 根据ID获取Bean对象
    IAccountService accountService = ac.getBean("accountService", IAccountService.class);
    accountService.saveAccount();
}

BeanFactory

public static void main(String[] args) {
    Resource resource = new ClassPathResource("bean.xml");
    BeanFactory factory = new XmlBeanFactory(resource);
    IAccountService accountService = factory.getBean("accountService", IAccountService.class);
    accountService.saveAccount();
}

 

5.3 创建 Bean 的三种方式

5.3.1 使用默认构造函数创建对象

使用默认构造函数创建,若类中没有默认构造函数,则对象无法创建

<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="accountService" class="com.tom.www.service.impl.AccountServiceImpl"></bean>
</beans>

 

5.3.2 使用某个类中的方法创建对象

这种是使用某个类中的方法,并存入 spring 容器

/**
 * 模拟一个工厂类(该类可能是存在于 jar 包中的,我们无法通过修改源码的方式来提供默认构造函数)
 */
public class InstanceFactory{
    public IAccountService getAccountService() {
        return new IAccountServiceImpl();
    }
}
<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="instanceFactory" class="com.tom.www.factory.InstanceFactory"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
</beans>

 

5.3.3 使用某个类中的静态方法创建对象

/**
 * 模拟一个工厂类(该类可能是存在于 jar 包中的,我们无法通过修改源码的方式来提供默认构造函数)
 */
public class StaticFactory{
    public static IAccountService getAccountService() {
        return new IAccountServiceImpl();
    }
}
<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="accountService" class="com.tom.www.factory.StaticFactory" factory-method="getAccountService"></bean>
</beans>

 

5.4 Bean 的作用范围(Scop)

5.4.1 属性列举

属性 描述
singleton (默认) 单例模式使用
prototype 多例模式
request 为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收
session 与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效
global-session 全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同

5.4.2 设置

使用 scope 关键字进行设置,默认模式为 singleton

<?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="accountDao" class="com.tom.dao.AccountDao" scope="singleton"></bean>
</beans>

 

5.5 Bean 对象的生命周期

  出生 活着 死亡 总结
单例对象 当容器创建时对象出生 只要容器还在,对象一直活着 容器销毁,对象消亡 单例对象的生命周期与容器相同
多例对象 当我们使用对象时由spring框架创建 对象只要在使用,就一直活着 当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收  

 

5.6 依赖注入(Dependency Injection)

在IOC的作用中,当一个类需要使用到其他类的对象,由IOC容器为我们提供,我们只需要在配置文件中说明,这样使用类通过被使用类获取对象的方式,我们就称为依赖注入。

5.6.1 依赖注入的数据类型

  • 基本类型和string
  • 其他 bean 类型(在配置文件中或注解中配置过的 bean)
  • 复杂类型/集合类型

 

5.6.2 依赖注入的方式

  • 使用构造器注入
  • 使用 setter 方式注入
  • 使用注解注入(推荐)

 

5.6.3 使用构造器注入

<!--  构造函数注入
          使用标签:constructor-arg
          标签出现的为止: bean标签内部
          标签中的属性:
              type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
              index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引位置从0开始
              name(c常用): 用于指定给构造函数中指定名称的参数赋值
              ======================= 以上三个用于指定给构造函数中哪个参数赋值 =====================
              value: 用于提供基本类型和string类型的数据
              ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
          优势:
              在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
          弊端:
              改变了 Bean 对象的实例化方式,使我们在创建对象时,若用不到这些数据,也必须提供
      -->
    <bean id="accountService" class="xyz.tom.www.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="小明"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
 
    <!--  配置一个日期对象  -->
    <bean id="now" class="java.util.Date"></bean>

 

5.6.4 使用 setter 方法注入

<!--  set方法注入
            设计的标签: property
            出现的为止: bean标签的内部
            标签中的属性:
              name(c常用): 用于指定注入时所调用的set方法名称
              value: 用于提供基本类型和string类型的数据
              ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
            优势:
              创建对象时没有明确的限制,可以使用默认构造函数
            弊端:
              如果有某个成员必须有值,则获取对象时有可能set方法没有执行
      -->
    <bean id="accountService2" class="xyz.tom.www.service.impl.AccountServiceImpl2">
        <property name="name" value="test"></property>
        <property name="birthday" ref="now"></property>
    </bean>
 
    <!--  复杂类型的注入/集合类型的注入
          用于给List结构集合注入的标签: list array set
          用于给Map结构注入的标签: map props
          同类型可以互换,即只要记: list map
    -->
    <bean id="accountService3" class="xyz.tom.www.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
 
        <property name="myList">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
 
        <property name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
 
        <property name="myMap">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
 
        <property name="myProps">
            <props>
                <prop key="testA">AAA</prop>
                <prop key="testB">BBB</prop>
                <prop key="testC">CCC</prop>
            </props>
        </property>
    </bean>

 

5.6.5 使用注解方式注入

使用注解前,需要在 bean.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
 
    <context:component-scan base-package="xyz.tom"></context:component-scan>
 
</beans>

常用的注解:

属性名 作用 位置 限制
@Component 创建 Bean 对象 类名头部  
@Autowired 注入变量 变量上,或方法上 要求 bean 的 id 唯一
@Value 注入基本类型或string类型数据 变量上 ${“xxx.xxx”}
@Scope 改变 Bean 对象的作用范围 类名头部  
@PreDestory、@PostConstruct 生命周期,指定销毁方法和初始方法 方法上  
@Qualifier 存在多个对象类型时,可以指定使用哪一个对象类型 类名头部  

根据mvc三层框架,spring分别提供了三个注解,这三个注解其实是一样的,只是使用地方有所区别:

  • @Controller:一般用在表现层
  • @Service:一般用在业务层
  • @Repository:一般用在持久层
  • @Component:其他

 

5.7 完全注解方式使用Spring

5.7.1 增加配置类,抛弃xml文件

新建 SpringConfiguration.java 类,作为 Spring 项目配置的根本配置类

@Configuration
@ComponentScan({"com.tom"})
public class SpringConfiguration {
}

修改启动类

public static void main(String[] args) {

    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    IAccountService accountService = ac.getBean("accountService", IAccountService.class);
    accountService.saveAccount();
}

 

5.7.2 多配置文件组合

在实际开发中,Spring 允许使用 @Import 注解加载其他配置

新建数据库配置类 JdbcConfig.java

// 表明这是一个配置类,会被扫描器扫到
public class JdbcConfig {
....
}

在主配置类中引用

@ComponentScan({"xyz.tom"}) // 指定父类路径
@Import(JdbcConfig.class) // 导入子类,此时子类无需加 @Configuration
public class SpringConfiguration {
   ...
}

 

5.7.3 将配置变量存放到配置文件并加载

在 resources 文件夹下创建 jdbcConfig.properties:

  

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://www.xxxx.xyz:3306/mysql?useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.user=root
jdbc.password=root

在配置类中,使用 @PropertySource 引入

//@Configuration
@ComponentScan({"xyz.tom"})
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}

在配置类中,用 @Value 使用

 
public class JdbcConfig {
 
    @Value("${jdbc.driver}")
    private String dirver;
 
    @Value("${jdbc.url}")
    private String url;
 
    @Value("${jdbc.username}")
    private String username;
 
    @Value("${jdbc.password}")
    private String password;
 
    /*
     *   用于创建一个 QueryRunner 对象
     */
    @Bean(name = "runner")
//    @Scope(value = "prototype")
    public QueryRunner createQueryRunner(DataSource datasource) {
        return new QueryRunner(datasource);
    }
 
    /*
     *  用于创建一个数据源对象
     */
    @Bean(name="dataSource")
    public DataSource createDataSource() {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(dirver);
//            System.out.println(url);
 
//            ds.setDriverClass("com.mysql.cj.jdbc.Driver");
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException(e);
        }
    }
}

 

六、Spring 搭建单元测试

加入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

创建测试类

/*
 *   使用Junit单元测试,测试我们的配置
 *   Spring整合junit的配置
 *           1. 导入spring整合junit的jar
 *           2. 使用Junit提供的一个注解将原有的main方法替换,改成spring提供的
 *               @RunWith
 *           3. 告知spring的运行期,spring和ioc创建是基于xml还是注解的,并说明位置
 *               @ContextConfiguration
 *                   locations: 指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                   classes: 指定注解类所在位置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class MainTest{

    @Autowired
    private IAccountService as;

    @Test
    public void test() {
        as.saveAccount();
    }
}

 

七、动态代理

7.1 概述

动态代理的作用是在不改变原有相关代码的情况下,实现对方法的增强。如统一的拦截

 

八、AOP

8.1 AOP 的概念

 

 

 

本文地址:https://blog.csdn.net/qq_34416331/article/details/107365760

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网