您可以通过提供模块类。
模块类是一个普通的Java类,您可以创建它来告知Tapestry您的服务和贡献。
注释和命名约定系统允许Tapestry确定模块提供的服务。
模块类存在的原因如下:
- 收件人绑定服务实现的服务接口
- 提供配置数据进入之内服务
- 收件人装饰通过提供服务拦截器在他们周围
- 为构建服务提供显式代码
- 设置默认值标记对于模块中定义的所有服务
模块类的所有公共方法都必须对Tapestry有意义(属于上述类别之一)。任何额外的公共方法都会导致启动异常(因为该方法可能包含输入错误)。
服务生成器方法
服务构建器方法是定义服务并提供构建逻辑的最初方法;尽管现在使用bind()方法更常见(也更简洁)地实现了这一点,但在许多情况下,服务构建器方法仍然很有用。
服务生成器方法是公共方法。它们通常是静态的。下面是一个微不足道的例子:
包org.example.myapp.services;公共类MyAppModule{公共静态索引器build(){return new IndexerImpl();}}
名称以“build”开头的任何公共方法(静态或实例)都是服务构建器方法,在模块中隐式定义服务。
这里我们围绕Indexer服务接口定义一个服务(可能也在org.example.myapp.services包中)。
每个服务都有一个唯一的id,用于在整个服务注册表中识别它(注册表是所有模块中所有服务的总和)。如果您没有提供显式服务id,如本例所示,则服务id是从返回类型中提取的;此服务的id为“Indexer”。
通过将服务添加到方法名buildIndexer()中,可以为服务提供显式id。当您不希望服务id与服务接口名称匹配时(例如,当您有实现同一接口的不同服务时),这很有用,或者,当您需要避免方法名上的名称冲突时(Java只允许一个具有给定名称和参数集的方法,即使返回类型不同,因此如果您有两个采用相同参数的不同服务构建器方法,您应该在方法名中为它们提供显式服务ID)。
Tapestry IoC是不区分大小写; 稍后,我们可以将此服务称为“indexer”或“indexer”或其任何变体,并连接到此服务。
服务ID必须唯一;如果另一个模块提供id为“Indexer”(或其任何情况变体)的服务,则在创建注册表时将发生运行时异常。
我们可以通过添加其他服务构建器方法或通过演示如何注入依赖项来扩展此示例。请参见服务文档了解更多详细信息。
汽车制造服务
主要条款:定义Tapestry IOC服务
另一种通常是首选的定义服务的方法是通过模块的bind()方法。前面的示例可以改写为:
包org.example.myapp.services;导入org.apache.tapestry5.ioc。ServiceBinder;公共类MyAppModule{公共静态void绑定(ServiceBinder绑定){binder.bind(Indexer.class,IndexerImpl.class);}}
有关更多详细信息,请参阅定义Tapestry IOC服务在大多数情况下,自动建筑是首选方法。
一般来说,您应该始终绑定和自动构建您的服务。唯一的例外情况是:
- 您希望做的不仅仅是实例化一个类;例如,将类注册为其他服务的事件侦听器。
- 有没有实现类; 在某些情况下,可以使用JDK动态代理或字节码生成动态创建实现。
bind()方法必须是静态的;如果bind()方法存在但为实例方法,则会引发异常。
缓存服务
您偶尔会发现自己处于将相同的服务重复注入服务构建器或服务装饰器方法的位置(自从引入服务自动构建以来,这种情况很少发生)。这可能会导致相当多的重复输入。代码越少越好,因此作为替代方法,您可以定义一个建造师对于接受带注释参数的模块(与服务构建器注入).
这使您有机会将公共服务存储在实例变量中,以便以后在服务构建器方法中使用。
公共类MyModule{ 专用最终JobScheduler调度程序;私有最终文件系统文件系统;公共MyModule(JobScheduler调度程序、文件系统文件系统){this.scheduler=调度程序;this.fileSystem=文件系统;}公共索引器build(){IndexerImpl索引器=新索引器Impl(文件系统);scheduler.scheduleDailyJob(索引器);返回索引器;}}
请注意,我们已经从静止的方法到实例方法。由于构建器方法不是静态的,因此MyModule类将被实例化,以便可以调用这些方法。构造函数接收两个常见的依赖项,它们存储在实例字段中,稍后可以在服务构建器方法(如buildIndexer())中使用。
这种方法远远不是必需的;如果您愿意,模块的所有构建器方法都可以是静态的。当您有许多常见的依赖项并且希望避免将这些依赖项定义为多个方法的参数时,可以使用它。
Tapestry IoC自动将参数类型(示例中为JobScheduler和FileSystem)解析为实现该类型的相应服务。当有多个服务实现服务接口时,您将收到一个错误(但可以使用附加注释和配置来确保注入的服务正确)。
对于模块,有两种额外的参数类型用于参考资源可以提供给模块实例(而不是服务其可以被注射)。
请注意,字段是最终的:这很重要。Tapestry IoC是线程安全的,您基本上不必考虑并发问题。但在繁忙的应用程序中,不同的线程可以同时构建不同的服务。每个模块类都是一个单例类,最多实例化一次,将这些字段设置为final可以确保这些值在多个线程中可用。参考Brian Goetz的Java并发编程实战为了更完整地解释final字段、构造函数和线程之间的关系。。。或者相信我们!
这种方法应该小心:在某些情况下,您可能会强制模块构造函数依赖于自身。例如,如果您从模块类的构造函数调用同一模块中定义的任何注入服务的方法,那么将需要服务实现。创建服务实现需要模块生成器实例。。。这是一个递归引用。
Tapestry检测到这些场景并抛出运行时异常以防止无休止的循环。
模块类实现注释
模块类的设计非常、非常易于实现。
同样,保持方法非常简单。使用参数注入以访问所需的依赖项。
注意继承。Tapestry将看到所有公众的方法,甚至那些从基类继承的方法。挂毯只有查看公共方法。
按照惯例,模块类名以module结尾,是最终类。
你没有有将方法定义为静态。只有在少数情况下,使用静态方法才是绝对必要的,因为模块的构造函数依赖于来自同一模块的贡献(这会产生一种通过静态方法解决的鸡毛蒜皮的情况)。
默认标记
服务通常由方法或构造函数参数上的特定标记接口引用。Tapestry将使用具有该精确标记和可按类型分配的服务的交集来查找要注入的唯一服务。
通常,模块中的所有服务都应该共享一个标记,这可以通过模块类上的@marker注释来指定。例如,TapestryOCModule:
@标记(内置类)公共最终类TapestryOCModule{. . .
这引用了一个特定的注释类,内置:
@目标({参数,字段})@保留(RUNTIME)@已记录public@interface内置{}
注释可以应用于方法和构造函数参数,以便在IoC容器中使用。它也可以应用于字段,尽管这是特定于Tapestry web框架的。
现场注入
@注入和@注入服务注释可以用于模块类的实例字段,作为通过构造函数传递模块依赖项的替代方法。
警告:通过字段注入使用反射使字段可访问。此外,它可能不如使用构造函数分配给最终字段那样线程安全。
使用此样式,可以重写前面的模块类示例:
公共类MyModule{@注入专用JobScheduler调度器;@注入私有文件系统文件系统;公共索引器build(){IndexerImpl索引器=新索引器Impl(文件系统);scheduler.scheduleDailyJob(索引器);返回索引器;}}