[weld] 问题专家interceptor哥

wuhaixing 2010-04-22
Interceptor的能力是在Java Interceptors规范中定义的. CDI用更复杂的、更语义化的、基于注解的方式对他进行了武装,让罩着bean的interceptor更强大。
Interceptors规范定义了两种拦截方式:
  • 业务方法拦截
  • 生命周期回调拦截

另外EJB规范还定义了一个timeout方法拦截。
当宣称成为interceptor哥小弟的业务方法被调用时,interceptor会现身:
public class TransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { 
   ... 
  }
}


当container调用bean的生命周期回调方法时,interceptor哥也敢上:
public class DependencyInjectionInterceptor {
   @PostConstruct 
   public void injectDependencies(InvocationContext ctx) { ... }
}


interceptor哥可以身兼多职,即可以拦截业务方法,也可以同时拦截生命周期回调方法。

timeout 方法的 interceptor会在container调用EJB的timeout方法是发挥作用:
public class TimeoutInterceptor {
   @AroundTimeout 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}

wuhaixing 2010-04-22
如何成为interceptor哥的小弟
常年写程序,总有需要事务处理的时候。你也知道这个问题可以交给interceptor哥。但首先你得先有个标,官方称谓是interceptor binding type。让你的bean带上这个标,interceptor哥就会认得他是自己人了。

@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}


给带上:
@Transactional
public class ShoppingCart { ... }


当然,你也可以只给某个方法带标:
public class ShoppingCart {
   @Transactional public void checkout() { ... }
}
wuhaixing 2010-04-22
interceptor哥是如何练成的
搞了这么久,还没见过interceptor哥究竟长什么样呢.其实他不神秘,他也带标。不过大哥有大哥的风范,头上还得有个@Interceptor。
@Transactional @Interceptor
public class TransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception {  
   ...  
   }
}

哦,他也是container的子民,能用DI。
@Transactional @Interceptor
public class TransactionInterceptor {
    @Resource UserTransaction transaction;
    @AroundInvoke 
    public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}


老大们很河蟹,一个标可以多个人带(根据配置决定出场次序)。
wuhaixing 2010-04-22
interceptor哥的营业执照
interceptor毕竟是个压力比较大的职业,一般是不让干的。想干的话,得在beans.xml里注册一下。注意,这个注册在beans.xml所在的archive外是不被承认的。

<beans
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>


熟悉的尖括号的味道!
XML配置在这里出现帮我们解决了两个问题:
  • 可以在这里说明所有interceptors的出场顺序,这样系统行为就可控了,不会乱了
  • 还有就是想让谁干就让谁干,不在这里出现的interceptor就不能出来活动,带标也没用。

比如你可以让安全哥比事务哥先出场。
<beans
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
      <class>org.mycompany.myapp.SecurityInterceptor</class>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>


如果你开发测试的时候想让他们俩都休息,只要别在beans.xml里提他们就行了。
wuhaixing 2010-04-22
可以用member选择让哪个interceptor哥出场
有时候你可能会觉得@Transactional标过于简单,表达能力太弱。像所有其他的标一样,你可以给他加member:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
   boolean requiresNew() default false;
}

member值不同的标意味着跟的是不同的大哥,CDI会根据requiresNew的值来判断你到底是想要TransactionInterceptor罩你,还是想让RequiresNewTransactionInterceptor罩你。
@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception {        ...   
   }
}


想用RequiresNewTransactionInterceptor,就这样
@Transactional(requiresNew = true)
public class ShoppingCart { ... }


如果你只有一个interceptor,并且想让container忽略requiresNew的值,可以用@Nonbinding:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
   @Nonbinding String[] rolesAllowed() default {};
}
wuhaixing 2010-04-22
可以召唤多个interceptor哥

看代码吧
@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }


可以合体,注意不是标合体

@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }


标可以不在一起,下面几种情况召唤的都是上面这个interceptor

public class ShoppingCart {
   @Transactional @Secure public void checkout() { ... }
}


@Secure
public class ShoppingCart {
   @Transactional public void checkout() { ... }
}


@Transactional
public class ShoppingCart {
   @Secure public void checkout() { ... }
}


@Transactional @Secure
public class ShoppingCart {
   public void checkout() { ... }
}


wuhaixing 2010-04-22
其实标也可以合体

java语言不支持annotations的继承。但实际上总会有需要重用的时候,比如你可能需要这样的东西:
public @interface Action extends Transactional, Secure { ... }

CDI可以提供等效的注解,把多个annotation绑定到一个annotation上。
@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }


这样,打上@Action 的标的会关联到TransactionInterceptor 和SecurityInterceptor. (如果有TransactionalSecureInterceptor,也可以)
wuhaixing 2010-04-22
@Interceptors能用,但还是别用了。
javaeye_mao 2010-04-26
别迷恋哥!!!!
lonvea 2010-04-26
wuhaixing 写道
@Interceptors能用,但还是别用了。


哥们,
你给我说说,interceptor中抛出异常,系统如何捕获??特别是在JSF中??

一半你浏览一个页面时,如果没有权限就会跳转到“无权限”页面。。。

@Inject
public UserAO(){
....
userBO.findAllUser();
}

findAllUser()被拦截器拦截。。。

Global site tag (gtag.js) - Google Analytics