Hibernate入门笔记

来源:互联网 发布:建设工程合同管理淘宝 编辑:程序博客网 时间:2024/04/30 05:32

一、理论知识

1.依赖注入、控制反转

依赖注入:在运行期,由外部容器动态地将依赖对象注入到组件中
控制反转:应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部窗口负责得。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓的反转。

2.spring的主要特性。

1)降低组件之间的耦合度,实现软件各层之间的解耦。
2)可以使用容器提供的众多服务,如:事务管理服务、消息服务、JMS服务、持久化服务等等。
3)容器提供单例模式支持,开发人员不再需要自己编写实现代码。
4)容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能。
5)容器提供的众多辅作类,使用这些类能够加快应用的开发,如:JdbcTemplateHibernateTemplate.
6)对主流的应用框架提供了集成支持。

3.常用技术

控制反转/依赖注入---面向切入编程---与主流框架的整合、管理---

 

二、基本实例

1.准备搭建环境

dist/spring.jar

lib/jakata-commons/commons-loggin.jar

如果使用了切面编程,还需下列jar文件:

lib/aspectj/aspectjweaver.jaraspectjrt.jar

lib/cglib/cglib-nodep-2.1.3.jar

如果使用了jsr-250中的注解,还需要下列jar文件:

lib/j2ee/common-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();

}

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][filesand editors][xml][xml catalog] add,在出现窗口的location中选“file system,然后在spring解压目录的dist/resources目录中选择“spring-beans-2.5.xsd,并将key Type值改为“SchemaLocation”,key值为:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
4Junit测试单元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();

    }

}
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
sessionglobal session这三个作用域主要用在web应用中

6.bean的生命周期

(1)什么时候初始化bean实例

scopesingleton,即默认情况,会在装载配置文件时实例化。如果希望在调用getBean时才初始化,可以使用lazy-init="true" 补充:如果希望希望该配置文件中的所有bean都延迟初始化,则应在beans根结点中使用lazy-init="true"

scope=prototype,在调用getBean()方法时才会初始化。
2)生命周期:

构造器、init方法、获取bean后的操作、destroy方法(ctx.close注意如果beanscope设为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>

       </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>

           </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();

    }

}

然后在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中有相应的beanId对应。它是属于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.实例

建立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-scanbase-package="com.asm.service"/>

    <context:component-scanbase-package="com.asm.dao.impl"/>

</beans>

说明:以上的配置会自动管理service包和impl包及它们子包下的带有component标记的类,上面两行配置代码等价于
<context:component-scanbase-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 methodis called");

    }

    public void test() {

       System.out.println("********************************");

       userDao.save();

       userDao2.save();

       System.out.println(userDao3);

       // userDao3.save();

       System.out.println("********************************");

    }

}

说明:如果使用Service这些注解时不指定名称,这些实例bean的名称就是类名(但首字母小写),也可以指定实例bean的名字,比如这里指定其名字为“usb,scope注解配置了bean的作用范围,PostConstruct注解指定了beaninit方法。关于其它的一些注解配置参文档(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() {

       System.out.println("save methodis called" + username);

    }

    @Override

    public void update() {

       System.out.println("updatemethod is called" + username);

    }

}

需求如下:如果实现类的username!=null,才可以调用saveupdate方法,为null则不能调用。当然要解决此问题,可以在saveupdate方法内部进行判断,但是如果在方法内部进行判断,代码则失去了灵活性,如果以后的需求改变,比如变成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 {

    @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实现代理

JDKProxy实现代理要求被代理的目标对象必须实现一个接口,而如果目标对象没有实现接口则不能使用Proxy来代理。其实也可以借助cglib来实现代理。操作步骤如下
步骤一、建立
UserDaoImp2类,它与UserDaoImp的唯一区别就是没有实现任何接口。
步骤二、导入cglib.jar包,创建cglib代理工厂,代码如下:
package com.asm.dao.impl.factory;

public class CglibFactory implements MethodInterceptor {

    private Object target;

    public ObjectcreateUserDaoImp2(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();

              // 例外通知

           } finally {

              // 最终通知

           }

       }

       return result;

    }

}

