1. 类型安全的依赖注入
2. 注入对象的上下文生存周期管理
3. 事件提醒模型
4. 绑定拦截器通过用户自定义的注解(Annotation)
5. 类型安全的装饰器
6. 为整合第三方框架提供了完整的 SPI 支持
7. 与 JSF,Servlet / JSP 进行了整合
8. 对 JSF 提供长会话(Conversation)上下文支持
Weld 是一个实现了 JSR-299 的框架。主要由JBoss完成其实现,项目主页: http://www.seamframework.org/Weld 。最新发布了 Weld 2.0.0.Alpha1 版本。
Weld 工程的搭建是非常简单的, 这里介绍一下 Weld+JSF 在 Tomcat 7.x 中的工程搭建的方法:
1)通过Eclipse 直接建立一个 Dynamic Web Project , Runtime 选择 tomcat7 , Servlet 版本选择3.0 。
2)添加所需jar文件:由于 tomcat 是非 servlet 容器,所以要加入 weld-servlet.jar 。 还有JSF 的 jsf-api.jar , jsf-imp.jar 和 jstl-1.2.jar
3)在web.xml 添加监听器来启动weld
<listener>
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
4)在META-INF下,添加context.xml 为tomcat容器JNDI绑定 BeanManager。
<Context>
<Manager pathname=""/> <!-- disables storage of sessions across restarts -->
<Resource name="BeanManager"
auth="Container"
type="javax.enterprise.inject.spi.BeanManager"
factory="org.jboss.weld.resources.ManagerObjectFactory"/>
<!-- Uncomment to enable injection into Servlet -->
<!-- <Listener className="org.jboss.weld.environment.tomcat.WeldLifecycleListener"/> -->
</Context>
5)在web.xml中,声明注册这个绑定
<resource-env-ref>
<description>Object factory for the CDI Bean Manager</description>
<resource-env-ref-name>BeanManager</resource-env-ref-name>
<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>
6)在WEB-INF下面,放一个空的bean.xml
在非jee6 或者 非servlet容器下,都需要这样配置的。在Jboss as7中就要简单的多,只需要加入 cdi-api.jar 跟一个 bean.xml 其他的地方就是jsf的一些配置。
二、上下文(Context)
Weld 中有5个上下文范围的容器:Request、Session、Application、Dependent 和 Conversation。前面3个就不再介绍了,他们的注解分别为@RequestScoped @SessionScoped @ApplicationScoped 。JSR299中参照了Seam,加入了一个新的会话范围 Conversation
@Dependent 默认范围,所有没有定义的上下文范围的Class或者Interface 具有这个默认的注解。用EL表达式每次应用一个Dependent范围的bean容器都会重新实例化一个。所以不要用EL应用Dependent范围的bean。每次注入这个范围的bean的时候,容器都会new一个新的实例给注入点。
@Conversation 可以叫它对话范围,它的范围比Request大,一个Conversation里面可以包含多个Request,比Session范围小。 需要注意的一点,Conversation范围和Session范围的bean会被持久化到你的磁盘上,从而节省你的内存,所以在使用这两个范围的bean的时候需要实现序列化接口(Serializable)。
在实际应用中,Conversation一般用于使用多个页面来做一件事情,相关的对象不会丢失。但是,Conversation范围并不是它的对象放在Session中,只是在HttpSession这个对象中存放了一个id。
Conversation 可以分为短对话和长对话两种情况。对于一般的Conversation,它的持续时间是跟Request一样的,并没有太大的区别。下面的方式就可以把一个短对话变成长对话。
1 @Inject
2 Conversation conversation;
3 public void start(){
4 conversation.start();
5 conversation.setTimeout(30000);//5分钟,默认10分钟
6 }
三、依赖注入 @Inject
JSR299 借鉴了现在很流行的依赖注入,自己通过注解也实现了依赖注入,无需像Spring那么多的配置文件。
weld 的三个注入点:
1. 构造方法参数:如果在一个bean的构造函数上加上@Inject注解,那么它的参数都是通过依赖注入传入的。也可以把@Inject放在形参的前面。
2. 初始化方法参数:如果一个方法上有@Inject注解,它不是构造函数的话,上面这中情况,那么这个方法就是
初始化方法 。初始化方法总是会在构造函数之后执行,注入他的参数。
3. 类属性: 这也是最常用的一种方法,类似与seam2中 @in 。
下面分别给出这3中情况的例子
@Inject
public HelloAction(CurrentUser user){
}
@Inject
public void sayHello(CurrentUser user){
System.out.println("Hello, "+user.getName());
}
@Inject
Logger logger;
四、限定词 @Qualifier
我们一般在注入service层的bean的时候,一般会直接注入一个接口,而不是它的实现类。限定词的作用主要用于区分一个接口的不同实现。
weld 中有两个默认的限定词 @Default 和 @Any:
@Default : 默认限定词,如果一个注入点没指定特定的限定词,那么它就默认使用@Default这个限定词。
@Any: 如果你一个接口有多个实现类,不同的实现类你使用了不同的限定词来区分,那么 @Any 就可以修饰所有的实现。
一般情况,需要我们自己定义个注解:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE,Element.METHOD,ElementType.FIEID,ElementType.PARAMENTER)
public @interface Morning{}
@Qualifier 说明这个新定义的注解是一个限定词。
@Retention 表明这个注解什么时候作用,一般都是写 RUNTIME
@Target 表明这个注解可以在什么地方使用,上面的定义分别是 类名上,方法上,属性,参数
@Inject @Morning HelloAction helloAction;
如果一个接口有很多个实现类的话,我们也不需要为每个实现类定义一个注解。我们可以给一个注解加个参数,通过这个参数来指定注入哪个实现类。
只需要在个实现类之前加上这个限定词,那么在注入这个接口的时候,就会自动注入这个实现类了。
@Morning
public class MorningHelloAction Implements HelloAction{
...
}
一个bean如果实现了多个接口,那么只要添加多个限定词就可以了。
使用了限定词,这样在使用一个接口的时候,我现在不需要关心这个接口实现类的类名,只要你的实现类加了这个限定词,在注入的时候就会自动找到这个类注入进来。这样可以大大的降低了耦合性。