SlideShare a Scribd company logo
我的 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/                 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                   文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/               文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/               文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                  文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/               文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/               文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/               文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                       文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/            文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                  文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/          文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                   文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                   文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                 文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                  文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/                  文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 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/              文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看
我的 je 博客: http://86asm.javaeye.com/




                           34
http://86asm.javaeye.com/       文章错误之处肯请大家留言指正,
再此谢过。
文章编排目录结构,大家可以在文档结构图下很方便地查看

More Related Content

What's hot

J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架fangdeng
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
wtxidian
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程appollo0312
 
Spring ioc详解
Spring ioc详解Spring ioc详解
Spring ioc详解
ygj_2008
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识yiditushe
 
Spring 2.0 技術手冊導讀
Spring 2.0 技術手冊導讀Spring 2.0 技術手冊導讀
Spring 2.0 技術手冊導讀
Justin Lin
 
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
Justin Lin
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
rainx1982
 
X Fire开发指南
X Fire开发指南X Fire开发指南
X Fire开发指南yiditushe
 
Oracle使用者安全設定
Oracle使用者安全設定Oracle使用者安全設定
Oracle使用者安全設定
Chien Chung Shen
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现hua qiu
 
Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤
Justin Lin
 
【 I Love Joomla 】- Joomla!佈景製作教學
【 I Love Joomla 】- Joomla!佈景製作教學【 I Love Joomla 】- Joomla!佈景製作教學
【 I Love Joomla 】- Joomla!佈景製作教學
ilovejoomla
 
Oracle SGA 介紹
Oracle SGA 介紹Oracle SGA 介紹
Oracle SGA 介紹
Chien Chung Shen
 
Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門
Justin Lin
 
Discuz技术交流
Discuz技术交流Discuz技术交流
Discuz技术交流
pigso
 
I Love Joomla! 佈景製作教學 0212
I Love Joomla! 佈景製作教學 0212I Love Joomla! 佈景製作教學 0212
I Love Joomla! 佈景製作教學 0212Asika Simon
 
了解Oracle在线重定义online redefinition
了解Oracle在线重定义online redefinition了解Oracle在线重定义online redefinition
了解Oracle在线重定义online redefinition
maclean liu
 

What's hot (19)

J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
Spring ioc详解
Spring ioc详解Spring ioc详解
Spring ioc详解
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识
 
Spring 2.0 技術手冊導讀
Spring 2.0 技術手冊導讀Spring 2.0 技術手冊導讀
Spring 2.0 技術手冊導讀
 
Jsp
JspJsp
Jsp
 
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
Spring 2.0 技術手冊第七章 - Spring Web MVC 框架
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 
X Fire开发指南
X Fire开发指南X Fire开发指南
X Fire开发指南
 
Oracle使用者安全設定
Oracle使用者安全設定Oracle使用者安全設定
Oracle使用者安全設定
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现
 
Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤
 
【 I Love Joomla 】- Joomla!佈景製作教學
【 I Love Joomla 】- Joomla!佈景製作教學【 I Love Joomla 】- Joomla!佈景製作教學
【 I Love Joomla 】- Joomla!佈景製作教學
 
Oracle SGA 介紹
Oracle SGA 介紹Oracle SGA 介紹
Oracle SGA 介紹
 
Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門
 
Discuz技术交流
Discuz技术交流Discuz技术交流
Discuz技术交流
 
I Love Joomla! 佈景製作教學 0212
I Love Joomla! 佈景製作教學 0212I Love Joomla! 佈景製作教學 0212
I Love Joomla! 佈景製作教學 0212
 
了解Oracle在线重定义online redefinition
了解Oracle在线重定义online redefinition了解Oracle在线重定义online redefinition
了解Oracle在线重定义online redefinition
 

Viewers also liked

J Bpm4 1中文用户手册
J Bpm4 1中文用户手册J Bpm4 1中文用户手册
J Bpm4 1中文用户手册yiditushe
 
性能测试实践1
性能测试实践1性能测试实践1
性能测试实践1yiditushe
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训yiditushe
 
Load runner测试技术
Load runner测试技术Load runner测试技术
Load runner测试技术yiditushe
 
性能测试实践2
性能测试实践2性能测试实践2
性能测试实践2yiditushe
 
