目  录

一、 Spring概述 1

1.1 Spring框架的作用 1

1.2 Spring框架的优点 1

1.3 Spring框架的容器 1

二、 Spring容器的基本应用 2

2.1如何将一个Bean组件交给Spring容器 2

2.2如何获取Spring容器对象和Bean对象 2

2.3如何控制对象创建的模式 2

2.4 Bean对象创建的时机 2

2.5为Bean对象执行初始化和销毁方法 2

2.6案例:Spring框架的使用以及2.1节-2.5节整合测试 3

三、 Spring框架IoC特性 5

3.1 IoC概念 5

3.2 DI概念 5

3.3案例:测试IoC(set注入) 5

3.4案例:测试IoC(构造注入) 6

3.5案例:不用JDBC访问数据库,而是采用Hibernate访问 6

四、 Spring中各种类型的数据注入 7

4.1 Bean对象注入 7

4.2基本数据的注入 7

4.3集合的注入 7

4.4案例:各类数据注入 7

五、 AOP概念 10

5.1什么是AOP 10

5.2 AOP和OOP的区别 10

5.3 AOP相关术语 10

5.4案例:AOP的使用,模拟某些组件需要记录日志的功能 11

5.5通知类型 11

5.6切入点 12

5.7案例:环绕通知,修改5.4案例使之动态显示所执行的操作 12

5.8案例:利用AOP实现异常处理,将异常信息写入文件 13

六、 Log4j日志记录工具 14

6.1 Log4j介绍 14

6.2 Log4j的使用 14

6.3案例:修改5.8案例,使用Log4j记录日志 14

七、 Spring注解配置 16

7.1组件扫描功能 16

7.2组件扫描的使用方法 16

7.3注入注解标记使用方法 17

7.4 AOP注解标记使用方法 17

八、 Spring对数据访问技术的支持 19

8.1 Spring提供了统一的异常处理类型 19

8.2 Spring提供了编写DAO的支持类 19

8.3 Spring提供了声明式事务管理方法 19

8.4 Spring框架如何使用JDBC技术 19

8.5连接池优点 22

8.6 Spring框架如何使用Hibernate技术 22

8.7 Spring+Hibernate如何使用Session、Query等对象 25

8.8 Spring框架和Struts2整合应用 25

8.9案例:采用SSH结构重构资费管理模块 27

九、 整合开发包struts-spring-plugin.jar 31

9.1 Struts2创建对象的方式 31

9.2 struts-spring-pligin.jar创建对象的方式 31

9.3 struts-spring-plugin.jar的内部实现 31

9.4原理图1 31

9.5原理图2 32

9.6注意事项 32

9.7注入规则 32

十、 Spring的事务管理 33

10.1声明式事务管理(基于配置方式实现事务控制) 33

10.2编程式事务管理(基于Java编程实现事务控制),不推荐用! 34

10.3 Spring中常用的事务类型 34

十一、 Spring的MVC 35

11.1 Spring MVC的体系结构 35

11.2 Spring MVC的工作流程 35

11.3案例:简易登录(基于XML配置,不推荐使用) 35

11.4案例:修改11.3案例(基于注解配置,推荐使用) 37

十二、 其他注意事项 39

12.1 Spring的核心模块 39

12.2表单中action属性的相对、绝对路径问题 39

12.3用SSH重构NetCTOSS项目模块的步骤 39



一、Spring概述

我们学习Spring框架的最终目的是用它整合Struts2、Hibernate框架(SSH)。

1.1 Spring框架的作用

Spring框架主要负责技术整合(可以整合很多技术),该框架提供IoC和AOP机制,基于这些特性整合,可以降低系统组件之间的耦合度,便于系统组件的维护、扩展和替换。

1.2 Spring框架的优点

其实与Spring框架的作用相同:

在SSH中,主要是利用Spring容器管理我们程序中的Action、DAO等组件,通过容器的IoC机制,可以降低Action、DAO之间的耦合度(关联度),利用AOP进行事务管理等共通部分的处理。

在SSH中,Struts2主要是利用它的控制器,而不是标签、表达式;Hibernate主要利用它的数据库访问;Spring主要是利用它的整合。

1.3 Spring框架的容器

