l Spring提供的RMI
假如我们在本地创建一个用户管理系统,使用User对象代表一个用户,定义一个服务接口UserService,并且有一个默认实现类UserServiceImpl。类结构图如下:
我们准备在Spring中将UserService作为一个RMI服务输出。步骤如下:
1) 在xml文件中配置如下:
<!--定义UserServiceImpl -->
<bean id=”userService” class=”UserServiceImpl”>
<!--将UserServiceImpl类和UserService 接口注入Spring容器-->
<bean id=”rmiService” class=”org.springframework.remoting.rmi.RmiServiceExporter”>
<property name=”serviceName” value=”UserService”/>
<property name=”service” ref=”userService”/>
<property name=”serviceInterface” value=”UserService”/>
<property name=”registryPort” value=”1099”/>
</bean>
2) 编写main启动Spring容器,main方法如下:
new ClassPathXmlApplicationContext(config.xml);
3) 客户端可以直接在xml配置文件中定义UserService并直接使用。如下:
<bean id=”userService” class=” org.springframework.remoting.rmi.RmiProxyFactoryBean”>
<property name=”serviceUrl” value=” rmi://localhost:1099/UserService”/>
<property name=”serviceInterface” value=”UserService”/>
</bean>
如果服务端不是通过Spring发布的服务,也可以通过如下代码实现:
public class Client(){
public static void main(String [] args) throws Exception{
RmiProxyFactoryBean fc = new RmiProxyFactoryBean();
fc.setServiceInterface(UserService.class);
fc.setServiceUrl(“rmi://localhost:1099/UserService”);
fc.afterPropertiesSet();
//获取UserService服务接口
UserService us = (UserService)fc.getObject();
//调用UserService:
us.creat(“creat”,”password”);.
}
}
l Spring邮件服务
Spring提供了非常方便的Mail抽象层,它通过一个MailSender接口封装了邮件发送Bean。发送邮件的具体步骤如下:
简单邮件实例:SimpleMailMessage封装了纯文本的简单邮件。
1) 由于JavaMail的实现库并没有集成到JDK 5.0中,需要手动从SUN下载mail.jar和activation.jar这两个库。
2) 编写Spring的config.xml文件,定义mailSender Bean,把发送邮件所需要的所有配置注入到Bean中。
<bean id=”mailSender” class=”org.springframework.mail.javamail.JavaMailSenderImpl”>
<property name=”host” value=”smtp.gmail.com”/>
<property name=”port” value=”465”/>
<property name=”username” value=”guangxu”/>
<property name=”password” value=”123”/>
<property name=”javaMailProperties” >
<props>
<prop key=”mail.smtp.auth”>true</prop>
<prop key=”mail.smtp.starttls.enable”>true</prop>
<prop key=”mail.smtp.socketFactory.class”>java.net.ssl.SSLSocketFactory
</prop>
</props>
</property>
3) 编写程序发送邮件:
ApplicationContext ct = new ClassPathXmlApplicationContext(“config.xml”);
JavaMailSender mailSender = (JavaMailSender)ct.getBean(“mailSender”);
//创建一个纯文本邮件
SimpleMailMessage mail = new SimpleMailMessage();
mail.setFrom(“guangxu@gmail.com”);
mail.setTo(“123@gmail.com”);
mail.setSubject(“A text Mail”);
mail.setText(“测试邮件发送”);
mailSender.send(mail);
MIME邮件实例:Spring提供了一个MimeMessageHelper助手类来操作MimeMessage。
只需更改邮件发送代码为:
ApplicationContext ct = new ClassPathXmlApplicationContext(“config.xml”);
JavaMailSender mailSender = (JavaMailSender)ct.getBean(“mailSender”);
//创建一个带附件的邮件
MimeMessage mime = mailSender.creatMimeMessage();
//创建Helper实例并声明编码为UTF-8,true表示Multipart
MimeMessageSender helper = new MimeMessageSender(mime,true,”UTF-8”);
helper.setFrom(“guangxu@gmail.com”);
helper.setTo(“123@gmail.com”);
helper.setSubject(“A text Mail”);
helper.setText(“<html><body>访问我的书店”+”<a href=’http://www.shanshibook.com’ target=’_blank’>”+”<imag src=’cid:logo’></a></body></html>”);
//添加一个嵌入的图片:
Helper.addInline(“logo”,new ClassPathResource(“logo.gif”));
//添加一个普通的附件
Helper.addAttachment(“freebsd.gif”,new ClassPathResource(“freebsd.gif”));
//发送
mailSender.send(mime);
l Spring任务调度服务
JDK提供了一个Timer类,可以周期性的执行某个任务,Spring把它装成ScheduledTimerTask,并且提供开源的Quaitz调度器。
假如我们要周期性的把日志内容发送给系统管理员。
使用ScheduledTimerTask的示例如下:
1) 需要定义一个任务,它派生自TimerTask
public class ReportTimerTask extends TimerTask{
public void run(){
String log = “read from log file”+new Date();
System.out.println(log);
try{
Thread.sleep(10000);//模拟一个耗时的任务
}catch(InterruptedException e){
e.printStackTrace(e);
}
public void sendMail(){}
}
}
2) 在XML配置文件中定义reportTask,并把依赖注入scheduledTask。
<bean id=”reportTask” class=”ReportTimerTask”/>
<bean id=”scheduledTask” class=”org.springframework.scheduling.timer.ScheduledTimerTask”>
<!--启动后等待10秒,然后开始执行 -->
<property name=”delay” value=”10000”/>
<!--每60秒执行一次-->
<property name=”period” value=”60000”/>
<property name=”timerTask” value=” reportTask”/>
3) reportTask定义了一个独立的任务,而scheduledTask定义了一个周期性的任务。最后,通过定义一个timerFactory来启动这个周期性任务。
<bean id=” timerFactory” class=”org.springframework.scheduling.timer.TimerFactoryBean”/>
<property name=” scheduledTimerTasks” >
<list>
<ref bean=” scheduledTask”/>
</list>
</property>
</bean>
4) 编写main,启动Spring容器。
public static void main(String []args){
new ClassPathXmlApplicationContext(“config.xml”);
try{
Threed.sleep(300000);//运行这些时间后自动停止
}catch(InterruptedException e){
System.exit();
}
}
使用Quaitz调度器的示例:
首先来介绍Quaitz实现任务调度的几个关键概念。
1) Job:定义一个任务。
2) Trigger:定义一个触发器,表示应该如何触发一个Job的执行。可以实现某一时刻触发或者周期性的触发。
3) Scheduler:真正调度任务的调度器,通过scheduleJob(Job, Trigger)方法就把一个关联了Trigger的Job对象放入了调度器中执行。
我们想设计一个检查磁盘剩余空间的CheckDiskFreeSpace。步骤如下:
1) 编写任务程序:
public class CheckDiskFreeSpace{
public void check(){
long freeSpace = Math.random()>0.5?10000000:20000000
System.out.println(“Check disk free space at”+new Date());
if(freeSpace<100*1024*1024){
System.out.println(“Warning!”);
}
}
}
2) 在config.xml文件中将其包装为JobDetailBean:
<bean name=” checkDisk” class=” CheckDiskFreeSpace”/>
<bean name=”checkDiskJob” class=”org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean”>
<property name=”targetObject” ref=” checkDisk”/>
<property name=” targetMethod” value=”check”/>
<property name=”concurrent” value=”false”/>//设置这个checkDiskJob能不能同时执行
</bean>
3) 定义Trigger,它定义了合适触发Job的执行。Spring封装了两种常用的TriggerBean,一种是SimpleTriggerBean可以周期性的执行Job,一种是CronTriggerBean可以在指定时间执行Job。conExpression的写法可以查看相关文档。
SimpleTriggerBean配置如下:
<bean id=” repeatTrigger” class=”org.springframework.scheduling.quartz.SimpleTriggerBean”>
<property name=”jobDetail” ref=” checkDiskJob”/>
<!--1分钟后启动-->
<property name=”startDelay” value=”60000”/>
<!--5分钟检查一次-->
<property name=”repeatInterval” value=”300000”/>
</bean>
CronTriggerBean配置如下:
<bean id=”cronTrigger” class=”org.springframework.scheduling.quartz.CronTriggerBean”>
<property name=”jobDetail” ref=” checkDiskJob”/>
<!--每周周一至周五中午12:00执行-->
<property name=”cronExpression” value=”0 0 12 ? * MON-FRI”/>
</bean>
4) 在配置文件中定义一个Scheduler,然后将所有的TriggerBean都注入进去。如下:
<bean id=”schedule” class=” org.springframework.scheduling.quartz.SchedulerFactoryBean”/>
<property name=”triggers”>
<list>
<ref bean=”repeatTrigger”/>
<ref bean=”cronTrigger”/>
</property>
5) 编写main启动Spring容器:
Public static void main(String []args){
New ClassPathXmlApplicationContext(“config.xml”);
}
l Spring 的JMS(Java Message Service)服务:
如果不适用Spring提供的功能,则在使用JMS前首先要引入编译好的mom4j 1.1版本的jar包。为了能发送JMS消息,需要从JNDI获得ConnectionFactory和Destination对象,ConnectionFactory用于创建到JMS服务器的链接,Destination则指定了消息的目的地。接收JMS消息和发送类似,也要通过ConnectionFactory创建到JMS服务器的链接,Destination指定从何处接收消息。此外,接收者必须实现MessageListener接口以便服务器回调。最后,在main方法中启动JMS服务器,然后分别启动发送和接受线程。
直接使用JMS API相当复杂,而Spring对JMS提供了非常简便的封装。通过使用Spring提供的JmsTemplate模板,操作JMS就很简单了。如下步骤:
1) 利用JmsTemplate,Sender只需要被注入JmsTemplate,发送的消息通过MessageCreator以回调的方式创建。
public class Sender{
private JmsTemplate jsmTemp;
public void setJmsTemp(JmsTemplate jsmTemp){
this. jsmTemp= jsmTemp;
}
public void send(final String text){
Sysytem.out.println(“Send:”+text);
jsmTemp.send(new MessageCreator(){
public Message creatMessage(Session session) throws JMSException{
return session.creatTextMessage(text);
}
});
}
}
2) 所需的JMS资源全部定义在XML配置文件中:
<bean id=”jmsConnectionFactory” class=”org.springframework.jndi.JndiObjectFactoryBean”>
<property name=”jndiName value=”QueueConnectionFactory”/>
</bean>
<bean id=”jmsQueue” class= “org.springframework.jndi.JndiObjectFactoryBean”>
<property name=”jndiName” value=”jms/queue”/>
</bean>
<bean id=”jmsTemplate” class=” org.springframework.jms.core.JmsTemplate”>
<property name=”connectionFactory” ref=” jmsConnectionFactory”/>
<property name=”defultdestination” ref=” jmsQueue”/>
</bean>
<bean id=”sender” class=”Sender”>
<property name=”jmsTemplate” ref=”jmsTemplate”/>
</bean>
3) 对于接受消息的Receiver类,仅实现MessageListener接口,甚至没有注入任何JMS资源。
public class Receiver implements MessageListener{
public void onMessage(Message msg){
if(msg instanceof TextMessage){//判断msg是否是TextMessage的实例
TextMessage text = (TextMessage)msg;
try{
System.out.println(“Receive:”+text.getText());
}catch(JMSException e){}
}
}
}
4) Spring提供了MessageListener容器来包裹MessageListener。通常情况下,使用DefaultMessageListenerContainer就足够了,需要在XML文件中定义:
<bean id=”receiver” class=”Receiver”/>
<bean id=” listenerContainer” class=”org.springframework.jms.listener.DefaultMessageListenerContainer”>
<property name=”connectionFactoy” ref=”jmsConnectionFactory”/>
<property name=”destination” ref=”jmsQueue”/>
<property name=”messageListener” ref=”receiver”/>
</bean>
5) 编写测试程序如下:
Public static void main(String []args)throws Exception{
Mom4jUtil.startJmsServer();
ApplicationContext ctx = new ClassPathXml ApplicationContext(“config.xml”);
Sender sender = (Sender)ctx.getBean(“sender”);
for(int i=0;i<10;i++){
sender.send(“Hello“+new Date());
Thread.sleep(1000);
}
System.exit();
}
l Spring的Acegi安全框架
Acegi为应用程序提供认证和授权的安全保护功能。在使用Acegi之前需要引入Acegi.jar和commons-codec.jar包。
1. 保护Web资源:Acegi安全框架对Web资源的保护是基于Filter的,所以我们需要多个Filter构成一个FilterChain来实现。虽然有多个Filter,但是我们只需要在web.xml中配置一个特殊的AcegiFilter,其他的Filter通过依赖注入的方式在Acegi的Spring XML配置文件中定义。
1) 我们将Acegi相关的Bean全部放入security.xml中,然后在web.xml中配置Listener和Filter。
web.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Acegi配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/security.xml</param-value>
</context-param>
<!-- 启动Spring容器 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- Acegi过滤器 -->
<filter>
<filter-name>acegiFilterChain</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<!-- Acegi过滤器URL映射 -->
<filter-mapping>
<filter-name>acegiFilterChain</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>acegiFilterChain</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- Spring的DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
2) 在security.xml中配置好一系列的安全过滤器,以及认证管理器和决策管理器。
配置认证管理器(AuthenticationManager):该组件的任务就是对用户进行认证。认证管理器通过识别Principal(主题,通常是用户名)和Credentials(凭证,通常是口令)来确定用户的身份。Acegi提供了一个默认的AuthenticationManager的实现类ProviderManager,可以直接使用。ProviderManager并不直接去验证用户提供的Principal和Credentials,而是将他们委托给一个或多个AuthenticationProvider来验证。ProviderManager将注意遍历每个AuthenticationProvider,只要有一个AuthenticationProvider成功的认证了用户,该认证就结束。可以使用的AuthenticationProvider如下:
a) AuthByAdapterProvider:通过Web容器来验证。
b) CasAuthenticationProvider:通过CAS服务器来验证用户身份。
c) DaoAuthenticationProvider:通过数据库存储的用户名和口令信息来验证用户身份。
d) JaasAuthenticationProvider:通过JAAS服务来验证用户身份。
e) PasswordDaoAuthenticationProvider:通过数据库认证,但是具体过程由底层数据源完成。
f) RememberMeAuthenticationProvider:通过浏览器端得Cookie来验证用户上次是否成功登陆并在有效期内。
g) RemoteAuthenticationProvider:通过远程服务验证用户身份。
决策管理器(AccessDecisionManager):身份认证完毕后,还需要由决策管理器决定是否允许用户访问某一受保护的资源。该接口的核心方法如下:
public void decide(Authentication au,Object obj,ConfigAttributeDefinition con) throws AccessDeniedException,InsufficientAuthenticationException;
如果没有抛出以上异常,则认为允许访问。AccessDecisionManager的决策是基于投票方式的。让一个或多个具有投票权的AccessDecisionVoter对象进行投票,然后根据投票结果判断是否允许访问资源。
Acegi提供了3个基本的AccessDecisionManager实现:
a) AffirmativeBased:至少有一个投票者允许访问时,就允许访问该资源。
b) ConsensusBased:所有投票这都允许访问时,才允许访问。
c) UnanimousBased:如果没有投票者拒绝,就允许访问。
以上3个AccessDecisionManager都有一个allowIfAllAbstainDecisions
属性,如果设置为true,就建立一个“沉默即同意”的策略,即所有投票者都弃权,也可以访问。每个AccessDecisionManager都有三种方式进行投票:ACCESS_GRANTED(赞成),ACCESS_DENIED(拒绝), ACCESS_ABSTAIN(弃权)。
Acegi提供了一个投票者实现:RoleVoter。它默认只对以“ROLE_”开头的授权进行判断。
3) 配置FilterChain。详细方法不再介绍了。