Spring笔记5 AOP

2016年1月29日

9:37

 

aop是什么:面向切面的编程(OOP是面向对象的编程)

 

为什么要用aop:如果我们要写一个简单的计算器,核心的代码只有加减乘除四个方法,但是如果我们还想要有一些日志来提示使用者,这些提示对每个方法来说都是相近的,如果我们在每个方法里面都加上提示输出,就会让代码显得很冗余,而且不利于后期的更新和维护,而这种情况下我们就可以使用aop。

 

首先我们用代理类的方法来实现这一功能:

CountIplm.java文件实现了Count.java接口(Iplm应该是Ipml,后来发现了不想改了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.firefly.spring.count;
 
public class CountIplmimplements Count   {
 
    public int add(inti, int j) {
        int result = i + j;
        return result;
    }
 
    public int sub(inti, int j) {
        int result = i - j;
        return result;
    }
 
    public int mul(inti, int j) {
        int result = i * j;
        return result;
    }
 
    public int div(inti, int j) {
        int result = i / j;
        return result;
    }
 
}

代理类CountLoggingProxy.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.firefly.spring.count;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
 
public class CountLogginProxy {
    //要代理的对象
    private Count target;
 
    public CountLogginProxy(Count target2) {
        this.target = target2;
    }
    public Count getLoggingProxy(){
        Count proxy = null;
 
        //代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型。即其中有哪些方法
        Class [] interfaces = new Class[]{Count.class};
        //当调用代理对象其中的方法时,该执行的代码
        InvocationHandler h = new InvocationHandler() {
            //正在返回的对象
            //method正在被调用的方法
            //arg2调用方法时传入的参数
            public Object invoke(Object arg0, Method arg1, Object[] arg2)
                    throws Throwable {
                StringmethodName = arg1.getName();
                System.out.println(“The method “+methodName+” begins with”+Arrays.asList(arg2));
                Objectresult = arg1.invoke(target, arg2);
                returnresult;
            }
        };
 
        proxy = (Count)Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
 
    }
}

调用类Main.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.firefly.spring.count;
 
public class Main {
 
    public static voidmain(String[] args) {
 
        Count target =new CountIplm();
        Count proxy = new CountLogginProxy(target).getLoggingProxy();
        int result = proxy.add(1, 2);
        System.out.println(result);
    }
 
}

本来代理类与spring关系不大,可是java基础太烂记下来当是复习了。

 

aop方法实现:

aop中的一些术语:

        切面:横切关注点,意会。。

        通知:切面必须完成的工作,例如例子中的打印日志提示。

        目标:被通知的对象,例子中4个不同的方法调用时生成的对象。

        代理:向目标对象应用通知后创建的对象,可以理解为被包装后的目标形成的新对象。

        连接点和切点以后详述。

 

CountIplm.java文件实现了Count.java接口,与上面唯一的不同是要用@Component把类加入到IOC容器中。

LoggingAspect.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.firefly.spring.aop;
 
import java.util.Arrays;
import java.util.List;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
 
//把这个类声明为一个切面:需要把该类放到IOC容器中,再声明为一个切面
@Aspect
@Component
public class LoggingAspect {
    //指定该方法在哪些类的哪些方法前执行
    @Before(“execution(public int com.firefly.spring.aop.Count.*(int,int))”)
    public void beforeMethod(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println(“The method “+methodName+” begins with “+args);
    }
}

bean的配置代码:

 

1
2
3
4
5
<!– 自动扫描的包 –>
<context:component-scan base-package=”com.firefly.spring.aop”></context:component-scan>
 
<!– 使aspject注解起作用:自动为匹配的类生成代理对象 –>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

调用的方法

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.firefly.spring.aop;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class Main {
 
    public static voidmain(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext(“applicationContext.xml”);
        Count count = ac.getBean(Count.class);
        int result = count.add(2, 3);
        System.out.println(result);
        result = count.mul(2, 3);
        System.out.println(result);
    }
 
}

以上例子中用到了前置通知,相应的还有后置通知,@after在目标方法执行后执行,配置与前置通知相仿。

后置通知不论方法是否发生异常它都会被执行

后置通知还不能访问执行结果。

返回通知可以访问方法的返回结果,是在方法正常执行后执行,@AfterReturning(value=“execution()”)配置

异常通知,程序出现异常时候调用,@AfterThrowing(value=“execution()”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@After(“execution(public int com.firefly.spring.aop.Count.*(..))”)
public void afterMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    System.out.println(“The method “+methodName+” end”);
}
 
@AfterReturning(value=”execution(public int com.firefly.spring.aop.Count.*(..))”,returning=”result”)
public void afterReturning(JoinPoint joinPoint,Object result){
    String methodName = joinPoint.getSignature().getName();
    System.out.println(“The method “+methodName+” end with “+result);
}
 
@AfterThrowing(value=”execution(public int com.firefly.spring.aop.Count.*(..))”,throwing=”e”)
public void afterThrowing(JoinPoint joinPoint,Exception e){
    String methodName = joinPoint.getSignature().getName();
    System.out.println(“The method “+methodName+” have exception “+e);
}

 

当有多个切面对同一个方法进行操作时,我们可以用@Order(n)来对切面设置优先级,n越小优先级越高。

 

 

1
2
3
//使用@Pointcut来声明一个切入点表达式,一般的这个方法里面不需要再写别的代码
    @Pointcut(“execution(public int com.firefly.spring.aop.Count.*(..))”)
    public void flog(){}

每次都去写切入点会很麻烦,所以我们把切入点写成一个表达式,用上面的方法,以后在配置的时候只要用flog就行了。

 

一下为用文件配置AOP的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<bean id=”count” class=”com.firefly.spring.aop.CountIplm”></bean>
 
    <bean id=”loggingAspect” class=”com.firefly.spring.aop.LoggingAspect”></bean>
 
    <!– 配置AOP –>
    <aop:config>
        <!– 配置切点表达式 –>
        <aop:pointcut expression=”execution(* com.firefly.spring.aop.Count.*(..))”
        id=”pointcut”/>
        <!– 配置切面通知 –>
        <aop:aspect ref=”loggingAspect” order=”2”>
            <aop:before method=”beforeMethod” pointcut-ref=”pointcut”/>
            <aop:after method=”afterMethod” pointcut-ref=”pointcut”/>
            <aop:after-throwing method=”afterThrowing” pointcut-ref=”pointcut” throwing=”e”/>
            <aop:after-returning method=”afterReturning” pointcut-ref=”pointcut” returning=”result”/>
        </aop:aspect>
    </aop:config>

 

已使用 Microsoft OneNote 2016 创建。rosoft OneNote 2016 创建。