Spring 笔记整理,本文是刚开始工作学习的时候整理的,所以会显得较乱,推荐查阅 Spring 官方文档,官方文档写的还是很不错的!
从 Spring4.x 版本开始,Spring 已经不推荐使用 xml 配置了,而是推荐 Java 配置(注解式开发),Java 配置也是 Spring Boot 推荐的配置方式。不过很多前辈、大牛,都很了解 xml 配置这种方式,这种方式对新手不是那么容易接受,至少浪子刚开始的时候确实如此。
不过得益于时代发展和技术更新,浪子工作的时候基本都是 Spring Boot 项目,还有更多都是要求掌握 Spring Cloud 了。Cloud也没什么,就是组件多了而已~~(其实是维护排查问题还有数据同步问题困难、麻烦)~~。
对于 xml 配置的方式进行项目开发接触不是太多。后面随着学习的深入,再看到这种项目浪子觉得 xml 是一个非常好的组件清单(你可以把它看成一个文档);维护者接手这个项目只要看一眼这个 xml 就能搞清楚系统的组件的“架构(包括依赖关系)”。不过 xml 文件没有类型检查,如果导入了一个错误的类,可能你需要找很长时间的错误(不过我们有智能 IDE!但字符串拼写还是有可能漏查的)。
- 一、Spring
- 二、Spring的核心API:ApplicationContext
- 三、Spring工厂创建复杂对象
- 四、Spring配置文件的细节
- 五、Spring注入方式
- 六、Spring工厂创建复杂对象的三种方式
- 七、如何控制Spring工厂创建对象的次数
- 八、对象的生命周期
- 九、配置文件参数化
- 十、类型转换器
- 十一、后置处理Bean
- 十二、静态、动态代理的概念
- 十三、Spring AOP(Aspect Oriented Programing) 编程
- 十四、拦截器、过滤器
- 十五、Spring的事务管理
- 十六、Spring MVC
一、Spring
Spring 是一个轻量级的解决方案,它有两大核心内容:AOP 和反转控制。
-
反转控制(Inverse of Control)
- 反转: 赋值交给 Spring,解耦合
- 把对于成员变量赋值的控制权,从代码反转到 Spring 工厂和配置文件中完成。
- 底层实现: 工厂设计模式。
-
依赖注入DI(Dependency Injection)
- 注入: 通过 Spring 的工厂及配置文件,为对象(bean、组件)的成员变量赋值。
- 依赖注入: 当以一个类需要另一个类时,就产生了依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过 Spring 配置文件进行注入(赋值)。
二、Spring的核心API:ApplicationContext
ApplicationContext 是 Spring 的核心接口,它用于对象的创建,可以把它当作一个 Bean 工厂。使用这个接口可以屏蔽实现的差异,从而解耦合。
ApplicationContext 包括:
- ClassPathXmlApplicationContext (非 Web 环境:main junit)
- XmlWebApplicationContext(Web 环境)
另外,Application 是一个重量级资源,对于这类资源,我们不应去频繁的创建。
三、Spring工厂创建复杂对象
-
创建工厂类型;
-
配置文件的配置 ApplicationContext.xml
-
通过工厂类获得对象
1
2
3ApplicationContext
|- ClassPathXmlApplicationContext
|- WebXmlApplicationContext
-
什么是复杂对象?
不能通过 new 关键字的构造方法创建的对象。例如,jdbc 的 Connection 对象,Mybatis 中的 SqlSessionFactory 等。 -
什么是简单对象?
可以直接通过 new 构造方法创建对象,这样的对象叫做简单对象。
接口加反射,什么都能做。Spring 工厂是可以调用对象私有的构造方法创建对象,其中大量使用反射来获取信息帮助我们创建对象,这就是 Spring 工厂比我们自己创造的简易工厂强大的地方。
四、Spring配置文件的细节
1. 只配置bean的class属性
1 | <!-- 该配置不含 id 属性 --> |
应用场景:如果这个 bean 只需要使用一次,那么就可以省略 id 值。如果 bean 会使用多次,或者被其他 bean 引用则需要设置 id 值。
2. name别名的使用
1 | <!-- name 就是一个别名,使用 getBean(name) 方法同样也可以创建 bean 对象,与 getBean(id) 是等效的。 --> |
id 和 name 的不同:
- 别名可以定义多个,id 只能定义一个(比如,人的大名只有一个,小名可以有多个)
- XML 对于 id 属性的值,命名要求:必须以字母开头,后面是字母、数字、下划线、连字符。不能以特殊字符开头。name 属性值没有要求。因此,name 可以用于比较特殊命名的场景下 [注①]。
- 代码
containBeanDefinition(id) 只能判断 id 是否存在,不能判断 name;containBean() 可以判断 id,也可以判断 name。
注①: XML 发展到了今天:ID 属性值的限制已经不存在了。这就是语言不断更新发展的好处。
3. ref标签
1 | <bean id="userDao" class="com.xxx.UserDaoImpl"> |
Spring 4.x 废除了
<ref local=""/>
,它和<ref bean=""/>
基本等效。但是前者只能引用本配置文件;后者除了可以引用本配置文件,还可以引用父配置文件。
五、Spring注入方式
名称 | 举例 | 所属 | 说明 |
---|---|---|---|
setter注入 | setxxx() | ||
自动注入 | @Autowired | Spring提供 | 默认根据类型注入 |
自动注入 | @Resource | JavaEE规范 | 默认根据名称注入 |
构造方法注入 | public xxxConstruct() | 推荐使用 |
对于 @Autowired 和 @Resource,如果按照默认的类型找不到目标类的话,会自动使用另一种方式去查找。
属性注入:@Value 注入 map 集合时,文件中必须使用 json 格式赋值,使用 #{${属性}}
取值,map的键如果相同,后面的值会覆盖前面的值。
1. set注入的简化写法
-
基于属性简化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- jdk 类型 -->
<!-- 原始写法 -->
<property name="id">
<value>11</value>
</property>
<!-- 简化后写法 -->
<property name="id" value="11" />
<!-- value 属性只能简化 8 种基本类型 + String 标签 -->
<!-- 用户自定义类型 -->
<!-- 原始写法 -->
<bean id="userService" class="com.xxx.UserServiceImpl">
<property name="userDao">
<ref bean="userDao" />
</property>
</bean>
<!-- 简化后写法 -->
<bean id="userService" class="com.xxx.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean> -
基于命名空间 p 简化写法
使用命名空间需要在 xml 的头声明中导入对应的 xsd 模板,否则会报错。
1
2
3
4
5<!-- jdk 类型简化写法 -->
<bean id="person" class="" p:name="xiaoming" p:id="100"/>
<!-- 自定义类型简化写法 -->
<bean id="" class="" p:userDao-ref="userDao"/>
2. 构造注入
-
提供有参构造方法
1
2
3
4
5
6
7
8
9public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
} -
Spring 的配置文件
1
2
3
4
5
6
7
8
9<bean id="" class="">
<!-- 一个标签对应一个参数 -->
<constructor-arg type="">
<value>小明</value>
</constructor-arg>
<constructor-arg type="">
<value>20</value>
</constructor-arg>
</bean>如果构造方法有重载,并且参数的个数相同,这个时候需要
<constructor-arg type="">
指明参数的类型才能完成注入。
3. 循环依赖
先说 Spring 解决循环引用的结论:两个对象都是 单实例 的情况下,且通过 set方式 进行注入才能成功解决该问题。
Spring AOP 创建的代理是在 创建对象-> 属性填充 -> 初始化的时候执行的。
思考:代理对象一定会在初始化的时候创建?
不一定,如果涉及循环引用,创建 -> singletonFactries -> lambda ->创建
Spring解决循环依赖的步骤:
步骤一:
1.singletonObjects null
2.earlySingleObjects null
3.singletonFactories null
步骤二:
1.singletonObjects null
2.earlySingleObjects null
3.singletonFactories != null lambda getEarlyBeanRederence(beanName, mbd, bean) 创建代理 proxy,然后从 singletonFactories 中移除,proxy 放到 earlySingletonObjects 中。
- 先到 singletonObjects 中获取,如果为 null,则在 earlySingleObjects 中获取,如果还为 null,在 singletonFactories 中获取。
- 通过lambda getEarlyBeanRederence(beanName, mbd, bean) 创建代理,然后从 singletonFactories 中移除,把 proxy 放到 earlySingletonObjects 中。
creationBean a(半成品)
属性的填充:涉及 getBean(“b”),过程和上面类似,在 b 中需要 a,上面过程中已经创建了 a,所以可以顺利拿到,进而创建 b。循环依赖就是 “你中有我,我中有你” 的解决方法。
六、Spring工厂创建复杂对象的三种方式
1. FactoryBean 接口
-
实现 FactoryBean 接口的三个方法:getObject(),书写创建复杂对象的代码并返回复杂对象、getObjectType(),返回创建的复杂对象的 Class 对象、isSinglrton(),return true 只创建一个复杂对象,return false 每一次调用,都生成一个复杂对象。
-
Spring 配置文件的配置
1 | <!-- 虽然配置是和简单对象是一样的,但是通过 id 获取的是这个类创建的复杂对象 Connection --> |
如果想要获取 ConnectionFactoryBean 对象,需要 getBean(“&conn”),就是在 id 的前面加上 &
。
2. 实例工厂
1 | <!-- 先声明实例对象,再引用 --> |
3. 静态工厂
1 | <!-- 直接使用 --> |
七、如何控制Spring工厂创建对象的次数
Q:为什么要控制对象的创建次数?
A:节省不必要的内存浪费。
-
什么样的对象只创建一次?
- SqlSessionFactory
- DAO
- Service
-
什么样的对象每一次都要创建
- Connection
- SqlSession | Session
- Controller | Struts2 Action
总之一句话:如果可以共用,并且是线程安全的,可以只创建一次。
1. 控制简单对象的创建次数
1 | <bean id="" scope="singleton|prototype" class=""> |
singleton: 只会创建一次;
prototype: 每一次都会创建新的对象。
默认值 singleton。
2. 控制复杂对象的创建次数
实现 FactoryBean 的接口的复杂对象:
isSingleton() {
return true;只会创建一次
return false;每一次都会创建新的
}
如果没有 isSingleton() 方法,还是配置 scope 属性。
八、对象的生命周期
1. 创建阶段
scope="singleton"
: Spring 工厂创建的同时(new ClassPathXmlApplicationContext()),对象被创建。
如果想要在 singleton 的情况下,使用 getBean()
时才能创建对象,可以加入 lazy-init 属性,lazy-init="true"
scope="prototype"
: 在调用 getBean() 的时候创建对象。
2. 初始化阶段
Spring 工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
- 初始化方法的提供:程序员根据需求,提供初始化方法,最终完成初始化操作。
- 初始化调用:Spring 工厂调用。
初始化实现方式:
- 实现 InitializingBean 接口,重写 afterPropertiesSet() 方法。
- 对象中提供一个普通的方法
在配置文件中添加 init-method 属性,值为我们定义的普通的方法名称,注意不需要()
。
Q: 如果一个对象实现了 InitializingBean 接口,也提供了普通的方法,那么执行的顺序是什么样子的呢?
A: 先执行 InitializingBean,再执行普通方法。
3. 销毁阶段
Spring 销毁对象前,会调用对象的销毁方法,完成销毁。销毁方法的操作只适用于 scope="singleton"
的对象。销毁操作主要指资源的释放操作。例如 io,connection 等的关闭。
销毁方法:程序员根据自己的需求,定义销毁方法。
调用:Spring 工厂完成调用
实现方式:
- 实现 DisposableBean 接口,重写 destroy() 方法;
- 自定义销毁方法,在配置文件的 destroy-method 属性中写自定义的方法名。
Q: Spring 什么时候销毁对象?
A: ctx.close();
Q: 如果一个对象实现了 DisposableBean 接口,也提供了普通的销毁方法,那么执行的顺序是什么样子的呢?
A: 执行顺序:先执行 DisposableBean 接口的实现方法,再执行普通方法。
九、配置文件参数化
把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中。例如,数据库的连接可能在后期维护中会更换账户和密码,这时就可以把这些内容放到另一个文件中,这个文件专门存储变化的数据。
- 提供一个小的配置文件(.properties 文件),名字、位置任意。
- Spring 的配置文件和小配置文件整合。
db.properties
文件:
1 | jdbc.driverClassName = com.mysql.jdbc.Driver |
spring-config.xml
文件:
1 | <!-- 配置小文件的路径 --> |
十、类型转换器
1. 自定义类型转换器实现
- 实现 Converter 接口,定义转换的类型与实现。
- 配置文件注册配置
1 | <!-- 创建自定义转换对象 --> |
注册 ConversionServiceFactoryBean 时的 id 必须为 conversionService,大小写也需要一样。
Spring 框架其实内置了日期类型的转换器,但只是针对2021/12/12
这种格式,至于其它格式依然需要程序员手动实现。
十一、后置处理Bean
BeanPostProcess 作用: 对 Spring 工厂所创建的对象,进行再加工。
实现 BeanPostProcessor 接口,重写其中的方法。最好是在 after 的方法中写需要的逻辑。
BeanPostProcessor 会对 Spring 工厂创建的所有对象都生效。
十二、静态、动态代理的概念
代理设计模式的作用就是额外功能的增强,可以通过动态字节码技术创建 包括JDK CGLIB ASM Javasist(MyBatis也支持)
装饰器设计模式:本职功能的增强
1. 静态代理
静态代理:为每一个原始类,手工编写一个代理类(有 .java
和 .class
文件)
由此我们可以知道静态代理存在的问题:
- 静态文件数量过多,不利于项目管理;
- 额外功能的维护性差(代理类中,额外功能修改复杂)。
2. 动态代理
- JDK 动态代理
Proxy.newProxyInstance()
通过接口创建代理的实现类。 - CGlib 动态代理 Enhancer 通过继承父类创建的代理类。
十三、Spring AOP(Aspect Oriented Programing) 编程
概念:基于 Spring 的动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护。
本质:JDK 动态代理。
思考:
- AOP 如何创建动态代理类?(动态字节码技术)
- Spring 的工厂是如何加工创建代理对象?(通过原始对象的id值,获得代理对象)(BeanPostProcessor)
SpringBoot中修改创建代理的方式,添加 @EnableApsectJAutoProxy
,覆盖SpringBoot的内置设置。
Spring AOP: 默认使用 JDK 代理
SpringBoot AOP: 默认使用 CGlib 代理
1. 开发步骤
- 原始对象
- 额外功能(MethodInterCeptor)
- 切入点
- 组装切面(额外功能 + 切入点)
2. AOP的底层实现原理
2.1 JDK动态代理
-
类加载器的作用
通过了类加载器把对应类的字节码文件加载到JVM
通过类加载器创建类的 Class 对象,进而创建这个类的对象。 -
如何获得类加载器?
每一个类的.class
文件自动分配与之对应的 ClassLoader. -
动态代理创建的过程中,需要 ClassLoader 创建代理类的 Class 对象,可是因为动态代理类没有对应的
.class
文件,JVM 也就不会为其分配 ClassLoader,如何创建?
借用一个 ClassLoader,任意的 ClassLoader 都可以。
2.2 CGlib 动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证两者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)。
3. 基于注解的AOP开发
Spring 配置文件加入:
1 | <!-- 告知Spring是基于注解的AOP --> |
在默认情况下:AOP 编程底层使用 JDK 动态代理创建代理对象。那么如何切换到 CGlib 动态代理呢?有两种方式,一种是针对 注解式 开发,另一种是针对 传统 开发。
-
注解式 AOP 开发:修改 Spring 的配置文件,在开启动态代理的标签中添加属性
proxy-target-class="true"
1
2<!-- 告知Spring是基于注解的AOP -->
<aop:aspect-autoproxy proxy-target-class="true"/> -
传统 AOP 开发切换到 CGlib 配置
1
2
3
4<aop:config proxy-target-class="true">
<aop:pointcut id="pc" expression="execution(* login(..)) or execution(* register(..))"/>
<aop:advisor advice-ref="arround" pointcut-ref="pc"/>
</aop:config>
编写一个 Aop 配置类:导入 aop 依赖;在 Aop 配置类上加上 @Aspect、@Configuration 注解;编写切面增强方法。
- 切入点表达式
- 方法级别的切入点表达式:
execution(* com.XXX.*.*(..))
,第一个 * 号代表可以返回任意类型值。 - 类级别的切入点表达式:
within(com.XXX.*)
- 自定义注解表达式:
@annotation(com.XXX)
- 方法级别的切入点表达式:
- 增强方式
- 前置增强 (@Befor)、后置增强(@After)、环绕增强 (@Arround)、后置返回增强 (@AfterReturning)、异常增强 (@AfterThrowing)
- 前置和后置都没有返回值,方法参数都是 JointPoint
- 环绕增强中,需要调用
proceed()
才能继续处理业务逻辑(类似拦截器),该方法返回值为业务的返回值,因此环绕增强的返回类型比较推荐设置为 Object。 - 环绕增强的方法参数是 ProceedingJointPoint
4. AOP开发过程中的坑
在同一个的业务类中,进行业务方法间的相互调用,只有最外层方法才加入了额外功能,内部的方法通过普通的方式调用,调用的都是原始方法。如果想让内层的方法也通过代理对象调用,必须实现 ApplicationContextAware 接口获得工厂,进而获得代理对象。
5. AOP总结
十四、拦截器、过滤器
1. 拦截器
- 实现 HandlerInterceptor 接口;
- 重写 preHandle、postHandle、afterCompletion 方法,其中 preHandle 方法中返回 true 代表放行,返回 false 代表中断。
preHandle 返回值为 true 时,执行控制器中的方法,当控制器方法执行完成后会返回拦截器中执行拦截器中的 postHandle 方法,postHandle 执行完成之后响应请求。在响应请求完成后会执行 afterCompletion 方法,该方法无论执行 成功 或者 失败 都会执行。
CustomerInterceptor.class 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CustomerInterceptor implements HandlerInterceptor {
/**
* 先执行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* 上面结果为 true 时执行
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 最后都会执行
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
-
配置拦截器:实现 WebMvcConfigurer 接口;重写 addInterceptors 方法,添加编写的拦截器。
1
2
3
4
5
6
7
public class WebMvcConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customerInterceptor());
}
}
拦截器只能拦截 controller 相关请求,不能拦截 jsp 静态资源文件;
拦截器可以中断请求轨迹;
请求之前如果该请求配置了拦截器,请求会先经过拦截器,放行之后执行请求的 controller,controller 执行完成后会回到拦截器继续执行拦截器代码。
如果配置了多个拦截器,默认执行的顺序和栈结构是一样的;但是也可以通过order()
方法修改,里面填 int 类型的数字,数字大的优先执行。
2. 过滤器
关于过滤器,它和拦截器比较像,编写方式也非常简单,实现 Filter 接口即可。但是要清楚:过滤器是 Java 提供的,拦截器是 Spring 提供的。下面的内容引用 Spring 的原文:
HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
汉译一下:HandlerInterceptor基本上类似于Servlet Filter,但与后者不同的是,它只允许自定义预处理(带有禁止执行处理程序本身的选项)和自定义后处理。过滤器更强大,例如,它们允许交换沿链传递的请求和响应对象。注意,过滤器是在web.xml中配置的,HandlerInterceptor是在应用程序上下文中配置的。
十五、Spring的事务管理
Spring 管理事物的方式:
- 编程式事务,在代码中硬编码。(不推荐使用)
- 声明式事务,在配置文件中配置(推荐使用)
- 基于XML的声明式事务
- 基于注解的声明式事务
1. 事务并发产生的问题
1.1 脏读
一个事务读取了另外一个事务没有提交的数据。
解决方案:@Transactional(isolation=Isolation.READ_COMMITTED)
1.2 不可重复读
在同一个事务中,多次查询同一条数据,但是读取的结果不同。
解决方案:@Transactional(isolation=Isolation.REPEATABLE_READ)
本质:一把行锁
1.3 幻影读
在一个事务中,对整表数据查询,显示的结果不一样。注意:是整表。会在本事务中出现数据不一致。
解决方案:@Transactional(isolation=Isolation.SERIALIZABLE)
本质:表级锁。
隔离属性在实战中的建议:推荐使用 Spring 指定的 ISOLATION_DEFAULT
- MySQL repeatable_read
- Oracle read_commited
在真正的项目中,并发访问情况是很低的。如果真的遇到并发问题,那么可以使用乐观锁解决,基于系统层面的,不会对系统的性能有太大的影响。
比如:Hibernate(JPA) Version、MyBatis 通过拦截器自定义开发。
2. 传播属性
概念:它描述了事务解决嵌套问题的特征。
什么是事务的嵌套:它指的是一个大的事务中,包含了若干个小的事务。
问题:大事务中融入了很多小的事务,它们彼此影响,最终就会导致外部大的事务失败,丧失了事务的原子性。
属性名称 | 外部不存在事务 | 外部存在事务 | 用法 | 备注 |
---|---|---|---|---|
REQUIRED | 开启事务 | 融合到外部事务中 | @Transactional(Propagation.REQUIRED) | 增删改 |
SUPPORTS | 不开启事务 | 融合到外部事务中 | @Transactional(Propagation.SUPPORTS) | 查询 |
REQUIRES_NEW | 开启新的事务 | 挂起外部事务,开启新的事务 | @Transactional(propagation.REQUIRES_NEW) | 日志记录 |
NOT_SUPPORTED | 不开启事务 | 挂起外部事务 | @Transactional(Propagation.NOT_SUPPORTED) | 极其不常用 |
NEVER | 不开启事务 | 抛出异常 | @Transactional(Propagation.NEVER) | 极其不常用 |
MANDATORY | 抛出异常 | 融合到外部事务 | @Transactional(Propagation.MANDATORY) | 极其不常用 |
2.1 只读属性(readOnly)
默认为 false,不开启。针对只查询的操作,可以设为 true 开启可以提高系统的运行效率。
2.2 超时属性(timeout)
timeout 以秒为单位,默认值为 -1,最终由对应的数据库来指定。
2.3 异常属性(rollbackFor)
Spring 的事务处理过程中,默认对于 RuntimeException 及其子类,采用的回滚的策略;
对于 Exception 及其子类,采用的是提交的策略。
手动指定:
rollbackFor = Exception.class
noRollbackFor = RuntimeException.class
建议在实战中使用 RuntimeException 及其子类,使用事务异常属性的默认值。
2.4 事务属性常见配置总结
- 隔离属性 默认值
- 传播属性 Required(默认值) 增删改 Supports 查询操作
- 只读属性 readOnly false 增删改 true 查询操作
- 超时属性 默认值 -1
- 异常属性 默认值
增删改操作 @Transaction
查询操作 @Transaction(propagation=Propagation.SUPPORTS, readOnly=true)
十六、Spring MVC
1. 为什么要整合MVC框架
- MVC 框架提供了控制器调用 Service 请求响应的处理,
- 接收请求参数 request.getParameter()
- 控制程序的运行流程
- 视图解析(jsp JSON Freemarker Thyemeleaf)
2. Spring 可以整合那些 MVC 框架
- struts 1
- webwork
- jsf
- struts 2
- springMVC
3. Spring整合MVC框架的核心思路
3.1 准备工厂
-
Web 开发过程中如何创建工厂
ApplicationContext ctx = new new WebXmlApplicationContext("/applicationContext.xml");
-
如何保证工厂唯一同时被共用
工厂存储在 ServletContext 这个作用域中 ServletContext.setAttribute(“xxxx”, ctx);
唯一:
WebXmlApplicationContext
@Configuration的使用:
使用这个注解可以让 Spring 工厂创建 bean,可以不用在 applicationContext.xml 中编写 bean 标签。
创建工厂的代码改变了,需要使用 AnnotationConfigApplicationContext。指定配置 bean 的 Class。
ApplicationContext ctx = new AnnotationConfigApplicationContext(xxxConfig.class);
指定配置 bean 所在的路径。
ApplicationContext ctx = new AnnotationConfigApplicationContext("com.config");
4. Spring工厂创建对象的优先级
bean 标签 > @Bean > @Component 及其衍生注解。
- Spring 读取Properties文件:
PropertiesPlaceholderConfigurer - 读取yml文件转换为Proterties:YamlPropertiesFactoryBean#setResources()YamlPropertiesFactoryBean#getObject()
Spring与YML集成依赖(最低所需版本:1.18)
1 | <dependency> |