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.字面量
- 整数:<property name="count" value="#{5}"/>
- 小数:<property name="frequency" value="#{88.8}"/>
- 科学计数法:<property name="capacity" value="#{1e4}"/>
- 布尔值:<property name="enable" value="#{false}"/>
- 字符串:<property name="name" value="#{'value'}"/>
- 字符串:<property name="name" value="#{"value"}"/>
但如果仅仅是用于字面量的赋值而使用SpEL,会显得这是多此一举。所以一般不用作字面量的赋值。
2.引用bean及其属性和方法
引用其它对象
<property name="prefix" value="#{prefixGenerator}"></property>引用其它对象属性
<property name="suffix" value="#{sequenceGenerator2.suffix}"></property>引用其它对象方法
<property name="suffix" value="#{sequenceGenerator2.toString}"></property>引用其它对象的静态属性
<property name="initValue" value="#{T(java.lang.Math).PI}"></property>
SpEL同时还支持运算符,例如:+、-、*、/、%等;支持逻辑运算符,例如and、or、not、if-else等;支持正则表达式。
Bean的生命周期
SpringIOC容器可以管理bean的生命周期,Spring允许在bean生命周期的特定点执行指定的任务。
SpringIOC容器对bean的生命周期进行管理的过程如下:
- 通过构造器或工厂方法创建bean实例
- 为bean的属性设置值和对其它bean的引用
- 调用bean的初始化方法
- bean可以使用了
- 当容器关闭时,调用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,它的生命周期将会发生变化,具体过程如下:
- 通过构造器或工厂方法创建bean实例
- 为bean的属性设置值和对其它bean的引用
- 将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法
- 调用bean的初始化方法
- 将bean实例传递给bean后置处理器的postProcessAfterInitialization方法
- bean可以使用了
- 当容器关闭时,调用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的配置文件中声明<context:component-scan>
然后在该节点中设置base-package属性,该属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包下的所有类。当发现这些类中有对应的注解之后,Spring会管理这些类。
如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,例如:
<context:component-scan
base-package="com.itcast.spring.bean.annotation"
resource-pattern="repository/*.class">
</context:component-scan>
它将只扫描repository包下的类。
<context:include-filter>和<context:exclude-filter>子节点还支持多种类型的过滤表达式。<context:include-filter>能够指定包含哪些指定表达式的组件;而<context:exclude-filter>能够指定排除哪些指定表达式的组件。
Spring框架完全掌握(上)的更多相关文章
- spring框架设计理念(上)
一.前言 spring的应用非常的广泛,在开发过程中我们经常接触,可能会有一种感觉:对spring即熟悉又陌生,熟悉体现在我们几乎每天都在使用,对spring的IOC.AOP功能都有了基本的了解 ...
- Spring框架基础(上)
spring是开源对轻量级框架 spring核心主要两部分 aop 面向切面编程,扩展功能不是修改源代码实现 aop采用横向抽取机制,取代了传统纵向继承体系重复代码(性能监视.事务管理.安全检查.缓存 ...
- Spring框架简单介绍
原文地址: http://my.oschina.net/myriads/blog/37922 1.使用框架的意义与Spring的主要内容 随着软件结构的日益庞大,软件模块化趋势出现,软件开发也须要多 ...
- Spring框架完全掌握(下)
接着上一篇文章的内容Spring框架完全掌握(上),我们继续深入了解Spring框架. Spring_AOP 考虑到AOP在Spring中是非常重要的,很有必要拿出来单独说一说.所以本篇文章基本上讲述 ...
- Spring框架系列(2) - Spring简单例子引入Spring要点
上文中我们简单介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文主要承接上文,向你展示Spring Framework组件 ...
- 关于我使用spring mvc框架做文件上传时遇到的问题
非常感谢作者 原文:https://blog.csdn.net/lingirl/article/details/1714806 昨天尝试着用spring mvc框架做文件上传,犯了挺多不该犯的毛病问题 ...
- 基于spring 3.0mvc 框架的文件上传实现
Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框 ...
- Spring框架学习总结(上)
目录 1.Spring的概述 2.Spring的入门(IOC) 3.Spring的工厂类 4.Spring的配置 5.Spring的属性注入 6.Spring的分模块开发的配置 @ 1.Spring的 ...
- Spring框架概述
Spring是最流行的Java企业级应用开发框架,全球数以百万的开发者在使用Spring框架创建高性能.易测试.可重用的代码. Spring框架的核心特性可以应用于任何Java应用,但扩展的JavaE ...
随机推荐
- 连续线性空间排序 起泡排序(bubble sort),归并排序(merge sort)
连续线性空间排序 起泡排序(bubble sort),归并排序(merge sort) 1,起泡排序(bubble sort),大致有三种算法 基本版,全扫描. 提前终止版,如果发现前区里没有发生交换 ...
- OpenGL实例:纹理映射
OpenGL实例:纹理映射 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 更多请查看:计算机图形学 1. 介绍 用于指定一维.二维和三维纹理的函数分别 ...
- CAS你知道吗?原子类AtomicInteger的ABA问题谈谈?
(1)CAS是什么? 比较并交换 举例1, CAS产生场景代码? import java.util.concurrent.atomic.AtomicInteger; public class CA ...
- c/c++ 混合编程.so
CC = gccC++ = g++LINK = g++ LIBS = -lz -lm -lpcre#must add -fPIC optionCCFLAGS = $(COMPILER_FLAGS) - ...
- zz传统方法和深度学习结合的感知策略探索
今天分享下 Pony.ai 在感知探索的过程中,使用的传统方法和深度学习方法.传统方法不代表多传统,深度学习也不代表多深度.它们都有各自的优点,也都能解决各自的问题.我们希望发挥它们的优点,并且结合起 ...
- springboot集成freemarker属性配置(不知道是针对于某个版本,2.0后有变动)
freemarker属性配置 freemarker属性配置: spring.freemarker.allow-request-override=false # 设置是否允许HttpServletReq ...
- 【2019.8.12 慈溪模拟赛 T1】钥匙(key)(暴力DP)
暴力\(DP\) 这题做法很多,有\(O(n^2)\)的,有\(O(n^2logn)\)的,还有徐教练的\(O(nlogn)\)的,甚至还有\(bzt\)的二分+线段树优化建图的费用流. 我懒了点,反 ...
- csps2019记
Day0: 上午疯狂颓板子(树状数组,KMP)屁都没考,感觉海星. 上午坐大巴去了德州,中午和skyh凑钱吃饭(其实是我请他)rp++. 下午在火车上和侯神打扑克,拉火车之神Get. 到燕大试机,敲了 ...
- [2019BUAA软工助教]助教学期总结
[2019BUAA软工助教]助教学期总结 一.量化自评 线上 博客点评:https://www.cnblogs.com/ChildishChange/MyComments.html 共 106 条 博 ...
- Office365激活方法(无需密钥)
@echo off title Activate Office 365 ProPlus for FREE - MSGuides.com&cls&echo =============== ...