Spring中的Advice类型及其应用
在Spring中,Advice都是通过Interceptor来实现的,主要有以下几种:
1. 环绕Advice:
//例子摘自Spring reference
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]"); //(1)
Object rval = invocation.proceed();
System.out.println("Invocation returned"); //(2)
return rval;
}
}
Object invoke(MethodInvocation invocation) throws Throwable;
}
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]"); //(1)
Object rval = invocation.proceed();
System.out.println("Invocation returned"); //(2)
return rval;
}
}
环绕advice类似一个拦截器链,这个拦截器链的中心就是被拦截的方法。在程序(1)(2)我们可以加入我们自己的代码,以表示在方法执行前后我们需要干什么。invocation.proceed()方法运行指向连接点的拦截器链并返回proceed()的结果。
2. Before Advice
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
void before(Method m, Object[] args, Object target) throws Throwable;
}
一个更简单的通知类型是before 通知。它不需要 MethodInvocation
对象,因为它只是在进入方法之前被调用。before advice的一个主要优点是它不需要调用proceed()
方法,因此就不会发生 无意间运行拦截器链失败的情况。
3. After advice
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
一个After advice可以访问返回值(但不能进行修改),被调用方法,方法参数以及目标对象。
4.Throws Advice
//ThrowsAdvice 是一个空接口,起标识作用
public interface ThrowsAdvice extends Advice {
}
//所给对象必须实现一个或者多个针对特定类型的异常通知方法,格式如下
afterThrowing([Method], [args], [target], subclassOfThrowable)
//只有最后一个参数是必须的。因此异常通知方法对方法及参数的需求,方法的签名将从一到四个参数之间变化。
public interface ThrowsAdvice extends Advice {
}
//所给对象必须实现一个或者多个针对特定类型的异常通知方法,格式如下
afterThrowing([Method], [args], [target], subclassOfThrowable)
//只有最后一个参数是必须的。因此异常通知方法对方法及参数的需求,方法的签名将从一到四个参数之间变化。
最后还有一个是introduction advice,这个我想什么时候自己单独做个例子理解一下。
做了个例子如下,想像一个用户登录场景:在登录之前,我们对其输入的用户名进行有效性检查;登录成功后,我们记上用户登录次数;如果登录失败,则进行异常处理。实现代码如下:
package com.learn.spring.test.advisor;
//登录的业务代码
public interface LoginService {
void login(String name, String password) throws UnauthorityException;
}
public class LoginServiceImpl implements LoginService {
public void login(String name, String password) throws UnauthorityException {
check(name, password);
System.err.println(name + " is logining system...");
}
private void check(String name, String password) throws UnauthorityException {
if("myyate".equals(name) && "pass".equals(password) ) {
System.err.println(name + " passed check....");
} else {
throw new UnauthorityException("invalid password");
}
}
}
//登录的业务代码
public interface LoginService {
void login(String name, String password) throws UnauthorityException;
}
public class LoginServiceImpl implements LoginService {
public void login(String name, String password) throws UnauthorityException {
check(name, password);
System.err.println(name + " is logining system...");
}
private void check(String name, String password) throws UnauthorityException {
if("myyate".equals(name) && "pass".equals(password) ) {
System.err.println(name + " passed check....");
} else {
throw new UnauthorityException("invalid password");
}
}
}
//用户名检查 拦截器
public class LoginNameCheckInterceptor implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.err.println("check user's name is valid?");
if(args[0] == null || "".equals(args[0].toString().trim())) {
throw new IllegalArgumentException();
}
}
}
public void before(Method method, Object[] args, Object target) throws Throwable {
System.err.println("check user's name is valid?");
if(args[0] == null || "".equals(args[0].toString().trim())) {
throw new IllegalArgumentException();
}
}
}
//用户登录次数统计拦截器
public class LoginCountInterceptor implements AfterReturningAdvice ...{
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable ...{
System.err.println("Counting the login counts of " + args[0]);
}
}
public class LoginCountInterceptor implements AfterReturningAdvice ...{
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable ...{
System.err.println("Counting the login counts of " + args[0]);
}
}
//异常处理拦截器
public class ExceptionThrowInterceptor implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, IllegalArgumentException ex) throws Throwable {
System.err.println("Login name is wrong, exception: " + ex);
}
public void afterThrowing(Method m, Object[] args, Object target, UnauthorityException ex) {
System.err.println(target.getClass() + "." + m.getName() +
"() throw a exception: " + ex.getMessage());
}
}
public void afterThrowing(Method m, Object[] args, Object target, IllegalArgumentException ex) throws Throwable {
System.err.println("Login name is wrong, exception: " + ex);
}
public void afterThrowing(Method m, Object[] args, Object target, UnauthorityException ex) {
System.err.println(target.getClass() + "." + m.getName() +
"() throw a exception: " + ex.getMessage());
}
}
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="loginServiceTarget"
class="com.learn.spring.test.advisor.LoginServiceImpl"/>
<bean id="loginNameCheckInterceptor"
class="com.learn.spring.test.advisor.LoginNameCheckInterceptor"/>
<!--
<bean id="loginCheckInterceptor"
class="com.learn.spring.test.advisor.LoginCheckInterceptor"/>
-->
<bean id="loginCountInterceptor"
class="com.learn.spring.test.advisor.LoginCountInterceptor"/>
<bean id="exceptionThrowInterceptor"
class="com.learn.spring.test.advisor.ExceptionThrowInterceptor"/>
<bean id="loginService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref local="loginServiceTarget"/></property>
<property name="proxyInterfaces">
<list>
<value>com.learn.spring.test.advisor.LoginService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>loginNameCheckInterceptor</value>
<value>loginCountInterceptor</value>
<value>exceptionThrowInterceptor</value>
</list>
</property>
</bean>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="loginServiceTarget"
class="com.learn.spring.test.advisor.LoginServiceImpl"/>
<bean id="loginNameCheckInterceptor"
class="com.learn.spring.test.advisor.LoginNameCheckInterceptor"/>
<!--
<bean id="loginCheckInterceptor"
class="com.learn.spring.test.advisor.LoginCheckInterceptor"/>
-->
<bean id="loginCountInterceptor"
class="com.learn.spring.test.advisor.LoginCountInterceptor"/>
<bean id="exceptionThrowInterceptor"
class="com.learn.spring.test.advisor.ExceptionThrowInterceptor"/>
<bean id="loginService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref local="loginServiceTarget"/></property>
<property name="proxyInterfaces">
<list>
<value>com.learn.spring.test.advisor.LoginService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>loginNameCheckInterceptor</value>
<value>loginCountInterceptor</value>
<value>exceptionThrowInterceptor</value>
</list>
</property>
</bean>
</beans>
测试代码运行:
public class Test {
public static void main(String[] args) throws Exception {
BeanFactory bf =
BeanFactoryFactory.getBeanFactory("beans.xml", Test.class);
LoginService ls = (LoginService) bf.getBean("loginService");
ls.login("myyate", "pass");
}
}
public static void main(String[] args) throws Exception {
BeanFactory bf =
BeanFactoryFactory.getBeanFactory("beans.xml", Test.class);
LoginService ls = (LoginService) bf.getBean("loginService");
ls.login("myyate", "pass");
}
}
输出结果:
check user's name is valid?
myyate passed check....
myyate is logining system...
Counting the login counts of myyate
myyate passed check....
myyate is logining system...
Counting the login counts of myyate