Spring

Sping - AOP - Advice

2020-01-17  本文已影响0人  HRocky

原文地址:https://mkyong.com/spring/spring-aop-examples-advice/

上面的文章有点老了,现在Spring AOP文档中描述中多了一种类型的advice:

  • After (final) advice:无论连接点退出的方式如何(正常或异常返回),都将执行advice。

Spring AOP(面向方面​​的编程)框架用于模块化切面的跨领域关注点。简而言之,它只是拦截某些过程的拦截器,例如,当方法执行时,Spring AOP可以劫持执行方法,并在方法执行之前或之后添加额外的功能。

在Spring AOP中,支持4种类型的advice:

advicehierarchy.jpg

以下示例向您展示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”的新代理对象。

<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通过其方法名称来拦截方法。

上一篇下一篇

猜你喜欢

热点阅读