Spring AOP使用

先是一些基本概念。。感觉大段概念太多,实际的不如你去问个老司机,说的感觉比这个让人容易理解多了,所以一切都是实践为王,这些基础用法都是扯淡~
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

主要的几个概念
方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
一、基于XML配置的Spring AOP
通过aop namespace下的一个标签aop:config来实现aop代理,这个也是用起来相当方便的一种配置方式

AOP配置标签描述
<aop:advisor> | 定义AOP通知器
<aop:after> | 定义AOP后置通知(不管该方法是否执行成功)
<aop:after-returning> | 在方法成功执行后调用通知
<aop:after-throwing> | 在方法抛出异常后调用通知
<aop:around> | 定义AOP环绕通知
<aop:aspect> | 定义切面
<aop:aspect-autoproxy> | 定义
@AspectJ注解驱动的切面
<aop:before> | 定义AOP前置通知
<aop:config> | 顶层的AOP配置元素,大多数的

第一种直接使用xml

使用方式,即直接使用xml配置这个例子是在网上找的,感觉很简单了,当然这里只写了核心用到的代码段~
首先是配置文件

    <bean id="userManager" class="com.tgb.aop.UserManagerImpl"/>

    <bean id="xmlHandler" class="com.tgb.aop.XMLAdvice" />
    <aop:config>
        <aop:aspect id="aspect" ref="xmlHandler">
            <aop:pointcut id="pointUserMgr" expression="execution(* com.tgb.aop.*.find*(..))"/>

            <aop:before method="doBefore"  pointcut-ref="pointUserMgr"/>
            <aop:after method="doAfter"  pointcut-ref="pointUserMgr"/>
            <aop:around method="doAround"  pointcut-ref="pointUserMgr"/>
            <aop:after-returning method="doReturn"  pointcut-ref="pointUserMgr"/>
            <aop:after-throwing method="doThrowing" throwing="ex" pointcut-ref="pointUserMgr"/>

        </aop:aspect>
    </aop:config>

下面是定义的你需要在切面前后做的事等

package com.tgb.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * Advice通知类
 * 测试after,before,around,throwing,returning Advice.
 * @author Admin
 *
 */
public class XMLAdvice {

    /**
     * 在核心业务执行前执行,不能阻止核心业务的调用。
     * @param joinPoint
     */
    private void doBefore(JoinPoint joinPoint) {
        System.out.println("-----doBefore().invoke-----");
        System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doBefore()------");
    }


    /**
     * 手动控制调用核心业务逻辑,以及调用前和调用后的处理,
     * 
     * 注意:当核心业务抛异常后,立即退出,转向After Advice
     * 执行完毕After Advice,再转到Throwing Advice
     * @param pjp
     * @return
     * @throws Throwable
     */
    private Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("-----doAround().invoke-----");
        System.out.println(" 此处可以做类似于Before Advice的事情");

        //调用核心逻辑
        Object retVal = pjp.proceed();

        System.out.println(" 此处可以做类似于After Advice的事情");
        System.out.println("-----End of doAround()------");
        return retVal;
    }

    /**
     * 核心业务逻辑退出后(包括正常执行结束和异常退出),执行此Advice
     * @param joinPoint
     */
    private void doAfter(JoinPoint joinPoint) {
        System.out.println("-----doAfter().invoke-----");
        System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doAfter()------");
    }

    /**
     * 核心业务逻辑调用正常退出后,不管是否有返回值,正常退出后,均执行此Advice
     * @param joinPoint
     */
    private void doReturn(JoinPoint joinPoint) {
        System.out.println("-----doReturn().invoke-----");
        System.out.println(" 此处可以对返回值做进一步处理");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doReturn()------");
    }

    /**
     * 核心业务逻辑调用异常退出后,执行此Advice,处理错误信息
     * @param joinPoint
     * @param ex
     */
    private void doThrowing(JoinPoint joinPoint,Throwable ex) {
        System.out.println("-----doThrowing().invoke-----");
        System.out.println(" 错误信息:"+ex.getMessage());
        System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doThrowing()------");
    }
}

这是main文件,

