ActionInvocation

Interceptor说明

Interceptor的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法。而它所依赖的参数ActionInvocation则是我们之前章节中曾经提到过的著名的Action调度者

我在这里需要指出的是一个很重要的方法invocation.invoke()。这是ActionInvocation中的方法,而ActionInvocation是Action调度者,所以这个方法具备以下2层含义(详细看DefaultActionInvocation源代码):
1. 如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。

2. 如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。

3.

DefaultActionInvocation部分源代码:

      if (interceptors.hasNext()) {
       final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
       UtilTimerStack.profile("interceptor: "+interceptor.getName(),
         new UtilTimerStack.ProfilingBlock<String>() {
       public String doProfiling() throws Exception {
           resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);//递归调用拦截器
           return null;
       }
       });
      } else {
       resultCode = invokeActionOnly();
      }

每个拦截器中的代码的执行顺序,在Action之前,拦截器的执行顺序与堆栈中定义的一致;而在Action和Result之后,拦截器的执行顺序与堆栈中定义的顺序相反。

Interceptor拦截类型

从上面的分析,我们知道,整个拦截器的核心部分是invocation.invoke()这个函数的调用位置。事实上,我们也正式根据这句代码的调用位置,来进行拦截类型的区分的。在Struts2中,Interceptor的拦截类型,分成以下三类:

1. before

before拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之前。这些代码,将依照拦截器定义的顺序,顺序执行

2. after

after拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之后。这些代码,将一招拦截器定义的顺序,逆序执行

 

PreResultListener
有的时候,before拦截和after拦截对我们来说是不够的,因为我们需要在Action执行完之后,但是还没有回到视图层之前,做一些事情。Struts2同样支持这样的拦截,这种拦截方式,是通过在拦截器中注册一个PreResultListener的接口来实现的。

如:在拦截器中使用如下代码,其中MyPreResultListener实现了PreResultListener 接口并在beforeResult方法中做了一些事情然后在拦截器类中加入action.addPreResultListener(new MyPreResultListener());

 

从源码中,我们可以看到,我们之前提到的Struts2的Action层的4个不同的层次,在这个方法中都有体现,他们分别是:拦截器(Interceptor)、Action、PreResultListener和Result。在这个方法中,保证了这些层次的有序调用和执行

 

问题

使用Struts2作为web框架,知道它的拦截器(Interceptor)机制,类似与Filter和Spring的AOP,于是实现了一个为Action增加自定义前置(before)动作和后置动作(after)的拦截器(曰:WInterceptor),不过用一段时间发现,在WInterceptor的after中,对Action对象的属性修改在页面看不到,对请求对象的属性设置也无效。为什么在调用了Action之后(invokeAction())之后,request就不能使用了呢,拦截器不能改变Action的Result么?

 问题的关键在于,在调用actionInvocation.invoke()的之后,不仅执行类Action,也执行类Result。因而,等退回到拦截器的调用代码时,Result已经生成,View已经确定,这时你再修改模型(Action的属性)或请求对象的属性,对视图不会有任何影响。

解决办法:

 方法一:使用现成的PreResultListener监听器事件

搞清楚原因,卷起袖子干吧,只要让WInterpretor的after事件,放在Result的生成之前就行了。

看看XWork的拦截器接口注入的actionInvocation,其实就提供增加Result执行的前置监听事件-PreResultListener:

Java代码 复制代码  收藏代码
  1. /**  
  2.  * Register a {@link PreResultListener} to be notified after the Action is executed and  
  3.  * before the Result is executed.  
  4.  * <p/>  
  5.  * The ActionInvocation implementation must guarantee that listeners will be called in  
  6.  * the order in which they are registered.  
  7.  * <p/>  
  8.  * Listener registration and execution does not need to be thread-safe.  
  9.  *  
  10.  * @param listener the listener to add.  
  11.  */  
  12. void addPreResultListener(PreResultListener listener);  
/** * Register a {@link PreResultListener} to be notified after the Action is executed and * before the Result is executed. * <p/> * The ActionInvocation implementation must guarantee that listeners will be called in * the order in which they are registered. * <p/> * Listener registration and execution does not need to be thread-safe. * * @param listener the listener to add. */ void addPreResultListener(PreResultListener listener);

 

因此,让拦截器实现这个接口,就可以自然实现Action执行after事件了。

 

 

方法二,实现自己的 ActionInvocation ,手动分离Action和Result的执行

本来前面的方法已经很好了,可是,可是啊,在addPreResultListener里的异常,不会被Struts的框架捕获,而且,addPreResultListener接口不能传递自己的上下文参数,难道动用ThreadLocal传参?

 

研究了一下XWork的ActionInvocation 接口默认实现类DefaultActionInvocation, 写了一个包装类,将Action的执行和Result的生成完全分开,或许有人用的着,放上来,见附件(ActionInvocationWrapper),如有不妥之处请告知。

 