性能测试技术
性能测试技术性能测试技术
性能测试技术yiditushe
 
J2 ee性能测试
J2 ee性能测试J2 ee性能测试
J2 ee性能测试yiditushe
 
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTSK TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
LiGhT ArOhL
 

Viewers also liked (8)

J Bpm4 1中文用户手册
J Bpm4 1中文用户手册J Bpm4 1中文用户手册
J Bpm4 1中文用户手册
 
性能测试实践1
性能测试实践1性能测试实践1
性能测试实践1
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训
 
Load runner测试技术
Load runner测试技术Load runner测试技术
Load runner测试技术
 
性能测试实践2
性能测试实践2性能测试实践2
性能测试实践2
 
性能测试技术
性能测试技术性能测试技术
性能测试技术
 
J2 ee性能测试
J2 ee性能测试J2 ee性能测试
J2 ee性能测试
 
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTSK TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
K TO 12 GRADE 9 LEARNER’S MATERIAL IN ARTS
 

Similar to Spring入门纲要

中远公司 Java培训资料
中远公司  Java培训资料中远公司  Java培训资料
中远公司 Java培训资料yiditushe
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程yiditushe
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非Tony Deng
 
中远公司 Java培训资料
中远公司  Java培训资料中远公司  Java培训资料
中远公司 Java培训资料yiditushe
 
由一个简单的程序谈起――之二
由一个简单的程序谈起――之二由一个简单的程序谈起――之二
由一个简单的程序谈起――之二yiditushe
 
Kissy design
Kissy designKissy design
Kissy design
yiming he
 
Django development
Django developmentDjango development
Django developmentloveyudu
 
配置Oracle 10g 双向流复制
配置Oracle 10g 双向流复制配置Oracle 10g 双向流复制
配置Oracle 10g 双向流复制maclean liu
 
六步教你学会简单Rmi
六步教你学会简单Rmi六步教你学会简单Rmi
六步教你学会简单Rmiyiditushe
 
Puppet安装测试
Puppet安装测试Puppet安装测试
Puppet安装测试Yiwei Ma
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
Chi-wen Sun
 
Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则
YUCHENG HU
 
網站設計100步
網站設計100步網站設計100步
網站設計100步evercislide
 
Free Marker中文文档
Free Marker中文文档Free Marker中文文档
Free Marker中文文档yiditushe
 
Mybatis学习培训
Mybatis学习培训Mybatis学习培训
Mybatis学习培训
flynofry
 
J2ee经典学习笔记
J2ee经典学习笔记J2ee经典学习笔记
J2ee经典学习笔记yiditushe
 

Similar to Spring入门纲要 (20)

中远公司 Java培训资料
中远公司  Java培训资料中远公司  Java培训资料
中远公司 Java培训资料
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非
 
中远公司 Java培训资料
中远公司  Java培训资料中远公司  Java培训资料
中远公司 Java培训资料
 
由一个简单的程序谈起――之二
由一个简单的程序谈起――之二由一个简单的程序谈起――之二
由一个简单的程序谈起――之二
 
Structs2簡介
Structs2簡介 Structs2簡介
Structs2簡介
 
Kissy design
Kissy designKissy design
Kissy design
 
Django development
Django developmentDjango development
Django development
 
配置Oracle 10g 双向流复制
配置Oracle 10g 双向流复制配置Oracle 10g 双向流复制
配置Oracle 10g 双向流复制
 
六步教你学会简单Rmi
六步教你学会简单Rmi六步教你学会简单Rmi
六步教你学会简单Rmi
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
Sun java
Sun javaSun java
Sun java
 
Puppet安装测试
Puppet安装测试Puppet安装测试
Puppet安装测试
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
运维自动化
运维自动化运维自动化
运维自动化
 
Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则
 
網站設計100步
網站設計100步網站設計100步
網站設計100步
 
Free Marker中文文档
Free Marker中文文档Free Marker中文文档
Free Marker中文文档
 
Mybatis学习培训
Mybatis学习培训Mybatis学习培训
Mybatis学习培训
 
J2ee经典学习笔记
J2ee经典学习笔记J2ee经典学习笔记
J2ee经典学习笔记
 

More from yiditushe