说明:注意注释的通知,通知就是拦截到方法后执行的一些代码,比如前置通知,就是说在回调目标方法时执行的一些操作。

步骤三、建立测试代码(省略,和.2的测试代码相似)

4.aop理论知识

横切性关注点:对哪些方法拦截,拦截后怎么处理,这些关注就称之为横切性关注点
切面(aspect):类是对物体特征的抽象,而切面是指对横切性关注点的抽象。

连接点(joinpoint:被拦截到的点,因为spring只支持方法类型的连接点,所以在spring中连接点指的就是被拦截到的方法。实际上连接点还可以是字段或构造器。
切入点(pointcut):对连接点进行拦截的定义。
通知(advice):所谓通知就是指拦截到连接点之后的要执行的代码。通知分为前置、后置、异常、最终。环绕通知五类。
目标对象:代理的目标对象。
织入(weave):将切面应用到目标对象并导致代理对象创建的过程
引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。

5.基于springAOP实现

步骤一、导入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("后置通知");

    }  

}

简析: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容器获取的对象。比如这里如果直接newUseDaoImp对象,则new出的对象是不能被spring的切面类监控管理。
补充:测试被代理对象未实现接口时,spring切面技术的应用。
步骤一、修改切面类
TheInterceptor切入点为如下内容:
@Pointcut("execution (*com.asm.dao.impl.*.*(..))") 
说明:拦截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("退出环绕");

    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 methodis called [ " + name+" ]");

    return "添加成功";

}
需求:获取调用add方法传递的参数。操作步骤如下:在切面类增加如下代码:
@Before("anyMethod() &&args(name)")// 前置通知,只针对UseDaoImpadd方法

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")

// 后置通知,监听返回结果,针对UserDaoImpgetUsername()方法

public void afterReturningRes(String result) {

    System.out.println("后置通知,返回结果:" + result);

}
说明:afterReturningRes方法的参数就是要返回的参数类型,returning标记的就是的结果,它的取值与该方法参数名相同。  测试代码同(4)。

6)获取抛出的异常

切面类的增加如下代码:

@AfterThrowing(pointcut="anyMethod",throwing="e")

public void catchException(Exception e){

    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.*
(java.lang.String,..))

拦截User类中第一个参数为String,后面参数任一的方法

待增加

 

待增加

 

9.总结

面向切面的常见应用(如权限拦截)、springaop依赖两种方式实现代理(依被代理的对象是否实现接口而定)、通知概念、基于注解与基于XML两种方式来配置切面、基本步骤(依要拦截的方法来设定切入点,依据业务需求实现拦截通知代码,切面纳入spring容器管理,要被监控的类只能是通过Spring容器获取)、切入点的格式。

六、与JDBC集成

1.搭建环境

建立spring_05_integrationJdbc项目 ,此项目使用dbcp作为数据源来整合JDBC技术。因此除了spring所需的jar包,还应导入dbcpjar包:commons-dbcp-1.2.2.jar及此 jar所依赖的两个apache开源jar包:commons-collections-3.1.jarcommons-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/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd

       http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd

      http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd

       http://www.springframework.org/schema/contexthttp://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"/>

</bean>

<bean id="txManager"     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <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(Useruser);

    void update(User user);

    User get(int id);

    List<User> getUsers();

}

