Sping - AOP - Advice
上面的文章有点老了,现在Spring AOP文档中描述中多了一种类型的advice:
- After (final) advice:无论连接点退出的方式如何(正常或异常返回),都将执行advice。
Spring AOP(面向方面的编程)框架用于模块化切面的跨领域关注点。简而言之,它只是拦截某些过程的拦截器,例如,当方法执行时,Spring AOP可以劫持执行方法,并在方法执行之前或之后添加额外的功能。
在Spring AOP中,支持4种类型的advice:
- Before advice – 在方法执行之前运行
- After returning advice – 在方法返回结果后运行
- After throwing advice - 在方法抛出异常后运行
- Around advice - 环绕方法执行,结合以上所有三个advice
以下示例向您展示Spring AOP advice的工作方式。
简单的Spring示例
package com.mkyong.customer.services;
public class CustomerService {
private String name;
private String url;
public void setName(String name) {
this.name = name;
}
public void setUrl(String url) {
this.url = url;
}
public void printName() {
System.out.println("Customer name : " + this.name);
}
public void printURL() {
System.out.println("Customer website : " + this.url);
}
public void printThrowException() {
throw new IllegalArgumentException();
}
}
文件:Spring-Customer.xml –一个bean配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
</beans>
运行:
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mkyong.customer.services.CustomerService;
public class App {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "Spring-Customer.xml" });
CustomerService cust = (CustomerService) appContext.getBean("customerService");
System.out.println("*************************");
cust.printName();
System.out.println("*************************");
cust.printURL();
System.out.println("*************************");
try {
cust.printThrowException();
} catch (Exception e) {
}
}
}
输出:
*************************
Customer name : Yong Mook Kim
*************************
Customer website : http://www.mkyong.com
*************************
Spring AOP Advices
现在,将Spring AOP advices附加到上述customer service中。
1. Before advice
它将在方法执行之前执行。创建一个实现MethodBeforeAdvice接口的类。
package com.mkyong.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class HijackBeforeMethod implements MethodBeforeAdvice
{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("HijackBeforeMethod : Before method hijacked!");
}
}
在bean配置文件(Spring-Customer.xml)中,为HijackBeforeMethod类创建一个bean,并创建一个名为“ customerServiceProxy”的新代理对象。
- 'target' - 定义您要劫持的bean
- ‘interceptorNames’ - 定义要在此代理/目标对象上应用的类(advice)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
<bean id="hijackBeforeMethodBean" class="com.mkyong.aop.HijackBeforeMethod" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackBeforeMethodBean</value>
</list>
</property>
</bean>
</beans>
注意: 要使用Spring代理,您需要添加CGLIB2库。在Maven pom.xml文件中添加以下内容。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
再次运行它,现在您将获得新的customerServiceProxybean而不是原始的customerService bean。
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mkyong.customer.services.CustomerService;
public class App {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "Spring-Customer.xml" });
CustomerService cust =
(CustomerService) appContext.getBean("customerServiceProxy");
System.out.println("*************************");
cust.printName();
System.out.println("*************************");
cust.printURL();
System.out.println("*************************");
try {
cust.printThrowException();
} catch (Exception e) {
}
}
}
输出:
*************************
HijackBeforeMethod : Before method hijacked!
Customer name : Yong Mook Kim
*************************
HijackBeforeMethod : Before method hijacked!
Customer website : http://www.mkyong.com
*************************
HijackBeforeMethod : Before method hijacked!
在执行每个customerService的方法之前,它将运行HijackBeforeMethod的before()方法。
2. After returning advice
将在方法返回结果后执行。创建一个实现AfterReturningAdvice接口的类。
package com.mkyong.aop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class HijackAfterMethod implements AfterReturningAdvice
{
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("HijackAfterMethod : After method hijacked!");
}
}
Bean配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
<bean id="hijackAfterMethodBean" class="com.mkyong.aop.HijackAfterMethod" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackAfterMethodBean</value>
</list>
</property>
</bean>
</beans>
重新运行,输出如下:
*************************
Customer name : Yong Mook Kim
HijackAfterMethod : After method hijacked!
*************************
Customer website : http://www.mkyong.com
HijackAfterMethod : After method hijacked!
*************************
在每个customerService的方法返回之后,它将运行HijackAfterMethod的afterReturning()方法。
3. After throwing advice
该方法将在引发异常后执行。创建一个实现ThrowsAdvice接口的类,并创建一个afterThrowing方法来劫持IllegalArgumentException异常。
package com.mkyong.aop;
import org.springframework.aop.ThrowsAdvice;
public class HijackThrowException implements ThrowsAdvice {
public void afterThrowing(IllegalArgumentException e) throws Throwable {
System.out.println("HijackThrowException : Throw exception hijacked!");
}
}
Bean配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
<bean id="hijackThrowExceptionBean" class="com.mkyong.aop.HijackThrowException" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackThrowExceptionBean</value>
</list>
</property>
</bean>
</beans>
重新运行,输出如下:
*************************
Customer name : Yong Mook Kim
*************************
Customer website : http://www.mkyong.com
*************************
HijackThrowException : Throw exception hijacked!
如果customerService的方法抛出异常,它将运行HijackThrowException的afterThrowing()方法。
4. Around advice
它结合了以上所有三个advice,并在方法执行期间执行。创建一个实现MethodInterceptor接口的类。您必须调用“methodInvocation.proceed()以继续执行原始方法,否则原始方法将无法执行。
package com.mkyong.aop;
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class HijackAroundMethod implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Method name : "
+ methodInvocation.getMethod().getName());
System.out.println("Method arguments : "
+ Arrays.toString(methodInvocation.getArguments()));
// same with MethodBeforeAdvice
System.out.println("HijackAroundMethod : Before method hijacked!");
try {
// proceed to original method call
Object result = methodInvocation.proceed();
// same with AfterReturningAdvice
System.out.println("HijackAroundMethod : Before after hijacked!");
return result;
} catch (IllegalArgumentException e) {
// same with ThrowsAdvice
System.out.println("HijackAroundMethod : Throw exception hijacked!");
throw e;
}
}
}
Bean配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
<bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackAroundMethodBean</value>
</list>
</property>
</bean>
</beans>
重新运行,输出如下:
*************************
Method name : printName
Method arguments : []
HijackAroundMethod : Before method hijacked!
Customer name : Yong Mook Kim
HijackAroundMethod : Before after hijacked!
*************************
Method name : printURL
Method arguments : []
HijackAroundMethod : Before method hijacked!
Customer website : http://www.mkyong.com
HijackAroundMethod : Before after hijacked!
*************************
Method name : printThrowException
Method arguments : []
HijackAroundMethod : Before method hijacked!
HijackAroundMethod : Throw exception hijacked!
在每个customerService的方法执行后,它将运HijackAroundMethod的invoke()方法。
总结
大多数Spring开发人员仅仅实现“Around Advice”,因为它可以应用所有的advice类型,但更好的做法是选择最合适的advice类型以满足要求。
Pointcut
在此示例中,客户服务类中的所有方法均被自动拦截(advice)。
但在大多数情况下,您可能需要使用Pointcut和Advisor通过其方法名称来拦截方法。