Castle Windsor 3 Interceptor 没有发布由类型化工厂创建的组件,但 2.5.4 发布了。为什么?
这与其他地方所述的模式类似,并详细在此博文中。我使用 Windsor 2.5.4 进行此工作,与博文中所述非常相似,但决定切换到使用 Windsor 3。当我这样做时,我注意到应用程序的内存使用量随着时间的推移而增加 - 我猜这将是组件没有被释放。
博文中的代码进行了一些修改,这可能导致行为有所不同。
这是我的 AutoRelease 拦截器(直接从博客文章中取出,这里是为了方便和懒惰;))
[Transient]
public class AutoReleaseHandlerInterceptor : IInterceptor
{
private static readonly MethodInfo Execute = typeof(IDocumentHandler).GetMethod("Process");
private readonly IKernel _kernel;
public AutoReleaseHandlerInterceptor(IKernel kernel)
{
_kernel = kernel;
}
public void Intercept(IInvocation invocation)
{
if (invocation.Method != Execute)
{
invocation.Proceed();
return;
}
try
{
invocation.Proceed();
}
finally
{
_kernel.ReleaseComponent(invocation.Proxy);
}
}
}
我与博客文章的偏差之一是类型化工厂使用的选择器:-
public class ProcessorSelector : DefaultTypedFactoryComponentSelector
{
protected override Func<IKernelInternal, IReleasePolicy, object> BuildFactoryComponent(MethodInfo method,
string componentName,
Type componentType,
IDictionary additionalArguments)
{
return new MyDocumentHandlerResolver(componentName,
componentType,
additionalArguments,
FallbackToResolveByTypeIfNameNotFound,
GetType()).Resolve;
}
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return null;
}
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
var message = arguments[0];
var handlerType = typeof(IDocumentHandler<>).MakeGenericType(message.GetType());
return handlerType;
}
}
可能值得注意的是我不使用默认解析器。 (这也许就是问题所在......)。
public class MyDocumentHandlerResolver : TypedFactoryComponentResolver
{
public override object Resolve(IKernelInternal kernel, IReleasePolicy scope)
{
return kernel.Resolve(componentType, additionalArguments, scope);
}
}
(为了简洁起见,我省略了 ctor - 那里没有发生什么特别的事情,它只是调用基本 ctor)。
我这样做的原因是因为默认解析器会尝试按名称而不是按类型解析并失败。在这种情况下,我知道我只需要按类型解析,因此我只是覆盖了 Resolve 方法。
最后一块拼图将是安装程序。
container.AddFacility<TypedFactoryFacility>()
.Register(
Component.For<AutoReleaseHandlerInterceptor>(),
Component.For<ProcessorSelector>().ImplementedBy<ProcessorSelector>(),
Classes.FromAssemblyContaining<MessageHandler>()
.BasedOn(typeof(IDocumentHandler<>))
.WithService.Base()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)),
Component.For<IDocumentHandlerFactory>()
.AsFactory(c => c.SelectedWith<ProcessorSelector>()));
单步执行代码,拦截器被调用并执行finally子句(例如,我没有弄错方法名称)。然而,该组件似乎没有被释放(使用性能计数器显示了这一点。每次调用工厂的 create 方法都会使计数器增加 1)。
到目前为止,我的解决方法是向我的工厂接口添加一个 void Release(IDocumentHandler handler) 方法,然后在执行 handler.Process() 方法后,它显式释放处理程序实例,这似乎完成了工作-性能计数器上升,处理完成后下降)。
这是工厂:
public interface IDocumentHandlerFactory
{
IDocumentHandler GetHandlerForDocument(IDocument document);
void Release(IDocumentHandler handler);
}
这是我使用它的方式:
IDocumentHandlerFactory handler = _documentHandlerFactory.GetHandlerForDocument(document);
handler.Process();
_documentHandlerFactory.Release(handler);
因此显式执行发布就不需要拦截器,但我真正的问题是为什么这种行为在发布之间有所不同?
This is a similar pattern to ones stated elsewhere and detailed in this blog post. I have this working using Windsor 2.5.4 pretty much as stated in the blogpost, but decided to switch to using Windsor 3. When I did this I noticed that the memory usage of the application go up over time - I guessed this would be that components were'nt being released.
There were a couple of modifications to the code in the blogpost, which may have caused the behaviour to differ.
Here is my AutoRelease interceptor (straight out of the blogpost, here for convenience and the lazy ;) )
[Transient]
public class AutoReleaseHandlerInterceptor : IInterceptor
{
private static readonly MethodInfo Execute = typeof(IDocumentHandler).GetMethod("Process");
private readonly IKernel _kernel;
public AutoReleaseHandlerInterceptor(IKernel kernel)
{
_kernel = kernel;
}
public void Intercept(IInvocation invocation)
{
if (invocation.Method != Execute)
{
invocation.Proceed();
return;
}
try
{
invocation.Proceed();
}
finally
{
_kernel.ReleaseComponent(invocation.Proxy);
}
}
}
One of my deviations from the blog post is the selector that the typed factory uses:-
public class ProcessorSelector : DefaultTypedFactoryComponentSelector
{
protected override Func<IKernelInternal, IReleasePolicy, object> BuildFactoryComponent(MethodInfo method,
string componentName,
Type componentType,
IDictionary additionalArguments)
{
return new MyDocumentHandlerResolver(componentName,
componentType,
additionalArguments,
FallbackToResolveByTypeIfNameNotFound,
GetType()).Resolve;
}
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return null;
}
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
var message = arguments[0];
var handlerType = typeof(IDocumentHandler<>).MakeGenericType(message.GetType());
return handlerType;
}
}
What might be noticeable is that I do not use the default resolver. (This is where, perhaps, the problem lies...).
public class MyDocumentHandlerResolver : TypedFactoryComponentResolver
{
public override object Resolve(IKernelInternal kernel, IReleasePolicy scope)
{
return kernel.Resolve(componentType, additionalArguments, scope);
}
}
(I omitted the ctor for brevity- nothing special happens there, it just calls the base ctor).
The reason I did this was because the default resolver would try to resolve by name and not by Type- and fail. In this case, I know I only ever need to resolve by type, so I just overrode the Resolve method.
The final piece of the puzzle will be the installer.
container.AddFacility<TypedFactoryFacility>()
.Register(
Component.For<AutoReleaseHandlerInterceptor>(),
Component.For<ProcessorSelector>().ImplementedBy<ProcessorSelector>(),
Classes.FromAssemblyContaining<MessageHandler>()
.BasedOn(typeof(IDocumentHandler<>))
.WithService.Base()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)),
Component.For<IDocumentHandlerFactory>()
.AsFactory(c => c.SelectedWith<ProcessorSelector>()));
Stepping through the code, the interceptor is called and the finally clause is executed (e.g. I didn't get the method name wrong). However, the component does not seem to be released (using the performance counter shows this. Each invocation of the factory's create method increases the counter by one).
So far, my workaround has been to add a void Release(IDocumentHandler handler) method to my factory interface, and then after it executes the handler.Process() method, it explicitly releases the handler instance, and this seems to do the job- the performance counter goes up, and as the processing is done, it goes down).
Here is the factory:
public interface IDocumentHandlerFactory
{
IDocumentHandler GetHandlerForDocument(IDocument document);
void Release(IDocumentHandler handler);
}
And here is how I use it:
IDocumentHandlerFactory handler = _documentHandlerFactory.GetHandlerForDocument(document);
handler.Process();
_documentHandlerFactory.Release(handler);
Doing the Release explicitly therefore negates the need for the interceptor, but my real question is why this behaviour differs between the releases?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
自我提醒:- RTFM。或者事实上,阅读 Breakingchanges.txt 文件。
这是影响此行为的更改(重点是我的):-
我没有发现修复建议在我的实例中非常有帮助,但是我在问题中的解决方案是你实际应该做什么(使用工厂发布)。如果其他人遇到这个(非)问题,我会保留它。
Note to self:- RTFM. Or in fact, read the Breakingchanges.txt file.
Here's the change that affects this behaviour (emphasis is mine):-
I didn't find the fix suggestion terribly helpful in my instance, however my solution in the question is what you should actually do (release using the factory). I'll leave it up in case anyone else has this (non) issue.