spring学习


1.Spring框架的作用

   利用Spring框架整合Struts2,Hibernate等技术,可以管理应用 程序中的DAO,Service等组件.利用SpringIoCAoP机制实现组件对象之间的低耦合调用.改善程序结构,便于扩展和维护.

*2.Spring容器对象实例化

   BeanFactory---->ClassPathResource(加载srcxml配置)

                      ---->FileSystemResource(加载其他目录下的xml配置)

   ApplicationContext(推荐)-->ClassPathXmlApplicationContext

                                         -->FileSystemXmlApplicationContext

   容器实例化后,通过getBean("id属性")方法获取容器中的Bean对象.

3.Spring容器对Bean组件的管理

   *1)Bean对象创建方式

      默认支持singletonprototype模式.

      默认为singleton,可以在<bean>定义时用scope=""指定

      singleton:容器中只存在一个Bean对象,每次getBean取出的Bean对象相同.

      prototype:每次getBean取出一个新的Bean对象.

       以后如果在Web环境中,可以通过追加配置,scope扩展成request,session等值.

   2)Bean对象的创建时机

      scope="singleton":Bean对象是随着容器创建而创建.

       通过lazy-init="true"属性可以将对象创建时机

       推迟到调用getBean方法时.

      其他创建方式:Bean对象是在getBean方法调用时创建.

   3)Bean对象的销毁时机

       singleton单例Bean对象,Spring容器销毁时才销毁.

       非单例交给垃圾回收期,和程序中对象变量销毁时机一样.

  4)Bean指定初始化和销毁方法

       <bean init-method="方法名">可以指定对象创建时执行的

初始化方法.该方法在构造方法后调用.

       <bean destroy-method="方法名">可以指定对象销毁前执行

       的销毁方法.该方法在对象销毁之前调用.(该属性适用于singleton)

*4.DIIoC概念及应用

   1)DI概念

     Dependency Injection : 依赖注入技术.

     在Spring容器中,两个组件之间存在调用关系,可以通过DI技术建立.

     在Spring容器中,主要采用以下注入方式:

      a. setter方式注入(推荐)

          依靠属性的set方法注入.

          使用方法如下:UserSerivce--调用-->UserDAO

          --UserSerivce实现类中,添加一个UserDAO属性变量和setter方法

          --Spring配置文件中,UserSerivce添加setter注入配置

            <bean id="userService" class="">

               <property name="属性名" ref="要注入的DAO对象的id">

               </property>

            </bean>

      b.构造方式注入

          依靠构造方法注入.

          使用方法:

           --UserService实现类中,添加一个UserDAO属性变量

           --UserService实现类中,添加一个带参数的构造方法,

参数类型为UserDAO类型.

           --Spring配置中,定义构造注入

             <bean id="" class="">

              <constructor-arg index="构造参数索引

              ref="要注入的bean对象id">

              </constructor-arg>

             </bean>

2)IoC概念

Inverse of Controller 称为反向控制或控制反转.确切理解可以称为控制转移.意思是将对象创建,初始化销毁和调用关系指定这些控制逻辑提取交给第三方Spring容器负责.

  在Spring框架中通过DI技术实现了IoC控制思想.

       ------------------------------------------------------------------------------------------------------------

       示例:UserService调用UserDAO体现出了IoC控制思想.

       由Spring容器负责ServiceDAO对象创建和销毁,并且由Spring容器负责控制这两个组件对象的使用关系.其中Spring容器在指定使用关系时,采用了DI技术建立关系.

5.DI注入的基本使用

     在使用SpringDI,可以注入多种数据类型.

     例如注入Bean对象,注入基本类型数据,注入一些集合数据.

     *1)注入Bean对象

       用ref指定Bean对象的id.

     2)注入基本类型数据

       用value指定字符串或数值数据.

     3)注入集合类型数据

       a. List集合

       b.Set集合

       c.Map集合

       d.Properties集合

  -----------------练习----------------------

  案例1:采用Spring IoC方式实现UserServiceUserDAO调用

  案例2:练习MessageService,注入各种类型数据.

  理论重点:DIIoC概念,SpringBean组件的管理