package com.tgb.aop;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

     public static void main(String[] args) {  
            BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");  
            UserManager userManager = (UserManager)factory.getBean("userManager");  

            //可以查找张三  
            userManager.findUserById(1);  
            System.out.println(userManager.findUserById(1)); 
            System.out.println("=====我==是==分==割==线=====");  

            try {  
                // 查不到数据,会抛异常,异常会被AfterThrowingAdvice捕获  
                userManager.findUserById(0);  
            } catch (IllegalArgumentException e) {  
            }  
        }  
}

有上面这些代码就够了
各个配置实现不同的功能before可以在这个方法调用之前使用,从而实现诸如权限控制,登录控制等,而after则可以实现日志记录等等,我理解的就是实现了一些公共模块的重复调用,并且细粒度可以精确到方法级别。这里可以把这个方法理解成一个切面。

第二种是使用自动注解

下面这个例子是网上找的自己改了点。。。原谅没那么多时间写新的,而且感觉我实在没那水平写的比人家更通俗易懂。
使用配置注解,首先我们要将切面在spring上下文中声明成自动代理bean,我们需要在web层的web-inf/dispatcher-servlet.xml文件中配置如下一句话即可

当然我们需要在xml的根目录beans下引用aop的命名空间和xsi,这里不写了~
使用@Aspect注解
声明一个切面,只需要在类名上添加@Aspect属性即可,具体的连接点,我们用@Pointcut和@Before、@After等标注。
接口类

package com.cym.aop;

public interface Move {  
    public void up(int i);  
    public void down(int i);  
    public void left(int i);  
    public void right(int i);  
}  

切面类

package com.cym.aop;

import org.springframework.stereotype.Component;  

@Component  
public class MyMove implements Move {  

    @Override  
    public void up(int i) {  
        System.out.println("I'm moving up " + i + " steps!");  
    }  

    @Override  
    public void down(int i) {  
        System.out.println("I'm moving down " + i + " steps!");  
    }  

    @Override  
    public void left(int i) {  
        System.out.println("I'm moving left " + i + " steps!");  
    }  

    @Override  
    public void right(int i) {  
        System.out.println("I'm moving right " + i + " steps!");  
    }  
}

编写打印日志类,注意此处在before注解后,需要加入通知的方法,并且括号中的参数一定要和方法的参数一致。例如下中,括号中int不能省略。可以用*表示所有含有一个int方法

package com.cym.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;  

@Aspect  
@Component  
public class LoggingMove {  
    //下面的()中int 不能省略  此处中类名为接口或者实现类都可以   
    @Before("execution(public void com.cym.aop.MyMove.up(int))")  
    public void beforeMove(JoinPoint joinPoint){  
        String name = joinPoint.getSignature().getName();  
        List<Object> args = Arrays.asList(joinPoint.getArgs());  
        System.out.println("I'm ready to move " + name +" " + args);  
    }  
}  

main类

package com.cym.aop;

import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  


public class Client  {  
    public static void main( String[] args )  
    {  
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext2.xml");  
        Move move = (Move) ctx.getBean("myMove");  
        move.up(3);        
        move.down(2);  
    }  
}  

然后是配置文件

<?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"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xmlns:context="http://www.springframework.org/schema/context"  
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd ">

        <context:component-scan base-package="com.cym.aop"></context:component-scan>  

        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  
</beans>

其实配置文件重点就两行

<context:component-scan base-package="com.cym.aop"></context:component-scan>  
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  

结果是

I'm ready to move up [3]
I'm moving up 3 steps!
I'm moving down 2 steps!

上面是只将切面设置为up方法。也可以使用下面的配置,从而将切面设置为整个类中的方法

@Before("execution(public void com.cym.aop.MyMove.*(int))")  

结果如下

I'm ready to move up [3]
I'm moving up 3 steps!
I'm ready to move down [2]
I'm moving down 2 steps!

在自己修改的时候进行配置时候的错误:

The prefix "context" for element "context:component-scan" is not bound.

这里可以参考http://blog.csdn.net/ytdxyhz/article/details/51520430
好了,先这样吧,实际的应用千变万化,这里写的只是1+1=2~实际项目中你会发现你需要做的是微积分~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值