Spring(1)
Spring框架学习
Spring框架概述
之前学习了struts2、Hibernate两大框架,知道了:
struts2是基于MVC模式的表现层框架,主要是作为控制层组件,还可影响Javabean、JSP。
Hibernate是基于ORM规则的持久层框架,简化了JDBC操作,使得用户可以采用面向对象的思想操作数据库数据。
下面来到Spring,Spring是什么呢(WHAT)?为什么要使用Spring(WHY)?具体要怎么操作呢(HOW)?
Spring提供了一个全面的、一站式的JavaEE解决方案,主要包括以下功能:
- 基于依赖注入(控制反转IoC)的核心机制
- 声明式的面向切面编程(AOP)支持
- 与多种技术整合
- 优秀的Web MVC框架
Spring是企业应用开发的一站式选择,贯穿了表达层、业务层、持久层,而且并不会取代原有的框架,而是以高度的可定制性与之无缝结合。
Spring具有以下优点:
1. 低侵入式的设计,无代码污染。
2. 独立与各种应用服务器。
3. IoC容器降低了业务对象替换的复杂性,降低了组件之间的耦合度。
4. AOP容器允许将一些通用任务如安全、事务、日志等进行集中式处理。
5. Spring中的ORM和Dao支持提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
6. Spring的高度开放,并不会强制要求开发者完全依赖于Spring,可自由选择使用部分功能。
Spring分为六个模块,常用的有5个。
- Core
- AOP
- DAO(Jdbc、包含事务)
- ORM
- Web
环境搭建
1) 引入jar文件 (3.2版本) 【spring-core】
commons-logging-1.1.3.jar 【单独下载】
spring-beans-3.2.5.RELEASE.jar 【spring源码, bean节点管理】
spring-context-3.2.5.RELEASE.jar 【spring上下文类】
spring-core-3.2.5.RELEASE.jar 【IOC容器】
spring-expression-3.2.5.RELEASE.jar 【spring表达式】
注意:
使用的版本Spring3.2
在这个版本中,只有spring自身的核心功能,spring依赖的其他组件,需要单独下载。 例如:日志jar文件,就需要单独下载。
3.0版本之前包含组件自身和依赖的组件包、
3.0版本之后,依赖的组件包需要单独下载。
2) 新建applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
3) 配置
<!-- 创建Dao实例 -->
<bean id="userDao" class="cn.ustb.dao.UserDao"></bean>
<!-- 创建Service实例 -->
<bean id="userService" class="cn.ustb.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 创建Action实例 -->
<bean id="userAction" class="cn.ustb.action.UserAction">
<property name="userService" ref="userService"></property>
</bean>
4) 测试
- 创建容器对象(加载applicationContext.xml配置文件)
new ClassPathXMLApplication("applicationContext.xml");
- 获取对象
getBean("name or id");
// 从IOC容器获取对象
@Test
public void testApp2() throws Exception {
// 容器对象(加载applicationContext.xml配置文件)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取对象
UserAction userAction = (UserAction) ac.getBean("userAction");
userAction.execute();
}
配置详解
<!-- 把对象加入IOC容器 -->
<!--
细节1:
id 与 name:
id 不能以数字开头,不能含有特殊符号, 不能有空格、逗号等; id 不能重复!
name 可以以数字开头,可以有特殊符合,如果name值重复,编译没有问题但运行报错!
<bean id="user" name="user2,user3 user4" class="cn.ustb.a_config.User"></bean>
<bean name="user5" class="cn.ustb.a_config.User"></bean>
<bean id="user6" class="cn.ustb.a_config.User"></bean>
<bean id="user6" class="cn.ustb.a_config.User"></bean>
-->
<!-- 细节2: (单例/多例) -->
<!--
scope="singleton" 默认表示单例!
prototype 多例
init-method="" 在创建完对象之后执行初始化方法
destroy-method="" 在调用容器类的destroy()方法时候,对单例的对象有效!
lazy-init="true" 延迟初始化 / 这个属性的设置只对单例有影响,对多例没有任何影响!
单例的对象默认是在创建容器的时候就创建所有单例的对象,如果希望在第一次访问的时候创建单例的对象,就设置延迟初始化
Bean生命周期:
bean在xml中配置,
singleton 单例
1) 创建对象
如果有配置延迟初始化,
lazy-init=true 如果单例的对象有配置延迟初始化, 在创建容器之后,在第一次从容器获取对象的时候
创建单例的对象!
如果没有配置或延迟初始化为默认值, 单例的对象会在创建容器的时候创建对象
2) 执行初始化方法 , init-method配置的方法会执行
3) 调用容器destroy() 方法时候,容器在销毁单例对象的实例的时候,会调用destroy-method对应的方法
此时bean对象会被销毁!
prototype 多例
1) 每次在从容器获取对象的时候,都会创建新的对象
2) 每次创建完对象后,就执行初始化方法
3) java会回收不用资源(jvm gc)
-->
<bean id="user"
class="cn.ustb.a_config.User"
init-method="init"
destroy-method="destroy_"
lazy-init="false"
scope="prototype">
</bean>
创建对象的几种方式
创建对象的几种方式:
1) 调用无参数构造器
2) 调用有参数构造器
3) 工厂方法
* 静态方法
<bean id=”’ class=”” factory-method=””/>
* 非静态方法
<bean id=”factory” class=”..”>
<bean id=”” factory-bean=” factory” factory-method=”实例方法” />
4) 反射
<!--IOC容器, 创建对象 -->
<!-- 1) 调用无参数构造器
<bean id="user1" class="cn.ustb.b_create_obj.User"></bean> -->
<!-- 2) 调用有参数构造器, 创建对象 -->
<!-- String str = new String("Jack"); -->
<bean id="str" class="java.lang.String">
<constructor-arg value="Jack"></constructor-arg>
</bean>
<!-- 创建user对象,且赋值 -->
<!--
value 当直接给属性值的时候使用value赋值
ref 当引用的是IOC容器中的对象的时候,使用ref
-->
<bean id="user" class="cn.ustb.b_create_obj.User">
<constructor-arg index="0" type="int" value="10000"></constructor-arg>
<constructor-arg index="1" type="String" ref="str"></constructor-arg>
</bean>
<!--
3) 工厂创建对象
* 静态方法
* 非静态方法
-->
<!-- * 工厂静态方法,创建对象 -->
<!--
class 指定工厂的类型;
factory-method: 工厂类的静态方法
-->
<bean id="user1" class="cn.ustb.b_create_obj.UserFactory" factory-method="getStaticInstace"></bean>
<!-- * 非静态方法创建对象 -->
<!-- 先创建工厂实例 -->
<bean id="factory" class="cn.ustb.b_create_obj.UserFactory"></bean>
<bean id="user" factory-bean="factory" factory-method="getInstace"></bean>
依赖注入(配置方式)
给对象属性赋值(Dependency Injection, 依赖注入),几种方式:
1) 构造函数赋值
<bean id="user1" class="cn.ustb.a_ioc.User" scope="prototype">
<constructor-arg index="0" value="jake"></constructor-arg>
<constructor-arg index="1" value="21221"></constructor-arg>
</bean>
2) set 方法注入值
普通字段赋值
+
集合属性 (list/map/property)
<!--IOC容器, 给对象属性赋值 -->
<bean id="user" class="cn.ustb.c_di.User">
<property name="id" value="1000"></property>
<property name="name" value="Jacky"></property>
<!-- list 集合赋值 -->
<property name="list">
<list>
<value>cn</value>
<value>usa</value>
</list>
</property>
<!-- map 集合赋值 -->
<property name="map">
<map>
<entry key="cn" value="China"></entry>
<entry key="usa" value="1234"></entry>
</map>
</property>
<!-- Properties 对象赋值 -->
<property name="prop">
<props>
<prop key="cn">China</prop>
<prop key="usa">America</prop>
</props>
</property>
</bean>
3) 案例
Dao/service/action实例,处理依赖关系
1、常用的通过set方法注入
+
2、内部bean
+
3、p 名称空间增加约束:xmlns:p="http://www.springframework.org/schema/p"
<!-- 配置: 需要创建的对象、对象依赖关系处理 -->
<!-- 方式1:通过set方法注入,这种方式最常用! -->
<bean id="userdao" class="cn.ustb.d_di2.UserDao"></bean>
<bean id="userService" class="cn.ustb.d_di2.UserService">
<property name="userDao" ref="userdao"></property>
</bean>
<bean id="userAction" class="cn.ustb.d_di2.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
<!-- 方式2: 内部bean -->
<!-- 总结: 当创建的对象,不被容器其他地方引用的时候,可以这样写! 这样写不通用,内部对象只能用一次 -->
<!-- 这里,action如果是单例,内部bean默认就是单例; action如果是多例,内部bean就是多例 -->
<bean id="userAction2" class="cn.ustb.d_di2.UserAction" scope="prototype">
<property name="userService">
<bean class="cn.ustb.d_di2.UserService">
<property name="userDao">
<bean class="cn.ustb.d_di2.UserDao"></bean>
</property>
</bean>
</property>
</bean>
<!-- 方式3: p名称空间,给属性注入值
需要增加约束:xmlns:p="http://www.springframework.org/schema/p"
-->
<bean id="userDao" class="cn.ustb.d_di2.UserDao"></bean>
<bean id="userService" class="cn.ustb.d_di2.UserService" p:userDao-ref="userDao"></bean>
<bean id="userAction" class="cn.ustb.d_di2.UserAction" p:userService-ref="userService"></bean>
自动装配
<!-- 方式4:自动装配 (了解) -->
<!--
a. default-autowire="byType" 配置到全局
当前所有的bean都采用”根据类型自动装配“
b. 配置到bean节点
autowire="byName" 根据名称自动装配, 会去容器找指定名称的对象,注入到set方法的参数中!
autowire="byType" 根据类型自动装配, 要确保改类型对应的对象在IOC容器中唯一,否则报错!
总结:
简化配置,但是维护麻烦!
-->
<bean id="userDao" class="cn.ustb.e_autowire.UserDao" autowire="default"></bean>
<bean id="userService" class="cn.ustb.e_autowire.UserService"></bean>
<bean id="userAction" class="cn.ustb.e_autowire.UserAction"></bean>
依赖注入(注解方式)
注解汇总:
Class注解:
@Component 表示一个组件(类),把当前组件加入ioc容器,加入容器的组件的名称默认是类名第一个字母小写
@Component(“”) 指定加入ioc容器的组件类的类名
@Repository 标识是一个持久层的组件【DAO】
@Service 标识是一个业务逻辑层的组件【Service】
@Controller 标识是一个控制层的组件【Action】
@Scope(“prototype”) 指定对象单例/多例【注解Class】
字段注解:
@Resource
1. 现根据字段名称(先id 再 name)查找。
2. 其次根据类型(Class)查找,如果该类型在ioc容器中有多个对象,报错。
3. 根据类型也没有找到对象,报错。
@Resource(name =“”) 会根据指定的名称去容器找对象自动注入
需要注意的一点细节:
- Component+Resource足够了,只不过为了详细区分,将Component分出了持久层Repository、逻辑层Service、控制层Controller
- Action因为是多例的,需要在Class上方注解
@Scope(“prototype”)
- Resource需要注解在setter的上方。
- 最最最重要的,需要在
bean.xml
中配置开启注解扫描,即
<context:component-scan base-package="cn.ustb.b_anno"></context:component-scan>
- Resource无参的注解,现根据字段名查找,再根据类查找。
注解的示例
bean.xml:
<context:component-scan base-package="cn.ustb.b_anno"></context:component-scan>
Java:
@Component//可以带参@Component(name="userAction"),无参则默认类名首字母小写为组件名
@Scope("prototype")//声明多例,默认单例
public class UserAction {
private UserService userService;
public UserService getUserService() {
return userService;
}
/*
* Resurce要注解在setter方法之上
*/
//@Resource(name="userService") //带参版
@Resource //先根据userService的字段名在容器中查找,再根据class类型查找(若同一类型存在多个实例抛出异常),再找不到也抛出异常
public void setUserService(UserService userService) {
this.userService = userService;
}
public void save(){
System.out.println("userAction save");
userService.save();
}
}
配置方式和注解方式的对比:
配置:便于维护,但是配置较多较繁琐。
注解:开发方便,几个字母就能搞定,简化配置。但是不利于后期维护,若修改对象创建关系需要修改源代码。
Spring与Struts2的整合
Spring与Struts2整合的关键点:将Action的创建交给Spring的IOC容器。因此需要引入Spring-Web的包。
struts2-spring-plugin-2.3.4.1.jar 【struts源码】 添加了struts2对spring的支持
spring-web-3.2.5.RELEASE.jar 【spring源码】添加了spring对struts的支持
整合的完整步骤:
1、引入jar包
- Struts核心jar
- Spring
- Core 核心 (5个)
- Web 对web支持 (2个)
2、配置文件
- bean.xml 【spring的配置文件,基本没什么变化。】
- struts.xml【struts的配置文件,Class不需要写全类名,和bean的id保持一致,action实例可以从ioc容器获取。】
- web.xml【配置spring的核心过滤器(监听器),启动时创建IOC容器】
- struts2核心过滤器
- spring配置文件读取
- spring监听器,启动时候,配置加载springIOC容器
web.xml配置:
<!-- 配置struts核心过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配合SPring的核心过滤器,在启动服务器时加载spring配置,创建ioc容器 -->
<!-- 配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bean-USER.xml</param-value>
</context-param>
<!-- 配置启动监听器,该类在spring-web包中 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
将action的创建交给IOC容器。因此struts.xml中的class
属性值为对应action在IOC中的id,而不再是类名。
需要注意的是,如果用到了注解,需要在bean.xml
开启注解扫描。
3、编写POJO、Action类。
需要再次强调的是,如果需要用到属性注入,在Action中的对象必须要new一下,除非在Spring中已经生成了该bean。
整合中出现过的问题:
1、action中的model注入部分为null
解决:model的不仅要设置setter方法,还要设置getter。并且要在Action中new一下。
2、action实例不了的原因,出现了nullpointer
原因:用到了注解,却忘记了在bean.xml中添加注解扫描。