1.AOP概念

  1)什么是AOP

     Aspect Oriented Programming (面向方面编程)

     OOP是面向对象编程,AOP是在OOP基础之上一种更高级的设计思想.

     OOPAOP之间也存在一些区别,OOP侧重于对象的提取和封装.

     AOP侧重于方面组件,方面组件可以理解成封装了通用功能的组件,方面组件可以通过配置方式灵活的切入到某一批目标对象方法上.

   

  2)相关概念

     *a.方面组件(Aspect)

        封装了共同处理逻辑,将来可以切入到其他目标对象方法上的.

        例如事务控制,日志记录,权限控制等都可以采用方面组件封装

     *b.切入点(Pointcut)

        用于指定目标对象或方法的一个表达式.符合该表达式的方法将来启用方面组件更能处理.

     c.连接点(JoinPoint)

        连接点是切入点集合的一个元素.包含了方面组件与某一个目标方法衔接的所有信息.

     *d.通知(Advice)

        用于指定方面组件在目标方法上执行的时机.

        例如在目标方法之前调用,在目标方法之后调用等.

     e.目标对象(Target)

        通过切入点指定的对象.或者说成要启用方面组件功能的对象

     f.动态代理(AutoProxy)

        SpringAOP机制采用了动态代理技术实现.

        当某个目标组件采用AOP切入之后,通过Spring容器getBean方法返回的对象,是一个动态生成的代理类型.后续使用代理对象执行业务方法时,代理对象会在内部调用目标方法和方面组件方法.

  

  3)AOP应用示例

     ------------AOP编程步骤----------------

     a.先根据需求提取方面组件功能,并编写实现.

     b.根据需求提取目标对象,编写切入点表达式.

     c.根据需求选择方面功能和目标对象的切入时机,即选择合适的通知类型

     e.spring容器配置中添加以下配置

       --将方面组件和目标组件都添加<bean>定义

       --添加aop配置元素.指定方面组件,切入点,通知元素.

 2.通知类型

    通知决定了方面组件功能在目标对象方法上执行的时机.

    Spring框架提供了以下5中类型通知.

    a.前置通知<aop:before>

       方面功能在目标方法之前调用.

    b.后置通知<aop:afterReturning>

       方面功能在目标方法之后调用.(如果目标方法抛出异常则不会执行方面功能)

    c.最终通知<aop:after>

       方面功能在目标方法之后调用.(目标方法有无异常都会执行方面功能)

    d.环绕通知<aop:around>

       方面功能在目标方法之前和之后调用.

    e.异常通知<aop:afterThrowing>

       方面功能在目标方法抛出异常之后调用.

    -----------------------------------

    try{

    //前置通知

    //环绕通知--前置部分

    执行目标方法   

    //环绕通知--后置部分

    //后置通知

    }catch(){

     //异常通知

    }finally{

       //最终通知

    }

 3.切入点表达式

    用于指定目标对象及其方法.