exeucteAction是执行Action,executeResult是执行Result

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DLNA(Digital Living Network Alliance)是一种无线传输技术,可用于在不同设备之间共享音频、视频和图像等媒体内容。在DLNA技术的支持下,用户可以使用一个设备将多媒体内容共享到另一个设备上,例如将手机上的视频投放到电视或音响上播放。下面是一个简单的DLNA投屏软件的源代码示例: ```java import org.teleal.cling.controlpoint.ActionCallback; import org.teleal.cling.controlpoint.ControlPoint; import org.teleal.cling.model.action.ActionInvocation; import org.teleal.cling.model.meta.RemoteDevice; import org.teleal.cling.model.meta.RemoteService; import org.teleal.cling.model.types.ServiceType; import org.teleal.cling.registry.Registry; import org.teleal.cling.registry.RegistryListener; import org.teleal.cling.support.avtransport.callback.SetAVTransportURI; import org.teleal.cling.support.avtransport.lastchange.AVTransportVariable; import org.teleal.cling.support.avtransport.lastchange.AVTransportVariable.TransportState; import org.teleal.cling.support.connectionmanager.callback.GetProtocolInfo; import org.teleal.cling.support.model.ProtocolInfos; import org.teleal.cling.support.model.TransportState; import org.teleal.cling.support.model.TransportStatus; import org.teleal.cling.support.renderingcontrol.callback.GetVolume; import org.teleal.cling.support.renderingcontrol.callback.SetVolume; import java.util.logging.Logger; public class DLNAThrower implements RegistryListener { private static final Logger log = Logger.getLogger(DLNAThrower.class.getName()); private ControlPoint controlPoint; public DLNAThrower(ControlPoint controlPoint) { this.controlPoint = controlPoint; } public void throwTo(String url, RemoteDevice renderer) { log.info("Throwing: " + url + " to " + renderer.getDetails().getFriendlyName()); // Get the A/V transport service RemoteService avTransportService = renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")); // Set the transport URI SetAVTransportURI setAVTransportURI = new SetAVTransportURI(avTransportService, url) { @Override public void success(ActionInvocation invocation) { // Start playing ActionCallback play = new ActionCallback(avTransportService.getAction("Play")); controlPoint.execute(play); } @Override public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) { log.warning("AVTransport failed: " + defaultMsg); } }; controlPoint.execute(setAVTransportURI); } public void stop(RemoteDevice renderer) { log.info("Stopping: " + renderer.getDetails().getFriendlyName()); // Get the A/V transport service RemoteService avTransportService = renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")); // Stop playback ActionCallback stop = new ActionCallback(avTransportService.getAction("Stop")); controlPoint.execute(stop); } public void setVolume(RemoteDevice renderer, int volume) { log.info("Setting volume of: " + renderer.getDetails().getFriendlyName() + " to " + volume); // Get the rendering control service RemoteService renderingControlService = renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")); // Set the volume SetVolume setVolume = new SetVolume(renderingControlService, volume) { @Override public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) { log.warning("SetVolume failed: " + defaultMsg); } }; controlPoint.execute(setVolume); } public int getVolume(RemoteDevice renderer) { log.info("Getting volume of: " + renderer.getDetails().getFriendlyName()); // Get the rendering control service RemoteService renderingControlService = renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")); // Get the current volume GetVolume getVolume = new GetVolume(renderingControlService) { @Override public void received(ActionInvocation actionInvocation, int currentVolume) { log.info("Volume is: " + currentVolume); } @Override public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) { log.warning("GetVolume failed: " + defaultMsg); } }; controlPoint.execute(getVolume); return 0; } public void showProtocolInfo(RemoteDevice renderer) { log.info("Getting protocol info of: " + renderer.getDetails().getFriendlyName()); // Get the connection manager service RemoteService connectionManagerService = renderer.findService(new ServiceType("schemas-upnp-org", "ConnectionManager")); // Get the protocol info GetProtocolInfo getProtocolInfo = new GetProtocolInfo(connectionManagerService) { @Override public void received(ActionInvocation actionInvocation, ProtocolInfos source, ProtocolInfos sink) { log.info("Protocol info received: " + source + ", " + sink); } @Override public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) { log.warning("GetProtocolInfo failed: " + defaultMsg); } }; controlPoint.execute(getProtocolInfo); } @Override public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice device) { log.info("Remote device discovery started: " + device.getDetails().getFriendlyName()); } @Override public void remoteDeviceDiscoveryFailed(Registry registry, RemoteDevice device, Exception ex) { log.warning("Remote device discovery failed: " + device.getDetails().getFriendlyName() + " - " + ex); } @Override public void remoteDeviceAdded(Registry registry, RemoteDevice device) { log.info("Remote device added: " + device.getDetails().getFriendlyName()); } @Override public void remoteDeviceUpdated(Registry registry, RemoteDevice device) { log.info("Remote device updated: " + device.getDetails().getFriendlyName()); } @Override public void remoteDeviceRemoved(Registry registry, RemoteDevice device) { log.info("Remote device removed: " + device.getDetails().getFriendlyName()); } @Override public void localDeviceAdded(Registry registry, LocalDevice device) { log.info("Local device added: " + device.getDetails().getFriendlyName()); } @Override public void localDeviceRemoved(Registry registry, LocalDevice device) { log.info("Local device removed: " + device.getDetails().getFriendlyName()); } @Override public void beforeShutdown(Registry registry) { log.info("Before shutdown"); } @Override public void afterShutdown() { log.info("After shutdown"); } } ``` 需要注意的是,这只是一个简单的示例代码,并不完整,如果要使用该代码,请根据自己的需求进行适当修改和完善。同时,该代码使用了UPnP协议,如果您需要使用其他协议,需要修改代码以适应其他协议的要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值