• 1. 北京传智播客教育 www.itcast.cnSpring2.5企业开发
  • 2. 北京传智播客教育 www.itcast.cn Spring是什么Spring是一个开源框架. 基于控制反转(Inversion of Control ,IoC)和面向切面(Aspect Oriented Programming , AOP)的容器框架. 主要目的是简化企业开发.
  • 3. 学习内容Spring全面展示 环境搭建 依赖注入 装配 事务管理 整合
  • 4. 北京传智播客教育 www.itcast.cn 为何要使用Spring降低组件之间的耦合度, 使各层间松耦。 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。 非侵入性,对Spring API的依赖减少到最低。 AOP技术,很容易实现如权限拦截、运行期监控等功能。 提供众多辅作类,加快应用的开发,如: JdbcTemplate、 HibernateTemplate。 对于主流的框架提供集成支持,如:JDBC、Hibernate、Struts等,更便于开发。
  • 5. 北京传智播客教育 www.itcast.cn IOC 控制反转 所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。 这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。 public class PersonServiceBean { private PersonDao personDao = new PersonDaoBean(); public void save(Person person){ personDao.save(person); } } PersonDaoBean 是在应用内部创建及维护的。
  • 6. 北京传智播客教育 www.itcast.cn 依赖注入(Dependency Injection)当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下: public class PersonServiceBean { private PersonDao personDao ; //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入。 public PersonServiceBean(PersonDao personDao){ this.personDao=personDao; } public void save(Person person){ personDao.save(person); } } 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。(这里通过构造器注入personDao )
  • 7. AOP public class ProxyFactory implements InvocationHandler{ private Object targetObject = null; public Object proxy(Object proxy, Method method, Object[] args) throws Throwable { return Proxy.newProxyInstance(this.targetObject.getClass(). getClassLoader(),this.targetObject.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if( hasRole() ) {//有权限才进行处理 return method.invoke(targetObject, args); } return null; } }
  • 8. AOPpublic class MyInterceptor { @Pointcut(“execution(* studio.foryou.bean.UserBean.*(..))”) //指定类,方法 private void interceptor() {}//声明一个切入点,从不被执行 @Before("interceptor()") public void doBefore() { System.out.println("MyInterceptor.doBefore():前置通知"); } @AfterReturning("interceptor()") public void doAfterReturning() { System.out.println("MyInterceptor.doAfterReturning():后置通知"); } @After ("interceptor()") public void doAfter () { System.out.println("MyInterceptor.doAfter():最终通知"); } @AfterThrowing ("interceptor()") public void doAfterThrowing () { System.out.println("MyInterceptor.doAfterThrowing():例外通知"); } @Around("interceptor()")//通常用来控制目标方法是否执行,可以进行权限控制 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("MyInterceptor.doAround():进入环绕通知"); //if(有权限){ Object obj = pjp.proceed();//此方法不调用,将会终止拦截方法调用链 //} System.out.println("MyInterceptor.doAround():退出环绕通知"); //return obj; } }
  • 9. 北京传智播客教育 www.itcast.cn 事务Hibernate的事务操作: public void save(){ Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Info info = new Info("传智播客"); info.setContent("国内实力最强的java培训机构"); session.save(info ); session.getTransaction().commit(); } JDBC的事务操作: Connection conn = null; try { ....... conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate("update person where name='叶天'"); conn.commit(); ..... } catch (Exception e) { conn.rollback(); } finally{conn.close();}
  • 10. 北京传智播客教育 www.itcast.cn 事务public void payment(){ bean1.update();//更新金额 bean2.save();//记录操作日志 } 如果我们不使用Spring,针对下面这两种业务需求,我们该如何做? 第1种可能的业务需求:要求bean1.update()和bean2.save()在同一个事务中执行。 第2种可能的业务需求:要求不管bean1.update() 的事务是否成功,都需要记录操作日志。 public class Bean1 { public void update(){//注意:下面省略了一些代码 Connection conn = null; conn.setAutoCommit(false); Statement.executeUpdate(“update account set amount=? where id=?"); } } public class Bean2 { public void save(){//注意:下面省略了一些代码 Connection conn = null; conn.setAutoCommit(false); Statement.executeUpdate(“insert into Log (content) values (?)"); } }
  • 11. 北京传智播客教育 www.itcast.cn 事务使用Spring,我们只需要通过声明式的事务属性配置就可以轻松地实现这两种业务需求 1.要求bean1.update()和bean2.save()的在同一个事务中执行 2.要求不管bean1.update() 的事务是否成功,都需要记录日志。 @Transactional(propagation=Propagation.Required) public void payment(){ bean1.update();//更新金额 bean2.save();//记录日志 } public class Bean1 { @Transactional(propagation=Propagation.Required) public void update(){ executeUpdate(“update account set amount=? where id=?"); } } public class Bean2 { @Transactional(propagation=Propagation.RequiresNew) public void save(){ executeUpdate(“insert into Log (content) values (?)"); } }
  • 12. 北京传智播客教育 www.itcast.cn 轻重量级的划分 经常有人说spring属于轻量级框架,EJB属于重量级框架。划分轻量级还是重量级,主要看它使用了多少服务.使用的服务越多,容器要为其所做的工作就越多,必然会影响到应用的运行性能. 对于spring容器,它提供了很多服务,但这些服务并不是默认为应用打开的,应用需要某种服务,还需要指明使用该服务,如果应用使用的服务很少,如:只使用了spring核心服务,那么我们可以认为此时应用属于轻量级的,如果应用使用了spring提供的大部分服务,这时应用就属于重量级。目前EJB容器就因为它默认为应用提供了EJB规范中所有的功能,所以它属于重量级。
  • 13. 北京传智播客教育 www.itcast.cn 环境搭建:运行类库到http://www.springsource.org/download下载spring,然后进行解压缩,在解压目录中找到下面jar文件,拷贝到类路径下 dist\spring.jar lib\jakarta-commons\commons-logging.jar 如果使用了切面编程(AOP),还需要下列jar文件 lib/aspectj/aspectjweaver.jar和aspectjrt.jar lib/cglib/cglib-nodep-2.1_3.jar 如果使用了JSR-250中的注解,如@Resource/@PostConstruct/@PreDestroy,还需要下列jar文件 lib\j2ee\common-annotations.jar
  • 14. 北京传智播客教育 www.itcast.cn spring的配置文件模版 ..... 该配置模版可以从spring的参考手册(docs\reference\html_single\index.html) 或spring的例子中得到。 配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。
  • 15. 北京传智播客教育 www.itcast.cn 编写spring配置文件时,不能出现帮助信息由于spring的schema文件位于网络上,如果机器不能连接到网络,那么在编写配置信息时候就无法出现提示信息,解决方法有两种: 1。让机器上网,eclipse会自动从网络上下载schema文件并缓存在硬盘上。 2。手动添加schema文件,方法如下: windwos->preferences->myeclipse->files and editors->xml->xmlcatalog 点"add",在出现的窗口中的Key Type中选择URI,在location中选"File system",然后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为Schema location,Key改为http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 3。支持Alt+/ 快捷键 windwos->preferences-> General->Keys 将 Content Assist 修改为Alt+/ 将 Word Completion修改为非 Alt+/ 用键即可
  • 16. 北京传智播客教育 www.itcast.cn 实例化spring容器实例化Spring容器常用的两种方式: 方法一: 在类路径下寻找配置文件来实例化容器 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"}); 方法二: 在文件系统路径下寻找配置文件来实例化容器 ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{“d:\\beans.xml“}); Spring的配置文件可以指定多个,可以通过String数组传入。
  • 17. 北京传智播客教育 www.itcast.cn 从spring容器中得到bean当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下: ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml”); OrderService service = (OrderService)ctx.getBean("personService"); getBean()参数可以指定id属性,也可以指定name属性。 id通常为有效的标识符。id="personServiceBean" name可以为非标识符。 name="person.server#bean“ 无论按照id属性还是name属性找到即可
  • 18. Spring容器原理利用dom4j解析 需要类库文件 dom4j-1.6.1/lib 下所有的jar文件
  • 19. 北京传智播客教育 www.itcast.cn 使用dom4j读取spring配置文件private void readBeansXML(String xml) { URL beansXML = this.getClass().getClassLoader().getResource(xml); SAXReader reader = new SAXReader(); Document doc = null; try { doc = reader.read(beansXML); Map map = new HashMap(); map.put("n", "http://www.springframework.org/schema/beans");//指定命名空间 XPath xpath = doc.createXPath("//n:beans/n:bean");//指定beans/bean查找路径 xpath.setNamespaceURIs(map);//设置命名空间 List beans = xpath.selectNodes(doc);//获取doc下bean的所有节点 for(Element bean : beans) {//遍历所有的节点元素 Bean theBean = new Bean(); theBean.setId( bean.attributeValue("id"));//获取id属性值 theBean.setClassName(bean.attributeValue("class"));//获取class属性值 beanMap.put(theBean.getId(),theBean); } } catch (Exception e) { e.printStackTrace(); } }
  • 20. 北京传智播客教育 www.itcast.cn 实例化bean1.使用类构造器实例化 2.使用静态工厂方法实例化 public class OrderFactory { public static OrderServiceBean createOrder(){ return new OrderServiceBean(); } } 3.使用实例工厂方法实例化: public class OrderFactory { public OrderServiceBean createOrder(){ return new OrderServiceBean(); } }
  • 21. 北京传智播客教育 www.itcast.cn Bean的作用域.singleton 在每个Spring IoC容器中一个bean定义只有一个对象实例。(缺省设置为singleton) 请注意Spring的singleton bean概念与“四人帮”(GoF)模式一书中定义的Singleton模式是完全 不同的。经典的GoF Singleton模式中所谓的对象范围是指在每一个ClassLoader中指定class 创建的实例有且仅有一个。把Spring的singleton作用域描述称一个container对应一个bean 实例最为体贴。 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); PersonService person = (PersonService)ctx.getBean("personServiceBean"); System.out.println("person="+person); ApplicationContext ctx1 = new ClassPathXmlApplicationContext("beans.xml"); PersonService person1 = (PersonService)ctx1.getBean("personServiceBean"); System.out.println("person1="+person1);
  • 22. (本页无文本内容)
  • 23. Bean的作用域.prototype 每次从容器获取bean都是新的对象。 根据经验,对有状态的bean应该使用prototype作用域, 而对无状态的bean则应该使用singleton作用域
  • 24. Bean的作用域.request 表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效 .session 表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效 .globalSession 不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session 的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。 以上3种均基于web的Spring ApplicationContext情形下有效,了解
  • 25. 延迟初始化bean 默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean时才初始化bean。如: 如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下: 26. 北京传智播客教育 www.itcast.cn Bean的初始化方法和销毁方法 public void init() { System.out.println(“已经初始化了”); } public void close() { System.out.println(“被销毁了”); } AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); PersonService person = (PersonService)ctx.getBean("personServiceBean"); ctx.close(); 注意:必须是无参的方法,可以有返回值。
  • 27. 北京传智播客教育 www.itcast.cn注入依赖对象基本类型对象注入: //构造器注入 //属性setter方法注入 注入其他bean:表示null值 方式一 方式二(使用内部bean,但该bean不能被其他bean引用)
  • 28. 北京传智播客教育 www.itcast.cn集合类型的装配public class OrderServiceBean { private Set sets = new HashSet(); private List lists = new ArrayList(); private Properties properties = new Properties(); private Map maps = new HashMap(); ....//这里省略属性的getter和setter方法 }
  • 29. 北京传智播客教育 www.itcast.cn集合类型的装配 pkbest pkbest best
  • 30. 北京传智播客教育 www.itcast.cn依赖注入使用构造器注入 使用属性setter方法注入 使用Field注入(用于注解方式) 注入依赖对象可以采用手工装配或自动装配,建议使用手工装配。 1.手工装配依赖对象 2.自动装配依赖对象
  • 31. 北京传智播客教育 www.itcast.cn依赖注入--手工装配手工装配依赖对象,在这种方式中又有两种编程方式 1. 在xml配置文件中,通过在bean节点下配置,如 //构造器注入 //属性setter方法注入 2. 在java代码中使用@Autowired或@Resource注解方式进行装配。但我们需要在xml配置文件中配置以下信息: 这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor 注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar
  • 32. 北京传智播客教育 www.itcast.cn依赖注入--手工装配在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。 @Autowired private PersonDao personDao;//用于字段上 @Autowired public void setOrderDao(OrderDao orderDao) {//用于setter方法上 this.orderDao = orderDao; } @Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下: @Autowired @Qualifier("personDaoBean") private PersonDao personDao; @Resource注解和@Autowired一样,也可以标注在字段或setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在setter方法上,即默认取bean名称寻找依赖对象。 @Resource(name=“personDaoBean”) private PersonDao personDao;//用于字段上 注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
  • 33. 北京传智播客教育 www.itcast.cn依赖注入--自动装配依赖对象对于自动装配,大家了解一下就可以了,实在不推荐大家使用。例子: autowire属性取值如下: byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。 byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。 constructor与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。 autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
  • 34. 北京传智播客教育 www.itcast.cn通过在classpath自动扫描方式把组件纳入spring容器中管理前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息: 其中base-package为需要扫描的包(含子包)。 @Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 @Scope用于指定scope作用域的(用在类上) @PostConstruct用于指定初始化方法(用在方法上) @PreDestory用于指定销毁方法(用在方法上)
  • 35. 北京传智播客教育 www.itcast.cn AOP代理 对象目标 对象当Bean实现接口时,Spring就会用JDK的动态代理 当Bean没有实现接口时,Spring使用CGLib是实现 通常可以做权限拦截、系统监控等
  • 36. 北京传智播客教育 www.itcast.cn JDK动态代理public class JDKProxy implements InvocationHandler { private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject){ this.targetObject = targetObject; /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法 */ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(this.targetObject, args);//把方法调用委派给目标对象 } } 当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
  • 37. 北京传智播客教育 www.itcast.cn 使用CGLIB生成代理public class CGLIBProxy implements MethodInterceptor { private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject){ this.targetObject = targetObject; Enhancer enhancer = new Enhancer();//该类用于生成代理对象 enhancer.setSuperclass(this.targetObject.getClass());//设置父类 enhancer.setCallback(this);//设置回调用对象为本身 return enhancer.create(); } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return methodProxy.invoke(this.targetObject, args); } } CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
  • 38. 北京传智播客教育 www.itcast.cn AOP中的概念Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象. joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器) Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义. Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知 Target(目标对象):代理的目标对象 Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入. Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
  • 39. 通知 private void invoke() { beforeAdvice();//前置通知: if(aroundAdvice( proceed() )) {//环绕通知 try{ method.invoke(target,args); afterAdvice();//后置通知 }catch(Exception e){ throw exceptionAdvice();//例外通知 }finally{ finallyAdvice();//最终通知 } } }
  • 40. 北京传智播客教育 www.itcast.cn 使用Spring进行面向切面(AOP)编程要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间: Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种: 基于XML配置方式进行AOP开发。 基于注解方式进行AOP开发。
  • 41. 北京传智播客教育 www.itcast.cn 基于注解方式声明切面首先启动对@Aspect注解的支持(蓝色部分):
  • 42. 基于注解方式声明切面@Aspect @Component public class MyInterceptor { @Pointcut("execution(* studio.foryou.bean.UserBean.*(..))") private void interceptor() {}//声明一个切入点,从不被执行 @Before("interceptor() && args(argsValue)") //argsValue为doBefore()参数名字,注意参数类型与拦截目标方法类型要一致 public void doBefore(int argsValue) { System.out.println("MyInterceptor.doBefore():前置通知 argsValue="+argsValue); } @AfterReturning(pointcut="interceptor()",returning="result") //result为doAfterReturning()参数名字,注意参数类型与拦截目标方法类型要一致 public void doAfterReturning(int result) { System.out.println("MyInterceptor.doAfterReturning():后置通知 result="+result); } @After ("interceptor()") public void doAfter () { System.out.println("MyInterceptor.doAfter():最终通知"); } @AfterThrowing (pointcut="interceptor()",throwing="e") //e为doAfterThrowing()参数名字,注意参数类型与拦截目标方法类型要一致 public void doAfterThrowing (Exception e) { System.out.println("MyInterceptor.doAfterThrowing():例外通知 e="+e); } @Around("interceptor()")//通常用来控制目标方法是否执行,可以进行权限控制 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("MyInterceptor.doAround():进入环绕通知"); //if(有权限){ Object obj = pjp.proceed();//此方法不调用,将会终止拦截方法调用链 //} System.out.println("MyInterceptor.doAround():退出环绕通知"); //return obj; return obj; } }
  • 43. 北京传智播客教育 www.itcast.cn 基于XML配置方式使用切面public class LogPrint { public void doAccessCheck() {}定义前置通知 public void doReturnCheck() {}定义后置通知 public void doExceptionAction() {}定义例外通知 public void doReleaseAction() {}定义最终通知 public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { return pjp.proceed();环绕通知 } }
  • 44. 北京传智播客教育 www.itcast.cn 基于基于XML配置方式声明切面
  • 45. 指示符expression-匹配方法执行的连接点,是Spring最主要的切入点 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 除了喊会类型模式(),名字模式和参数模式以外,其余的都是可选的,*为任意通配符 expression=“execution(* cn.itcast.service..*.*(..))” 表示作用在cn.itcast.service包及子包下所有的类的所有方法上 expression=“execution(* cn.itcast.service..*.*(java.lang.String,..))” 表示作用在cn.itcast.service包及子包下所有的类的第一个参数为String类型的方法上 expression=“execution(java.lang.String cn.itcast.service..*.*(..))” 表示作用在cn.itcast.service包及子包下所有的类的返回值类型为String的方法上 expression=“execution(!java.lang.String cn.itcast.service..*.*(..))” 表示作用在cn.itcast.service包及子包下所有的类的返回值类型不是String的所有方法上 expression=“execution(* set**(..))” 表示作用在任意以set开始的方法上 within-限定匹配特定类型的连接点 expression=“winthin(cn.itcast.*)” 表示作用在cn.itcast包及子包下的所有连接点
  • 46. 北京传智播客教育 www.itcast.cn Spring+JDBC组合开发 使用Spring+JDBC集成步骤如下: 配置数据源,如: .....略 上面使用了Apache组织的dbcp数据源,我们需要把lib\jakarta-commons下的commons-dbcp.jar和commons-pool.jar加入类路径下. 配置事务。配置事务时,需要在xml配置文件中引入用于声明事务的tx命名空间(见下页),事务的配置方式有两种:注解方式和基于XML配置方式。
  • 47. 北京传智播客教育 www.itcast.cn 在spring配置文件中引入用于声明事务的tx命名空间
  • 48. 北京传智播客教育 www.itcast.cn 配置数据源
  • 49. 北京传智播客教育 www.itcast.cn 使用属性占位符方式配置数据源使用属性占位符
  • 50. 北京传智播客教育 www.itcast.cn 采用注解方式配置事务采用注解方式 @Service @Transactional public class PersonServiceBean implements PersonService { }
  • 51. 北京传智播客教育 www.itcast.cn 采用基于XML方式配置事务
  • 52. 北京传智播客教育 www.itcast.cn 使用JdbcTemplate进行insert/update/delete操作@Service @Transactional public class PersonServiceBean implements PersonService { private JdbcTemplate jdbcTemplate; @Resource public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } //添加 public void save(Person person) throws Exception{ jdbcTemplate.update("insert into person (name) values(?)", new Object[]{person.getName()}, new int[]{java.sql.Types.VARCHAR}); } }
  • 53. 北京传智播客教育 www.itcast.cn 使用JdbcTemplate获取一条记录@Service @Transactional public class PersonServiceBean implements PersonService { private JdbcTemplate jdbcTemplate; @Resource public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public Person getPerson(Integer id){ RowMapper rowMapper = new RowMapper(){ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new Person(); person.setId(rs.getInt("id")); person.setName(rs.getString("name")); return person; } }; return (Person)jdbcTemplate.queryForObject("select * from person where id=?", new Object[]{id}, new int[]{java.sql.Types.INTEGER}, rowMapper); }}
  • 54. 北京传智播客教育 www.itcast.cn 使用JdbcTemplate获取多条记录@Service @Transactional public class PersonServiceBean implements PersonService { private JdbcTemplate jdbcTemplate; @Resource public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public List getPersons(){ RowMapper rowMapper = new RowMapper(){ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new Person(); person.setId(rs.getInt("id")); person.setName(rs.getString("name")); return person; } }; return jdbcTemplate.query("select * from person", rowMapper); } }
  • 55. 北京传智播客教育 www.itcast.cn 事务传播属性 REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。 NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。 REQUIRES_NEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。 MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。 SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。 Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。 NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效
  • 56. 北京传智播客教育 www.itcast.cn 事务传播属性NESTED介绍public class PersonServiceBean implements PersonService { @Resource OtherService otherService ; public void xxx(){ stmt.executeUpdate("update person set name='888' where id=1"); otherService.update();//OtherService的update方法的事务传播属性为NESTED stmt.executeUpdate("delete from person where id=9"); } }
  • 57. 北京传智播客教育 www.itcast.cn 上面xxx()方法事务在内部执行的过程Connection conn = null; try { conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate("update person set name='888' where id=1"); Savepoint savepoint = conn.setSavepoint(); try{ conn.createStatement().executeUpdate("update person set name='222' where sid=2"); }catch(Exception ex){ conn.rollback(savepoint); } stmt.executeUpdate("delete from person where id=9"); conn.commit(); stmt.close(); } catch (Exception e) { conn.rollback(); }finally{ try { if(null!=conn && !conn.isClosed()) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
  • 58. 北京传智播客教育 www.itcast.cn 数据库系统提供了四种事务隔离级数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read ,如Mysql Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)。 Read Commited:读已提交数据(会出现不可重复读和幻读) Repeatable Read:可重复读(会出现幻读) Serializable:串行化 脏读:一个事务读取到另一事务未提交的更新新据。 不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。 幻读:一个事务读取到另一事务已提交的insert数据。
  • 59. Spring2.5+Hibernate3.3+Struts1.3整合Spring2.5 jar文件 dist\spring.jar dist\modules\spring-webmvc-struts.jar lib\jakarta-commons\commons-logging.jar lib\aspectj\aspectjweaver.jar、aspectjrt.jar lib\cglib\cglib-nodep-2.1_3.jar lib\j2ee\common-annotations.jar Struts1.3 jar文件 apps\struts-blank-1.3\WEB-INF\lib 下有所有的jar文件 Hibernate3.3 jar文件 hibernate3.jar lib\required\*.jar lib\optional\ehcache-1.2.3.jar hibernate 注解安装包下的 lib\test\slf4j-log4j12.jar Struts 下载struts-1.3.8-lib.zip,需要使用到解压目录下的所有jar,建议把jstl-1.0.2.jar和standard-1.0.2.jar更换为1.1版本。Spring中已经存在一个antlr-2.7.6.jar,所以把struts中的antlr-2.7.2.jar删除,避免jar冲突。 数据库驱动jar
  • 60. 第一步:Spring+Hibernate cn/itcast/bean/Person.hbm.xml hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false hibernate.format_sql=false
  • 61. Hibernate模板.hbm.xml
  • 62. 第二部:最后集成Struts1.3一共有3中方案
  • 63. 北京传智播客教育 www.itcast.cn 整合Struts1.3 在web容器中实例化spring容器, contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener
  • 64. 整合Struts1.3方案1 web.xml Struts Blank Application contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener action org.apache.struts.action.ActionServlet config /WEB-INF/struts-config.xml 2 action *.do
  • 65. 整合Struts1.3方案1 struts-config.xml
  • 66. 整合Struts1.3方案1 beans.xml
  • 67. 整合Struts1.3方案1 LoginAction.javapublic class LoginAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { //获取你的业务bean WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( this.getServlet().getServletContext()); DAO dao = (DAO)ctx.getBean("dao"); dao.save(); return mapping.findForward("login"); } }
  • 68. 整合Struts1.3方案1为了获得业务服务,使用 WebApplicationContextUtils获得一个 ApplicationContext。可以查找一个 Spring bean。 这种技术很简单并且易于理解。不幸的是,它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring,那么您必须重写代码。并且,由于 Struts 动作不在 Spring 的控制之下,所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时,这种技术可能有用,但是在大多数情况下,这种方法不如另外两种方法合适。
  • 69. 整合Struts1.3方案2 struts-config.xml
  • 70. 整合Struts1.3方案2 beans.xml 71. 整合Struts1.3方案2 LoginAction.javapublic class PersonAction extends Action{ @Resource private PersonService personService; @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) Throws Exception { request.setAttribute("persons",personService.getPersons()); return mapping.findForward("list"); } }
  • 72. 整合Struts1.3方案2动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring,不对代码作任何改变就可用于非 Spring 应用程序中。RequestProcessor 的改变不会影响它,并且它可以利用 Spring AOP 特性的优点。 动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作,您就可以使用 Spring 给动作补充更强的活力。例如,没有 Spring 的话,所有的 Struts 动作都必须是线程安全的。如果您设置 标记的 singleton 属性为“false”,那么不管用何种方法,您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性,但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如,当实例化 Struts 动作时, 标记的 init-method 属性被用于运行一个方法。类似地,在从容器中删除 bean 之前,destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法,它们以一种与 Servlet 生命周期相同的方式进行管理。
  • 73. 整合Struts1.3方案3 struts-config.xml
  • 74. 整合Struts1.3方案3 beans.xml 75. 整合Struts1.3方案3 PersonAction.javapublic class PersonAction extends Action{ @Resource private PersonService personService; @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) Throws Exception { request.setAttribute("persons",personService.getPersons()); return mapping.findForward("list"); } }
  • 76. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts1.3整合开发Hibernate二级缓存的配置 cn/itcast/bean/Person.hbm.xml hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false hibernate.format_sql=false hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=false hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
  • 77. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts1.3整合开发在需要缓存的实体bean配置文件中加入缓存配置项 usage说明了缓存的策略,region指定缓存的区域名 注意:使用二级缓存的所对应的方法必须没有事务管理
  • 78. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts1.3整合开发Ehcache默认的配置文件ehcache.xml(放在类路径下) defaultCache节点为缺省的缓存策略 maxElementsInMemory 内存中最大允许存在的对象数量 eternal 设置缓存中的对象是否永远不过期 overflowToDisk 把溢出的对象存放到硬盘上 timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉 timeToLiveSeconds 指定缓存对象总的存活时间 diskPersistent 当jvm结束是是否持久化对象 diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
  • 79. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts1.3整合开发使用spring解决struts1.3乱码问题。 encoding org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 encoding /*
  • 80. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts1.3整合开发使用spring解决hibernate因session关闭导致的延迟加载例外问题。 OpenSessionInViewFilter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter sessionFactoryBeanName sessionFactory OpenSessionInViewFilter /*
  • 81. 北京传智播客教育 www.itcast.cn Spring2.5+JPA+Struts1.3整合开发这里JPA的实现采用hibernate,需要使用到下面的jar文件 Hiberante核心包(8个文件) hibernate-distribution-3.3.1.GA --------------------------------------------- hibernate3.jar lib\bytecode\cglib\hibernate-cglib-repack-2.1_3.jar lib\required\*.jar Hiberante注解包(3个文件):hibernate-annotations-3.4.0.GA ------------------------------------------------------------------------------------ hibernate-annotations.jar lib\ejb3-persistence.jar、hibernate-commons-annotations.jar Hibernate针对JPA的实现包(3个文件):hibernate-entitymanager-3.4.0.GA ------------------------------------------------------------------------------------------------------ hibernate-entitymanager.jar lib\test\log4j.jar、slf4j-log4j12.jar
  • 82. 北京传智播客教育 www.itcast.cn Spring2.5+JPA+Struts1.3整合开发JPA规范要求在类路径的META-INF目录下放置persistence.xml,文件的名称是固定的,配置模版如下:
  • 83. 北京传智播客教育 www.itcast.cn Spring2.5+JPA+Struts1.3整合开发在spring中配置EntityManagerFactory
  • 84. 北京传智播客教育 www.itcast.cn Spring2.5+JPA+Struts1.3整合开发使用spring解决JPA因EntityManager关闭导致的延迟加载例外问题。 Spring OpenEntityManagerInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter entityManagerFactoryBeanName entityManagerFactory
  • 85. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts2整合开发使用到struts2的lib目录下的以下jar文件, struts2-core-2.x.x.jar :Struts 2框架的核心类库 xwork-2.x.x.jar :XWork类库,Struts 2在其上构建 ognl-2.6.x.jar :对象图导航语言(Object Graph Navigation Language),Struts 2框架使用的一种表达式语言 freemarker-2.3.x.jar :Struts 2的UI标签的模板使用FreeMarker编写 commons-logging-1.0.x.jar :ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。 struts2-spring-plugin-2.0.11.1.jar commons-fileupload-1.2.1.jar
  • 86. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts2整合开发在web容器中实例化spring容器和配置struts2 contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener 配置struts2 struts2 org.apache.struts2.dispatcher.FilterDispatcher struts2 /*
  • 87. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts2整合开发struts2的配置文件模版struts.xml /WEB-INF/page/message.jsp /WEB-INF/page/persons.jsp /WEB-INF/page/add_person.jsp /WEB-INF/page/edit_person.jsp
  • 88. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts2整合开发为了能从spring容器中寻找到Action bean,要求action配置的class属性值和spring中bean的名称相同,如下: .....
  • 89. 北京传智播客教育 www.itcast.cn Spring2.5+Hibernate3.3+Struts2整合开发struts2的标签 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib uri="/struts-tags" prefix="s" %> 姓名:
    , 修改