*a.方法限定表达式

      execution(修饰符返回类型 方法名(参数列表抛出异常?)

      示例1:匹配所有对象中以set开始的方法.

         execution(* set*(..))

      示例2:匹配UserService类中的所有方法

         execution(* tarena.service.UserService.*(..))

       示例3:匹配tarena.service包下所有类的所有方法

         execution(* tarena.service.*.*(..))

       示例4:匹配tarena.service包下及其子包中所有类的所有方法

          execution(* tarena.service..*.*(..))

*b.类型限定表达式

       within(包名.类名)

       示例1:匹配UserService类中所有方法

          within(tarena.service.UserService)

       示例2:匹配tarena.service包下所有类的方法

           within(tarena.service.*)

        示例3:匹配tarena.service包及其子包中所有类型的方法

           within(tarena.service..*)

c.Beanid值限定表达式

       bean(Bean组件定义的IdName)

       <bean id="标示符" name="标示符">

       nameid作用相同,区别是name允许使用/特殊字符,id不允许.

 

       示例1:匹配Bean定义时id=userService的对象的方法

          bean(userService)

       示例2:匹配Bean定义时id值以Service结尾的对象的方法

          bean(*Service)

d.参数限定表达式

       args(参数类型)

       示例1:匹配只有一个参数,而且类型符合List的方法

          args(java.util.List)

     

     提示:以上表达式可以组合应用,利用&&,||进行连接.

1.Spring的注解配置

  从Spring2.5版本开始,支持两套配置方法,Schema XML方式和注解方式.

  从JDK5.0开始支持注解技术,spring2.5基于jdk5.0技术.

  注解方式特点:简单,方便,快捷.

  1)组件的自动扫描

    可按指定包路径,扫描该包下所有类,遇到指定注解标记,会将该Bean组件纳入到Spring容器.(等价与<bean>)

    -------------使用方法如下-------------

    a.首先在Spring XML配置文件开启组件扫描,指定扫描路径

      <context:component-scan base-package="扫描包路径"/>

    b.在类定义前面使用下列标记

      @Component : 通用(各种组件都可以)

      @Controller  : 控制组件使用 (Action)

      @Service : 业务组件使用 (Service)

      @Repository : 数据访问组件使用 (DAO)

    注意:扫描bean组件,默认用类名首字母小写当id属性值.如果需要自定义,可以用@Repository("id")

    c.如果需要对Bean对象创建进行控制,可以使用以下注解

      @Scope : 等价于原scope属性

      @PostConstruct : 等价于原init-method属性

      @PreDestroy : 等价于原destory-method属性

    d.如果需要将一个Bean对象给另一个Bean注入

       定义一个Bean对象变量,然后在变量定以前或setter方法定义前使用下面两种DI注入的注解配置.

       @Resource : 可以按类型匹配

       @Resource(name="id") : 按指定id值名称匹配

       @Autowired : 可以按类型匹配

       @Autowired @Qulifier("id") : 按指定id值名称匹配

 

   2)AOP注解配置   

      ---------------------------------使用方法如下------------------------------------------

      a.Spring XML配置中开启AOP注解配置

         <aop:aspectj-autoproxy />

      b.定义方面组件,在方面组件中使用下面注解标记

        @Component //将方面组件扫描到容器(类定义前使用的标记)

        @Aspect //将当前组件指定为方面组件(类定义前使用的标记)

        @Pointcut("表达式") //指定切入点表达式(方法定义前使用的标记)

        @AfterThrowing()//异常通知(处理方法前使用)

        @AfterReturning()//后置通知(处理方法前使用)

        @After()//最终通知(处理方法前使用)

        @Before()//前置通知(处理方法前使用)

        @Around()//环绕通知(处理方法前使用)

 

 2.Spring整合数据访问技术

    Spring框架提供了整合JDBCHibernate等技术的支持.

    Spring框架对数据访问基础主要提供了以下支持.

    ---Spring提供了一致的异常处理,Spring提供了一套异常类型,封装了不同数据访问技术的特有异常类型.

        例如DataAccessException

    ---Spring提供了一套DAO支持类.

        DaoSupport,Template系列组件.

        例如JdbcDaoSupport,HibernateDaoSupport

               JdbcTemplate,HibernateTemplate

    ---Spring提供了统一的事务处理(采用AOP方式管理).

======================================

1)Spring如何整合JDBC技术

      a.首先引入JDBC相关的开发包

         驱动开发包(mysqldriver.jar),

         连接池开发包(commons-dbcp.jar,commons-collection.jar,commons-pool.jar)

      b.根据数据表编写实体类

      c.编写DAO实现(基于JdbcDaoSupport,JdbcTemplate两个类)

      d.Spring容器中配置DAO.需要注入dataSource对象.

           

  getBean("dao")<--UserDAO(使用template)<--注入--DataSource(Connection资源)

 JdbcTemplate基本使用:

         update() : 用于执行insert,update,delete操作的SQL语句

         queryForObject: 用于执行最多返回一行记录的select查询

         query:用于执行返回多行记录的select查询

         queryForInt:用于执行返回一行一列的select查询.