Flex3中文教程
Flex3中文教程Flex3中文教程
Flex3中文教程yiditushe
 
开放源代码的全文检索Lucene
开放源代码的全文检索Lucene开放源代码的全文检索Lucene
开放源代码的全文检索Luceneyiditushe
 
基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍yiditushe
 
Lucene In Action
Lucene In ActionLucene In Action
Lucene In Actionyiditushe
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1yiditushe
 
Lucene2 4 Demo
Lucene2 4 DemoLucene2 4 Demo
Lucene2 4 Demoyiditushe
 
Lucene 全文检索实践
Lucene 全文检索实践Lucene 全文检索实践
Lucene 全文检索实践yiditushe
 
Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析yiditushe
 
7 面向对象设计原则
7 面向对象设计原则7 面向对象设计原则
7 面向对象设计原则yiditushe
 
10 团队开发
10  团队开发10  团队开发
10 团队开发yiditushe
 
9 对象持久化与数据建模
9  对象持久化与数据建模9  对象持久化与数据建模
9 对象持久化与数据建模yiditushe
 
8 Uml构架建模
8  Uml构架建模8  Uml构架建模
8 Uml构架建模yiditushe
 
6 架构设计
6  架构设计6  架构设计
6 架构设计yiditushe
 
5 架构分析
5  架构分析5  架构分析
5 架构分析yiditushe
 
4 软件架构导论
4  软件架构导论4  软件架构导论
4 软件架构导论yiditushe
 
3 Uml行为建模与Ooa
3  Uml行为建模与Ooa3  Uml行为建模与Ooa
3 Uml行为建模与Ooayiditushe
 
2 需求开发与Uml用例
2  需求开发与Uml用例2  需求开发与Uml用例
2 需求开发与Uml用例yiditushe
 
1 软件开发过程概述
1  软件开发过程概述1  软件开发过程概述
1 软件开发过程概述yiditushe
 
构建高性能Web站点(精选版)
构建高性能Web站点(精选版)构建高性能Web站点(精选版)
构建高性能Web站点(精选版)yiditushe
 
Spring的Io C容器
Spring的Io C容器Spring的Io C容器
Spring的Io C容器yiditushe
 

More from yiditushe (20)

Flex3中文教程
Flex3中文教程Flex3中文教程
Flex3中文教程
 
开放源代码的全文检索Lucene
开放源代码的全文检索Lucene开放源代码的全文检索Lucene
开放源代码的全文检索Lucene
 
基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍
 
Lucene In Action
Lucene In ActionLucene In Action
Lucene In Action
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1
 
Lucene2 4 Demo
Lucene2 4 DemoLucene2 4 Demo
Lucene2 4 Demo
 
Lucene 全文检索实践
Lucene 全文检索实践Lucene 全文检索实践
Lucene 全文检索实践
 
Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析
 
7 面向对象设计原则
7 面向对象设计原则7 面向对象设计原则
7 面向对象设计原则
 
10 团队开发
10  团队开发10  团队开发
10 团队开发
 
9 对象持久化与数据建模
9  对象持久化与数据建模9  对象持久化与数据建模
9 对象持久化与数据建模
 
8 Uml构架建模
8  Uml构架建模8  Uml构架建模
8 Uml构架建模
 
6 架构设计
6  架构设计6  架构设计
6 架构设计
 
5 架构分析
5  架构分析5  架构分析
5 架构分析
 
4 软件架构导论
4  软件架构导论4  软件架构导论
4 软件架构导论
 
3 Uml行为建模与Ooa
3  Uml行为建模与Ooa3  Uml行为建模与Ooa
3 Uml行为建模与Ooa
 
2 需求开发与Uml用例
2  需求开发与Uml用例2  需求开发与Uml用例
2 需求开发与Uml用例
 
1 软件开发过程概述
1  软件开发过程概述1  软件开发过程概述
1 软件开发过程概述
 
构建高性能Web站点(精选版)
构建高性能Web站点(精选版)构建高性能Web站点(精选版)
构建高性能Web站点(精选版)
 
Spring的Io C容器
Spring的Io C容器Spring的Io C容器
Spring的Io C容器
 

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/ 文章错误之处肯请大家留言指正, 再此谢过。 文章编排目录结构,大家可以在文档结构图下很方便地查看