• Spring AOP学习笔记


    一、通过一个最基础的例子记录基于Schema方式的spring AOP实现。

        以下模拟一个业务处理类:

    public class HelloWorldServiceImpl implements HelloWorldService {
    	@Override
    	public String sayHello(String param,People people) {
    		System.out.println("People's name is " + people.getName());
    		return "success";
    	}
    }


        以下模拟切面通知实现类,其他诸如环绕通知、后置异常通知就不一一列出了:

    public class HelloWorldAspect {
    	public void beforeAdvice(String pa,People arg) {
    		System.out.println("=================前置通知!" + pa);
    	}
    	
    	public void afterFinallyAdvice(String pa, People arg) {
    		System.out.println("=================后置最终通知!" + arg.getName());
    	}
    	
    	public void afterReturn(String pa,People arg,String value) {
    		System.out.println("=================value = " + value);
    	}
    }


        以下是applicationContext.xml文件配置:

    <bean id="helloWorldService" class="cn.com.enorth.service.impl.HelloWorldServiceImpl"/>
    <bean id="aspect" class="cn.com.enorth.aop.HelloWorldAspect"/>
    
    <aop:config>
    	<aop:pointcut expression="execution(* cn.com.enorth..*.*(..)) and args(pa,arg)" id="pointcut"/>
    	<aop:aspect ref="aspect">
    		<aop:before pointcut-ref="pointcut" method="beforeAdvice"/>
    		<aop:after pointcut-ref="pointcut" method="afterFinallyAdvice"/>
    		<aop:after-returning pointcut-ref="pointcut" method="afterReturn" returning="value"/>
    	</aop:aspect>
    </aop:config>


        以下是测试方法:

    public class Test {
    	public static void main(String[] args) {
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    		HelloWorldService service = ctx.getBean("helloWorldService",HelloWorldService.class);
    		People people = new People();
    		people.setName("Super Man");
    		service.sayHello("Hello", people);
    	}
    }

        按照Spring的正常调用方式调用业务类的相应方法,如果该方法匹配AOP的切入点模式,就会自动调用切面实现类中的相应方法。

        如果通知方法中需要使用被匹配方法的参数,则要在匹配模式中加入"and args(参数列表)",参数名要与通知方法中的参数名一一对应。如果需要使用被匹配方法的返回值,则要在后置返回通知中定义return属性,其值与通知方法中的参数名一致。如上述配置中return=”value“与HelloWorldAspect类中afterReturn方法的第三个参数名”value“相一致。


        关于环绕通知:

        环绕通知十分强大,可以决定目标方法(被匹配到的方法)是否执行,什么时候执行,执行的时候是否替换参数,执行完毕是否替换返回值。环绕通知通过<aop:around>标签及其属性进行配置。

        环绕通知的第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型,在通知方法内部使用PorceedingJoinPoint的proceed()方法是目标方法执行。proceed()方法可选择是否传入Object[]数组替换目标方法执行时的参数。目标方法的返回值,将由通知方法的返回值取代。如果目标方法返回值类型为void,则在通知方法内执行proceed()方法返回null。而如果目标方法有返回值,通知方法为void类型,调用目标方法的程序也将获得null。


    二、基于@AspectJ的AOP

    1、Spring默认并不支持@AspectJ风格的切面声明,为了启用支持需要做如下配置:

    <aop:aspectj-autoproxy />

    2、声明切面

        使用@Aspect注解进行声明

    @Aspect
    public class NewAspect {}


        然后将该切面在配置文件中声明为bean,Spring就能自动识别并进行AOP方面的配置:

    <bean id="aspect" class="....NewAspect"/>


        切入点以及各类通知都可以在该类中定义,见下文。

    3、一个@AspectJ风格的AOP实现如下:

        以下模拟业务处理类:

    public class NewServiceImpl implements NewService {
    	@Override
    	public void newSay(People people) {
    		System.out.println(people.getName());
    	}
    }


        以下模拟一个切面处理类:

    public class NewAspect {
    	@Pointcut(value="execution(* cn.com.enorth..*.new*(..)) && args(people)")
    	public void pointCut(People people){}
    	
    	/*
    	 * value取值可以是命名切入点“pointCut(people)”
    	 * 也可以是新的切入点表达式“execution(* cn.com.enorth..*.*(..)) && args(people)”
    	 */
    	@Before(value="pointCut(people)")
    	public void befort(People people){
    		System.out.println("===============" + people.getName());
    	}
    	
    	@AfterReturning(value="pointCut(people)",returning="value")
    	public void afertReturning(People people,Object value){
    		System.out.println("===============" + value);
    	}
    }


        以下是applicationContext.xml中需要注册的bean:

    <bean id="newService" class="cn.com.enorth.service.impl.NewServiceImpl"/>
    <bean id="newAspect" class="cn.com.enorth.aop.NewAspect"/>


        以下是测试代码:

    public class Test {
    	public static void main(String[] args) {
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    		NewService service = ctx.getBean("newService",NewService.class);
    		People people = new People();
    		people.setName("Super Man");
    		service.newSay(people);
    	}
    }


        对照Schema风格的AOP实现,@AspectJ风格也就很好理解了。差异比较大的一点是,@AspectJ风格中可引用的切入点是通过单独定义的一个方法实现的,在上下文中通过方法名以及参数列表引用,即:

    @Pointcut(value="execution(* cn.com.enorth..*.new*(..)) && args(people)")
    public void pointCut(People people){}


        而在Schema风格中,这是通过<aop:pointcut>标签实现的,在上下文中通过切入点的id引用,即:

    <aop:pointcut expression="execution(* cn.com.enorth..*.*(..)) and args(pa,arg)" id="pointcut"/>

        另外,@AspectJ风格中的&&、||、!在Schema风格中分别用and、or、not表示。


    三、参数传递

        上面的介绍中已经通过在定义切入点的时候,使用“args(参数列表)”匹配模式的方式传递被匹配方法的参数到通知方法中。下面介绍在通知方法中使用JoinPoint接口类型获取更多信息。

        任何通知方法的第一个参数都可以是JoinPoint类型,例外的是环绕方法的第一个参数必须是ProceedingJoinPoint,它是JoinPoint的子类。第一个参数也可以是JoinPoint.StaticPart,它只包含连接点的静态信息。

    1、JoinPoint接口方法说明

    String toString();//连接点所在位置的相关信息

    String toShortString();//简短相关信息

    String toLongString();//详细相关信息

    Object getThis();//返回AOP代理对象

    Object getTarget();//返回目标对象

    Object[] getArgs();//返回被通知方法的参数列表

    Signature getSignature();//返回当前连接点签名

    SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置

    String getKind();//返回连接点类型

    StaticPart getStaticPart();//返回连接点静态部分

    2、ProceedingJoinPoint接口定义:用于环绕通知,使用proceed方法执行目标方法。

    public interface ProceedingJoinPoint extends JoinPoint {
        public Object proceed() throws Throwable;
        public Object proceed(Object[] args) throws Throwable;
    }

    3、JoinPoint.StaticPart:提供连接点的静态信息部分

    public interface StaticPart{
        Signature getSignature(); // 返回当前连接点签名
        String getKind(); //返回连接点类型
        int getId(); //返回连接点的唯一标识
        String toString(); //同上
        String toShortString(); //同上
        String toLongString(); //同上
    }


    四、通知执行顺序

    1、同一个切面中通知的执行顺序

    ①前置通知 / 环绕通知proceed方法执行之前的部分//这两者的先后顺序不确定

    ②被通知方法

    ③后置通知 / 环绕通知proceed方法执行之后的部分//这两者的执行顺序不确定

    2、不同切面中通知的执行顺序

        当定义在不同切面的同类型的通知(比如都是前置通知),需要在用一个连接点执行时,如果没有指定切面的执行顺序,那么这两个通知的执行顺序是未知的。

        如果有必要确定执行顺序,需要执行切面执行的优先级。可以通过实现org.springframework.core.Ordered接口或者使用注解@Order指定切面优先级。Order.getValue()返回值越小优先级越高。不推荐使用接口方法指定优先级,两种风格下配置优先级的方式如下:

    @Aspect
    @Order(2)
    public class OrderAspect2{}
    <aop:aspect ref="..." order="1">...</aop:aspect>


  • 相关阅读:
    hdu4841 圆桌问题[STL vector]
    hdu1002 A + B Problem II[大数加法]
    hdu1501 Zipper[简单DP]
    C语言学习之结构体
    C++学习之从C到C++
    一键自动格式化你的代码
    C标准库string.h中几个常用函数的使用详解
    Jlink使用技巧系列教程索引
    Jlink使用技巧之合并烧写文件
    Jlink使用技巧之烧写SPI Flash存储芯片
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3257972.html
Copyright © 2020-2023  润新知