建立UserDaoImp类实现UseDao接口,代码如下:
packagecom.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 fromuser 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 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 intouser(name) values(?)", new Object[] {user.getName() },

              new int[] { java.sql.Types.VARCHAR });

    }

 

    public void update(User user) {

       jdbcTemplate.update("update userset 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容器管理,使用setter注入方式完成对datasource的注入,实质是完成的JdbcTemplate对象的初始化。(2)该类CRUD方法都使用了Spring容器提供的JdbcTemplate对象来简化了CRUD操作,在spring文档的11.2.1.JdbcTemplate类作了较详细的介绍。(3)此类打上了@Transactional注解,表示此类中的业务方法都会受beans.xml配置的 <tx:annotation-driventransaction-manager="txManager" />管理

步骤四、编写测试类

package com.asm.test;

public class TestSJ {

    private UserDao ud = (UserDao) newClassPathXmlApplicationContext("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获取它的实现类对象将会报错。

步骤五、感知事务管理。

UserDaoImpdelete方法中增加如下代码:

jdbcTemplate.update("delete fromuser where id=?", new Object[] {user.getId() },   new int[] {java.sql.Types.INTEGER });

int i=5/0;

jdbcTemplate.update("delete fromuser where id=2");

spring默认的事务管理方式:运行期异常进行事务回滚,非运行期异常不进行事务回滚。因此增加上面的代码后,会出现ArithmeticException运行期异常。所以当出现此异常时,delete方法中的数据库操作都会进行回滚,因而id=12这两条记录都不会被删除。 如果把UseDaoImp类前标记@Transactional的注解去掉,id=1的记录会被删除,因为失去了spring容器的事务管理。
小结spring事务:
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_SUPPORTED

声明业务方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事务,如果方法在一个事务中被调用,则该事务会被挂起,在该方法调用结束后,原先的事务恢复执行

NEVER

指定业务方法绝对不能在事务范围中执行,如果业务方法在某个事务中执行,容器会抛出例外;只有业务方法没有关联到任何事务,才能正常执行。

MANDATORY

指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外

NESTED

如果一个活动的事务存在,它会自己产生一个单独的而且拥有多个可以回滚的保存点的事务,然后嵌套运行在活动的事务中,实质它可以看成是一个内部事务且具有回滚保存点,所以内部自身的事务回滚并不会引起外部活动事务的回滚,它只是回滚到内部事务的保存点。 如果没有活动事务,则按REQIIRED属性执行。需要注意的是此配置只对DataSourceTransactionManager事务管理器生效。


说明:上面多次提到业务方法,它实质就是UserDaoImpsave等这样的方法。但是这些方法前会有一个设定了详细属性的@Transactional注解。比如:
@Transactional(propagation="",isolation="",noRollbackFor="")
2)隔离级别属性isolation:这个是依赖数据库系统而言,数据库系统提供四种事务隔离级别。(具体的事务隔离级别分析在些略过,可以参看网络资源及相关文档)
3)只读属性
readOnlyfalse--读写性、true--只读性事务.

4)超时属性timeoutint型,以秒为单位。

5)回滚属性:根据抛出的异常决定是否回滚事务,它包括四种可选属性。参文档9.5.6.1的表9.3

4.扩展:抽取dpcp配置文件。

如果希望把dbcp的配置单独放到一个properties配置文件中去,可以使用如下做法。操作步骤:在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的事务管理

步骤一、拷贝UserDaoImpUserDaoImp2,然后把内面所有关于事务的注解去掉

步骤二、建立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="*" />         -->拦截所有方法  

    </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.xhibernatespring的集成。集成前的一个比较关键的因素是搭建好环境,这一步要求我们对集成的框架所用到的jar包有比较清楚的认识。下面列表展示了三个框架所用到的jar包。
hibernate3.3
所用到的jar

antlr-2.7.6.jar                   开源语法分析生成器(lib/required)

commons-collections-3.1.jar       Commons集合类库,与连接池有关(lib/required)

dom4j-1.6.1.jar                   xml解析类库(lib/required)

javassist-3.9.0.GA.jar            分析,编辑和创建java字节码类库(lib/required)

jta-1.1.jar                       事务处理api   (lib/required)       

slf4j-api-1.5.8.jar               日志处理 (lib/required)-->log4j实现

