Spring框架完全掌握(上)

简介: Spring框架完全掌握(上)

引言

前面我写了一篇关于Spring的快速入门,旨在帮助大家能够快速地了解和使用Spring。既然是快速入门,讲解的肯定只是一些比较泛的知识,那么对于Spring的一些深入内容,我决定将其分为上、下两部分,希望能帮到你们。

Bean的作用域

作用域相信大家都了解,我们先来看一个案例。
创建一个bean类:

package com.itcast.spring.bean.scope;

public class Car {
   

    private String brand;
    private String corp;
    private double price;
    private int maxSpeed;

    public String getBrand() {
   
        return brand;
    }

    public void setBrand(String brand) {
   
        this.brand = brand;
    }

    public String getCorp() {
   
        return corp;
    }

    public void setCorp(String corp) {
   
        this.corp = corp;
    }

    public double getPrice() {
   
        return price;
    }

    public void setPrice(double price) {
   
        this.price = price;
    }

    public int getMaxSpeed() {
   
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
   
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
   
        return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price + ", maxSpeed=" + maxSpeed + "]";
    }
}

在配置文件中进行配置:

<?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="car" class="com.itcast.spring.bean.scope.Car">
        <property name="brand" value="BMW"></property>
        <property name="corp" value="ShangHai"></property>
        <property name="price" value="350000"></property>
        <property name="maxSpeed" value="240"></property>
    </bean>

</beans>

接下来编写测试代码:

public static void main(String[] args) {
   
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");
        Car car = (Car) ctx.getBean("car");
        Car car2 = (Car) ctx.getBean("car");
        System.out.println(car == car2);
}

运行结果:

true

从这里我们得知,从容器中取出的两个Car其实是同一个对象,这是为什么呢?
这就涉及到bean的作用域,其实默认情况下Spring会给bean设置一个作用域singleton,此时的bean是一个单例的bean,也就是说在整个容器中这个bean只会存在一个实例。我们可以通过bean节点中的scope属性来设置bean的作用域。

<?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="car" class="com.itcast.spring.bean.scope.Car" scope="prototype">
        <property name="brand" value="BMW"></property>
        <property name="corp" value="ShangHai"></property>
        <property name="price" value="350000"></property>
        <property name="maxSpeed" value="240"></property>
    </bean>

</beans>

这里我将bean的作用域设置为了prototype,意思是原型的,也就就说,在这种情况下,每次去容器中获取bean,它都会返回一个新的对象。
我们重新运行测试代码,会发现结果为false,验证了刚才的结论。

使用外部属性文件

在配置文件里配置bean时,有时候需要在bean的配置里混入系统部署的细节信息,而这些部署细节实际上需要和bean相分离。
对于这样的需求,Spring提供了一个PropertyPlaceholderConfigurer的BeanFactory后置处理器,这个处理器允许用户将bean配置的部分内容外移到属性文件中,可以在bean配置文件里使用形式为${var}的变量,PropertyPlaceholderConfigurer从属性文件中加载属性,并使用这些属性来替换变量。
假设我现在需要在Spring配置文件中配置关于数据库连接的基本信息,此时我们应该将这些基本信息抽取成一个属性文件(db_info.properties)放在外面:

user=root
password=123456
url=jdbc:mysql:///test
driver=com.mysql.jdbc.Driver

然后配置一下c3p0的bean类:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 导入属性文件 -->
    <context:property-placeholder location="classpath:db_info.properties"/>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
        <property name="driverClass" value="${driver}"></property>
        <property name="jdbcUrl" value="${url}"></property>
    </bean>

</beans>

编写测试代码:

public static void main(String[] args) throws SQLException {
   
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-properties.xml");
        ComboPooledDataSource dataSource = (ComboPooledDataSource) ctx.getBean("dataSource");
        System.out.println(dataSource.getConnection());
    }
}

运行结果:

com.mchange.v2.c3p0.impl.NewProxyConnection@35a50a4c

这样也能成功获取数据库连接,而且在之后的修改过程将非常方便, 只需要去寻找连接数据库的属性文件即可。

SpEL