2)Spring如何整合Hibernate技术

       a.引入以下开发包

           mysql驱动

           dbcp连接池

           hibernate开发包

       b.根据表添加实体类和hbm.xml映射文件

       c.编写DAO实现(基于HibernateDaoSupport,HibernateTemplate两个类)

       d.Spring容器中配置DAO.需要注入sessionFactory对象.

       

UserDAO(使用template)<--注入--SessionFactory<--注入--DataSource(Connection资源)

 

HibernateTemplate的使用:

      save() : 保存对象

      update() : 更新对象

      delete() : 删除对象

      get()/load() : 按主键id查询

      find() : 执行hql语句

===============练习========================

1.采用Spring+JDBC方式实现对d_user的基本操作

2.采用Spring+Hibernate方式实现对d_user的基本操作

    -----------------回顾Spring整合Hibernate步骤--------------------------------------

1)按以下顺序引入开发包

   a.SpringIoCAop开发包

   b.Hibernate开发包(含驱动)

   c.dbcp开发包

2)添加实体类和hbm.xml映射文件

3)编写DAO接口,然后编写实现类

4)Spring容器中配置DAO组件.

5)完毕,测试DAO

Spring整合Struts2步骤---------

 

6)引入Struts2核心开发包(6)

7)添加Struts2 struts.xml配置文件和web.xml控制器配置

8)根据请求编写Action组件,采用注入方式使用dao对象

9)Spring容器配置Action Bean.注入需要的DAO组件

10)struts.xml配置Action和请求的对应关系

     --添加一个整合插件包struts2-spring-plugin.jar.

        可以使struts访问spring容器.

     --<action>元素的class属性与spring容器中<bean>元素的id值一致

11)web.xml中添加ContextLoaderLisener配置,在启动时实例化Spring容器

12)修改JSP,利用标签和表达式显示数据

 

========================================================

1.SSH框架工作流程

  1)启动Tomcat服务器.

    --加载web.xml,创建StrutsFilter控制器对象和ContextLoaderLisener对象

    --创建Struts控制器时,需要加载struts.xml,struts-plugin.xml,

sstruts-default.xml,default.properties

    --创建ContextLoaderLisener对象时,需要加载springxml配置

  2)浏览器发送请求,请求进入StrutsFilter控制器对象

     --如果请求为list.action/list格式,属于Action请求,会进入struts.xml中寻找匹配的<action>定义

     --如果请求为其他格式,例如/list.jsp,属于非Action请求会直接去调用jsp资源生成响应.

   3)struts.xml配置中,根据<action>定义,struts-spring-plugin.jar

      提供的ObjectFactorySpring容器获取Action对象

   4)Spring容器创建Action对象,DAO注入给Action对象,Struts返回.

   5)Struts控制器调用Action对象进行请求的处理,处理完毕返回一个String类型的result标识.

   6)Struts2控制器根据result标识调用Result组件对象生成响应信息.

   7)将响应信息输出给客户端.

   

   list.action-->web.xml-->struts.xml-->applicationContext.xml

整合SSH两种方案:

方案一:

 

方案分析:

Action交给Spring容器管理。

缺点:增加了Spring容器的负担。

方案二:

 

方案分析:

Action独立管理,和单独使用Struts时一样。

优点:

减少了Spring容器的负担,开发简单。

1.Spring框架事务处理

  Spring提供以下两种事务管理方式;

*1)声明式事务管理(通过配置实现)

a.首先在Spring容器定义一个事务管理Bean组件

        DataSourceTransactionManager(适用于JDBC技术)

        HibernateTransactionManager(适用于Hibernate技术)