hibernate3.jar                    核心
ehcache-1.2.3.jar                 
二级缓存(lib/optional/ehcache

Spring2.5安装包所用的jar包:
dist/spring.jar
lib/aspectj/aspectjweaver.jar
aspectjrt.jar

lib/cglib/cgligb-nodep-2.1_3.jar
lib/j2ee/common-annotations.jar

lib/jakarta-commons/commons-logging.jarcommons-dbcp.jarcommons-pool.jar

dist/modules/spring-webmvc-struts.jar

lib/log4j/log4j-1.2.15.jar   srpinghibernate使用的日志记录jar

lib/slf4j/ slf4j-log4j12-1.5.0.jar  日志转换jar包,实现把log4j.jar包适配到slf4j标准。

struts1.10所用到的jar

导入lib目录下的所有jar包。但为了避免jar包冲突,不要导入antlr.jarhibernate已经导入)
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日志记录,可以在springlib子目录中找到。Spring框架中除了常用的jar包外,还增加了dbcp连接池相关的包,需要说明的是连接池的基本包为:dbcppoolcollections(在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)在每个方法上都要加上事务注解,特别是saveupdate方法一定要加上事务注解,因为把sessionFactory对象是通过spring注入(注解方式注入),而spring中配置的sessionFactory对象又纳入了spring容器的事务管理,所以要加上事务标记。 (2)一定要通过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" />

       -->

    </bean>

   

<bean id="userServiceBean" class="com.asm.service.impl.UserServiceBean"/>

说明:这里配置的sessionFactory实质就是对hibernatesessionFactory进行了一次包装,它的配置依赖于一个数据源实例bean,最后再把sessionFactory纳入spring容器的事务管理,但是要注意的是这时使用的是事务管理器类为
HibernateTransactionManager
配置简图:

步骤四、建立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("李某某"));

    }

    ...其它测试方法省略。

}
测试通过,完成了MVCM层业务逻辑层的开发。

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 StrutsConfiguration 1.3//EN"

         "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<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, ActionFormform,

           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_CONTEXT_ATTRIBUTE,spring容器实例WebApplicationContext 对象)。因此我们也可以通过保存的名字来获取WebApplicationContext对象,但是为了方便,spring提供一个工具类WebApplicationContextUtils来获取此对象,关于这个工具类的使用,在UserAction就是使用了此工具类来获取spring容器实例。保存一个List<User>对象后,在showUser.jsp遍历List对象,代码如下:

<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, ActionFormform,

    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的取名必须保证与actionpath属性值相同(为什么如此做,后面有解释)。
步骤三、添加一个ActionServet的处理器类。Struts1.x中的ActionServlet最终其实是把请求派发给RequestProcessor类来处理,而如果想自己编写处理请求类,可以在struts-config.xml中配置一个<controller>来处理这些请求。Spring提供了一个DelegatingRequestProcessor类,它继承自RequestProcessor类,它除了具备父类的功能,还主要完成一件重要的工作:把ActionServlet派发过来的请求交给在spring中配置的Action bean实例来处理(由于最终它是把请求交给了spring中配置的 action bean 实例处理,所以在struts-config.xml中关于此actiontype属性可以不写)。因此我们还应struts-config.xml的中增加如下配置代码:
<controller>

<set-property property="processorClass"

value="org.springframework.web.struts.DelegatingRequestProcessor"/>

</controller>

关于这个处理器也可如下配置:

<controllerprocessorClass=
"org.springframework.web.struts.DelegatingRequestProcessor"/>

说明了为什么要在beans.xml配置的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"

        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,ActionActionForm中获取数据时自然就出现了乱码。解决此问题很简单,只需把spring提供的一个过滤器类配置到web.xml中,即增加如下代码:

<filter>

    <filter-name>encodingFilter</filter-name>

    <filter-class>

       org.springframework.web.filter.CharacterEncodingFilter 

    </filter-class>

    <init-param>

       <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核心包

/lib/bytecode/cglib/Hibernate-cglib-repack.jar

hibernate注解包

hibernate-annotations.jar

Lib/Ejb3-persistence.jarhibernate-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文件,此文件的主要内容如下:
<persistencexmlns="http://java.sun.com/xml/ns/persistence"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"

    version="1.0">

    <persistence-unitname="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" />

           <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-driventransaction-manager="txManager" />

说明:localEMF 实例bean的使用是把JPApersistence.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工厂类来完成此对象的注入。强调的是在saveupdate方法上一定要打上事务注解标记。

步骤四、测试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 Stringexecute() throws Exception {

       User user=new User(username);

       userService.save(user);

       return "success";

    }

...省略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

    "-//ApacheSoftware 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注解标志涉及到保存或更新操作时,一定要打上事务标记

 

 

原创粉丝点击