Spring框架的核心是提供了一个容器(是我们抽象出来的,代指后面的类型)。该容器类型是BeanFactory或ApplicationContext(建议用这个类型,它是BeanFactory的子类,功能更多)。

该容器具有以下功能:

1)容器可以创建和销毁组件对象,等价于原来“工厂”类的作用。

2)容器可以采用不同的模式创建对象,如单例模式创建对象。

3)容器具有IoC机制实现。

4)容器具有AOP机制实现。

二、Spring容器的基本应用

2.1如何将一个Bean组件交给Spring容器

1)Bean组件其实就是个普通的Java类!

2)方法:在applicationContext.xml中添加以下定义,见2.6案例中step4。

  <bean id="标识符" class="Bean组件类型"></bean>

2.2如何获取Spring容器对象和Bean对象

1)实例化容器:

ApplicationContext ac=new ClassPathXmlApplicationContext("/applicationContext.xml");

//FileSystemXmlApplicationContext("");//去指定的磁盘目录找,上面的为去Class路径找

2)利用getBean("标识符")方法获取容器中的Bean对象。见2.6案例中step5。

2.3如何控制对象创建的模式

Spring支持singleton(单例)和prototype(原型,非单例)两种模式。

默认是singleton模式,可以通过<bean>的scope属性修改为prototype模式。以后在Web程序中,通过扩展可以使用request、session等值。见2.6案例中step4、step7。

例如:<bean id="标识符" scope="prototype" class="Bean组件类型"></bean>

u 注意事项:对于NetCTOSS项目,一个请求创建一个Action,所以用Spring时必须指明prototype,否则默认使用singleton会出问题。而DAO则可用singleton模式。

2.4 Bean对象创建的时机

1)singleton模式的Bean组件是在容器实例化时创建。

2)prototype模式是在调用getBean()方法时创建。

3)singleton模式可以使用<bean>元素的lazy-init="true"属性将对象的创建时机推迟到调用getBean()方法。也可以在<beans>(根元素)中使用default-lazy-init="false"推迟所有单例Bean组件的创建时机。见2.6案例中step3、step4。

例如:<bean id="标识符" scope="singleton" lazy-init="true" class="Bean组件类型"></bean>

   <beans ...... default-lazy-init="false"></beans>

2.5为Bean对象执行初始化和销毁方法

1)初始化:①可以利用<bean>元素的init-method="方法名"属性指定初始化方法。

②指定的初始化方法是在构造方法调用后自动执行。若非单例模式,则每创建一个对象,则执行一次初始化方法(单例、非单例模式都可)。见2.6案例中step3、step4。

u 注意事项:

v 初始化的三种方式:写构造方法中;或写{ }中(代码块);Spring框架中<bean>元素写init-method="方法名"属性。

v 初始化不能用static{ },它是类加载调用,比创建对象要早。

2)销毁:①可以利用<bean>元素的destroy-method="方法名"属性执行销毁方法。

②指定的销毁方法是在容器关闭时触发,而且只适用于singleton模式的组件(只能为单例模式)。见2.6案例中step3、step4、step6。

2.6案例:Spring框架的使用以及2.1节-2.5节整合测试

step1:导入Spring开发包:spring.jar、commons-logging.jar和配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

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

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:jee="http://www.springframework.org/schema/jee"

xsi:schemaLocation="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/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/jee     http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

</beans>

step2:在org.tarena.dao包下,创建接口CostDAO,添加两个方法

  public void delete(); public void save();

step3:在org.tarena.dao包下,创建JdbcCostDAO类,并实现CostDAO接口

public JdbcCostDAO(){ System.out.println("创建CostDAO对象"); }

public void myinit(){ System.out.println("初始化CostDAO对象"); }

public void mydestroy(){ System.out.println("销毁CostDAO对象"); }

public void delete() { System.out.println("利用JDBC技术实现删除资费记录"); }

public void save() { System.out.println("利用JDBC技术实现保存资费记录"); }

step4:在applicationContext.xml配置文件中,将Bean组件(Java类)交给Spring容器

<bean id="jdbcCostDAO" scope="singleton" lazy-init="true" init-method="myinit"

   destroy-method="mydestroy" class="org.tarena.dao.JdbcCostDAO">

</bean>