b.采用SpringAOP配置方式将事务管理Bean作用到目标对象方法

 2)编程式事务管理(通过编码实现)

    public Object someServiceMethod() {

    return transactionTemplate.execute(new TransactionCallback() {

      public Object doInTransaction(TransactionStatus status) {

        updateOperation1();

        return resultOfUpdateOperation2();

      }

    });

  

  

 2.SpringAOP事务管理的事务类型

 

REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。

这是最常见的选择。

SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。

MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。

REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。

NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,

就把当前事务挂起。

NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作。

拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对DataSourceTransactionManager有效

Spring开发异常处理:

异常解决:

1、在返回JSON字符串的类中,其成员变量如果是由Spring注入的并提供了getter方法,则抛此异常。

2、懒加载导致JSONException

生 成json的类是hibernate的model类,当中有一些对象是延迟加载的,这些对象并不是原model类中的属性,而是由cglib这个字节码生 成器动态生成的对象。Hibernate在这个子类中附加了hibernateLazyInitializer等等的附加属性。由于jsonplugin 并不区分类和动态生成的类,所以也会试图序列化hibernateLazyInitializer属性,从而导致出现上述异常。

将那些延迟加载的对象都用一个默认构造方法的对象来代替,但这样不利用复用。

思路如下:

1、判断这个对象是否由cglib生成的。代码如下:

boolean isCreateByCGLIB=clazz.getName().indexOf("$$EnhancerByCGLIB$$")>-1?true:false; 如果此对象确实是由cglib生成的,那么我们取此对象的基类,这一步很关键,因为通过取得此对象的基类,我们就忽略了所有由cglib生成的跟 hibernate相关的属性。

2、通过反射,取得此对象的所有继承于基类的属性。

3、通过反射,取得此对象的所有get方法器(这一步不能省,不然出来的属性就少了)

4、输出json

解决方案:

1、Spring注入的方法只提供setter方法,不提供getter方法。

2、排除hibernateLazyInitializer属性除外:

<result name="resultValue" type="json">

<param name="excludeProperties">.*hibernateLazyInitializer</param>

</result>

这样存在一个问题:关联的对象不能序列化成一个json

第二个解决方案:

JsonPlugin在分析类结构并序列化时,对于CGLig动态生成的类也是按照一般类来看待的,这就导致了如下的问题:

在一个应用中,某些情况下,一个服务类返回的实体并不是原有实体类的对象,而是CGLib动态生成的子类。例如使用Hibernate的时候,某些情况下DAO返回的是EntityClassName$$EnhancerByCGLIB$$ac21e

这样的类的对象。Hibernate在这个子类中添加了hibernateLazyInitializer等等的附加属性。由于jsonplugin并不区分类和动态生成的类,所以也会试图序列化hibernateLazyInitializer属性,

从而导致出现如下异常:

java.sql.SQLException:Positioned Upadate not supported.

at com.mysql.jdbc.ResultSet.getCursorName(ResultSet.java:1800)

另外,CGLIB生成的类,某些方法上的@JSON标记奇怪的丢失了,导致标记了@JSON(serialize=false)的属性也被序列化。

在网上查了很久没有发现相关的文章,所以手动修改了jsonplugin的代码

类:com.googlecode.jsonplugin.JSONWriter,修改bean()方法:

java代码:

private void bean(Object object)throw JSONException{

this.add("{");

BeanInfo info;

try{

Class clazz=object.getClass();

info=((object==this.root)&&this.ignoreHierarchy)?Introspector.getBeanInfo(clazz);

PropertyDescriptor[] props=info.getPropertyDescriptors();

boolean hasData=false;

for(int i=0;i<props.length;++i){

PropertyDescriptor prop=props[i];

String name=prop.getName();

Method accessor=prop.getReadMethod();

Method baseAccessor=null;//这里添加一个临时变量作为真实希望序列化的属性的accessor方法引用

if(clazz.getName().index("$$EnhancerByCGLIB$$")>-1){

//如果是CGLIB动态生成的类

try{

//下面的逻辑是根据CGLIB动态生成的类名,得到原来的实体类名

}

}

 

}

}

}

 

ssh中哪些用到了动态代理?

Spring的AOP,hibernate的延迟加载,struts2中的映射类,映射到的是一个ActionProxy

Struts2.1.6控制器有问题,有中问乱码问题。先取值再设置编码。2.1.8换了控制器。

 

posted @ 2013-04-10 17:12  志成中国  阅读(685)  评论(0编辑  收藏  举报