More Related Content Similar to Spring入门纲要 (20) Spring入门纲要1. 我的 je 博客: http://86asm.javaeye.com/
一、理论知识
1.依赖注入、控制反转
依赖注入:在运行期,由外部容器动态地将依赖对象注入到组件中
控制反转:应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部窗口
负责得。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓的反转。
2.spring 的主要特性。
( 1 ) 降 低 组 件 之 间 的 耦 合 度 , 实 现 软 件 各 层 之 间 的 解 耦 。
(2)可以使用容器提供的众多服务,如:事务管理服务、 消息服务、 服务、JMS 持久化服务
等 等 。
(3)容器提供单例模式支持,开发人员不再需要自己编写实现代码。
( 4 ) 容 器 提 供 了 AOP 技 术 , 利 用 它 很 容 易 实 现 如 权 限 拦 截 , 运 行 期 监 控 等 功 能 。
(5)容器提供的众多辅作类,使用这些类能够加快应用的开发,如:
JdbcTemplate 、 HibernateTemplate.
(6)对主流的应用框架提供了集成支持。
3.常用技术
控制反转/依赖注入---面向切入编程---与主流框架的整合、管理---
二、基本实例
1.准备搭建环境
distspring.jar
libjakata-commonscommons-loggin.jar
如果使用了切面编程,还需下列 jar 文件:
libaspectjaspectjweaver.jar 和 aspectjrt.jar
libcglibcglib-nodep-2.1.3.jar
如果使用了 jsr-250 中的注解,还需要下列 jar 文件:
libj2eecommon-annotations.jar
2.搭建并测试环境
建立名为spring_01_base项目,根据需求导入jar包。建立一个Junit测试单元
SpringEnvTest,测试代码如下:
@Test
public void testEnv() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
}
beans.xml 配 置 文 件 在 此 省 略 ( 见 下 ) 。 运 行 此 测 试 如 无 错 , 则 说 明 环 境 搭 建 成 功 。
说明: beans.xml 可以在类路径下进行配置,也可以在具体的目录下配置。 可以是一个配置文
件,也可以是多个配置文件组成 String 数组传入。
3.实例
作如下准备工作:(1)建立UseDao接口,代码如下:
package com.asm.dao;
public interface UserDao {
void save();
}
1
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
2. 我的 je 博客: http://86asm.javaeye.com/
(2)建立UserDao接口的实现类,UserDaoImpl
package com.asm.dao.impl;
import com.asm.dao.UserDao;
public class UserDaoImpl implements UserDao{
public void save() {
System.out.println("执行save方法...");
}
}
(3)在src目录下配置此beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="userDaoImpl" class="com.asm.dao.impl.UserDaoImpl" />
</beans>
说 明 : bean 代 表 一 个 实 质 的 java 类 , 通 过 它 的 id 可 以 获 取 一 个 此 类 的 一 个 对 象 。
补充:让 xml 配置文件在编译时提示
[windows][preferences][myeclipse][files and editors][xml][xml catalog] 点 add,
在出现窗口的 location 中选“file system”,然后在 spring 解压目录的 dist/resources
目录中选择“spring-beans-2.5.xsd”,并将 key Type 值改为“Schema Location”,key
值 为 : http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
(4)Junit 测试单元 SpringEnvTest 中增加如下代码测试:
@Test
public void base() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = (UserDao) ctx.getBean("userDaoImpl");
userDao.save();
}
以上的代码就是通过配置文件 beans.xml 获取所需要的实例对象。
4.三种 bean 的生成方式
除 了 上 面 使 用 的 类 直 接 生 成 方 式 , 还 有 bean 静 态 工 厂 及 bean 实 例 工 厂 。
bean 静 态 工 厂 的 配 置 如 下 :
<bean id="userDaoImpl2" class="com.asm.dao.impl.UserDaoImplFactory"
factory-method="getUserDaoImpl"/>
相应的工厂类代码如下:
package com.asm.dao.impl;
public class UserDaoImplFactory {
public static UserDaoImpl getUserDaoImpl(){
return new UserDaoImpl();
}
}
2
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
3. 我的 je 博客: http://86asm.javaeye.com/
bean实例工厂的配置如下:
<bean id="factory" class="com.asm.dao.impl.UserDaoImplFactory2"/>
<bean id="userDaoImpl3" factory-bean="factory" factory-
method="getUserDaoImpl"/>
相应的工厂类的代码如下:
package com.asm.dao.impl;
public class UserDaoImplFactory2 {
public UserDaoImpl getUserDaoImpl() {
return new UserDaoImpl();
}
}
5.bean 的作用域
singleton:返回 bean 的同一个实例,也是默认的作用域(无状态 bean 使用此作用域)
prototype: 每 次 请 求 都 会 创 建 一 个 实 例 ( 有 状 态 bean 使 用 此 作 用 域 )
request、session、global session 这三个作用域主要用在 web 应用中
6.bean 的生命周期
(1)什么时候初始化 bean 实例
当 scope=singleton,即默认情况,会在装载配置文件时实例化。 如果希望在调用 getBean
时才初始化,可以使用 lazy-init="true" 补充:如果希望希望该配置文件中的所有 bean
都延迟初始化,则应在 beans 根结点中使用 lazy-init="true"。
当 scope=prototype 时 , 在 调 用 getBean() 方 法 时 才 会 初 始 化 。
(2)生命周期:
构造器、 init 方法、 获取 bean 后的操作、 destroy 方法(ctx.close、注意如果 bean 的 scope
设为 prototype 时,当 ctx.close 时,destroy 方法不会被调用)
7.属性注入 Setter 方式
(1)简单属性(如String):
<bean id="userServiceBean" class="com.asm.service.UserServiceBean">
<property name="id" value="10"></property>
<property name="username" value="张某某"></property>
</bean>
(2)对象属性-外部 bean 注入:在上面的<bean>中增加如下配置:
<property name="userDao" ref="userDaoImpl"/>
对象属性-内部bean注入:在上面的<bean>中增加如下配置:
<property name="userDao">
<bean id="temp" class="com.asm.dao.impl.UserDaoImpl"/>
</property>
(3)集合属性注入:
<property name="list">
<list>
<value>List值一</value>
<value>List值二</value>
<value>List值三</value>
</list>
3
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
4. 我的 je 博客: http://86asm.javaeye.com/
</property>
<property name="set">
<set>
<value>Set值二</value>
<value>Set值一</value>
<value>Set值三</value>
</set>
</property>
<property name="map">
<map>
<entry key="one" value="一" />
<entry key="two" value="二" />
<entry key="three" value="三" />
</map>
</property>
<property name="pro">
<props>
<prop key="p1">first</prop>
<prop key="p2">second</prop>
<prop key="p3">third</prop>
</props>
</property>
注意:在相应的字段上一定要有 setter 方法,才能注入。
补充:使用继承。在beans.xml中的配置如下:
<bean abstract="true" id="parent" >
<property name="username" value="张某某"></property>
</bean>
<bean id="XXX" class="com.asm.vo.User" parent="parent">
<property name="password" value="123456"></property>
</bean>
相当于在 XXX bean 实例中也有 username 属性设置。
8.属性注入构造器方式
<bean id="userServiceBean2" class="com.asm.service.UserServiceBean">
<constructor-arg index="0" value="李某某"/>
<constructor-arg index="1" ref="userDaoImpl" />
<constructor-arg index="2">
<list>
<value>List值一</value>
<value>List值二</value>
<value>List值三</value>
4
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
5. 我的 je 博客: http://86asm.javaeye.com/
</list>
</constructor-arg>
</bean>
UserServiceBean对应的构造方法代码如下:
public UserServiceBean(String username, UserDao userDao, Set<String>
set) {
this.username=username;
this.userDao=userDao;
this.set=set;
}
注意:此方法会覆盖掉默认的构造方法,导致要依赖默认构造方法的配置不可用,因此我们还
应为此类提供一个默认的构造器。
三、使用注解方式注入
1.准备
注解方式的注入主要针对对象属性的注入。
使用注解功能要引用注解包,另beans.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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-
context-2.5.xsd">
<context:annotation-config /><!-- 开启注解功能 -->
</beans>
2.Resource 注解实例
拷贝上一个项目为 spring_02_annotation 项目,修改 UserServiceBean 为如下形式:
package com.asm.service;
public class UserServiceBean {
@Resource(name = "userDaoImpl")
private UserDao userDao;
private UserDao userDao2;
@Resource
public void setUserDao2(UserDao userDao2) {
this.userDao2 = userDao2;
}
public void test() {
userDao.save();
userDao2.save();
}
5
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
6. 我的 je 博客: http://86asm.javaeye.com/
}
然后在 bean.xml 中的配置如下:
<bean id="userDaoImpl" class="com.asm.dao.impl.UserDaoImpl" />
<bean id="userServiceBean" class="com.asm.service.UserServiceBean" />
简要说明:Resouce注解可以在字段上标记,也可以在对应的setter方法上标记。 此注解可
以不使用name属性,它会自动去查找匹配的类型(先以字段名称为name的值查找,如找不
到会再根据依赖对象的类型查找)。但只要使用了name属性,就应确保name的值在xml中有
相应的bean Id对应。它是属于java本身的注解,Resource默认按属性名称装配
3.Autowired注解实例
@Autowired(required=false)
@Qualifier("userDaoImplXXX")
private UserDao userDao3;
说 明 :Autowired 默认是按照类型来查找对应的 bean 实例注入,如果想注入指定名称的
bean 实例,可以使用 Qualifier 注解来指定名字。Required 属性设为 true 时,如果不能成
功注入则会报告异常,如果为设为 false 而不能成功注入,则会将 userDao3 设为 null。同
样地,它也实用于 setter 方法。它属于 spring 特有的注解,Autowired 默认按类型装配。
4.自动装配
自动装配(了解,不建议使用):除了要设置字段的setter方法外,还应在beans.xml配置
文件中设置如下内容:
<bean id="userServiceBean2"
class="com.asm.service.UserServiceBean2" autowire="byType"/>
说明:除了byType外,autowire的可选属性如下:
byName:根据类中的字段名来查找对应的bean,如不能成功注入,则字段设为null.
byType:根据类型装配,如果发现多个类型都能够匹配,则抛出异常。
Consturctor:也byType相似,不同之处在于它应用于构造器的参数,如果容器中没有找到
与构造器参数类型一致的bean,则抛出异常。
Autodetect:通过bean类的自省机制来决定是使用consturctor还是byType方式进行自动装
配。如果发现默认的构造器,那么将使用byType方式。
四、自动扫描管理 bean
1.准备工作
在前面使用注解的时候,除了<beans>结点配置增加了名称空间说明,另还增加了
<context:annotation-config/>配置,它的作用就是注册一个处理器。 通常情况下,我
们要使用某个bean实例,总会配置<bean>相关内容。 Spring最新版本可以简化这一操作,即
是说我们只要在配置文件作如下设置:
<context:component-scan base-package="包名"/>便可以自动管理指定包名及子包
下标住了@service (业务层组件)、 @controller(控制层组件)、 @repository(数据访问
组件)或@component(泛指组件)的类,并把它们作一个实例bean,相当于在beans.xml中
配置了<bean>元素。 需要说明的是,使用了此配置同时意味着还注册了注解配置的处理器,
所以在这些类中用到了注解配置时并不需要再配置<context:annotation-config/>。
为什么提出自动扫描管理:在一些比较大的项目中,涉及到的bean实例会有很多,如果依
次对每个bean实例进行配置,不但配置内容繁琐,而且配置文件也会显得杂乱。 因此spring
提出了自动扫描bean,它依据在xml文件中指定的包名和类中标记的component系列注解。
2.实例
6
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
7. 我的 je 博客: http://86asm.javaeye.com/
建立spring_03_autoscan项目,内容基本和前面两个项目一样,只是在要纳入spring管理
的类前增加了component这样的注解。Beans.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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-
context-2.5.xsd">
<context:component-scan base-package="com.asm.service"/>
<context:component-scan base-package="com.asm.dao.impl"/>
</beans>
说明:以上的配置会自动管理 service 包和 impl 包及它们子包下的带有 component 标记的
类 , 上 面 两 行 配 置 代 码 等 价 于
<context:component-scan base-package="com.asm"/>
下面以 UserServiceBean 为例进行说明,代码如下:
package com.asm.service;
@Service("usb")@Scope("singleton")
public class UserServiceBean {
@Resource(name = "userDaoImpl")
private UserDao userDao;
private UserDao userDao2;
@Autowired(required = true)
@Qualifier("userDaoImpl")
private UserDao userDao3;
@Resource
public void setUserDao2(UserDao userDao2) {
this.userDao2 = userDao2;
}
public UserServiceBean() {
}
@PostConstruct
public void init() {
System.out.println("init method is called");
}
public void test() {
System.out.println("********************************");
userDao.save();
userDao2.save();
System.out.println(userDao3);
7
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
8. 我的 je 博客: http://86asm.javaeye.com/
// userDao3.save();
System.out.println("********************************");
}
}
说明:如果使用Service这些注解时不指定名称,这些实例bean的名称就是类名(但首字
母小写),也可以指定实例bean的名字,比如这里指定其名字为“usb”,scope注解配置了
bean的作用范围,PostConstruct注解指定了bean的init方法。关于其它的一些注解配置
参文档(3.11-3.12)。
它的junit测试代码如下:
public class AutoScanTest {
@Test
public void base() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
UserServiceBean udb = (UserServiceBean) ctx.getBean("usb");
udb.test();
}
}
小结使用自动扫描管理的核心:配置扫描的包、类前的component标记、了解常用注解。
五、AOP 技术
1.引出问题
建立spring_04_aop项目,在该项目下有一个UserDao接口,代码如下:
package com.asm.dao;
public interface UserDao {
void save();
void update();
}
该接口的实现类 UseDaoImp,代码如下:
package com.asm.dao.impl;
import com.asm.dao.UserDao;
public class UserDaoImp implements UserDao {
private String username;
public UserDaoImp() {
}
public UserDaoImp(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
@Override
public void save() {
8
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
9. 我的 je 博客: http://86asm.javaeye.com/
System.out.println("save method is called:" + username);
}
@Override
public void update() {
System.out.println("update method is called" + username);
}
}
需求如下:如果实现类的 username!=null,才可以调用 save 与 update 方法,为 null 则不
能调用。 当然要解决此问题,可以在 save 与 update 方法内部进行判断,但是如果在方法内
部进行判断,代码则失去了灵活性,如果以后的需求改变,比如变成 username.equals 时,
则又要在 update/save 方法中重新进行一次判断。 如果 save/update 这样的方法很多,这样
就会很麻烦。 其实要解决此问题,可以通过动态代理技术实现。 这里的需求其实就是根据要
求来拦截一些业务方法,这种编程问题称之为横切性关注点。
代理的目标对象必须实现了一个接口---横切关注点
2.动态代理实现[JDK]
建立代理工厂ProxyFactory,代码如下:
package com.asm.dao.impl.factory;
public class ProxyFactory implements InvocationHandler {
private Object target;
public Object createUserDaoImp(Object target) {
this.target = target;
return
Proxy.newProxyInstance(this.target.getClass().getClassLoader()
, this.target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
UserDaoImp udi = (UserDaoImp) target;
Object result = null;
if (udi.getUsername() != null) {
result = method.invoke(target, args);
}
return result;
}
}
简析动态代理:此类根据传递的 target 对象来生成此目标对象的代理对象,需要强调的是
动态代理技术所要代理的对象必须实现一个接口。newProxyInstance 参数说明:第一个
参数是目标对象的类装载器,第二个参数是目标对象的接口,第三个参数是回调对象,生
成的代理对象要执行方法时就是依靠这个回调对象的 invoke 方法来进行目标对象的方法回
调。关于动态代理的其它细节不在此讨论。
建立 junit 测试代码,内容如下:
public class AopProxyTest {
9
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
10. 我的 je 博客: http://86asm.javaeye.com/
@Test //用户名为空,不执行方法
public void testProxy(){
ProxyFactory pf=new ProxyFactory();
UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp());
ud.save();
}
@Test //用户名为不为空,才执行save方法
public void testProxy2(){
ProxyFactory pf=new ProxyFactory();
UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp("张某"));
ud.save();
}
}
3.cglib 实现代理
JDK的Proxy实现代理要求被代理的目标对象必须实现一个接口,而如果目标对象没有实现
接口则不能使用Proxy来代理。其实也可以借助cglib来实现代理。操作步骤如下
步骤一、建立UserDaoImp2类,它与UserDaoImp的唯一区别就是没有实现任何接口。
步骤二、导入cglib.jar包,创建cglib代理工厂,代码如下:
package com.asm.dao.impl.factory;
public class CglibFactory implements MethodInterceptor {
private Object target;
public Object createUserDaoImp2(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// cglib创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final
方法进行覆盖,但在覆盖方法时会添加一些拦截代码。
enhancer.setCallback(this); //注册回调器
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[]
args, MethodProxy methodProxy)
throws Throwable {
UserDaoImp2 udi = (UserDaoImp2) target;
Object result = null;
if (udi.getUsername() != null) {
// 前置通知
try {
result = methodProxy.invoke(target, args);
// 后置通知
} catch (Exception e) {
e.printStackTrace();
10
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
11. 我的 je 博客: http://86asm.javaeye.com/
// 例外通知
} finally {
// 最终通知
}
}
return result;
}
}
说明:注意注释的通知,通知就是拦截到方法后执行的一些代码,比如前置通知,就是说
在回调目标方法时执行的一些操作。
步骤三、建立测试代码(省略,和五.2 的测试代码相似)
4.aop 理论知识
横切性关注点:对哪些方法拦截,拦截后怎么处理,这些关注就称之为横切性关注点
切面(aspect):类是对物体特征的抽象,而切面是指对横切性关注点的抽象。
连接点(joinpoint):被拦截到的点,因为 spring 只支持方法类型的连接点,所以在
spring 中连 接点 指的 就是 被拦 截到 的方 法。 实际 上连 接点 还可 以是 字段 或构 造器 。
切 入 点 ( pointcut ) : 对 连 接 点 进 行 拦 截 的 定 义 。
通知(advice):所谓通知就是指拦截到连接点之后的要执行的代码。 通知分为前置、 后置、
异 常 、 最 终 。 环 绕 通 知 五 类 。
目 标 对 象 : 代 理 的 目 标 对 象 。
织 入 ( weave ) : 将 切 面 应 用 到 目 标 对 象 并 导 致 代 理 对 象 创 建 的 过 程
引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些
方法或字段。
5.基于 spring 的 AOP 实现
步 骤 一 、 导 入 spring 开 发 的 基 本 包 ( 包 括 切 面 及 注 解 包 )
步骤二、编写切面类 TheInterceptor,代码如下:
package com.asm.dao.impl.factory;
@Aspect
public class TheInterceptor {
@Pointcut("execution (* com.asm.dao.impl.UserDaoImp.*(..))")
// 声明一个切入点(第一个*后要留一个空格)
private void anyMethod() {
}
@Before("anyMethod()")// 前置通知
public void before() {
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")//后置通知
public void afterReturning(){
System.out.println("后置通知");
}
}
11
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
12. 我的 je 博客: http://86asm.javaeye.com/
简析:Aspect 注解声明此类为一个切面类,Pointcut 注解用来声明一个切入点,括号中的
参数是切入点的表达式,这里的表达式的意思是对 UserDaoImp 类的所有方法进行拦截。关
于切入点表达式后面会有详细的说明。anyMethod 是为切入点起一个名字,后面的“通知”
都 要 依 赖 这 个 名 字 。
步骤三、beans.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy /><!-- 开启切面编程功能 -->
<bean id="userDaoImp" class="com.asm.dao.impl.UserDaoImp" />
<bean id="theInterceptor"
class="com.asm.dao.impl.factory.TheInterceptor" />
</beans>
说明:要想切面类起作用,首先要把切面类纳入spring容器管理。
步骤四、编写junit测试单元
@Test
public void base() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
// UserDaoImp udii=(UserDaoImp) ctx.getBean("userDaoImp");
UserDao ud = (UserDao) ctx.getBean("userDaoImp");
System.out.println(ud.getClass().getName());
ud.save();
}
说明:由于开启了切面编程功能,所以当我们获取一个被切面类监控管理的bean对象—
UserDaoImp时,它实际上获取的是此对象的一个代理对象,而在spring中对代理对象的处
理有如下原则:(1)如果要代理的对象实现了接口,则会按照Proxy的方式来产生代理对象,
这即是说产生的代理对象只能是接口类型,比如起用上面注掉的代码就会报错,而且通过下面
的打印语句我们也可以看出产生的是一个代理对象。(2)要代理的对象未实现接口,则按
cglib方式来产生代理对象。 另还要注意:要想spring的切面技术起作用,被管理的bean对
象只能是通过spring容器获取的对象。比如这里如果直接new出UseDaoImp对象,则new出的
对象是不能被spring的切面类监控管理。
补充:测试被代理对象未实现接口时,spring切面技术的应用。
步骤一、修改切面类TheInterceptor切入点为如下内容:
@Pointcut("execution (* com.asm.dao.impl.*.*(..))")
12
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
13. 我的 je 博客: http://86asm.javaeye.com/
说明:拦截impl包下的所有类所有方法
步骤二、在beans.xml中增加如下内容:
<bean id="userDaoImp2" class="com.asm.dao.impl.UserDaoImp2" />
步骤三、junit测试代码如下:
public void base2() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
UserDaoImp2 udi2 = (UserDaoImp2) ctx.getBean("userDaoImp2");
System.out.println(udi2.getClass().getName());
System.out.println(udi2.getClass().getSuperclass().getName());
udi2.save();
}
说明:UseDaoImp2 未实现任何接口,因此在 spring 中利用切面技术来管理此类使用的动态
代理技术实质是 cglib 的动态代理方式,所以产生的代理对象实质是被代理对象的一个子类,
通 过 上
面 的 控 制 台 打 印 语 句 可 以 看 出 。
小结:(1)声明 aspect 的切面类要纳入 spring 容器管理才能起作用。 2)被管理的 bean
(
实例要通过容器的 getBeans 方法获取。 (3)依据被管理的 bean 是否实现接口,spring 采
取两种方式来产生代理对象。(4)在 xml 文件中启用<aop:aspectj-autoproxy/>
6.通知应用实例(基于注解)
在前一节,我们应用了前置通知和后置通知,除了这两个通知外,下面接着演示其它通知
的应用。
(1)最终通知
在切面类TheInterceptor中增加如下代码即可:略去测试。
@After("anyMethod()")// 最终通知
public void after() {
System.out.println("最终通知");
}
(2)异常通知
为了演示此实例,我们在 UseDaoImp 中增加如下代码以抛出异常:
int i=1/0;在然后在切面类TheInterceptor中增加如下代码:
@AfterThrowing("anyMethod()") // 例外通知
public void AfterThrowing() {
System.out.println("例外通知");
}
当获取代理对象并调用 save 方法时会抛出异常,例外通知便会得以执行。
(3)环绕通知
@Around("anyMethod()") //环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入环绕");
//if(){ // 进行一些判断,再执行环绕
Object result = pjp.proceed();
//}
System.out.println("退出环绕");
13
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
14. 我的 je 博客: http://86asm.javaeye.com/
return result;
}
注意的是方法的参数及抛出异常类型的固定写法(方法名可以是任意得),另在该方法中必须
执行 pjp.proceed()才能让环绕通知中的两处打印代码得以执行。即是说要想环绕通知的拦截
处理代码起作用必须调用 pjp.proceed 方法。 补充:环绕通知通常可以用来测试方法的执行
时间,在 pjp.proceed 前获取一个时间,在 pjp.proceed 方法后再获取一个时间。最后两个
时间相减即可得方法执行时间。
(4)传递参数给通知
首先在UseDao接口中增加如下代码:
String add(String name);
然后再在UserDaoImp中实现此方法,代码如下:
public String add(String name) {
System.out.println("add method is called [ " + name+" ]");
return "添加成功";
}
需求:获取调用add方法传递的参数。操作步骤如下:在切面类增加如下代码:
@Before("anyMethod() && args(name)")// 前置通知,只针对UseDaoImp的add方法
public void beforeAdd(String name) {
System.out.println("前置通知:" + name);
}
说明:在前置通知的方法中有一个参数,然后再把此参数作为拦截条件(即是说拦截带有一个
String类型参数的方法)。args的名字和beforeAdd方法参数名字相同。
测试代码:
public void advieeTest() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
UserDao ud=(UserDao) ctx.getBean("userDaoImp");
ud.add("xxx");
}
(5)获取方法的返回值
我们知道add方法有一个返回值,我们对此方法进行拦截并获取返回值,在切面类中增加如
下代码:
@AfterReturning(pointcut = "anyMethod()", returning = "result")
// 后置通知,监听返回结果,针对UserDaoImp的getUsername()方法
public void afterReturningRes(String result) {
System.out.println("后置通知,返回结果:" + result);
}
说明:afterReturningRes 方法的参数就是要返回的参数类型, returning 标记的就是的
结果,它的取值与该方法参数名相同。 测试代码同(4)。
(6)获取抛出的异常
切面类的增加如下代码:
@AfterThrowing(pointcut="anyMethod",throwing="e")
public void catchException(Exception e){
14
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
15. 我的 je 博客: http://86asm.javaeye.com/
System.out.println("获取抛出的异常:"+e);
}
throwing 的取值和方法的参数名相同,测试代码省略。
7. 通知应用实例(基于 XML)
步骤一、复制 TheInterceptorX 类为 TheInterceptorXML,并去掉所有注解。
步骤二、建立beansXML.xml配置文件,内容如下:
<aop:aspectj-autoproxy /><!-- 开启切面编程功能 -->
<bean id="userDaoImp" class="com.asm.dao.impl.UserDaoImp" />
<bean id="aspectBean"
class="com.asm.dao.impl.factory.TheInterceptorXML" />
<aop:config>
<aop:aspect id="asp" ref="aspectBean"> --声明一个切面类
<aop:pointcut id="thecut" --声明一个切入点
expression="execution(* com.asm.dao.impl.UserDaoImp.*(..))" />
<aop:after-returning pointcut-ref="thecut"
method="afterReturningRes" returning="result" />
<aop:around pointcut-ref="thecut" method="around"/>
<aop:after-throwing pointcut-ref="thecut"
method="catchException" throwing="e"/>
<aop:after pointcut-ref="thecut" method="after" />
<aop:before pointcut-ref="thecut" method="before" />
</aop:aspect>
</aop:config>
测试代码如下:
public void advieeTest() {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beansXML.xml");
UserDao ud=(UserDao) ctx.getBean("userDaoImp");
ud.add("xxx");
}
未解决问题:不能成功传参给前置通知。
8.解析切入点表达式
1. 格 式 : execution( 返 回 值 空 格 方 法 选 择 ) 。 两 部 分 组 成 , 中 间 一 定 要 有 空 格 。
返回值:可以是*,说明拦截任何方法。 Java.lang.String(全名),拦截返回值为 String 类
型的方法。 常用的实例如下:
方法选择:包名[类名].*()。设定要拦截的方法签名。
表达式(省略 execution) 说明
(java.lang.String 方法选择略) 拦截返回值为 String 类型的方法
(!void 方法选择略) 拦截返回值非空的方法
(* com.asm..*.*(..)) 拦截 com.asm 包及子包下每个类的全部方法
(* com.asm.*.*(..)) 拦截 com.asm 包下每个类的全部方法
(* com.asm.User.*(..)) 拦截 asm 包下 User 类的所有方法
(* com.asm.User.* 拦截 User 类中第一个参数为 String,后面参
15
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
16. 我的 je 博客: http://86asm.javaeye.com/
(java.lang.String,..)) 数任一的方法
待增加
待增加
9.总结
面向切面的常见应用(如权限拦截)、spring 的 aop 依赖两种方式实现代理(依被代理的
对象是否实现接口而定)、通知概念、基于注解与基于 XML 两种方式来配置切面、基本步骤
(依要拦截的方法来设定切入点,依据业务需求实现拦截通知代码,切面纳入 spring 容器
管理,要被监控的类只能是通过 Spring 容器获取)、切入点的格式。
六、与 JDBC 集成
1.搭建环境
建立 spring_05_integrationJdbc 项目 ,此项目使用 dbcp 作为数据源来整合 JDBC 技术。
因此除了 spring 所需的 jar 包,还应导入 dbcp 的 jar 包:commons-dbcp-1.2.2.jar 及此
jar 所依赖的两个 apache 开源 jar 包:commons-collections-3.1.jar、commons-pool.jar。
由于涉及到数据库操作,还应导入数据库驱动包,这里选择的是 mySQL 驱动。
2.基于注解的事务管理
步骤一、配置事务管理器。
建立beans.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-
context-2.5.xsd">
<bean id="theDatasource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/sjdbc" />
<property name="username" value="root" />
<property name="password" value="123456" />
<property name="initialSize" value="2" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="2" />
<property name="minIdle" value="1" />
16
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
17. 我的 je 博客: http://86asm.javaeye.com/
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManag
er">
<property name="dataSource" ref="theDatasource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
说明:首先是配置了一个数据源,以供数据源事务管理器配置引用。接着配置了数据源事务管
理器,随后开启了基于@Transactional注解的事务管理器,开启后,只要是被spring管理
的bean且打有@Transactional注解的bean都会受配置的事务管理。关于这里的配置可参看
spring文档9.5.6
步骤二、准备使用环境。
建立UserDao接口,代码如下:
package com.asm.dao;
public interface UserDao {
void save(User user);
void delete(User user);
void update(User user);
User get(int id);
List<User> getUsers();
}
建立UserDaoImp类实现UseDao接口,代码如下:
package com.asm.dao.impl;
@Transactional
public class UserDaoImp implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setDatasouce(DataSource datasource) {
jdbcTemplate = new JdbcTemplate(datasource);
}
public void delete(User user) {
jdbcTemplate.update("delete from user where id=?", new
Object[] { user.getId() },
new int[] { java.sql.Types.INTEGER });
}
public User get(int id) {
return (User) jdbcTemplate.queryForObject("select * from user
where id=?",
new Object[] { id }, new int[] { java.sql.Types.INTEGER
}, new RowMapper() {
public Object mapRow(ResultSet rs, int arg1) throws
17
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
18. 我的 je 博客: http://86asm.javaeye.com/
SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
return user;
}
});
}
@SuppressWarnings("unchecked")
public List<User> getUsers() {
return (List<User>) jdbcTemplate.query("select * from user",
new RowMapper() {
public Object mapRow(ResultSet rs, int arg1) throws
SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
return user;
}
});
}
public void save(User user) {
jdbcTemplate.update("insert into user(name) values(?)", new
Object[] { user.getName() },
new int[] { java.sql.Types.VARCHAR });
}
public void update(User user) {
jdbcTemplate.update("update user set name=? where id=?", new
Object[] { user.getName(),
user.getId() }, new int[] { java.sql.Types.VARCHAR,
java.sql.Types.INTEGER });
}
}
步骤三、把UserDaoImp纳入spring容器管理。
在beans.xml中增加对应的配置内容如下:
<bean id="userDaoImp" class="com.asm.dao.impl.UserDaoImp">
<property name="datasouce" ref="theDatasource" />
</bean>
结合配置文件解析UserDaoImp实现类:(1)此类作为一个bean实例纳入spring容器管理,
18
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
19. 我的 je 博客: http://86asm.javaeye.com/
使用setter注入方式完成对datasource的注入,实质是完成的JdbcTemplate对象的初始
化。(2)该类CRUD方法都使用了Spring容器提供的JdbcTemplate对象来简化了CRUD操作,
在spring文档的11.2.1.节对JdbcTemplate类作了较详细的介绍。(3)此类打上了
@Transactional注解,表示此类中的业务方法都会受beans.xml配置的
<tx:annotation-driven transaction-manager="txManager" />管理
步骤四、编写测试类
package com.asm.test;
public class TestSJ {
private UserDao ud = (UserDao) new
ClassPathXmlApplicationContext("beans.xml").getBean("userDaoImp");
public static void main(String[] args) {
TestSJ sj = new TestSJ();
sj.save();
sj.delete();
}
public void save() {
User user = new User();
user.setName("张某某");
ud.save(user);
}
public void delete() {
User user = new User();
user.setId(1);
ud.delete(user);
}
//其它测试方法省略...
}
说明:注意这里通过getBean获取的是UserDao接口对象,而非UserDao接口的实现类
UserDaoImp对象,因为spring的事务管理也是利用了aop技术,所以必须要面向接口,如果
想通过getBean获取它的实现类对象将会报错。
步骤五、感知事务管理。
在UserDaoImp的delete方法中增加如下代码:
jdbcTemplate.update("delete from user where id=?", new Object[]
{ user.getId() }, new int[] { java.sql.Types.INTEGER });
int i=5/0;
jdbcTemplate.update("delete from user where id=2");
spring 默认的事务管理方式:运行期异常进行事务回滚,非运行期异常不进行事务回滚。
因此增加上面的代码后,会出现 ArithmeticException 运行期异常。所以当出现此异常
时,delete 方法中的数据库操作都会进行回滚,因而 id=1 和 2 这两条记录都不会被删除。
如果把 UseDaoImp 类前标记@Transactional 的注解去掉,id=1 的记录会被删除,因为失
去了 spring 容器的事务管理。
小结 spring 事务:
19
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
20. 我的 je 博客: http://86asm.javaeye.com/
(1)在 beans.xml 中配置事务管理器,可以是依赖于数据源的事务管理器,也可以其它的
事务管理器(比如和 JPA 集成的事务管理器等),这些事务管理器类都继承自
AbstractPlatformTransactionManager 抽象类。(2)事务管理器配置后,要想让配置的事
务管理器对某些类的事务管理起作用,可以有两种方式配置:一种是声明式配置(两种:
基于注解或基于 XML),一种是编程是配置。(3)上面的一些操作,都是基于注解的声明
式事务配置:关键两点:开启基于事务的注解支持(<tx:annotation-driven
transaction-manager="txManager" />);被管理的类打上事务注解标记。 (4)除了
可以在类前声明事务标记,也可以在类的方法中具体声明详细的事务标记。(5)事务标记
具有多个可选属性,具体可参文档 9.5.6.1。
3.简析事务注解属性
@Transactional 注解具有大属性:
(1)传播属性 propagation:
可选属性 说明
REQUIRED 业务方法需要在一个事务中运行,如果方法运行时,已经处在一个事务中
那么加入到该事务,否则自己创建一个新的事务。
REQUIRESNEW 不管是否存在事务,业务方法总会为自己发起一个新的事务,如果方法已
经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方
法执行结束,新事务才算结束,原先的事务才恢复执行。
SUPPORTS 如果业务方法在某个事务范围内被调用,则业务方法成为该事务的一部分
如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行
NOT_SUPPORTE 声明业务方法不需要事务,如果方法没有关联到一个事务,容器不会为它
D 开启事务,如果方法在一个事务中被调用,则该事务会被挂起,在该方法
调用结束后,原先的事务恢复执行
NEVER 指定业务方法绝对不能在事务范围中执行,如果业务方法在某个事务中执
行,容器会抛出例外;只有业务方法没有关联到任何事务,才能正常执行。
MANDATORY 指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己
的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外
NESTED 如果一个活动的事务存在,它会自己产生一个单独的而且拥有多个可以回
滚的保存点的事务,然后嵌套运行在活动的事务中,实质它可以看成是一
个内部事务且具有回滚保存点,所以内部自身的事务回滚并不会引起外部
活动事务的回滚,它只是回滚到内部事务的保存点。 如果没有活动事务,
则 按 REQIIRED 属 性 执 行 。 需 要 注 意 的 是 此 配 置 只 对
DataSourceTransactionManager 事务管理器生效。
说明:上面多次提到业务方法,它实质就是 UserDaoImp 中 save 等这样的方法。 但是这些方
法 前 会 有 一 个 设 定 了 详 细 属 性 的 @Transactional 注 解 。 比 如 :
@Transactional(propagation="",isolation="",noRollbackFor="")
(2)隔离级别属性 isolation:这个是依赖数据库系统而言,数据库系统提供四种事务
隔 离级 别。 (具 体的 事务 隔离 级别 分析 在些 略过 ,可 以参 看网 络资 源及 相关 文档 )
(3)只读属性 readOnly:false--读写性、true--只读性事务.
(4)超时属性 timeout:int 型,以秒为单位。
(5)回滚属性:根据抛出的异常决定是否回滚事务,它包括四种可选属性。参文档 9.5.6.1
的表 9.3
4.扩展:抽取 dpcp 配置文件。
如果希望把dbcp的配置单独放到一个properties配置文件中去,可以使用如下做法。 操作步
20
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
21. 我的 je 博客: http://86asm.javaeye.com/
骤:在src的路径下建立dbcp.properties文件,内容如下:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/sjdbc
username=root
password=123456
initialSize=2
maxActive=100
maxIdle=2
minIdle=1
然后把此配置读取数据源bean中去,形式如下:
<bean id="theDatasource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
...省略部分属性的获取,形式如下:
<property name="xxx" value="${dbcp.properties配置文件中的键}" />
...
</bean>
注意的是使用${}来获取值,必须把 properties 文件引入到 spring 的容器管理,因此要想
成功通过${}来获取相应的值,还应 beans.xml 中增加如下内容:
<context:property-placeholder location="classpath:dbcp.properties" />
而要想使用<context:XXX/>还应在 beans.xml 增加名称空间:
xmlns:context="http://www.springframework.org/schema/context"
和 schema 信 息 : http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-
context-2.5.xsd
关于这里的名称空间及 schema 信息可参 A2.8 节。
5.基于 XML 的事务管理
步骤一、拷贝 UserDaoImp 为 UserDaoImp2,然后把内面所有关于事务的注解去掉
步骤二、建立beansXML.xml文件,并配置事务管理器(和六.2.步骤一完全相同)。
步骤三、配置事务
<aop:config>
<aop:pointcut id="txPointcut"
expression="execution(* com.asm.dao.impl.UserDaoImp2.*(..)) " />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /
>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
-->拦截get开头的方法,并设置只
读属性
<tx:method name="*" /> -->拦截所有方法
21
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
22. 我的 je 博客: http://86asm.javaeye.com/
</tx:attributes>
</tx:advice>
解析:根据 txManager 事务管理器来配置一个事务通知器:txAdvice。而<tx:method>中的
name 属性指定方法名(可以使用通配符),其它属性就好比注解的传播属性配置(可参 六.
3(1))。然后再使用切面配置一个切入点,再对这个切入点引入事务通知器。
步骤四、把 UserDaoImp2 纳入 spring 容器管理。即在 beansXML.xml 做如下配置:
<bean id="userDaoImp2" class="com.asm.dao.impl.UserDaoImp2">
<property name="datasouce" ref="theDatasource" />
</bean>
步骤五、编写写测试类,测试事务管理器是否起作用,省略...
5.总结与 JDBC 的集成
(1)首先是配置好一个事务管理器 (2)基于注解:一是涉及到事务的类打上事务注解标
记,二是在 xml 配置文件中开启事务注解功能;基于配置:事务通知器加进 apo 配置中。
(3)涉及到事务的类必须纳入 spring 容器管理,事务才能起作用。(4)强调:不论是基
于注解还是基于 xml 配置实现事务,都要依赖于动态代理技术。以 UserDaoImp 说明,它实
现了接口,所以它的代理对象实质是一个接口对象,因而通过 getBean 获取的 UseDaoImp
实质上是一个接口对象,所以特别要注意类型转换(参六.2.步骤四.说明)。(5)为了简
化 CRUD 操作,我们通常会使用 spring 提供的 JdbcTemplate 类。
七、SSH 集成实例
1.分析实例及准备环境
建立 spring_06_SSH 项目,此项目主要演示 struts1.x、 hibernate、spring 的集成。 集成前
的一个比较关键的因素是搭建好环境,这一步要求我们对集成的框架所用到的 jar 包有比
较 清 楚 的 认 识 。 下 面 列 表 展 示 了 三 个 框 架 所 用 到 的 jar 包 。
hibernate3.3 所用到的 jar 包:
antlr-2.7.6.jar 开源语法分析生成器(librequired)
commons-collections-3.1.jar Commons 集合类库,与连接池有关(librequired)
dom4j-1.6.1.jar xml 解析类库(librequired)
javassist-3.9.0.GA.jar 分析,编辑和创建 java 字节码类库(librequired)
jta-1.1.jar 事务处理 api (librequired)
slf4j-api-1.5.8.jar 日志处理 (librequired)-->用 log4j 实现
hibernate3.jar 核 心
ehcache-1.2.3.jar 二级缓存(liboptionalehcache)
Spring2.5 安 装 包 所 用 的 jar 包 :
distspring.jar
libaspectjaspectjweaver.jar、aspectjrt.jar
libcglibcgligb-nodep-2.1_3.jar
libj2eecommon-annotations.jar
libjakarta-commonscommons-logging.jar 、commons-dbcp.jar、commons-pool.jar
distmodulesspring-webmvc-struts.jar
liblog4jlog4j-1.2.15.jar 供 srping 与 hibernate 使用的日志记录 jar 包
libslf4j slf4j-log4j12-1.5.0.jar 日 志 转 换 jar 包 , 实 现 把 log4j.jar 包 适 配 到
slf4j 标准。
struts1.10 所用到的 jar 包
22
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
23. 我的 je 博客: http://86asm.javaeye.com/
导入 lib 目录下的所有 jar 包。但为了避免 jar 包冲突,不要导入 antlr.jar(hibernate
已 经 导 入 )
jar 包 说 明 :绿色字样是框架所需的一些基本包 。hibernate 的开发 jar 包主要集中在
required 目录和核心 jar 包,还需要特别说明的是它的日志包问题: hibernate 使用了
slf4j 来记录日志,但是 slf4j 只是一个日志记录标准,需要具体的实现才可以进行日志
记录,我们可以使用它本身的实现(如 slf4j-simple-1.5.8.jar),也可以使用其它的实
现,如 log4j 来记录日志,但是使用 log4j 需要导入 slf4j-log4j12-1.5.8.jar 来进行适
配。 在这里为了和 spring 的日志记录结合,我们使用了 log4j 的实现。 而要使用 log4j 日志
记录,可以在 spring 的 lib 子目录中找到。Spring 框架中除了常用的 jar 包外,还增加了
dbcp 连接池相关的包,需要说明的是连接池的基本包为: dbcp、pool、collections(在
hibernate 中已导入)。为了与 strus 相结合,还增加了一个 spring-webmvc-struts.jar 包。
经验:spring 的作用是来集成其它的框架,所以对于许多集成所要用到的 jar 包,在
spring 中都能找到,因而对于项目中出现的类装载相关的错误,应首先查看 jar 包是否冲
突,是否导入了需要的包,建议集成时,其它框架只需导入它们自己所需的基本包,而对
于集成所用的包都可以从 spring 提供的 lib 中查询。
2.集成 spring+hibernate
对于框架的集成最好的方式是分布集成,比如这里先集成 spring+hibernate 并测试,测试
通 过 后 再 来 集 成 struts 。
步骤一、建立接口 UserServeice,代码如下:
package com.asm.service;
public interface UserService {
public abstract void save(User user);
public abstract void delete(Integer id);
public abstract void update(User user);
public abstract User getUser(Integer id);
@SuppressWarnings("unchecked")
public abstract List<User> getUsers();
}
步骤二、对应的实现类UserServiceBean,代码如下:
package com.asm.service.impl;
public class UserServiceBean implements UserService {
@Resource
private SessionFactory sf;
@Transactional
public void save(User user) {
sf.getCurrentSession().persist(user);
}
...其它实现方法省略
}
注意事项:(1)在每个方法上都要加上事务注解,特别是save和update方法一定要加上事务
注解,因为把sessionFactory对象是通过spring注入(注解方式注入),而spring中配置
的sessionFactory对象又纳入了spring容器的事务管理,所以要加上事务标记。 (2)一定
23
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
24. 我的 je 博客: http://86asm.javaeye.com/
要通过getCurrentSession方法得到session对象,因为使用openSession方式得到的
Session对象不受spring容器管理。
实体类User及相应的User.hbm.xml配置在此省略。
步骤三、在beans.xml中进行配置,内容如下:
<bean id="theDatasource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/ssh" />
<property name="username" value="root" />
<property name="password" value="123456" />
...数据源配置,省略其它,可参看“与jdbc的集成”。
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager
">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="theDatasource" />
<property name="mappingResources">
<list>
<value>com/asm/entity/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=true
</value>
</property>
<!—
如果想就把此bean对应的属性配置放到hibernate.cfg.xml中去配置,可以在
hibernate.cfg.xml中配好后,再使用下面的配置加载hibernate.cfg.xml配置文件:
<property name="configLocation"
value="hibernate.cfg.xml" />
-->
24
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
25. 我的 je 博客: http://86asm.javaeye.com/
</bean>
<bean id="userServiceBean"
class="com.asm.service.impl.UserServiceBean" />
说明:这里配置的 sessionFactory 实质就是对 hibernate 的 sessionFactory 进行了一
次包装,它的配置依赖于一个数据源实例 bean,最后再把 sessionFactory 纳入 spring 容
器 的 事 务 管 理 , 但 是 要 注 意 的 是 这 时 使 用 的 是 事 务 管 理 器 类 为
HibernateTransactionManager 。
配 置 简 图 :
数据源 SessionFactory – 被包装 事务管理器
注入
被管理
UserServiceBean 的 sf 属性
步骤四、建立junit单元进行测试,测试代码如下:
package junit.test;
public class UserServiceTest {
private static UserService us;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext ctx=new
ClassPathXmlApplicationContext("beans.xml");
us=(UserService) ctx.getBean("userServiceBean");
}
@Test
public void testSave() {
us.save(new User("李某某"));
}
...其它测试方法省略。
}
测试通过,完成了MVC的M层业务逻辑层的开发。
3.集成 struts 框架
首先准备好struts框架的基本配置环境:(1)在web.xml中配置struts的核心控制器
Actionservlet(配置代码省略)。 (2)在struts-config.xml中建立strus-config.xml配
置文件,它的配置内容如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
1.3//EN"
"http://struts.apache.org/dtds/struts-config_1_3.dtd">
25
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
26. 我的 je 博客: http://86asm.javaeye.com/
<struts-config>
<action-mappings>
<action path="/list" type="com.asm.action.UserAction">
<forward name="success" path="/WEB-
INF/page/showUser.jsp"></forward>
</action>
</action-mappings>
</struts-config>
(3)上面所用到的UserAction的代码如下:
package com.asm.action;
public class UserAction extends Action {
@Override
public ActionForward execute(ActionMapping mapping, ActionForm
form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
WebApplicationContext ctx = WebApplicationContextUtils
.getWebApplicationContext(this.servlet.getServletContext());
UserService us = (UserService)
ctx.getBean("userServiceBean");
List<User> users = us.getUsers();
request.setAttribute("users",users);
return mapping.findForward("success");
}
}
说明:要想此实例真正可用,还需在 web.xml 中增加一个监听器的配置,内容如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
此 监 听 器 的 作 用 就 是 在 启 动 时 把 spring 容 器 进 行 实 例 化 , 并 把 spring 容 器 实 例
WebApplicationContext 对象 放到 application 作用域中,大致进行类似这样的操作:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_C
ONTEXT_ATTRIBUTE,spring 容器实例 WebApplicationContext 对象)。因此我们也可以通
过保存的名字来获取 WebApplicationContext 对象,但是为了方便,spring 提供一个工
具 类 WebApplicationContextUtils 来 获 取 此 对 象 , 关 于 这 个 工 具 类 的 使 用 , 在
UserAction 就是使用了此工具类来获取 spring 容器实例。保存一个 List<User>对象后,
在 showUser.jsp 遍历 List 对象,代码如下:
26
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
27. 我的 je 博客: http://86asm.javaeye.com/
<c:forEach items="${users}" var="user">
${user.id } -- ${user.name }<br/>
</c:forEach>
4.改进集成 struts 框架
上面的代码其实并非整合struts的最佳方式,整合struts的优雅方式应该是把Action也纳
入spring容器管理。具体操作如下:
步骤一、修改UserAction类,修改后的代码如下:
@Resource
private UserService userService;
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws
Exception {
request.setAttribute("users", userService.getUsers());
return mapping.findForward("success");
}
说明:上面的userService属性通过spring的注解方式注入,这样可以避免获取
UserService对象的繁琐过程。
步骤二、在beans.xml配置UserAction,纳入spring容器管理,配置内容如下:
<bean name="/list" class="com.asm.action.UserAction"/>
说明:此实例bean的配置使用了name属性,而未使用id属性,因为id不支持“/”这样的特
殊字符。还需要说明的是这里的name之所以取值为“/list”,是因为纳入spring容器管理的
Action的取名必须保证与action的path属性值相同(为什么如此做,后面有解释)。
步骤三、 添加一个ActionServet的处理器类。 Struts1.x中的ActionServlet最终其实是把
请求派发给RequestProcessor类来处理,而如果想自己编写处理请求类,可以在struts-
config.xml中配置一个<controller>来处理这些请求。Spring提供了一个
DelegatingRequestProcessor类,它继承自RequestProcessor类,它除了具备父类的
功能,还主要完成一件重要的工作:把ActionServlet派发过来的请求交给在spring中配置
的Action bean实例来处理(由于最终它是把请求交给了spring中配置的 action bean
实例处理,所以在struts-config.xml中关于此action的type属性可以不写)。因此我们
还应struts-config.xml的中增加如下配置代码:
<controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingRequestProcessor" />
</controller>
关于这个处理器也可如下配置:
<controller processorClass=
"org.springframework.web.struts.DelegatingRequestProcessor"/>
27
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
28. 我的 je 博客: http://86asm.javaeye.com/
list.do Spring 容器中存在名
请求 ActionServlet 为“ /list”bean 实例
Action:UserAction
根据 list.do 的请求,
分析出在 beans.xml 中
存在一个名 DelegatingRequestProcessor
为“ /list” 的 bean 实
例,然后把请求交给 说明了为什么要在 beans.xml 配
这个 bean 实例处理。 置的 UserAction 的名字为“/list”
5.使用二级缓存
步骤一、在使用hibernate框架时,可以为hibenate配置一个第三方缓存。通常的做法是在
hibernate的主配置文件中开启缓存相关的功能。在此项目中,由于是集成了spring框架,
所以我们可以在spring容器的beans.xml中的sessionFactory bean实例的
hibernateProperties属性中增加如下配置:
cache.use_second_level_cache=true -->开启二级缓存
hibernate.cache.use_query_cache=false -->不使用查询缓存
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
-->第三方缓存实现类
步骤二、第三方缓存EhCache的配置,在src目录下编写ehcache.xml配置文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
defaultCache节点为缺省的缓存策略
maxElementsInMemory 内存中最大允许存在的对象数量
eternal 设置缓存中的对象是否永远不过期
overflowToDisk 把溢出的对象存放到硬盘上
timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉
timeToLiveSeconds 指定缓存对象总的存活时间
diskPersistent 当jvm结束是是否持久化对象
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮
询时间
-->
<ehcache>
<diskStore path="C:cache"/>
<defaultCache maxElementsInMemory="1000" eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="180"
diskPersistent="false"
28
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
29. 我的 je 博客: http://86asm.javaeye.com/
diskExpiryThreadIntervalSeconds="60"/>
<cache name="com.asm.entity.User" maxElementsInMemory="100"
eternal="false"
overflowToDisk="true" timeToIdleSeconds="300"
timeToLiveSeconds="600" diskPersistent="false"/>
</ehcache>
步骤三、在User.htm.xml中的<class>目录下增加如下配置:
<cache usage="read-write" region="com.asm.entity.User"/>
说明:这里的region的值和ehcache.xml配置下的<cache>中的name属性值相同。结合
echcache.xml作如下说明:通常情况下,实体类的二级缓存配置通常使用默认的
<defaultCache>这个配置,但是如果想使用特有的缓存配置,可以用实体类的<cacche
regin>来和ehcache.xml中的<cache name>关联。
步骤四、测试二级缓存。在UserServiceTest中增加如下测试代码:
public void testGetUser() {
System.out.println(us.getUser(1).getName());
System.out.println("关闭数据库服务");
try {
Thread.sleep(1000*40);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("关闭数据库准备从缓存中获取");
System.out.println(us.getUser(1).getName());
}
说明:当执行到控制台显示“关闭数据库服务”时,我们手工停掉数据库服务,休眠40秒
后,仍能获取数据,证明是从缓存中取得的数据。
小结使用缓存步骤:开启支持缓存的配置、 对第三方缓存类进行配置、 在实体类配置文件中
标记使用二级缓存。
6.乱码问题
当前台传递的数据包含中文时,获取这些中文数据时会出现乱码。大致 原 因 如下:在
struts 框 架 中 , 我 们 把 配 置 的 *.do 的 请 求 会 被 ActionServlet 拦 截 到 , 而 访 问
ActionServlet 时 用 到 的 默 认 编 码 为 ISO8859-1 , 即 是 说 设 置 了
request.setCharacterEncoding(“ISO8859-1”)。这时 ActionServlet 在获取参数
时就会得到乱码,ActionServlet 填充 ActionForm 时就把乱码填充进 ActionForm。当填充
完毕后会把*.do 的请求派发给相应的 Action,而 Action 从 ActionForm 中获取数据时自然
就出现了乱码。解决此问题很简单,只需把 spring 提供的一个过滤器类配置到 web.xml 中,
即增加如下代码:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
29
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
30. 我的 je 博客: http://86asm.javaeye.com/
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在以前我们是自己编写此 filter 类,而 spring 为我们提供了 CharacterEncodingFilter
类,我们就自需配置即可。另要注意在配置时一定要指定一个 encoding 参数名及值. 补充:
关于这里的乱码问题可参“struts1.x 深入学习笔记 -四、2.步骤五.问题”。
7.OSIV
Spring 对于 OpenSessionInView 也提供一个 filter 类来解决此问题:
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
八、SSJ 集成实例
1.搭建环境
hibernate 核心包
libbytecodecglibHibernate-cglib-repack.jar
hibernate 注解包
hibernate-annotations.jar
LibEjb3-persistence.jar、hibernate-commons-annotations.jar
Hibernate 针对 JPA 的实现包
Hibernate-entitymanager.jar
上面列举的 jar 包(基于 hibernate 实现的 JPA 包)+SSH 集成时的所有 jar 包
2.集成 spring+JPA
建立 spring_07_SSJ 项目并导入相关 ja 包,此项目基本参照上一个项目,只是把 JPA 的实
现加了进去。
步骤一、搭建 JPA 开发环境
在src下建立META-INF/persistence.xml文件,此文件的主要内容如下:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="jpaDemo"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
30
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
31. 我的 je 博客: http://86asm.javaeye.com/
<property name="hibernate.format_sql" value="true" />
<!-- 配置要连接的数据库信息:-->
<property name="hibernate.connection.driver_class"
value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/ssj" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="123456"
/>
</properties>
</persistence-unit>
</persistence>
对应的持久化 User 的代码如下:
package com.asm.entity;
@Entity
public class User {
@Id
@GeneratedValue
private Integer id;
@Column(name="u_name")
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
...省略get/set方法,特别注意要在实体在打上实体相关的标记:@Entity @Id
@GeneratedValue @Column
}
步骤二、把JPA纳入spring容器管理
在beans.xml中的配置如下:
<context:annotation-config />
<bean id="localEMF"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="jpaDemo" />
</bean>
<bean id="txManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="localEMF" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
31
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
32. 我的 je 博客: http://86asm.javaeye.com/
说明:localEMF 实例 bean 的使用是把 JPA 的 persistence.xml 配置的 jpaDemo 持久化单
元 加 进 JPA 实 体 工 厂 管 理 ( LocalEntityManagerFactoryBean 包 装 了 JPA 所 使 用 的
EntityManager 对象的工厂类 EntityManagerFactoryBean)。再把工厂类加进事务管
理器。
步骤三、UserServiceBean 的代码如下:
package com.asm.service.impl;
public class UserServiceBean implements UserService {
@PersistenceContext
private EntityManager em;
@Transactional
public void save(User user) {
em.persist(user);
}
...省略其它的实现方法
}
说明:em属性依赖于spring容器注入。 只要把此类交给spring容器管理,spring容器会根据
@PersistenceContext注解,来用配置的产生EntityManager对象的JPA工厂类来完成此
对象的注入。强调的是在save、update方法上一定要打上事务注解标记。
步骤四、测试 UserServiceBean 的业务方法
测试代码同上一个项目的测试代码
3.集成 struts:参上一个项目
4.OSIV 问题
在集成 hibernate 时,OSIV 指的是 sesion 关闭后的的解决方案。而在使用 JPA 时,取代
session 的是 EntityManager,所以要解决因 EntityManager 关闭的 OSIV 问题,应使用
spring 提供的 org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
九、SSH2 集成实例
1.搭建环境并集成 hibenate
基本环境 SSH 集成相同,只是要导入开发 struts2 的开发包(在 2.1.8 需要导入 6 个基本
包),与 hibenate 的集成可以前面的项目说明。
2.集成 strust2
步骤一、建立 UserAction,代码如下:
package com.asm.action;
public class UserAction extends ActionSupport {
@Resource
private UserService userService;
private String username;
@Override
public String execute() throws Exception {
User user=new User(username);
userService.save(user);
return "success";
}
32
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
33. 我的 je 博客: http://86asm.javaeye.com/
...省略username 的get/set方法
}
说明:userService 属性通过 spring 注入。在 bean.xml 配置 UserAction 如下:
<bean id="userAction" class="com.asm.action.UserAction"/>
配置此action,在src建立struts.xml文件,配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="baseSSH" namespace="/" extends="struts-default">
<action name="add" class="com.asm.action.UserAction">
<result name="success">/success.jsp</result>
</action>
</package>
</struts>
步骤二、 配置一个监听器,实例化 spring 容器,和集成 struts1 相同。 即在 web.xml 中增加
如下代码:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
步骤三、前台 index.jsp 页面,代码如下:
<form action="<%=request.getContextPath() %>/add.action">
用户名:<input type="text" name="username">
<input type="submit" value="提交">
</form>
步骤四、发布测试。
十、整合总结:
十一、错误总结:
1.使用 new ClassPathXmlApplicationContext 对象时未传递 beans.xml 配置文件出错:
BeanFactory not initialized or already closed
另外如果 ctx 对象关闭后,再刷新也可以重新使用 ctx 对象来获取 bean 实例
OpenSession 得到的 session 不受 Spring 容器管理
JPA 实体一定要有 entity 注解标志—涉及到保存或更新操作时,一定要打上事务标记
33
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
34. 我的 je 博客: http://86asm.javaeye.com/
34
http://86asm.javaeye.com/ 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看