spring的AOP编程

Posted 逢甘霖成大事

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring的AOP编程相关的知识,希望对你有一定的参考价值。

基于注解的方式

cfg.xml中

1     <!-- 通过注释的方式来实现AOP所以要扫描包 -->
2     <context:component-scan base-package="spring_aop_helloworld"></context:component-scan>    
3     <!-- 要把切面自动生成代理写上 -->
4     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

要在计算器的每个方法的开始和结束时打印日志文件

首先计算器类被配置成bean,然后

Logging.java

 1 package spring_aop_helloworld;
 2 
 3 import java.util.Arrays;
 4 
 5 import org.aspectj.lang.JoinPoint;
 6 import org.aspectj.lang.ProceedingJoinPoint;
 7 import org.aspectj.lang.annotation.After;
 8 import org.aspectj.lang.annotation.AfterReturning;
 9 import org.aspectj.lang.annotation.AfterThrowing;
10 import org.aspectj.lang.annotation.Around;
11 import org.aspectj.lang.annotation.Aspect;
12 import org.aspectj.lang.annotation.Before;
13 import org.aspectj.lang.annotation.Pointcut;
14 import org.springframework.core.annotation.Order;
15 import org.springframework.stereotype.Component;
16 
17 @Order(2)
18 @Aspect
19 @Component
20 public class Logging {
21     //以这种方式实现重用,不用每次都写execution(* spring_aop_helloworld.Calculator.*(int,int))
22     @Pointcut(value="execution(* spring_aop_helloworld.Calculator.*(int,int))")
23     public void getExpression(){}
24     
25     /**
26      * 前置通知,在执行* spring_aop_helloworld.Calculator.*(int,int)(使用了AspectJ表达式)前执行此方法
27      * 
28      * JoinPoint j传入参数
29      * j.getSignature().getName()获取方法名
30      * j.getArgs()获取参数数组
31      * */
32     @Before(value="getExpression()")
33     public void before(JoinPoint j){
34         System.out.println("Before通知"+j.getSignature().getName()+":"+Arrays.asList(j.getArgs())    );
35     }
36     
37     /**
38      * 后置通知,在执行后执行此方法,不管这个方法有没有异常,能不能执行
39      * */
40     @After(value="execution(* spring_aop_helloworld.Calculator.*(int,int))")
41     public void after(JoinPoint j){
42         System.out.println("After通知"+j.getSignature().getName()+":"+Arrays.asList(j.getArgs()));
43     }
44     
45     /**
46      * 返回通知,在方法返回结果之后执行
47      * returning="result"通过参数中和其名字相同的参数传入可以得到被代理方法的返回值
48      * */
49     @AfterReturning(value="execution(* spring_aop_helloworld.Calculator.*(int,int))",returning="result")
50     public void afterReturning(JoinPoint j,Object result){
51         System.out.println("AfterReturning通知:"+j.getSignature().getName()+":"+Arrays.asList(j.getArgs())+":"+result);
52     }
53     
54     /**
55      * 异常通知,在方法抛出异常之后执行,若没有异常则不执行
56      * throwing="e"通过参数中和其名字相同的参数传入发生的异常。
57      * 也可以只在指定异常抛出后通知
58      * */
59     @AfterThrowing(value="execution(* spring_aop_helloworld.Calculator.*(int,int))",throwing="e")
60     public void afterEhroeing(JoinPoint j,Exception e){
61         System.out.println("AfterThrowing通知:"+j.getSignature().getName()+":"+Arrays.asList(j.getArgs())+":"+e);
62     }
63     
64     /**
65      * 环绕通知
66      * */
67     @Around(value="execution(* spring_aop_helloworld.Calculator.*(int,int))")
68     public Object around(ProceedingJoinPoint p){
69         String methodName = p.getSignature().getName();
70         Object o = null;
71         
72         try {
73             System.out.println("-->等同前置通知");
74             o = (int) p.proceed();
75             System.out.println("-->等同返回通知");
76         } catch (Throwable e) {
77 //            throw new RuntimeException(e);
78             System.out.println("-->等同异常通知");
79         }
80         System.out.println("-->等同后置通知");
81         return o;
82     }
83 }

验证器类

package spring_aop_helloworld;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


/**
 * Order(1)代表切面被调用的优先级,数字越小越先调用
 * Aspect 表示这是一个切面
 * Component 表示这是一个组件,IOC容器可以扫描到
 * */
@Order(1)
@Aspect
@Component
public class Validation {
    
    //spring_aop_helloworld.Logging.getExpression()返回要代理的方法,实现重用
    @Before(value="spring_aop_helloworld.Logging.getExpression()")
    public void before_validation(JoinPoint j){
        System.out.println("0000验证前置"+j.getSignature().getName());
    }
    
}

基于配置文件的方式

cfg.xml

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    
    
    <bean id="calculatorImpl" class="spring_aop_xml.CalculatorImpl"></bean>
    <bean id="logging" class="spring_aop_xml.Logging"></bean>
    <bean id="validation" class="spring_aop_xml.Validation"></bean>
    
    <aop:config>
        <aop:pointcut expression="execution(* spring_aop_xml.Calculator.*(int, int))" id="expression"/>
        <aop:aspect ref="logging" order="2">
            <aop:before method="before" pointcut-ref="expression"/>
            <aop:after method="after" pointcut-ref="expression"/>
            <aop:after-returning method="afterReturning" pointcut-ref="expression" returning="result"/>
            <aop:after-throwing method="afterthrowing" pointcut-ref="expression" throwing="e"/>
            <aop:around method="around" pointcut-ref="expression"/>
        </aop:aspect>
        <aop:aspect ref="validation" order="1">
            <aop:before method="before_validation" pointcut-ref="expression"/>
        </aop:aspect>
    </aop:config>
    
</beans>

验证器类和日志文件类都和基于注解的一样,只不过去掉注解。

 

传统的代理方法实现面向切面编程

package spring_aop_old;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CalculatorProxy {
    private Calculator c;
    
    //通过构造器传入被代理的对象
    public CalculatorProxy(Calculator c) {
        this.c = c;
    }
    //获取代理对象
    public Object getInstance(){
        Object proxy = null;
        
        InvocationHandler handler = new InvocationHandler() {
        
            /**
             * 当想要运行被代理对象的相关方法时,代理对象通过调用invoke来调用被代理对象的方法
             * 
             * Object proxy是getInstance()返回的代理对象
             * Method method被代理对象的方法
             * Object[] args被代理对象的方法中的参数
             * */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {
                Object result = null;
                
                System.out.println("之前..类似前置");
                try {
                    result = method.invoke(c, args);
                } catch (Exception e) {
                    System.out.println("异常..类似异常");
                }
                System.out.println("之后..类似后置");
                
                return result;
            }
        };
        proxy = Proxy.newProxyInstance(c.getClass().getClassLoader(), new Class[]{Calculator.class}, handler);
        
        return proxy;
    }
}
package spring_aop_old;

public class TestAOPHello {

    public static void main(String[] args) {
        Calculator c = (Calculator) new CalculatorProxy(new CalculatorImpl()).getInstance();
        System.out.println(c.add(1, 2));
        System.out.println(c.div(10, 0));
    }

}

 

以上是关于spring的AOP编程的主要内容,如果未能解决你的问题,请参考以下文章

Spring AOP

Spring的AOP面向切面编程

Spring的AOP面向切面编程

Spring框架 AOP

Spring框架--AOP编程,JDBC支持

spring-AOP(面向切面编程)