SpringAOP主要是通过动态代理技术。 主要有三部分
1、Advice 真正实现业务逻辑的地方,对被拦截的方法做处理都是在Advice中完成
2、Pointcut 决定那些方法需要被代理
3、Advisor 结合Advice与Pointcut构成一个Aop。
Advice
Advice接口的子接口有BeforeAdvice与AfterAdvice,分别用于被代理对象的前后做出处理
BeforeAdvice的常用的子类有MethodBeforeAdvice,用于在被代理方法被调用前做出处理
如果实现MethodBeforeAdvice接口,那么就需要实现它的void before(Method method, Object[] args, Object target) throws Throwable;方法,被代理方法被调用前就会先执行该方法。
AfterAdvice的常用的子类有AfterReturningAdvice,用于在被代理方法被调用完成后做出处理
如果实现AfterReturningAdvice接口,那么就需要实现它的void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;方法,在被代理方法调用完成后,就会执行该方法。
还有一种Advice,是ThrowsAdvice,它继承自AfterAdvice,当方法执行过程中如果发生了异常,那么就会被他捕获。 在ThrowsAdvice接口中没有定义任何方法,所以我们的自己定义方法, 但是也有限制,可以看一下org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.ThrowsAdviceInterceptor(Object)方法。
/**
* Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
* @param throwsAdvice the advice object that defines the exception
* handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
* implementation)
*/
public ThrowsAdviceInterceptor(Object throwsAdvice) {
Assert.notNull(throwsAdvice, "Advice must not be null");
this.throwsAdvice = throwsAdvice;
Method[] methods = throwsAdvice.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(AFTER_THROWING) &&
(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
) {
// Have an exception handler
this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
if (logger.isDebugEnabled()) {
logger.debug("Found exception handler method: " + method);
}
}
}
if (this.exceptionHandlerMap.isEmpty()) {
throw new IllegalArgumentException(
"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
}
}
从中可以看出方法的定义:方法名必须是AFTER_THROWING,AFTER_THROWING常量的值是:afterThrowing ,并且该方法的参数必须是1个或4个, 并且最后一个参数必须是Throwable类型的或其子类。
所以,如果我们在发生异常的时候不需要获取具体发生异常的方法的信息时可以这样定义方法:
public void afterThrowing(Exception e) {
System.out.println("afterThrowing:" + e.getMessage());
}
如果需要获取发生异常的方法的其他信息,那么就需要这样定义方法了。
public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
System.out.println("afterThrowing:" + e.getMessage());
}
上面的方法名必须是afterThrowing,参数名可以自定义,但参数类型必须如上。
实例:同时实现了MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice几个接口
package com.spring.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
public class ZFMethodAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("method before ... " + method.getName());
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("method after ... " + method.getName());
}
public void afterThrowing(Exception e) {
System.out.println("afterThrowing:" + e.getMessage());
}
public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
System.out.println("afterThrowing:" + e.getMessage());
}
// 该方法可以重载,根据异常类型的不同
public void afterThrowing(Method method, Object[] args, Object target, NullPointerException e) {
System.out.println("afterThrowing:空指针");
}
}
那么ZFMethodAdvice现在就可以使用了。将它使用到ProxyFactoryBean中。
<bean id="ZFMethodAdvice" class="com.spring.advice.ZFMethodAdvice" />
<bean id="personService" class="com.spring.service.impl.PersonServiceImpl" scope="prototype">
<property name="name" value="is_zhoufeng" />
</bean>
<bean id="personServiceByLog" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>com.spring.service.PersonService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>ZFMethodAdvice</value>
</list>
</property>
<property name="targetName" value="personService" />
</bean>
那么从BeanFactory中获取到ZFMethodAdvice后,调用起方法的时候就会看到自己定义的Advice起作用了。org.springframework.aop.framework.ProxyFactoryBean中的interceptorNames属性是一个string集合,来指定Advice列表。 value里面的类型必须是Advice类型的。 可以从ProxyFactoryBean的一个内部校验方法看出
/**
* Look at bean factory metadata to work out whether this bean name,
* which concludes the interceptorNames list, is an Advisor or Advice,
* or may be a target.
* @param beanName bean name to check
* @return <code>true</code> if it's an Advisor or Advice
*/
private boolean isNamedBeanAnAdvisorOrAdvice(String beanName) {
Class namedBeanClass = this.beanFactory.getType(beanName);
if (namedBeanClass != null) {
return (Advisor.class.isAssignableFrom(namedBeanClass) || Advice.class.isAssignableFrom(namedBeanClass));
}
// Treat it as an target bean if we can't tell.
if (logger.isDebugEnabled()) {
logger.debug("Could not determine type of bean with name '" + beanName +
"' - assuming it is neither an Advisor nor an Advice");
}
return false;
}
其中的beanName就是通过interceptorNames属性指定的值
未完待续。。。 下班走人