SpEL是Spring表达式语言,是一个支持查询和操作对象图的表达式语言。
它的语法类似于jsp中的EL,但它以#{}作为定界符,所有在大括号内的字符都将被认为是SpEL。
1.字面量

  • 整数:
  • 小数:
  • 科学计数法:
  • 布尔值:
  • 字符串:
  • 字符串:

但如果仅仅是用于字面量的赋值而使用SpEL,会显得这是多此一举。所以一般不用作字面量的赋值。
2.引用bean及其属性和方法

  • 引用其它对象
  • 引用其它对象属性
  • 引用其它对象方法

  • 引用其它对象的静态属性

SpEL同时还支持运算符,例如:+、-、*、/、%等;支持逻辑运算符,例如and、or、not、if-else等;支持正则表达式。

Bean的生命周期

SpringIOC容器可以管理bean的生命周期,Spring允许在bean生命周期的特定点执行指定的任务。
SpringIOC容器对bean的生命周期进行管理的过程如下:

  1. 通过构造器或工厂方法创建bean实例
  2. 为bean的属性设置值和对其它bean的引用
  3. 调用bean的初始化方法
  4. bean可以使用了
  5. 当容器关闭时,调用bean的销毁方法

很多人可能会奇怪,一个普通的bean类怎么会有初始化和销毁方法呢?这当然是没有的,只不过Spring提供了一种方式来指定bean的初始化和销毁方法。
我们编写一个案例感受一下。
创建一个bean类:

package com.itcast.spring.bean.cycle;

public class Car {
   

    private String brand;

    public Car() {
   
        System.out.println("Constructor...");
    }

    public void setBrand(String brand) {
   
        System.out.println("setBrand...");
        this.brand = brand;
    }

    public void init() {
   
        System.out.println("init...");
    }

    public void destory() {
   
        System.out.println("destory...");
    }
}

在配置文件中进行配置:

<?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="car" class="com.itcast.spring.bean.cycle.Car"
        init-method="init" destroy-method="destory">
        <property name="brand" value="BMW"></property>
    </bean>

</beans>

编写测试代码:

public static void main(String[] args) {
   
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
        Car car = (Car) ctx.getBean("car");
        System.out.println(car);

        //关闭IOC容器
        ctx.close();
    }

运行结果:

Constructor...
setBrand...
init...
com.itcast.spring.bean.cycle.Car@91161c7
八月 21, 2019 2:05:00 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3f91beef: startup date [Wed Aug 21 14:05:00 CST 2019]; root of context hierarchy
destory...

从运行结果可以看出,容器首先构造出了Car类,然后为其设置了属性值,接着调用了init方法,最后在容器关闭的时候调用destory方法。需要注意的是,在Car类中的初始化和销毁方法并不一定非得写成init和destory,方法名可以是任意的,只需要在配置时指定初始化和销毁的方法即可。

对于bean的生命周期管理,Spring框架为我们提供了更加细粒度的操作——后置处理器。
bean后置处理器允许在调用初始化方法前后对bean进行额外的处理。而且它能够对IOC容器中的所有bean实例逐一处理,而非单一实例,其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
添加了bean后置处理器之后的bean,它的生命周期将会发生变化,具体过程如下:

  1. 通过构造器或工厂方法创建bean实例
  2. 为bean的属性设置值和对其它bean的引用
  3. 将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法
  4. 调用bean的初始化方法
  5. 将bean实例传递给bean后置处理器的postProcessAfterInitialization方法
  6. bean可以使用了
  7. 当容器关闭时,调用bean的销毁方法

那么如何实现后置处理器呢?
创建一个类实现接口BeanPostProcessor:

package com.itcast.spring.bean.cycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
   

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   
        System.out.println("postProcessAfterInitialization...:" + bean + "," + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   
        System.out.println("postProcessBeforeInitialization...:" + bean + "," + beanName);
        return bean;
    }
}

然后在配置文件中加上对后置处理器的配置:

<!-- 配置bean的后置处理器 -->
<bean class="com.itcast.spring.bean.cycle.MyBeanPostProcessor"></bean>

这是一个比较特殊的bean,所以无需指定id,Spring会自动寻找该bean进行处理。
其它代码不用改动,接下来重新运行测试代码:

