spring的作用及优势
Spring的作用
Spring用于整合软件开发中的各种框架,提供组件之间松耦合的平台,目的就是将耦合度降至最低,即解耦。(开源,都是相通的。所有的框架都是基于反射实现的。)
1. spring用于整合各种框架,简化开发比如DAO层,只要使用spring的一个工具类即可
2. 解耦,松耦合让类与类之间的联系不大。降低依赖性,换了一个类,不影响整个项目。项目开发过程中,难点不是模块不好开发,功能能否实现,而是怎么整合各个程序员开发的程序代码。类比较多的时候不好管理,spring是将类与类之间的关联降至最低。
3. 我们在使用Spring框架时,主要是使用Spring容器的两个特性:IoC和AOP。
spring的优势
1. spring是一个开源框架,开放源代码
2. spring 为JavaEE应用提供轻量级的解决方案;
3. spring提倡”最少侵入”;(降低耦合度,换了谁都可以)
4. spring是一个优秀的MVC框架
5. 基于依赖注入的核心机制,基于AOP的声明式事务管理
spring容器
概念
spring框架的核心就是提供了一个容器。该容器类型是BeanFactory或者ApplicationContext(建议用这个类型,它是BeanFactory的子类,功能更多)
Spring的核心接口:BeanFactory
该容器具有以下功能:
a. 容器可以创建和销毁组件对象,等价于原来”工厂”类的作用。
b. 容器可以采用不同的模式创建对象,如单例模式创建对象
c. 容器具有IOC机制实现
d. 容器具有AOP机制实现
容器实例化
BeanFactory创建并负责管理Spring bean的上下文的生命周期。BeanFactory接口指明了spring到底干了什么事。
org.springframework.beans.factory.xml.XmlBeanFactory 是BeanFactory的实现类,在XMLBeanFactory中,以xml结构方式描述对象及对象之间的依赖关系。
创建BeanFactory实例时,必须提供Spring容器管理bean的详细配置信息,相应的XML文件作为参数传入。
BeanFactory factory = new ClassPathXmlApplicationContext(“applicationContext.xml”);
BeanFactory factory = new FileSystemXmlApplicationContext(“F:/applicationContext.xml”);
推荐使用ClassPathXmlApplicationContext方式
Bean对象
Bean指代的就是对象,BeanFactory是产生和管理bean的容器。
a. 产生bean
b. 控制Bean的产生数量
c. 控制Bean的产生时间
d. 控制Bean的生命周期
Spring通过BeanFactory管理和配置bean ,在Spring里,任何的java对象、java组件bean处理。
spring环境的搭建
1. 创建web工程
2. 导入jar spring.jar commons-logging.jar log4j-1.2.15.jar
3. 导入配置文件 log4j.properties applicationContext.xml
实例化Bean对象方式
a. 用构造器来实例化
<bean id=”XXBean” class=”” />
b. 使用静态工厂方法实例化
<bean id=”XXbean” class=”XXXfactoryClass” factory-method=”” />
c. 使用实例工厂方法实例化
<bean id=”XXfactory” class=”” /> <bean id=”XXXbean” factory-bean=”XXX” factory-method=”” />
将一个Bean组件交给Spring容器管理,获取spring容器对象和Bean对象
案例
1. 新建web功能
2. 导入jar包
3. 创建Bean1对象
public class Bean1{}
4. 工厂类
public class Bean1Factory { public Bean1Factory(){ System.out.println("创建工厂对象"); } //使用静态方法获得对象 public static Bean1 getBean1(){ return new Bean1(); } //使用实例方法获得对象 public Bean1 getBean(){ return new Bean1(); } }
5. 添加配置文件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:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "> <!-- 把bean1配置到是pring,让spring管理bean1 id:该bean的标识,用来区分开每一个bean(类的对象) class:指明配置的具体的类,包名+类名,底层利用反射创建对象class.forName()--> <bean id="bean1" class="web.test.bean.Bean"></bean> <!-- 在spring中调用工厂的静态方法创建对象 --> <bean id="bean2" class="web.test.factory.Bean1Factory" factory-method="getBean1"></bean> <!-- 在spring中,调用工厂的实例方法,获得对象 --> <!-- 1、先把工厂实例化 --> <bean id="factory" class="web.tarena.factory.Bean1Factory"></bean> <!-- 2、调用工厂中的实例方法获得对象 --> <bean id="bean3" factory-bean="factory" factory-method="getBean1"></bean> </beans>
5. 测试类
public class TestSping { public void test1() { /** * ClassPathXmlApplicationContext:用来加载核心配置文件。applicationContext.xml,比较消耗资源,重量级对象,所以应该写成静态的,只加载一次。 * 重量级对象:功能强大,消耗资源较多,ClassPathXmlApplicationContext和HibernateSessionFactory都是重量级对象 * BeanFactory接口 Object getBean(String name); name就是指配置文件中的id值 返回结果是Object,spring可以管理任意的对象 */ ApplicationContext beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过构造器创建Bean对象 Bean bean1 = (Bean)beanFactory.getBean("bean1"); System.out.println(bean1); // 通过工厂的静态方法创建Bean对象 Bean bean2 = (Bean)beanFactory.getBean("bean2"); System.out.println(bean2); // 通过实例化工厂方法创建Bean对象 Bean bean3 = (Bean)beanFactory.getBean("bean3"); System.out.println(bean3); } public void test2() { // 单例模式下不同的beanFactory创建出来的Bean对象不是同一个 BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("---------------------------"); Bean bean1 = (Bean)beanFactory.getBean("bean1"); System.out.println(bean1); BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean bean11 = (Bean)factory.getBean("bean1"); System.out.println(bean11); } }
spring容器对Bean组件的管理
Bean对象的创建模式
Spring支持singleton(单例)和prototype(原型,非单例)两种模式。Singleton:每次调用getBean()返回同一个对象。prototype:每次调用getBean()返回新的对象。
默认是singleton模式,可以通过<bean>的scope属性修改为prototype模式。例如:<bean id=”标识符” scope=”prototype” class=”Bean组件类型” />
Bean对象的创建时机
a. singleton模式的Bean组件是在容器实例化时创建的。
b. prototype模式是在调用getBean()方法时创建。
c. singleton模式可以使用<bean>元素的lazy-init=”true”属性将对象的创建时机推迟到调用getBean()方法。也可以在<beans>(根元素)中使用default-lazy-init=”true”推迟所有单例Bean组件的创建时机。
Bean对象的初始化和销毁
初始化
1. 可以利用<bean>元素的init-method=”方法名”属性指定初始化方法。
2. 指定的初始化方法是在构造方法调用后自动执行。若非单例模式,则每创建一个对象,则执行一次初始化方法。
初始化的三种方式
1. 写在构造方法中
2. 写在{}中(代码块);
2. spring框架中<bean>元素写init-method=”方法名”
初始化不能用static{},它是类加载调用,比创建对象要早。
销毁
1. 可以利用<bean>元素的idestory-method=”方法名”属性执行销毁方法。
2. 销毁方法是在容器关闭时触发,而且只适用于singleton模式的组件
//spring容器关闭 AbstractApplicationContext ac=new ClassPathXmlApplicationContext(); ac.getBean(“feeDAO”); ac.close();//该方法执行后,Bean对象被销毁
spring框架的IOC特性
IOC概念
IOC,Inverse of Controller被称为控制反转或反向控制,其实真正体现的是”控制转移”。所谓的控制指的是负责对象关系的指定、对象创建、初始化、和销毁等逻辑。
IOC指的是将控制逻辑交给第三方框架或容器负责(即把Action中的控制逻辑提出来,交给第三方负责),当两个组件发生改变时,只需要修改框架或容器的配置即可。
控制反转就是应用本身(Action)不负责依赖对象(Dao)的创建及维护,依赖对象的创建及维护是由外部容器(BeanFactory)负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
DI概念
DI,Dependency Injection依赖注入
依赖注入就是指:在运行期,由外部容器(BeanFactory)动态地将依赖对象(Dao)注入到组件(Action)中。Spring框架采用DI技术实现了IoC控制思想。
Spring提供了两种形式的注入方式:
①(常用)setter方式注入:依靠set方法,将组件对象传入(可注入多个对象)
a. 首先添加属性变量和set方法
b. 在该组件的<bean>定义中采用下面的描述方式 <property name=”属性名” ref=”要注入的Bean对象的id值”></property>
c. 属性名与set方法名对应,和属性名无关。例如CostAction中有costDao属性,而他的标准set方法名为setCostDAO,那么配置文件中的name就应该写costDAO(去掉set,首字母小写)。name不是看定义的属性名,而是set方法名。
②(用的少)构造方法注入:依靠构造方法,将组件对象传入
a. 在需要注入的组件中,添加带参数的构造方法。
b. 在该组件<bean>定义中,使用下面格式描述<constructor-arg index=”参数索引” ref=”要注入的Bean对象的id值” />
DI和IOC的关系
Spring是具有IoC特性的框架,实现Ioc是由Spring容器完成的,Spring容器通过DI建立起对象之间的关系。可以这样理解:DI是IoC实现的一种手段,IoC的理论通过DI实现。
案例
UserDao
public interface UserDAO { public void saveUser(String user); }
UserDAOOracleImpl
public class UserDAOOracleImpl implements UserDAO{ public void saveUser(String user) { System.out.println("oracle实现"); } }
UserDAOMysqlImpl
public class UserDAOMysqlImpl implements UserDAO{ public void saveUser(String user) { System.out.println("mysql实现"); } }
UserAction
public class UserAction { private UserDAO userDao; //Action依赖DAO,保存数据。把依赖的UserDao写到方法的外面,可以通过set方法注入不同实现的dao对象 //这样操作不同的数据库的时候,不用对UserAction进行任何的修改或者完全通过配置文件进行配置 public void saveUser(String username){ userDao.saveUser(); } //set方式注入 //public void setUserDao(UserDAO userDao) { // this.userDao = userDao1; //} //构造方式注入 public UserAction(UserDAO userDao) { this.userDao=userDao; } }
applicationContext.xml
<beans ...> <!-- 把所有的Dao和Action全部配置到spring当中,全部纳入spring的管理 --> <bean id="userDaoMySql" class="com.test.dao.impl.UserDaoMySqlImpl"></bean> <bean id="userDaoOracle" class="com.test.dao.impl.UserDaoOracleImpl" ></bean> <bean id="userAction” class="com.tarena.web.action.UserAction" > <!-- set方式注入 --> <!--<property name="userDao1" ref="userDaoOracle"></property>--> <!-- 构造器方式注入 --> <constructor-arg ref="userDaoDB2"></constructor-arg> </bean> </beans>
各种数据类型的注入
Bean3
//使用spring创建对象的时候,创建的同时给对象属性赋值 public class Bean3 { private int id; private String name; private String[] arrayValue; private List listValue; private Set setValue; private Map mapValue; private Properties propValue; //get set方法 }
appliactionContext.xml
<beans ..> <bean id="bean3" class="web.tarena.bean.Bean3"> <!-- set方式注入 --> <property name="id" value="12"></property> <property name="name" value="liu"></property> <property name="arrayValue"> <list> <value>a</value> <value>b</value> <value>c</value> </list> </property> <property name="listValue"> <!-- list和数组很相似 --> <list> <value>list1</value> <value>list2</value> <value>list3</value> </list> </property> <property name="setValue"> <set> <value>set1</value> <value>set2</value> </set> </property> <property name="mapValue"> <map> <entry key="k1" value="v1"></entry> <entry key="k2" value="v2"></entry> </map> </property> <property name="propValue"> <props> <prop key="p1">v1</prop> <prop key="p2">p2</prop> </props> </property> </bean> </beans>
AOP概念
什么是AOP
AOP,Aspect Oriented Programming被称为面向方面编程。对单个对象(一对一)的解耦用IoC,而当有共通组件,它对应多个其他组件(一对多),则解耦用AOP。如拦截器。这也是为何在程序中大量的用IoC,而AOP却用的很少,因为程序中不可能有很多的共通部分。
AOP主要解决共通处理和目标组件之间解耦。
AOP的相关术语
1. 方面Aspect,指的是封装了共通处理的功能组件。该组件可以作用到某一批目标组件的方法上。方面也可以说是通知的集合。
2. 通知Advice,用于指定方面组件的目标组件方法之间的作用时机。例如:先执行方面组件再执行目标方法,或先执行目标方法再执行方面组件。
3. 目标对象Target,要使用Advice操作的方法的对象
4. 连接点JoinPoint,指的是方面组件和具体的哪一个目标组件的方法有关系。
5. 切入点Pointcut,用于指定目标组件的表达式。指的是方面组件和哪一批目标组件方法有关系。多个连接点组成的集合就是切入点。
AutoProxy动态代理
采用AOP之后,容器返回的对象是代理对象。用户在使用时,由代理对象调用切面组件和目标对象的功能。
spring支持JDK动态代理和cglib动态代理。JDK动态代理默认提供的,spring以JDK代理为主。cglib动态代理,spring需要依赖第3方cglib-nodep-2.1_3.jar来实现。
注意spring在默认情况下
1. 目标对象有接口采用JDK动态代理。比如 FeeDaoImpl implements FeeDAO
2. 目标对象没有接口采用CGLIB代理。
修改配置<aop:config proxy-target-class="true"/> proxy-target-class默认是false,表示有接口使用JDK,没有接口使用cglib代理。设置成true,强制都使用cglib代理
AOP案例
想让所有的操作进行日志记录,那么按以前的方式就需要给所有Action或DAO中添加日志记录的代码,如果Action或者DAO很多,那么不容易维护。而使用AOP机制,则可以很方便的实现上述功能。
1. 导入AOP需要的包 aspectjrt.jar aspectjweaver.jar
2. 目标对象
3. 切面类
//切面类(Aspect) public class LoggerBean { //这个类下面的方法都是通知。通知(Advice),切面类中的方法就是通知 public void logger(){ System.out.println("记录当前的操作"); } }
4. 配置文件
<beans ...> <!-- 切面类 --> <bean id="loggerBean" class="com.test.aop.aspect.LoggerBean"></bean> <!-- 目标对象 --> <bean id="feeDao" class="com.test.dao.impl.feeDaoImpl"></bean> <!-- 配置AOP --> <aop:config> <!-- 切入点,如何找连接点,pointcut就是一个表达式,用来指向连接点(要操作的方法),within(com.test.dao.impl.*)在这个包下面的所有类和所有方法 --> <aop:pointcut expression="within(com.tarena.dao.impl.*)" id="feeDaoPoint"/> <!-- 切面,id切面的标识, --> <aop:aspect id="loggerAspect" ref="loggerBean"> <!-- 通知前,就是在调用连接点之前调用方法logger,切入点feeDaoPoint所对应的路径com.test.dao.impl.*,即impl包下所有的方法在执行前都用logger方法 --> <aop:before method="logger" pointcut-ref="feeDaoPoint"/> </aop:aspect> </aop:config> </beans>
通知类型和切入点
通知类型
通知决定方面组件和目标组件作用的关系。主要有以下几种类型通知
a. 前置通知 <aop:before/>,方面组件在目标方法之前执行
b. 后置通知<aop:after-returning/>,方面组件在目标方法之后执行,目标方法没有抛出异常才执行方面组件
c. 最终通知<aop:after>,方面组件在目标方法之后执行,目标方法有没有异常都会执行方面组件。
d. 异常通知<aop:after-throwing>,方面组件在目标方法抛出异常后执行。
e. 环绕通知<aop:around>,方面组件在目标方法之前和之后执行。环绕通知等价于前置+后置通知
try{ //前置通知 //执行目标方法 //后置通知 } catch(){ //异常通知 }fially{ //最终通知 }
切入点
切入点用于指定目标组件和方法,Spring提供了多种表达式写法
方法限定表达式
指定哪些方法启用了方面组件
①execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)
②示例
execution(public * *(..)):匹配容器中所有修饰符是public(不写则无要求的),返回类型、方法名,参数列表也不要求的方法。
execution(* set*(..)):匹配容器中,方法以set开头的所有方法
execution(* org.test.CostDAO.*(..)):匹配CostDAO类中的所有方法。
execution(* org.test.dao.*.*(..)):匹配dao包下所有类所有方法
execution(* org.test.dao..*.*(..)):匹配dao包及子包中所有类所有方法
execution(* com.test.dao.impl.feeDaoImpl.save*(..))
第一个*指的是返回值任意
第二个*指的是方法名以save开头的任意方法
第三个..指的是方法的参数任意
类型限定表达式
指定哪些类型的组件的所有方法启用方面组件(默认就是所有方法都启用,且只到类型,不到方法)
①形式:within(类型)
②示例
within(com.xyz.service.*),匹配service包下的所有类所有方法
within(com.xyz.service..*),匹配service包及其子包中的所有类所有方法
within(org.test.dao.CostDAO),匹配CostDAO所有方法
注意:within(com.xyz.service.*.*)是错误的,只到类名,不到方法名
Bean名称限定
按<bean>元素的id值进行匹配。
①形式:Bean(id值)
②示例
bean(costDAO),匹配id=costDAO的bean对象
bean(*DAO),匹配所有id值以DAO结尾的bean对象
args参数限定表达式
按方法参数类型限定匹配
①形式:args(类型)
②示例
args(java.io.Serializable)匹配方法只有一个参数,并且类型符合Serializable的方法。public void f1(String s)、public void f2(int i)都能匹配。
上述表达式可以使用&&、||运算符连接使用
案例A-环绕通知
环绕通知与其他通知有点区别,它要求程序员调用目标对象的连接点方法,否则连接点方法不会执行
案例:通过logger方法显示当前用户操作的目标对象的类型以及目标对象操作的方法 (连接点的方法)
1. 导入包 aspectjrt.jar aspectjweaver.jar cglib.jar
2. 目标对象
public interface UserDao { void saveUser(String username); }
public class UserDaoImpl implements UserDao{ //连接点 public void saveUser(String username) { System.out.println("保存操作"); } }
3. 切面
public class LoggerBean { //利用反射 //连接点对象ProceedingJoinPoint public void logger(ProceedingJoinPoint pjp) throws Throwable{ //调用目标对象的连接点方法,如果不加这句那么连接点方法不执行 pjp.proceed(); //获得当前执行的方法的类型名 String className=pjp.getTarget().getClass().getName(); //获得当前执行的方法的名字 String methodName=pjp.getSignature().getName(); System.out.println("您正在执行"+className+"类的"+methodName+"方"); } }
4. 配置
<beans ...> <!-- 切面 --> <bean id="loggerBean" class="com.tarena.aop.aspect.LoggerBean"></bean> <!-- 目标对象 --> <bean id="userDao" class="com.tarena.dao.impl.UserDaoImpl"></bean> <!-- 配置AOP --> <aop:config> <aop:pointcut id="userDaoPointcut" expression="execution(* com.test.dao..*.*(..))"/> <aop:aspect id="loggerAspect" ref="loggerBean"> <aop:around method="logger" pointcut-ref="userDaoPointcut"/> </aop:aspect> </aop:config> </beans>
案例B-异常通知
目标对象运行产生异常时,把异常信息通过日志方式记录到文件中。在A案例中添加
1. log4j.properties
#--------console-----------
#log4j.rootLogger=warn,myconsole
#log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
#log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
#--------file-----------
log4j.rootLogger=error,myfile
log4j.appender.myfile=org.apache.log4j.FileAppender
log4j.appender.myfile.File=D:\error.htm
log4j.appender.myfile.layout=org.apache.log4j.HTMLLayout
2. 添加切面类
//切面,(截获异常信息) public class ExceptionLogger { private Log log=LogFactory.getLog(this.getClass()); //将异常的信息写到文件中,D盘下面的error.html public void exceptionLogger(Exception ex){ //1、获得当前异常的堆栈信息 StackTraceElement[] els=ex.getStackTrace(); //2、需要放入第一行的内容els[0] (java.lang.NullPointerException) log.error(ex.getClass().getName()); log.error(els[0]); } }
3. 配置信息
<beans ...> <!-- 切面 --> <bean id="loggerBean" class="com.tarena.aop.aspect.LoggerBean"></bean> <bean id="exceptionLogger" class="com.tarena.aop.aspect.ExceptionLogger"></bean> <!-- 目标对象 --> <bean id="userDao" class="com.tarena.dao.impl.UserDaoImpl"></bean> <!-- 配置AOP --> <aop:config> <aop:pointcut id="userDaoPointcut" expression="execution(* com.test.dao..*.*(..))"/> <aop:aspect id="loggerAspect" ref="loggerBean"> <aop:around method="logger" pointcut-ref="userDaoPointcut"/> </aop:aspect> <aop:aspect id="exceptionLoggerAspect" ref="exceptionLogger"> <!-- throwing:和方法exceptionLogger中的参数名相同。注意一定要把异常抛出来才行,try-catch了不行 --> <aop:after-throwing method="exceptionLogger" pointcut-ref="userDaoPointcut" throwing="ex"/> </aop:aspect> </aop:config> </beans>
spring注解配置
注解技术从JDK5.0推出,之后很多框架开始提供注解配置形式。Spring框架从2.5版本开始支持注解配置。
功能:简化spring的配置文件,把spring的配置文件的信息,放入到各个类当中。
优点:方便快捷,可以减少spring的配置文件
缺点:当项目过大的时候,里面可能会有很多个类(比如1000个),在每个类当中加入注解。容易出现错误,而且不易于管理和维护。
开始是否使用注解,主要看公司的要求以及项目的规模。
AOP注解(了解)
@Aspect
@Before
@After
@Around
@AfterReturning
@AfterThrowing
@Pointcut
案例-使用注解方式
1. 在配置文件中加入:xmlns:context="http://www.springframework.org/schema/context",content可以直接管理注解
2. applicationContext.xml文件;
<beans ...xmlns:context="http://www.springframework.org/schema/context" > <!-- 开启AOP注解 ,启动注解--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 切面 --> <bean id="exceptionBean" class="com.tarena.aop.aspect.ExecptionLogger"></bean> <bean id="loggerBean" class="com.tarena.aop.aspect.LoggerBean"></bean> <!-- 目标对象 --> <bean id="userDao" class="com.tarena.dao.impl.UserDaoImpl"></bean>
<!-- 使用注解后,就没有AOP的配置了 --></beans>
3. 切面(修改最多的)
// 使用AOP注解,首先确定切面,然后确定通知 @Aspect public class LoggerBean { // 只有一个pointcut @Around("execution (* com.tarena.dao..*.*(..))") public void logger(ProceedingJoinPoint pjp) throws Throwable { pjp.proceed(); String className = pjp.getTarget().getClass().getName(); String methodName = pjp.getSignature().getName(); String key = className + "." + methodName; String value = PropertiesUtil.getValue(key); System.out.println("用户的操作信息是:" + value); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); System.out.println("操作的时间是:" + dateFormat.format(new Date())); } }
@Aspect public class ExecptionLogger { @Pointcut("execution (* com.tarena.dao..*.*(..))") // 定义一个方法,该方法只起到标记一个id的作用 public void myPointCutId() { } // 要放两部分信息,pointcut,throwing。pointcut里面不要直接放表达式 @AfterThrowing(pointcut = "myPointCutId()", throwing = "ex") public void exceptionLogger(Exception ex) { StackTraceElement[] els = ex.getStackTrace(); System.out.println(els[0]); Logger logger = Logger.getLogger(this.getClass()); logger.error(ex.getClass().getName()); logger.error(els[0]); } }
4. 目标对象(没有变化)
public class UserDaoImpl implements UserDao { // 连接点 public void deleteUserById(int id) { System.out.println("删除操作"); } public String findUserById(int id) { System.out.println("查询操作"); return null; } public void saveUser(String uname) { System.out.println("保存操作"); String str = null; str.charAt(1);// 抛出异常 } public void updateUser(String user) { System.out.println("修改操作"); } }
web开发中的注解(组件)
applicationContext.xml文件中有<bean>元素的配置,来生成Bean对象。web开发中的注解用来取消这些配置。简化操作。
(1)组件扫描功能
spring可以按照指定的包路径扫描内部的组件,当发现组件类定义前有相应的注解
标记,会将该组件纳入spring容器管理中。
web分为三层,dao是持久层或者是模型层,action或者servlet是控制层。
@Repository:专门指代Dao层(dao层属于持久层或者模型层)
@Service:专门指代Service层
@Controller:专门用来指代控制层(action/servlet)
@Component:指代任意的一层,切记在web三层中少用,为影响对架构控制。
用多了,哪一方是dao,哪一方是service都分不清楚。
@Resource
@Autowired
注意:上述4个注解任意使用也可以,但不符合规范。
注解只能用在类定义前、方法定义前、成员变量定义前。
一般面试问的注解都是web开发中的注解,不是指AOP注解。
3、案例,使用组件注解方式
1)applicationContext.xml
<beans ...>
<!-- 开启AOP注解 ,启动注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 启动组件扫描:逐个扫描com.tarena下的类 -->
<context:component-scan
base-package="com.tarena"></context:component-scan>
</beans>
2)切面
@Component
@Aspect
public class ExecptionLogger {
@Pointcut("execution (* com.tarena.dao..*.*(..))")
//定义一个方法,该方法只起到标记一个id的作用
public void myPointCutId(){}
//要放两部分信息,pointcut,throwing。pointcut里面不要放表达式
@AfterThrowing(pointcut="myPointCutId()",throwing="ex")
public void exceptionLogger(Exception ex){
.....
}
}
@Component
@Aspect
public class LoggerBean {
@Around("execution (* com.tarena.dao..*.*(..))")
public void logger(ProceedingJoinPoint pjp) throws Throwable{
.....
}
}
3)目标对象
@Repository("userDao")//userDao就是Bean对象的id标识
public class UserDaoImpl implements UserDao {
public void deleteUserById(int id) {
System.out.println("删除操作");
}
....
}
4)测试
public class TestSpring{
public void test1(){
BeanFactory factory=
new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao dao=(UserDao) factory.getBean("userDao");
dao.findUserById(1);//测试LoggerBean切面
dao.saveUser("");//测试ExceptionLogger切面
}
}
4、web分层,控制层 业务层 DAO层
1)dao层
public interface UserDao {
void saveUser(String uname);
...
}
public class UserDaoImpl implements UserDao {
public void saveUser(String uname) {
System.out.println("dao保存操作");
}
public void updateUser(String user) {
System.out.println("修改操作");
}
}
2)业务层
根据业务需要1次或者多次的调用Dao层,业务层具体解决问题层。
业务层依赖于dao层对数据库操作,实现业务需要。
public interface UserService {
void saveUser(String uname);
}
public class UserServiceImpl implements UserService{
//调用dao
private UserDao userDao;//省略get set 方法
public void saveUser(String uname) {
System.out.println("业务:保存");
userDao.saveUser(uname);
}
}
3)action层
省略
4)applicationContext.xml
<beans ...>
<!-- 配置dao -->
<bean id="userDao" class="com.tarena.dao.impl.UserDaoImpl"></bean>
<!--配置service -->
<bean id="userService" class="com.tarena.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 配置Action(省略) -->
</beans>
5)测试
public void test1(){
BeanFactory factory=
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service=(UserService) factory.getBean("userService");
service.saveUser("");
}
5、使用注解来维护依赖关系(注入注解)
要求给UserServiceImple注入UserDao属性。
Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解,
如:@Resource、 @PostConstruct及@PreDestroy。
@PostConstruct //等价于设置了init-method=”方法名”属性
public void myinit(){...}
@PreDestroy //等价于设置了destroy-method=”方法名”属性
public void mydestroy{...}
@Resource
private UserDao userDao;
userDao是名字,UserDao是类型
1)@Resource
@Resource是J2EE提供的,需导入Package:javax.annotation.Resource;
javaee1.5不需要额外导入包,javaee1.4需要导入包common-annotations.jar
@Resource有两个中重要的属性:name和type 。
Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为
bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使
用type属性时则使用 byType自动注入策略。如果既不指定name也不指定type
属性,这时将通过反射机制使用byName自动注入策略,byName匹配不到后再使
用byType自动注入策略。
@Resource装配顺序
(1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进
行装配,找不到则抛出异常;
(2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不
到则抛出异常;
(3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到
或者找到多个,都会抛出异常;
(4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;
如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
不需要set方法
//凡是出現注解的地方都不需要set方法
2)@Autowired
@Autowired:跟@Resource功能很相似,都是用来维护依赖的关系。
@Autowired是spring提供的,默认只根据类型匹配注入。如果匹配到多个将报异常。
可以手动的按照名称匹配。
@Autowired
@Qualifier("userDao")
private UserDao userDao;
3)@Resource和@Autowired的区别
a、@Autowired 与@Resource都可以用来装配bean. 都可以写在字段上,或写在
setter方法上;写在字段上可以不用写set方法
b、@Autowired 默认按类型装配。
如果我们想使用名称装配可以结合 @Qualifier注解进行使用;
c、@Resource(这个注解属于J2EE的),默认安装名称进行装配,名称可以通过
name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取
字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。
当找不到与名称匹配的bean时才按照类型进行装 配。但是需要注意的是,如
果name属性一旦指定,就只会按照名称进行装配。
d、 推荐使用@Resource注解在字段上,是属于J2EE的,减少了与Spring的耦
合,这样代码看起就比较优雅 。
@Resource性能好,如果是@Autowired,会把UserDao接口对应的实现类全部
遍历一遍
企业开发一般都使用@Resource
4)案例
DAO层
接口
public interface UserDao {
void saveUser(String uname);
}
两个实现类
@Repository("userDao")//userDao就是Bean对象的id标识
@Scope("prototype")//单例还是多例
public class UserDaoImpl implements UserDao {
public void saveUser(String uname) {
System.out.println("保存操作");
}
}
@Repository
public class UserDaoMysqlImpl implements UserDao{
public void saveUser(String uname) {
...
}
}
业务层
接口
public interface UserService {
void saveUser(String uname);
}
实现类
@Service("userService")//userService就相当于<bean>元素中id标识的作用
public class UserServiceImpl implements UserService{
@Resource
private UserDao userDao;
public void saveUser(String uname) {
System.out.println("业务:保存");
userDao.saveUser(uname);
}
}
配置文件
<beans ..>
<!-- 开启AOP注解 ,启动注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 启动组件扫描:逐个扫描com.tarena下的类 -->
<context:component-scan base-package="com.tarena"></context:component-scan>
</beans>
测试类
public void test1(){
BeanFactory factory=
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service=(UserService) factory.getBean("userService");
service.saveUser("");
}
八、数据连接池
1、为什么要使用数据连接池?
获得数据库连接的过程DriverManager.getConnection()是非常消耗系统性能的操作,如
果过于频繁获得连接,会导致系统性能急剧下降,甚至崩溃。
一般获得连接会占用85%的时间。一般上线的项目都使用连接池。
2、连接池原理
连接池中有很多创建好的连接。请求过来后,申请连接,连接池返回一个连接,用完连
接以后,释放连接(不是关闭连接),别人可以继续使用这个连接。
连接池主要做三件事:建立连接(服务器启动,建立一定数量的连接)
管理连接
释放连接(服务器关闭,连接池也关闭,连接释放)
连接池原理:复用已经建立好的数据库连接
hibernate内部默认有一个小连接池。
3、连接池的使用步骤
1)建立数据库连接池对象(服务器启动的时候创建)
2)按照事先设计好的初始化连接数量,创建数据库的连接
3)对于一个数据库的访问,直接从连接池获取连接
4)操作数据库
5)将连接重新放入连接池中
(3---5,在项目运行中反复的出现)
6)释放连接池对象
(服务器关闭,维护期间,释放数据库连接池,释放所有的连接)
4、DBCP连接池
使用必须要导入两个jar包
commons-dbcp-1.2.2.jar
commons-pool.jar
5、连接池的核心类和核心接口
1)DataSource接口 javax.sql.DataSource
DataSource 对象所表示的物理数据源的连接。作为 DriverManager 工具的替代项,
DataSource 对象是获取连接的首选方法 。
DataSource有抽象方法getConnection();由实现类覆盖。
所有连接池的核心都会间接或直接实现该接口。
2)BasicDataSource类
org.apache.commons.dbcp.BasicDataSource,实现了DataSource接口
九、spring整合JDBC
1、工具类JdbcTemplate
spring整合Jdbc所用的工具类,里面封装了所有对jdbc的基本操作。
企业开发中,一般dao操作数据库都是一条sql语句+工具类的一个方法
Dao层:
String sql = “”
jdbcTemplate.方法(sql,参数)
2、创建JdbcTemplate对象
class JdbcTemplate {
带DataSource参数的构造方法来生成对应的JdbcTemplate对象。
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
所以创建JdbcTemplate对象对象需要传入一个DataSource参数
3、dao层
public class CostDaoImpl implements CostDao {
private JdbcTemplate jdbcTemplate ;
public void setDataSource(DataSource dataSource){
jdbcTemplate = new JdbcTemplate(dataSource);
}
}
必须注入一个DataSource对象,强调是实现DataSource接口的对象,一般注入的是连
接池核心类对象,比如BasicDataSource。
4、JdbcTemplate的常用方法
update() 增删改
query() 查询
queryForObject()
queryForList()
queryForLong()
queryForMap()
注意:查询对象的操作
jdbc返回的结果封装到ResultSet对象当中,Dao返回的结果,往往是一个entity对象。
需要从ResultSet集合转换成entity对象。需要实现spring提供的RowMapper接口。
或者使用spring的BeanPropertyRowMapper类。
public class CostMapper implements RowMapper{
//把rs转化成一个Object对象,不会有任何的问题
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Cost cost=new Cost();
cost.setId(rs.getInt("ID"));
cost.setName(rs.getString("NAME")); .....
return cost;
}
}
public Cost findById(int id) {
String sql = "select * from cost" + " where id=?";
CostMapper costMapper = new CostMapper();
// return (Cost) jdbcTemplate.queryForObject(sql, new Object[]{id},costMapper );
return (Cost) jdbcTemplate.queryForObject(sql, new Object[] { id },
new BeanPropertyRowMapper(Cost.class));
//BeanPropertyRowMapper:实现RowMapper接口的对象
//不稳定,如果对象的字段为空的时候,容易出现异常
// 不同的spring版本,返回不同的结果
}
5、案例
1)实体类以及实体类对应的Mapper类
public class Cost {
private Integer id;
private String name;
private Integer baseDuration;
private Double baseCost;
private Double unitCost;
private String status;
private String descr;
private Date createTime;
private Date startTime;
private String costType;
....
}
public class CostMapper implements RowMapper{
//把rs转化成一个Object对象,不会有任何的问题
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Cost cost=new Cost();
cost.setId(rs.getInt("ID"));
cost.setName(rs.getString("NAME"));
cost.setBaseDuration(rs.getInt("BASE_DURATION"));
.....
return cost;
}
}
2)配置文件applicationContext.xml
<beans ....>
<!-- spring整合jdbc -->
<!---让spring来管理数据连接源对象--->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!---基本属性 driverClassName url username password --->
<property name="driverClassName"
value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url"
value="jdbc:oracle:thin:@172.17.3.6:1521:ora10g"></property>
<property name="username" value="szsd1307"></property>
<property name="password" value="szsd1307"></property>
<!-- initialSize:连接池创建的时候,初始化连接数量,不配置默认是0个
maxWait:如果没有连接,最长的等待时间,单位是毫秒数
maxActive:连接池在同一时间内分配最大的使用连接数量-->
<property name="initialSize" value="10"></property>
<property name="maxWait" value="2000"></property>
<property name="maxActive" value="50"></property>
</bean>
<!-- 该实现DataSource接口的BasicDataSource注入到dao当中
创建JdbcTemplate对象时,需要传入参数DataSource -->
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
3)DAO层
接口
public interface CostDao {
void saveCost(Cost cost);
void deleteCost(Cost cost);
void updateCost(Cost cost);
Cost findById(int id);
List<Cost> findAll();
long findCount();
//查询最高(unit_cost,最低unit_cost,平均unit_cost)
Map findUnitCost();
}
实现类
public class CostDaoImpl implements CostDao{
private JdbcTemplate jdbcTemplate;
//通过外界注入参数(数据库连接参数)
public void setDataSource(DataSource datasource){
jdbcTemplate=new JdbcTemplate(datasource);
}
public void deleteCost(Cost cost) {
String sql="delete from COST_QIN where id=?";
jdbcTemplate.update(sql, new Object[]{cost.getId()});
//第一个参数放sql语句,第二个参数放对象数组,里面放入?对应的值
}
public List<Cost> findAll() {
String sql="select * from COST_QIN";
return jdbcTemplate.query(sql,new CostMapper());
}
public Cost findById(int id) {
String sql="select * from cost_qin where id=?";
RowMapper costMapper=new CostMapper();
return (Cost)jdbcTemplate.queryForObject
(sql, new Object[]{id}, costMapper);
}
public void saveCost(Cost cost) {
String sql="insert into COST_QIN(ID,NAME,BASE_DURATION,
BASE_COST,UNIT_COST,STATUS,DESCR,CREATIME,
STARTIME,COST_TYPE) " +
"values(COST_SEQ.NEXTVAL,?,?,?,?,?,?,?,?,?) ";
jdbcTemplate.update(sql, new Object[]{cost.getName(),cost.getBaseCost(),
cost.getBaseCost(),cost.getUnitCost(),cost.getStatus(),cost.getDescr(),cost.get
CreateTime(),cost.getStartTime(),cost.getCostType()});
}
public void updateCost(Cost cost) {
String sql="update COST_QIN set NAME=?,BASE_DURATION=?,
BASE_COST=?,UNIT_COST=?,STATUS=?,DESCR=?,
CREATIME=?,STARTIME=?,COST_TYPE=? WHERE ID=?";
jdbcTemplate.update(sql, new Object[]{cost.getName(),cost.getBaseCost(),
cost.getBaseCost(),cost.getUnitCost(),cost.getStatus(),cost.getDescr(),cost.get
CreateTime(),cost.getStartTime(),cost.getCostType(),cost.getId()});
}
public long findCount() {
//sql语句当中,切记尽量少出现"*"
//一般对于数据的都用long类型
String sql="select count(id) from cost_qin";
return jdbcTemplate.queryForLong(sql);
}
public Map findUnitCost() {
String sql="select max(unit_cost),min(unit_cost),avg(unit_cost)
from cost_qin";
return jdbcTemplate.queryForMap(sql);
}
}
4)测试类
public class TestSpring {
public void test1(){
BeanFactory factory=new
ClassPathXmlApplicationContext("applicationContext.xml");
CostDao costDao=(CostDao) factory.getBean("costDao");
Cost cost=new Cost();
cost.setId(168);
costDao.deleteCost(cost);
}
....
6、DaoSupport
是spring提供专门用来Dao层公共抽象类,spring建议程序员在写Dao层,
所有的类最好都继承DaoSupport下属子类。
1)针对jdbc:继承JdbcDaoSupport
JdbcDaoSupport:内部包含一个JdbcTemplate对象,以简化操作。
可以通过 getJdbcTemplate()方法获得JdbcTemplate对象
public abstract class JdbcDaoSupport extends DaoSupport {
private JdbcTemplate jdbcTemplate;
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource !=
this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = createJdbcTemplate(dataSource);
initTemplateConfig();
}
}
public final JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
}
之所以要继承JdbcDaoSupport,主要是因为JdbcDaoSupport类中有一个属性
JdbcTemplate。不继承也可以,在dao实现类中添加属性JdbcTemplate
2)针对hibernate:继承HibernateDaoSupport
包含一个hibernateTemplate
spring建议使用hibernate做开发的时候,dao最好继承
HibernateDaoSupport,以便利用里面工具类hibernateTemplate
7、修改Dao实现类,采用继承JdbcDaoSupport方式
public class CostDaoImpl extends JdbcDaoSupport implements CostDao{
// CostDaoImpl 继承了 JdbcDaoSupport类,
//相当于CostDaoImpl有了下面的代码
private JdbcTemplate jdbcTemplate;
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource !=
this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = createJdbcTemplate(dataSource);
initTemplateConfig();
}
}
//有了属性JdbcTemplate,创建JdbcTemplate依然要注入DataSource参数。
//applicationContext.xml配置文件不需要改变
public void deleteCost(Cost cost) {
String sql="delete from COST_QIN where id=?";
this.getJdbcTemplate().update(sql, new Object[]{cost.getId()});
}
public List<Cost> findAll() {
String sql="select * from COST_QIN";
return this.getJdbcTemplate().query(sql,new CostMapper());
}
.....
public Map findUnitCost() {
String sql="select max(unit_cost),min(unit_cost),avg(unit_cost) from cost_qin";
return this.getJdbcTemplate().queryForMap(sql);
}
}
十、spring整合hibernate
1、spring整合Hibernate步骤
1)导入hibernate的jar包 hibernate常用的版本是3.2或者3.5
2)导入hibernate的配置文件
3)生成对应po类和orm映射文件
4)导入spring的jar包
5)导入spring核心配置文件
2、spring整合hibernate的工具类
HibernateTemplate类
封装了spring针对hibernate的方法,简化hibernate的操作。
该类的构造器
public HibernateTemplate(SessionFactory sessionFactory) {
setSessionFactory(sessionFactory);
afterPropertiesSet();
}
说明在创建HIbernateTemplate对象时,需要传入一个Sessionfactory参数。
public class CostDaoImpl implements CostDao {
private HibernateTemplate hibernateTemplate;
//使用spring的DI依赖注入,使用set注入一个SessionFactory
//让spring管理hibernate的数据源
public void setSessionFactory(SessionFactory sessionFactory){
//创建带sessionFactroy的hibernateTemplate对象
hibernateTemplate = new HibernateTemplate(sessionFactory);
}
}
3、注入SessionFactory
1、创建hibernateTemplate对象时,需要传入一个参数SessionFactory
SessionFactory代表了hibernate配置文件中的所有信息,sessionfactory只有一份
1)、数据库的连接信息
2)、hibernate自身的属性信息
3)、hibernate映射文件
注意不是注入DataSource,DataSource只是SessionFactory真的一小部分
2、使用spring核心配置文件注入SessionFactroy(第一种方式)
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
上述配置注入了一个SessionFactory,
类型为org.springframework.orm.hibernate3.LocalSessionFactoryBean,
部分源代码:
public class LocalSessionFactoryBean extends AbstractSessionFactoryBean
implements BeanClassLoaderAware {
private Configuration configuration;
public void setConfigLocation(Resource configLocation) {
this.configLocations = new Resource[] {configLocation};
}
}
LocalSessionFactoryBean类中的属性configuration用来用来加载资源文件,
这里加载hibernate配置文件。
3、 问题:LocalSessionFactoryBean类型并不是SessionFactory接口类型
(即没有实现Sessionfactory接口),如何实现注入的?
LocalSessionFactoryBean 本身不是一个session factory,但是spring会自动把对这
个bean的引用替换成LocalSessionFactoryBean 里面的真正的session factory。
(1)在LocalSessionFactoryBean 中有个字段是存放真正的session factory的:
LocalSessionFactoryBean 继承了抽象类AbstractSessionFactoryBean
抽象类中有:
public abstract class AbstractSessionFactoryBean
implements FactoryBean, InitializingBean, DisposableBean,
PersistenceExceptionTranslator {
private DataSource dataSource;
private SessionFactory sessionFactory;
public Object getObject(); {
returnthis.sessionFactory;
}
当引用这个LocalSessionFactoryBean 的时候,比如
applicationContext.getBean("localSessionFactoryBean ")这样,spring返回的不是
LocalSessionFactoryBean 本身,他会自动调用getObject()这个方法,把真正的
session factory返回,得到的都是session factory而不是
LocalSessionFactoryBean 。
(2)LocalSessionFactoryBean实现了org.springframework.beans.factory.FactoryBean
接 口, spring在装配的时候, 如果发现实现了factory.FactoryBean接口, 就会
使用FactoryBean#getObject() 方法返回的对象装配。
如果你想拿到LocalSessionFactoryBean实例, 在id前面加个'&'就可以了,在你
的配置文件中BeanFactory.getBean('&sessionFactory')拿到的 就是
LocalSessionFactoryBean的实例.
4、案例
1)导入hibernate的jar包 hibernate常用的版本是3.2或者3.5
2)导入hibernate的配置文件
<hibernate-configuration>
<session-factory>
<!-- 方言,指定Hibernate语句生成的sql类型 -->
<property name="dialect">org.hibernate.dialect.OracleDialect</property>
<!-- 连接数据库属性 -->
<propertyname="connection.url">
jdbc:oracle:thin:@172.17.3.6:1521:ora10g</property>
<property name="connection.username">szsd1307</property>
<property name="connection.password">szsd1307</property>
<property name="connection.driver_class">
oracle.jdbc.OracleDriver</property>
<!-- 将底层的sql操作显示出来 -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!--映射文件->
<mapping resource="com/tarena/pojo/Cost.hbm.xml" />
</session-factory>
</hibernate-configuration>
3)生成对应po类和orm映射文件
public class Cost implements java.io.Serializable {
private Integer id;
private String name;
private Long baseDuration;
private Double baseCost;
private Double unitCost;
private String status;
private String descr;
private Date creatime;
private Date startime;
private String costType;
get/set方法
}
Cost.hbm.xml 省略
4)导入spring的jar包
5)导入spring核心配置文件
配置文件applicationContext.xml
<beans ..>
<!-- 利用spring的工具类加载hibernate的核心配置文件hibernate.cfg.xml -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 加载核心配置文件放入sessionfactory属性configLocation中 -->
<property name="configLocation"
value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 配置CostDao -->
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
6)DAO层(注意事务问题,下节讲)
public interface CostDao {
void saveCost(Cost cost);
void deleteById(int id);
void updateCost(Cost cost);
Cost findById(int id);
List<Cost> findAll();
//查询总的cost数量
long findCount();
List<Cost> findPage(int page,int pageSize);
}
第一种方式:添加一个HibernateTemplate类
public class CostDaoImpl implements CostDao{
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory){
hibernateTemplate=new HibernateTemplate(sessionFactory);
}
public void saveCost(Cost cost) {
//不要使用hql语句,hql语句可以查询,修改,删除就不是不能保存
hibernateTemplate.save(cost);
}
public void updateCost(Cost cost) {
hibernateTemplate.save(cost);
}
public Cost findById(int id) {
return (Cost) hibernateTemplate.get(Cost.class, id);
}
public List<Cost> findAll() {
//spring整合hibernate时,查询方法一般使用find方法,find方法返回一
//般都是list集合,spring没有整合uniqueResult,
//如果使用hql查询,使用find,返回永远都是list
//这是spring整合hibernate不太成功之一
String hql="from Cost";
return hibernateTemplate.find(hql);
}
public long findCount() {
String hql="select count(id) from Cost";
//最好先变成string再转成int。不要强转,spring的版本不同,返回可能
//是Long,Integer类型
List list=hibernateTemplate.find(hql);
long count=Long.parseLong(list.get(0).toString());
return count;
}
public List<Cost> findPage(final int page,final int pageSize) {
final String hql="from Cost";
//spring整合hibernate的时候,对分页没有很好的支持
//必须要使用hibernate中的session来实现分页
//使用回调的方式(固定写法,必须记住)
return (List<Cost>) hibernateTemplate.execute(new HibernateCallback() {
//内部类使用外界的变量的时候,外界变量必须使用final修饰
@Override
public Object doInHibernate(org.hibernate.Session session)
throws HibernateException, SQLException {
int begin=(page-1)*pageSize;
returnsession.createQuery(hql)
.setFirstResult(begin).setMaxResults(pageSize).list();
}
});
}
public void deleteById(int id) {
Cost cost=new Cost();
cost.setId(id);
hibernateTemplate.delete(cost);
}
}
第二种方式:继承HibernateDaoSupport类
public class CostDaoImpl extends HibernateDaoSupport implements CostDao{
public void saveCost(Cost cost) {
this.getHibernateTemplate().save(cost);
}
public void updateCost(Cost cost) {
this.getHibernateTemplate().save(cost);
}
public Cost findById(int id) {
return (Cost)this.getHibernateTemplate().get(Cost.class, id);
}
public List<Cost> findAll() {
String hql="from Cost";
return this.getHibernateTemplate().find(hql);
}
public long findCount() {
String hql="select count(id) from Cost";
List list=this.getHibernateTemplate().find(hql);
long count=Long.parseLong(list.get(0).toString());
return count;
}
public List<Cost> findPage(final int page,final int pageSize) {
final String hql="from Cost";
return (List<Cost>) this.getHibernateTemplate().
execute(new HibernateCallback() {
public Object doInHibernate(org.hibernate.Session session)
throws HibernateException, SQLException {
int begin=(page-1)*pageSize;
return session.createQuery(hql)
.setFirstResult(begin).setMaxResults(pageSize).list();
}
});
}
public void deleteById(int id) {
Cost cost=new Cost();
cost.setId(id);
this.getHibernateTemplate().delete(cost);
}
}
5、spring+hibernate如何使用Session、Query等对象
(1)方式一
利用HIbernateDaoSupport提供的getSession方法。
没用到延迟加载的API,那么用这个方式简单些。但是有延迟加载的API,则会
出现问题:session关闭早了,页面可能获取不到数据;不关闭,一旦出了方法体
则关不上了。而多次访问数据库后,就发现没结果,因为连接数用完了。
Session session = getSession();
..............利用session进行一些操作.......
session.close();//注意:一定要释放
getHibernateTemplate中的API都有释放操作,所以自己不用再写。
(2)方式二
利用HibernateTemplate.execute()方法,以回调函数方式使用。这种方式不用担心
方式一出现的问题,session的关闭由HibernateTemplate统一管理。
getHibernateTemplate().execute(
new HibernateCallBack(Session session){
public Object doInHibernate()throws HibernateException,SQLException{
//回调函数中使用session
}
}
);
6、spring配置SessionFactory的第2中方式(更常用)
修改applicationContext.xml文件
将hibernate.cfg.xml文件去掉,把该文件的信息配置到applicationContext.xml文件中。
添加数据连接池的配置
<beans ...>
<!-- 配置SessionFactory的第二种方式(企业项目上线的时候)
主要配置的是3部分参数
1)DataSource(连接池对象)
2)hibernate属性
3)orm映射文件 -->
<!-- 配置DBCP连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="oracle.jdbc.OracleDriver"></property>
<property name="url"
value="jdbc:oracle:thin:@172.17.3.6:1521:ora10g"></property>
<property name="username" value="szsd1307"></property>
<property name="password" value="szsd1307"></property>
<property name="initialSize" value="10"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/tarena/pojo/Cost.hbm.xml</value>
</list>
</property>
</bean>
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
十一、spring事务管理
JDBC的底层是默认自动提交事务,每次执行JDBC都会在底层默认提交事务。HIbernate底层是默认手动提交事务,需要我们程序员自己去提交事务。
真实企业开发中:需要我们控制事务的提交,回滚等,比如银行转账,减钱和加钱同时提交事务才可以。
1、事务的传播特性
1)事务边界
比如银行转账,减钱开始前和加钱结束后就是事务边界,共用同一个事务。
2)事务的传播特性
事务的传播特性是指连续一次或者多次对数据库的操作,事务的控制。
指多次对数据进行操作时,如何对事务进行控制就是事务的传播特性。
对事务进行控制就是指控制什么时候开启,什么时候提交,什么时候回滚。
(1)propagation_required 数据库操作1 数据库操作2 数据库操作3 ...
连续两次或者多次操作数据库,都沿用同一个事务。
如果存在一个事务,则支持当前事务,如果没有事务,则开启一个事务。
比如:第一次查询没有事务,第二次删除操作,开启事务,第三次是修改操作,
沿用删除操作开启的事务。
增删改是required特性,必须要有事务。
(2)propagation_not_supported
非事务执行。一般用于查询操作(比如分页查询)
(3)企业开发中,一般增删改操作配置成required,查询配置成not_supported
2、事务的隔离级别
脏数据(永远不要出现):读到没有提交的数据。脏读就是读取没提交的数据
不可重复读:数据提交到数据库,可以读数据。
一条数据被修改的同时,另一个人也在修改该数据。
同一时刻,多个人修改同一条数据,读出来的数据和自己修改的数据
不一样
幻象读: 整张表数据被修改,又插入一些新数据
一个人对所有数据进行修改时,另一个人又插入一些新数据
一个人访问所有数据的同时又有人添加了一些新数据
脏读一定不能出现,不可重复读和幻象读可以出现
1)read_uncommited
最低的隔离级别,数据没有提交就能读,能看到
企业开发一定尽量不要使用
2)read_commited
提交后才能读(不能脏读)
3)repeatable_read
在我访问某条数据的时候,别人都不能访问,但可以访问其他数据。
不会出现不可重复读,会出现幻觉读(指的是整张表中所有的数据)
4)serializable
整张表在同一时刻只有一个人可以访问,访问结束后其他人才可以访问。
不会出现不可重复读,不会出现幻想读。最昂贵的。
最最常用的隔离级别是read_commited
隔离一定要避免脏读(数据位提交,一定不能读取)
面试问在隔离级别中要避免什么?避免脏读。
spring管理事务就是管理事务的传播特性和隔离级别
3、声明式事务(hibernate版本的)
通过spring配置文件来控制事务
通过spring配置来管理我们的事务(事务的传播特性和事务的隔离级别)。
(1)指定事务管理器
(事务管理器在spring底层来控制事务的对象,开启事务,提交事务,回滚事务等)
类HibernateTransactionManager中管理事务的方法
1)doBegin(){ hibTx = session.beginTransaction();} 开启事务
2)doCommit(){} 提交事务
3)doRollBack(){} 回滚事务
<!-- 指定当前的事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 使用HibernateTransactionManager必须注入一个SessionFactory -->
<property name="sessionFactory"ref="sessionFactory"></property>
</bean>
(2)配置事务特性(方法执行的时候,事务如何控制)
a)事务的传播特性
PROPAGATION_REQUIRED :增删改
PROPAGATION_NOT_SUPPORTED:查询
b)事务的隔离级别
READ_COMMITED
c)配置事务的特性(事务的传播特性和事务的隔离级别)
<!--
transaction-manager指定事务管理器,由transactionManager来管理事务
DEFAULT指的是当前数据库默认的隔离级别
mysql必须修改隔离级别,oracle可以不修改,默认是read_commited
delete*":*匹配后面的任意的名字,比如匹配deleteUser... 其他同理
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" propagation="NOT_SUPPORTED"
read-only="true" isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="delete*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="update*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
(3)固定的Aop
<aop:config>
<aop:pointcut expression="execution(* com.tarena.dao..*.*(..))" id="txPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
4、applicationContext.xml(hibernate版本)
将spring整合hibernate的案例添加事务管理
<beans >
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate.cfg.xml"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" propagation="NOT_SUPPORTED"
read-only="true" isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="delete*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="update*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.tarena.dao..*.*(..))" id="txPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
5、使用注解方式配置事务
1)applicationContext.xml
<beans ...>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 1、指定当前事务的管理器
管理事务 事务开启 提交 回滚 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 2、开启事务的注解配置
transaction-manager:指明让transactionManager管理事务 -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager"/>
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
2)在业务组件的类定以前或方法中使用@Transactional注解即可
public class CostDaoImpl extends HibernateDaoSupport implements CostDao{
@Transactional(isolation=Isolation.READ_COMMITTED,
propagation=Propagation.REQUIRED)
public void saveCost(Cost cost) {
.....
}
@Transactional(isolation=Isolation.READ_COMMITTED,
propagation=Propagation.REQUIRED)
public void updateCost(Cost cost) {
.....
}
@Transactional(isolation=Isolation.READ_COMMITTED,
propagation=Propagation.REQUIRED)
public Cost findById(int id) {
....
}
@Transactional(isolation=Isolation.READ_COMMITTED,
propagation=Propagation.REQUIRED)
public List<Cost> findAll() {
...
}
}
3)注意事项
如果将Action当作目标,需要在<tx:annotation-driven>添加 proxy-target-class="true"
属性,表示不管有没有接口,都采用CGLIB方式生成代理类。
6、编程式事务管理
基于Java编程实现事务控制,不推荐使用。
主要是利用TransactionTemplate的execute()方法,以回调方式将多个操作封装在一个
事务中。
7、spring针对JDBC的声明式事务
企业开发中,两层架构事务配置在action上,三层架构配置在service上面。
applicationContext.xml文件
<beans ...>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"></property>
<property name="username" value="system"></property>
<property name="password" value="1234"></property>
<property name="initialSize" value="10"></property>
<property name="maxWait" value="2000" ></property>
<property name="maxActive" value="50"></property>
</bean>
<!-- jdbc声明式事务 -->
<!-- 指定jdbc中的事务管理器DataSourceTransactionManager-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的特性(事务传播特性和隔离级别) -->
<tx:advice id="txadvice"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"
isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED"
isolation="READ_COMMITTED" />
<tx:method name="update*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="delete*" propagation="REQUIRED"
isolation="READ_COMMITTED" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--事务配置到那个层(Action,dao)是pointcut决定的,
一般来说如果Action,Dao结构,事务一般配置到Action
如果是Action,service,dao结构事务一般配置到service
切记:事务尽量不要配置到dao层,在web项目中
-->
<aop:pointcut expression="execution (* com.tarena.dao..*.*(..))" id="curd"/>
<!-- 针对事务,spring一般使用专用标签advisor,把通知和切入点连接起来-->
<aop:advisor advice-ref="txadvice" pointcut-ref="curd"/>
</aop:config>
<bean id="costDao" class="com.tarena.dao.impl.CostDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
十二、spring整合struts2
1、struts2 中常量定义
<constant name="" value=""></constant>
struts2这个框架,在运行本身配置很多的常量,而这些是应用于一般性的开发,这些常
量全部都配置在default.properties。
default.properties文件在struts2-core02.1.8.jar包中 /org/apache/struts2/default.properties
1)# struts.objectFactory = spring :由spring来提供struts2中
action对象的创建及维护,默认情况下给注释了。
struts本身负责创建action对象的类是:StrutsObjectFactory类
2)struts.i18n.encoding=UTF-8 :struts2默认编码是UTF-8
3)struts.action.extension=action,,
提交的请求的后缀,struts默认情况下可以接收以.action和不加任何后缀结尾的请求
2、修改常量
1)default.properties该文件中定义的常量的信息,可以随意的进行修改,修改方法
在struts.xml中
<constant name="" value=""></constant>重新定义值
eg:<constant name="struts.action.extension" value="do"></constant>。
更改为接收以.do为结尾的请求
2)spring整合struts2,spring管理struts2中的Action对象的创建及维护,
必须要修改default.properties中配置信息
导入一个struts2-spring-plugin-2.0.11.2.jar
有一个配置文件:struts-plugin.xml中
<constant name="struts.objectFactory" value="spring" />
修改default.properties中配置信息,由spring来创建并维护struts2中的Action对象
struts-plugin.xml文件内容
<struts>
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring"
class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
指定struts的action对象由spring来创建维护,即StrutsSpringObjectFactory类。
这样struts请求过来时,Action对象将交给整合包spring容器获取,即struts2不再
产生Action对象。
<constant name="struts.objectFactory" value="spring" />
</struts>
3、struts2中默认继承的包struts-default
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
默认关联ActionSupport
<!-- 跳转到新增页面的Action -->
<action name="toAddCost" >
<result name="success">/WEB-INF/cost/addCost.jsp</result>
</action>
toAddCost请求------》默认关联ActionSupport------》execute方法,返回值是success
4、spring整合struts2
1)原理:把struts2中的action对象的创建和维护,全部交给spring 统一的创建和维护。
必须要导入struts2-spring-plugin-2.0.11.2.jar
自动修改default.properties中的
struts.objectFactory = spring,交由spring来维护action对象
2)什么时候加载spring核心配置?(把spring配置文件中bean全部实例化)
监听器:
启动服务器,就去实例化spring中的bean,提交性能
3)spring 提供加载配置文件的类:
org.springframework.web.context.ContextLoaderListener
4) 修改web.xml文件
5、案例整合资费模块
1) 首先添加spring,struts2,hibernate的jar包和配置文件
2) 对po类,修改成符合hibernate操作的po和orm映射文件
3) 修改spring核心配置文件,用来整合hibernate
4) 添加spring核心配置文件,用来配置action ,dao
spring整合框架的配置文件最好单独作为一个配置文件
5) spring整合struts2
6) 重写CostDaoImpl(spring整合hibernate)
7) 重构Action,去掉工厂,使用spring的IOC,提供Action所依赖dao对象
1)导入包
2)po类和orm映射文件
3)applicationContext_hibernate.xml文件
<beans ...>
<!-- 配置DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url"
value="jdbc:oracle:thin:@172.17.3.6:1521:ora10g"></property>
<property name="username" value="szsd1307"></property>
<property name="password" value="szsd1307"></property>
<property name="initialSize" value="10"></property>
<property name="maxWait" value="2000"></property>
<property name="maxActive" value="50"></property>
</bean>
<!-- 配置SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>web/netctoss/cost/entity/Cost.hbm.xml</value>
<value>web/netctoss/account/entity/Account.hbm.xml</value>
....
</list>
</property>
</bean>
<!-- 指定事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务特性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" propagation="NOT_SUPPORTED"
isolation="READ_COMMITTED" read-only="true"/>
<tx:method name="delete*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="update*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
<!-- 添加action中的execute方法,暂时先用required -->
<tx:method name="execute" propagation="REQUIRED"
isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
<!-- AOP -->
<aop:config>
<aop:pointcut expression="execution(* web.netctoss.cost.action.*.*(..) )"
id="costPointcut"/>
<aop:pointcut expression="execution(* web.netctoss.account.action.*.*(..) )"
id="accountPointcut"/>
<aop:pointcut expression="execution(* web.netctoss.service.action.*.*(..) )"
id="servicePointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="costPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
</beans>
4)applicationContext_bean.xml
<!--资费模块模块 action,dao
把struts2中的Action,纳入spring的管理,
struts2中的Action的创建和维护都交给spring统一处理
还有一个问题,struts2中的action是多例,spring中的bean是单例-->
<bean id="costDao" class="web.netctoss.cost.dao.impl.CostDAOImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="findCostAction" class="web.netctoss.cost.action.FindCostAction"
scope="prototype">
<property name="costDao" ref="costDao"></property>
</bean>
5)action配置文件
<package name="cost" namespace="/cost" extends="netctoss">
<!-- struts中的action对象都是由spring来创建和维护,不用struts2来维护
每一个action中的class属性的值,指向spring生成的Action对象bean的id -->
<action name="findCost" class="findCostAction">
<interceptor-ref name="netctossStack"></interceptor-ref>
<!-- 属性注入 -->
<param name="pageSize">3</param>
<result name="success">/WEB-INF/cost/cost_list.jsp</result>
</action>
6)修改action类
public class FindCostAction {
.....输入输出属性
private CostDAO costDao; 依赖注入dao类
public String execute(){
try {
costs=costDao.findByPages(page, pageSize);
totalPage=costDao.findTotalPage(pageSize);
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "success";
}
}
7)修改dao类
添加工具类HibernateTemplate类
8)修改web.xml文件
<web-app ...>
<!-- 配置参数 -->
<context-param>
<param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext_*.xml</param-value>
</context-param>
<!-- 配置监听器,加载spring的核心配置文件 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>Struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>Struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
十三、Spring MVC
struts2和webwork是基于过滤器实现的
struts1和spring是基于servlet实现的
struts1的Action,servlet,springMVC的Controller是单例的,struts2的Action是多例的
springMVC流程
1、客户端发送请求
2、客户端发送的请求到达控制器,控制器由Servlet(DispacherServlet)实现的,完成请求的转发
3、该控制器(DispatcherServlet)调用一个用于映射的类HandlerMapping
该类用于将请求映射到对应的处理器来处理请求
4、HandlerMapping将请求映射到对应的处理器Controller(相对于Action)
在Spring当中如果写一些处理器组件,一般实现Controller接口
5、在Controller中就可以调用一些Service或DAO来进行数据操作
6、ModelAndView可以存放从Dao中取出的数据,还可以存放响应视图的一些数据
7、如果想将处理结果返回给用户,那么在Sping框架中还提供了一个视图组件ViewResolver,
该组件根据Controller返回的标识,找到对应的视图,将响应返回给用户
案例
1、新建一个web项目导入jar包
commons-logging.jar spring-webmvc.jar spring.jar
2、web.xml文件
<web-app ...>
<!-- springMVC首先截获所有.do结尾的请求,都交给DispatcherServlet统一处理
DispatcherServlet加载src目录下的spring-mvc.xml配置文件
springMVC的controller是单例
-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
3、login.jsp
<!-- struts1和springMVC所截获.do结尾的请求
struts2的请求后缀可以任意修改 -->
<form action="login.do" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
<span style="color:red;">${errormessage }</span>
</form>
ok.jsp
<body>
${message }
</body>
4、control
User类
public class User {
//用来保存请求发送的请求数据,
//每一个请求都有一个对应的user对象来保存数据
private String username;
private String password;
get/set方法
}
public class LoginControl extends SimpleFormController{
//核心方法onSubmit,等价于struts2中的execute方法
// servlet中的service(doPost和doGet方法)
//command指代用来保存用户提交请求参数的对象,比如user对象
@Override
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
//1.接收请求参数
User user = (User) command;
//2.判断用户名为liu,密码为123才通过
if(user!=null&&"liu".equals(user.getUsername())&&
"123".equals(user.getPassword())){
//3.用来保存数据,显示到页面中
// ModelAndView表示返回的信息,两部分信息
// ModelAndView(跳转页面的名字,要显示到页面中的信息 ModelMap map = new ModelMap();
map.put("message", user.getUsername()+"登录成功");
return new ModelAndView("ok", map);//
}else{
ModelMap map = new ModelMap();
map.put("errormessage", "登录失败,用户名或者密码错误");
return new ModelAndView("login", map);//
}
}
5、sprin-mvc.xml
<!-- springMVC分成3个部分
第一部分controlBean
第二部分映射关系HandleMapping
第三部分跳转信息 -->
<!-- 配置bean信息 -->
<bean id="loginControl" class="com.tarena.control.LoginControl">
<property name="commandClass" value="com.tarena.control.User"></property>
</bean>
<!-- 请求和control的对应关系 -->
<bean id="urlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!-- mappings 请求和对应的处理类映射 -->
<property name="mappings">
<props>
<!-- 有多少个请求就写多少个 -->
<prop key="login.do">loginControl</prop>
</props>
</property>
</bean>
<!-- 跳转信息 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp"></property><!-- 后缀 -->
<property name="prefix" value="/"></property><!-- 前缀 -->
</bean>