step5:在org.tarena.test包下,创建TestApplicationContext类,获取Spring容器对象,并测试

@Test

public void test1(){ String conf="/applicationContext.xml";

ApplicationContext ac=new ClassPathXmlApplicationContext(conf);//实例化容器

CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO");//获取Bean对象

costDAO.save(); costDAO.delete(); }

step6:在TestApplicationContext类中添加方法,测试销毁对象

@Test

/**关闭容器才会触发销毁,但关闭容器方法封装在AbstractApplicationContext类中 */

public void test2(){ String conf="/applicationContext.xml";

ApplicationContext ac=new ClassPathXmlApplicationContext(conf);

AbstractApplicationContext ac=new ClassPathXmlApplicationContext(conf);

CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO"); ac.close(); }

step7:在TestApplicationContext类中添加方法,测试单例

@Test

public void test3(){ String conf="/applicationContext.xml";

ApplicationContext ac= new ClassPathXmlApplicationContext(conf);

CostDAO costDAO1=(CostDAO)ac.getBean("jdbcCostDAO");

CostDAO costDAO2=(CostDAO)ac.getBean("jdbcCostDAO");

System.out.println(costDAO1==costDAO2);//true,所以Spring默认为单例模式 }

……………………

………………

…………

五、AOP概念

5.1什么是AOP

Aspect Oriented Programming,被称为面向方面编程。对单个对象(一对一)的解耦用IOC,而当有个共通组件,它对应多个其他组件(一对多),则解耦用AOP。如,拦截器。这也是为何在程序中大量的用IoC,而AOP却用的很少,因为程序中不可能有很多的共通部分。

5.2 AOP和OOP的区别

OOP是面向对象编程,AOP是以OOP为基础的。

OOP主要关注的是对象,如何抽象和封装对象。

AOP主要关注的是方面,方面组件可以以低耦合的方式切入到(作用到)其他某一批目标对象方法中(类似于Struts2中的拦截器)。

AOP主要解决共通处理和目标组件之间解耦。

5.3 AOP相关术语

1)方面(Aspect):指的是封装了共通处理的功能组件。该组件可以作用到某一批目标组件的方法上。
2)连接点(JoinPoint):指的是方面组件和具体的哪一个目标组件的方法有关系。

3)切入点(Pointcut):用于指定目标组件的表达式。指的是方面组件和哪一批目标组件方法有关系。多个连接点组成的集合就是切入点。如:a、b为切入点,1、2为连接点。


4)通知(Advice):用于指定方面组件和目标组件方法之间的作用时机。例如:先执行方面组件再执行目标方法;或先执行目标方法再执行方面组件。

5)目标(Target):利用切入点指定的组件和方法。

6)动态代理(AutoProxy):Spring同样采用了动态代理技术实现了AOP机制。当使用AOP之后,从容器getBean()获取的目标组件,返回的是一个动态生成的代理类。然后通过代理类执行业务方法,代理类负责调用方面组件功能和原目标组件功能。

Spring提供了下面两种动态代理技术实现:

1)采用CGLIB技术实现(目标组件没有接口采用此方法)

  例如:public class 代理类 extends 原目标类型 { }

     CostAction action=new 代理类();//代理类中有原来类的方法

2)采用JDK Proxy API实现(目标组件有接口采用此方法,即实现了某个接口)

  例如:Public class 代理类 implements 原目标接口 { }

       CostDAO costDAO=new 代理类();//代理类去实现了原目标接口,所以没有原来类的方法

5.4案例:AOP的使用,模拟某些组件需要记录日志的功能

接3.3、3.4案例,想让所有的操作进行日志记录,那么按以前的方式就需要给所有Action或DAO中添加记录日志的代码,如果Action或DAO很多,那么不便于维护。而使用AOP机制,则可以很方便的实现上述功能:

step1:导入AOP需要的包:aopalliance.jar、aspectjrt.jar、aspectjweaver.jar、cglib-nodep-2.1_3.jar

step2:在org.tarena.aop包下新建LoggerBean类,并添加logger方法用于模拟记录日志功能

  public void logger(){ System.out.println("记录了用户的操作日志"); }

step3:在applicationContext.xml配置文件中,添加AOP机制