Constructor...
setBrand...
postProcessBeforeInitialization...:com.itcast.spring.bean.cycle.Car@77caeb3e,car
init...
postProcessAfterInitialization...:com.itcast.spring.bean.cycle.Car@77caeb3e,car
com.itcast.spring.bean.cycle.Car@77caeb3e
八月 21, 2019 2:17:28 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3f91beef: startup date [Wed Aug 21 14:17:28 CST 2019]; root of context hierarchy
destory...

此时对于bean生命周期的掌控将更加到位。

FactoryBean实现配置Bean

Spring为我们提供了很多种方式配置bean,其中FactoryBean配置bean的方式也是十分常用的。如何实现呢?
创建类实现FactoryBean接口:

package com.itcast.spring.bean.factorybean;

import org.springframework.beans.factory.FactoryBean;

public class CarFactoryBean implements FactoryBean<Car> {
   

    @Override
    public Car getObject() throws Exception {
   
        return new Car("BMW",480000);
    }

    @Override
    public Class<?> getObjectType() {
   
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
   
        return true;
    }
}

需要实现三个方法,分别返回对象实例,对象类型,和该对象是否为单例。
在配置文件中进行配置:

<?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="car" class="com.itcast.spring.bean.factorybean.CarFactoryBean">

    </bean>

</beans>

编写测试代码:

public static void main(String[] args) {
   
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-beanfactory.xml");
        Car car = (Car) ctx.getBean("car");
        System.out.println(car);
}

运行结果:

Car [brand=BMW, price=480000.0]

这样的方法配置的bean是实现FactoryBean接口的类实例,得到的却是getObject方法返回的实例。

注解实现配置Bean

在Spring中还能够通过注解来配置bean,但在这之前,我们必须了解一下Spring中的组件扫描。
组件扫描(component scanning):Spring能够从classpath路径下自动扫描、侦测和实例化具有特定注解的组件。
特定组件包括:

  • @Component:基本注解,标识了一个受Spring管理的组件
  • @Respository:标识持久层组件
  • @Service:标识业务层组件
  • @Controller:标识表现层组件

对于扫描到的组件,Spring有默认的命名策略:即使用非限定类名,第一个字母小写,也可以在注解中通过value属性值标识组件的名称。
虽然每个注解对应着相应的组件,但是Spring并不能准确识别出项目中的层级分布,也就是说,这些注解其实是可以随意使用的,但是为了规范,项目中还是会按照这个标准来使用。
当我们在组件类上使用的特定的注解之后,还需要在Spring的配置文件中声明
然后在该节点中设置base-package属性,该属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包下的所有类。当发现这些类中有对应的注解之后,Spring会管理这些类。
如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,例如:

<context:component-scan
    base-package="com.itcast.spring.bean.annotation"
    resource-pattern="repository/*.class">
</context:component-scan>

它将只扫描repository包下的类。

和子节点还支持多种类型的过滤表达式。能够指定包含哪些指定表达式的组件;而能够指定排除哪些指定表达式的组件。

相关文章
|
15天前
|
XML 安全 Java
|
19天前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
27 0
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
58 4
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
173 1
|
2月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
41 0
|
1天前
|
IDE Java 测试技术
互联网应用主流框架整合之Spring Boot开发
通过本文的介绍,我们详细探讨了Spring Boot开发的核心概念和实践方法,包括项目结构、数据访问层、服务层、控制层、配置管理、单元测试以及部署与运行。Spring Boot通过简化配置和强大的生态系统,使得互联网应用的开发更加高效和可靠。希望本文能够帮助开发者快速掌握Spring Boot,并在实际项目中灵活应用。
19 5
|
11天前
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
43 8
|
21天前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
34 6
|
26天前
|
Java 数据库连接 数据库
不可不知道的Spring 框架七大模块
Spring框架是一个全面的Java企业级应用开发框架,其核心容器模块为其他模块提供基础支持,包括Beans、Core、Context和SpEL四大子模块;数据访问及集成模块支持数据库操作,涵盖JDBC、ORM、OXM、JMS和Transactions;Web模块则专注于Web应用,提供Servlet、WebSocket等功能;此外,还包括AOP、Aspects、Instrumentation、Messaging和Test等辅助模块,共同构建强大的企业级应用解决方案。
47 2