• 1. Spring企业级开发 访问企业服务王健
  • 2. 本章目标:在Spring中访问JDNI资源。 发送并格式化电子邮件。 调度任务。
  • 3. JNDI:JNDI : 是指Java命名和目录服务。它是SUN公司定义的一组接口,用于获取其他容器管理的对象。包括DataSource数据源。 JNDI主要解决的问题是:将数据源(或其他对象)配置到Spring容器之外。在运行时,向容器请求某个对象。 JNDI一般用一个名称来定义。如连接数据库的数据源为:jdbc/base等。
  • 4. 在Tomcat中配置JNDI:一般JNDI都配置到大型服务器中,如Bea WebLogic,IBM WebSphere. 在tomcat下选择JNDI DataSource:打开以后,里面有详细的教程。
  • 5. 在Tomcat中配置jndi的步骤:1、在server.xml的元素中配置元素。建议新建立一个xml文件,将它放到Tomcat_home/conf/Catalina/localhost目录下。(发布一个应用程序的三种方式,不再赘述。上面只是其中一种,不建议修改server.xml文件。) 2、在自己的项目中,引用资源。 3、在项目中通过InitialContext查询找名称。
  • 6.
  • 7. JNDI:上面的代码中,虽然我们实现了JDNI的配置,并可以从Servlet中获取JNDI数据源。但遗憾的是,我们的JNDI查找代码严重与我们的应用程序代码耦合在一起。 尽管如此,并没有以改变我们有时会需要从JNDI查找对象的事实。为了利用应用服务器的连接池,你经常在应用服务器如weblogic中配置数据连接。那么我们如何才可以利用JNDI提供的连接池,而又不依赖JNDI呢? 答案就是建立依赖注入(DI)。
  • 8. 注入JNDI对象:Spring的JndiObjectFactoryBean用于获取JNDI配置的对象。 它的配置如下:如果查找发生在JEE环境中,则要加上java:comp/env前缀。 你可以自己将这个前缀加到jndiName指定的值上。或是设置resourceRef的值为true,让JndiObjectFactoryBean自动的在jndiName上添加java:comp/env前缀。 在tomcat服务器上,必须要指定下面这一句,在weblogic中则不需要。
  • 9. 回退对象:试想一下,如果JNDI中没有找到对象会怎样呢? JndiObjectFactoryBean可以很好的从JNDI中获取数据源,不过,它还有一个回退机制。可用在无法从JNDI中获取对象的情况。要想使用这个机制,必须要配置defaultObject属性。
  • 10. 使用jee:命名空间:Spring的jee命名空间,省去了定义JndiObjectFactoryBean的冗长代码。 jee的命名空间如下: http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd 以下是具体配置方式:
  • 11. 在WebLogic中配置JNDI:Weblogic的安装 - 略。 以下是启动weblogic:
  • 12. 发送电子邮件:Spring带有一个邮件抽象API,可以简化发电子邮件的复杂性。 Spring电子邮件抽象层核心接口为MailSender。Spring带有此接口的两个实现 : CosMailSenderImpl – 以JasonHunter中的COS(com.oreilly.servlet)实现为基础的SMTP邮件发送器的简单实现 JavaMailSenderImpl – 一个基于JavaMailApi的邮件发送器实现。允许发送MIME邮件以及非SMTP邮件。一种通用且标准的实现。邮件 发送器邮件电子邮件 服务器
  • 13. 构建电子邮件: - 简单邮件
  • 14. 使用MimeMessageHelper设置邮件内容:- HTML
  • 15. 发送带附件的邮件:
  • 16. 内嵌的样式的邮件:inline
  • 17. 通过配置文件配置发送邮件:将邮件发送器配置到Spring的配置文件中。
  • 18. 通过配置文件配置发送邮件:
  • 19. 调度任务:并非系统中所有的事情都是由用户的动作引起的,有些时候,系统根据任务高度规划发起一些动作称为调度任务。如定时给用户发送电子邮件。 Spring支持两种调度方式: 使用JDK的Timer和TimerTask (基于jdk1.4) 在Jdk1.5以后,Spring使用了jdk1.5的线程池进行调度。(基于JDK1.5) 使用Quartz。(这是重点,基于第三方支持包)
  • 20. 使用JDK1.4的JavaTimer调度任务:从JDK1.3开始,Java就通过java.util.Timer类提供了简单的调度任务。此类,允许你调用一个按任意周期运行的任务,一个任务就是一个java.util.TimerTask类的子类。 Spring使用TimerFactoryBean提供对Timer类的支持。
  • 21. JDK1.4 Timer用法:c2.add(Calendar.MINUTE, 1); //在当前时间上加上一分钟 Timer.schedule(new MyTask(),c2.getTime(),1000*60*60*24) //每24小时执行一次。
  • 22. Timer类:工具类,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 (当前台线程都执行完成时,后台线程自动终止运行)。 与每个 Timer 对象相对应的是单个后台线程,用于顺序地执行所有计时器任务。 即,每一次new Timer();相当于创建一个后台线程。也就是,无论有多少个TimerTask,都会用同一个线程来运行它。(可以通过ThreadLocal来测试是否为同一个线程)。 timer.schedule(….) 指定在某个时间[重复]执行的任务。 timer.scheduleAtFixedRate(….) 指定在某个的时间,以固定的速度执行,如果还有没有执行过的,则被以较快的速度补充上。 更多说明见JDKAPI文档。
  • 23. 在Spring中通过TimerFactoryBean调度任务:TimerFactoryBean默认的内部实现为Timer。 TimerFactoryBean是一个Spring工厂,用于在应用程序中生成Java Timer。 TimerFactoryBean与Timer一样,需要一个TimerTask来执行任务。
  • 24. 设置每隔一秒调用 一次。如果要一天 调用一次可以设置 成86400000秒。 遗憾的是,使用Timer 无法精确到何时 运行何时不运行。 如周1到周5工作时间 等。由于ApplicationContext是Spring的高级容器 ,也只能使用这个容器设置调度。BeanFactory 不适用。
  • 25. Jdk1.5的线程池调度:在jdk1.5以后,SUN又添加了几个新的类,以便于更好的管理和调度线程。它们是: Executor – 最高执行器接口。 ExecutorService – 执行器接口,是Executor的子接口。 ScheduleExecutorService – 是调度任务执行器接口。它是ExecutorService的子类。 上面的三个接口,一般情况下,使用ExecutorService执行马上执行的任务。ScheduleExecutorService用于执行重复执行的任务。 为了可以快速获取Executor执行器,SUN提供了Executors类,里面有很多静态方法可以快速获取执行器。 Spring3也使用了jdk1.5新类库。上节讲的TimerFactoryBean,ScheduleTimerTask已经被打上了不赞成使用的标签。
  • 26. ExecutorService示例:由于线程池中只有一个线程,所以,每次 只可以执行一个任务,后续的任务将会排队 等待,直到执行完成所有任务。 执行完成以后,程序并不会终止,而是仍然 有一个线程在等待执行任务。
  • 27. Jdk1.5 - 创建固定数量的线程池:
  • 28. ThreadPoolExecutor:ThreadPoolExecutor可以定义线程池的最小值,最大值及何时停止运行等信息。 ThreadPoolExecutor类的构造方法如下: 可以通过构造方法初始化ThreadPoolExecutor类。 关于构造的细节,可以通过查看Executors.newX..方法了解更多细节。
  • 29. ThreadPoolExecutor:
  • 30. 线程池任务延时执行:ScheduledExecutorService 遗憾的是,使用这种线程池也无法精确到何时运行何时不运行。 如周1到周5工作时间等。
  • 31. 在Spring中的TaskExecutor:Spring中的TaskExecutor是java.util.concorrent.Executor的子类。 它的一些子类如: 立刻执行的类有: SimpleAsyncThreadExecutor – 为每一个任务创建一个线程。不会重用任何线程。有点类似于Executors.newCachedThreadPool();它比较适合于线程集中运行并很快能结束的任务。 ThreadPoolTaskExecutor - 可以定义线程池中大小,队列大小等。类似于java.util.concurrent.ThreadPoolExecutor类。当任务大于最大值时,才会启动最大值个线程,如在maxSize=5,设置了10个任务。则只会有5个线程工作,后面的5个任务将会进入等待队列。 延迟执行的类有: 以下类都可以接收一个Trigger可以更精确的定义时间: ConcurrentTaskScheduler 以一个线程的方式运行多个任务。 ThreadPoolTaskScheduler – 可以定义一个线程池。 (这两个类类似在此只在代码中演示第一个的用法,在配置文件中演示第二个类的用法。)
  • 32. SimpleAsyncThreadExecutor示例: 为每一个任务创建一个线程,且不会用:
  • 33. ThreadPoolTaskExecutor示例:
  • 34. ConcurrentTaskScheduler:延迟执行
  • 35. ConcurrentTaskScheduler:确定在某个时间点执行:
  • 36. Cron(时间单位)表达式:Spring3.0以后,自己已经完全支持更加精确的时间,而不需要Quartz的支持: Spring3.0同样也使用cron表达式。与Quartz不同的是,Spring3.0不支持年, 而Quartz支持年。但这点好象并不是非常重要。 cron表达式:-是用空格分开的时间字段,不使用年。*(秒0-59) *(分钟0-59) *(小时0-23) *(日期1-31) *(月份1-12或是JAN-DEC) *(星期1-7或是SUN-SAT)说明:对于上面每一个时间的定义: */3 :: 表示每3。如 */3在秒上则表示为每3秒钟。如果在小时上则为每3小时,或是能被3整除。 1,2 :: 如果用,(逗号)则为1或是2。如在小时上则为凌晨1点或是2点。 1-5 :: 如果使用-(横线),则为从1到5.
  • 37. Cron时间示例:*/5 * * * * 6-7 :: 每个周6到周日,每隔5秒钟执行一次。 */1 * * 7-9 1-2 1-7 :: 1月到2月中的7号到9号,且必须要满足周一到周日,每隔1秒钟执行一次。 */1 * * 7-9 1,5 1-7 :: 注意里面的,(逗号),只有1月和5月的7到9号,且必须要满足周一到周日,每一秒钟执行一次。 */1 17-59 * 7-9 1,5 1-7 :: 只解释17-59,是指从第17分钟到第59分钟,在指定的时间内,每一秒种执行一次 * 17-59 * 7-9 1,5 1-7 :: 此代码的功能与上面完全相同。如果不写秒即为每一秒执行一次。 59 19-23 * 7-9 1,5 1-7 :: 19分-23分的每59秒钟时只执行一次。 59 19,26 * 7-9 1,5 1-7 :: 注意里面的,(逗号),是指只有19分或是26分的56秒钟时执行一次。 * * 16-23 7-9 1,5 1-7 :: 定义每天的16点到23点每一秒钟执行一次。 59 59 23 * * 1-5 :: 定义每周1到周5,晚上23:59:59秒只执行一次。这个相当用有。可以工作时间每天给用户发邮件。
  • 38. 在Spring的配置文件中配置定时执行的任务:Task命名空间:在Spring3.0中,task命名空间用于定义一个计划任务: 用于定义一个ThreadPoolTaskScheduler.task:scheduled-tasks :元素引用一个scheduler的定义,它的子元素task:scheduled 可以引用一个普通的POJOs在自己的初始化的线程中运行。 在使用task元素,必须要在spring的配置文件中引入task命名空间: 如下: (以下配置可以在spring-context.jar中META-INF中找到) http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
  • 39. 在Spring的配置文件中配置定时执行的任务: 配置一个每隔3秒执行一次的任务:1、先定义一个POJOs2、配置线程调度3、在配置好以后,实例化容器即可
  • 40. 通过Cron描述符定义一个定时执行的任务:在task:scheduled元素中,fixed-delay或是cron必须配置一项。 关于更多cron的写法,请参考前面的说明。
  • 41. 使用注解支持任务调试:在Spring3.0中,添加了@Scheduled注解。此注解用于添加到方法上。 需要添加aop包的支持,和aopalliance包的支持。 示例如下:
  • 42. 使用注解支持任务调试:在添加了注解以后,使用task:annotation-driven/>执行被添加了注解的任务方法。尽管有注解方式,但我们仍然优选配置方式。因为它可以不污染我们的类,且扩展和修改方便。
  • 43. 使用Quartz调度器:Quartz框架是目前非常流行的调度框架。 开发Quartz需要导入quartz.jar及它所依赖的相关包如log4j和slf4j。 Quartz的重要接口: Job – 需要用户实现,并实现execute方法。 需要使用JobDetail接收Job类型。 Scheculer – 调度器。 Trigger – 触发器,何时调用及如何调用。
  • 44. 使用纯Quartz的示例代码:
  • 45. Quartz的其他示例:每一秒执行一次 withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().withInterval(1, IntervalUnit.SECOND)); 以下设置在每天的16:20分执行 .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(16,20)) 以下设置在16:54分开始,每一秒执行一次,注意日期中的日与周互斥,必须有一个忽略,即? .withSchedule(CronScheduleBuilder.cronSchedule("* 54 16 * * ?")) 关于更多Quartz的示例可见Quartz中的示例。
  • 46. 了解Quartz的配置文件:Quartz在启动时默认会开启10个线程。这是由quartz.properties文件中定义的。如果用户没有提供此文件,quartz将使用默认的配置文件。 quartz.properties资源文件,应该位于classpath的根目录下。 用户定义配置内容,会覆盖系统的设置:org.quartz.scheduler.instanceName: DefaultQuartzScheduler org.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false org.quartz.scheduler.wrapJobExecutionInUserTransaction: false org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount: 10 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true org.quartz.jobStore.misfireThreshold: 60000 org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
  • 47. 在Spring中使用Quartz完成调度:请使用quartz_1.x.jar版本。因为Spring3.x用的是Quartz1.x。切记。 同时添加以下包: log4j-1.2.15.jar org.springframework.aop-3.0.7.jar aopalliance.jar quartz-all-1.8.5.jar org.springframework.transaction-3.0.7.RELEASE.jar slf4j-api-1.6.1.jar slf4j-log4j12-1.6.4.jar
  • 48. Spring中使用Quartz完成调度:几个重要的类: QuartzJobBean – 类似于一个TimerTask。程序应该完成它的子类 定义任务 JobDetailBean – 用于接收QuartzJobBean并通过Map给QuartzJobBean设置属性的值。 引用任务。 SimpleTriggerBean和CronTriggerBean – 前者提供简单的实现,者提供复杂的cron表达式。 设置任务何时发生。 SchedulerFactoryBean –启动任务。
  • 49. 以下配置实现每一秒执行一次任务:配置完成以后,使用应用上下文启动应用
  • 50. 使用CronTriggerBean定义一个任务:-定义某个时间触发
  • 51. QuartzDetailBean、TriggerFactoryBean和SchedulerFactoryBean的关系:SchedulerFactoryBeanTriggerFactoryBean2QuartzDetailBean2TriggerFactoryBeanQuartzDetailBean
  • 52. 按调度计划调度方法:Spring致力于一种更加简明的方法。如果一个任务不用必须是TimerTask或是QuartzJobBean的子类,是不是很棒! Spring提供以下两个类用于完成上面的工作: MethodInvokingTimerTaskFactoryBean. 运行时在后台创建一个TimerTask类. MethodInvokingJobDetailFactoryBean 运行时在后台创建一个JobDetail类。 因为现在我们讲的是Quartz所有我们使用此类做为示例。
  • 53. MethodInvokingJobDetailBean示例:配置文件:声明一个普通类即可,不必是JobDetail的子类:
  • 54. 总结:一个项目中,一种只拥有一个SchedulerFactoryBean,所有的Trigger都可以配置到其中。 一个Trigger只能拥有一个JobDetail。在Trigger中应该设置何时、以何种频率调用。 以下只定义了一个任务调度器,但它包含项目中的所有Trigger,这是可以的。