<bean id="loggerBean" class="org.tarena.aop.LoggerBean"></bean>

<aop:config>

<!--定义切入点,指定目标组件和方法。id:可任意起个名字。expression:指定哪些组件是目标,并作用在这些目标的方法上。下面表示所有Action中的所有方法为切入点-->

<aop:pointcut id="actionPointcut" expression="within(org.tarena.action.*)" />

<!--定义方面,将loggerBean对象指定为方面组件,loggerBean从普通Bean组件升级为了方面组件-->

<aop:aspect id="loggerAspect" ref="loggerBean">

<!-- aop:before在操作前执行 aop:after操作后执行 -->

<!-- 定义通知,aop:before:指定先执行方面组件的logger方法,再执行切入点指定的目标方法。aop:after:与aop:before相反 -->

<aop:before pointcut-ref="actionPointcut" method="logger"/>

</aop:aspect>

</aop:config>

step4:执行3.3案例step3,则发现添加操作已有了记录日志功能

创建CostDAO对象 初始化CostDAO对象   记录了用户的操作日志

处理资费添加操作 利用JDBC技术实现保存资费记录

step5:执行3.4案例step3,则发现删除操作已有了记录日志功能,记得加无参构造方法!

记录了用户的操作日志 处理资费删除操作

利用Hibernate技术实现删除资费记录

u 注意事项:DeleteAction用的是构造注入,所以此处要把无参构造器再加上!因为AOP底层调用了DeleteAction的无参构造方法。不加则报错:Superclass has no null constructors but no arguments were given

5.5通知类型

通知决定方面组件和目标组件作用的关系。主要有以下几种类型通知:

1)前置通知:方面组件在目标方法之前执行。

2)后置通知:方面组件在目标方法之后执行,目标方法没有抛出异常才执行方面组件。

3)最终通知:方面组件在目标方法之后执行,目标方法有没有异常都会执行方面组件。

4)异常通知:方面组件在目标方法抛出异常后才执行。

5)环绕通知:方面组件在目标方法之前和之后执行。

  try{ //前置通知执行时机<aop:before>

  //执行目标方法

  //后置通知执行时机<aop:after-returning>

  }catch(Exception e){//异常通知执行时机<aop:after-throwing>

  }finally{ //最终通知执行时机<aop:after>

  }//环绕通知等价于前置+后置<aop:around>

5.6切入点

切入点用于指定目标组件和方法,Spring提供了多种表达式写法:

1)方法限定表达式:指定哪些方法启用方面组件。

①形式:execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)

②示例:

  execution(public * * (..)),匹配容器中,所有修饰符是public(不写则是无要求的),返回类型、方法名都没要求,参数列表也不要求的方法。

    execution(* set*(..)),匹配容器中,方法以set开头的所有方法。

    execution(* org.tarena.CostDAO.*(..)),匹配CostDAO类中的所有方法。

    execution(* org.tarena.dao.*.*(..)),匹配dao包下所有类所有方法。

    execution(* org.tarena.dao..*.*(..)),匹配dao包及其子包中所有类所有方法。

2)类型限定表达式:指定哪些类型的组件的所有方法启用方面组件(默认就是所有方法都启用,且知道类型,不到方法)。

①形式:within(类型) ②示例:

  within(com.xyz.service.*),匹配service包下的所有类所有方法

  within(com.xyz.service..*),匹配service包及其子包中的所有类所有方法

  within(org.tarena.dao.CostDAO),匹配CostDAO所有方法

u 注意事项:within(com.xyz.service..*.*),为错误的,就到方法名!

3)Bean名称限定:按<bean>元素的id值进行匹配。

①形式:Bean(id值) ②示例:

  bean(costDAO),匹配id=costDAO的bean对象。

  bean(*DAO),匹配所有id值以DAO结尾的bean对象。

4)args参数限定表达式:按方法参数类型限定匹配。

①形式:args(类型) ②示例:

  args(java.io.Serializable),匹配方法只有一个参数,并且类型符合Serializable的方法,public void f1(String s)、public void f2(int i)都能匹配。

u 注意事项:上述表达式可以使用&&、| | 运算符连接使用。

5.7案例:环绕通知,修改5.4案例使之动态显示所执行的操作

………………

…………

……


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