Spring---AOP面向切面编程

来源:互联网 发布:软件生命周期 编辑:程序博客网 时间:2024/05/16 02:26

我查阅了一些资料 aop还是有些难以理解的,oop面向对象编程,aop是oop的一个完善,oop定义了了程序从上到下的关系,而aop定义了程序左右关系,oop中如果想给分散的对象引入一个公共的功能,例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的与核心功能无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而aop它使用了一种“横切”技术,把程序横向切开,并且将影响了多个类的公共行为封装起来,变成一个可重用的模块,并将其命名为“Aspect“ 这样就不会重复造轮子,减少了代码的重复,降低了模块之间的耦合度,并且有利于后期的维护

AOP的相关概念:

        1.关注点:增加的是什么功能,如日志,安全等        2.切面:aspect一个关注点的模块化,这个关注点实现可能另外横切多个对象        3.通知:在切面某个特定的连接点上执行的动作        4.织入:把切面连接到其他应用程序类型或对象上,并创建一个被通知的对象

四种通知类型
第一种 前置通知

             实现一个接口MethodBeforeAdvice
public class BeforeAdvice implements MethodBeforeAdvice{    /**     * method:被调用的 方法对象     * args:参数列表     * target:目标对象     */             @Override    public void before(Method method, Object[] args, Object target)            throws Throwable {        System.out.println("前置通知");    }}       

第二种后置通知
实现的接口:AfterReturningAdvice

public class AfterAdvice implements AfterReturningAdvice{    /**     * values:返回值     * method:要执行的方法对象     * args:参数列表     * target:目标对象     */    @Override    public void afterReturning(Object values, Method method, Object[] args,            Object target) throws Throwable {        System.out.println("后置通知");    }}   

第三种异常通知
实现的接口是ThrowsAdvice

public class ExceptionAdvice implements ThrowsAdvice{    /**     * method:被调用的 方法对象     * args:参数列表     * target:目标对象     * Exception:异常类型     */    public void afterThrowing(Method method, Object[] args, Object target, Exception e){        System.out.println("方法 " + method.getName() + " 异常出现 : " + e.getClass().getName());    }    public void afterThrowing(Throwable e){        System.out.println("异常出现 : " + e.getClass().getName());    }}

第四种环绕通知,环绕通知是上面三种通知的结合体,我们可以二选一,写了上面三个通知就不用使用环绕通知,当然我们也可以只写一个环绕通知

public class AroundAdvice implements MethodInterceptor {    @Override    public Object invoke(MethodInvocation invocation) throws Throwable {        Object obj = null;        System.out.println("环绕通知");        try {            // 前置通知            System.out.println("前置通知"+invocation.getMethod().getName());            // 调用具体方法            obj = invocation.proceed();            // 后置通知            System.out.println("后置通知"+invocation.getMethod().getName());        } catch (Exception e) {            // 异常通知            e.printStackTrace();            System.out.println("异常通知" + e.getClass().getName());            throw e;        }        return obj;    }}

下面是beans.xml的配置 (两种方式)
第一种方式 AspectJ方式(直接在下配置)

<?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.xsd"        >    <bean id="service" class="cn.bjsxt.service.impl.UserServiceImpl"></bean>    <bean id="before" class="cn.bjsxt.avice.BeforeAdvice"></bean>    <bean id="after" class="cn.bjsxt.avice.AfterAdvice"></bean>    <bean id="e" class="cn.bjsxt.avice.ExceptionAdvice"></bean>    <bean id="around" class="cn.bjsxt.avice.AroundAdvice"></bean>    <aop:config>    <!-- expression:代表在什么情况下执行advice功能,属性值为一个表达式             execution代表执行             * cn.bjsxt.service.impl.*.*(..)            第一个*代表所有返回类型            cn.bjsxt.service.impl代表需要执行advice功能的包            第二个*代表包中所有的类            第三个*代表类中所有的方法            (..)代表任意参数结构     --><aop:pointcut expression="execution(* cn.bjsxt.service.impl.*.*(..))" id="pointcut"/><!-- advice-ref:通知对象的引用,pointcut-ref:切入点对象的引用 -->        <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>        <aop:advisor advice-ref="e" pointcut-ref="pointcut"/>        <aop:advisor advice-ref="around" pointcut-ref="pointcut"/>    </aop:config></beans>

上面我把前置通知,后置通知注释了,因为同时配置这三个通知会产生干扰,所以执行的时候看一下,需要执行什么。

第二种配置方式 :POJO方式(在下配置)
首先创建一个Logs类

public class Logs {    public void before(){        System.out.println("前置通知");    }    public void after(){        System.out.println("后置通知");    }    public void around(){        System.out.println("环绕通知");    }}

配置:配置时注意顺序,后置通知如果配置在前置通知前将不会执行

   <!-- 配置srvice -->    <bean id="userService" class="cn.bjsxt.service.impl.UserServiceImpl"/>    <!-- 基于pojo配置 -->    <bean id="logs" class="cn.bjsxt.avdvice.Logs"/>    <aop:config>    <!--引用pojo -->        <aop:aspect ref="logs">            <aop:pointcut expression="execution( * cn.bjsxt.service.impl.*.*(..))" id="aspect"/>            <aop:before method="before" pointcut-ref="aspect"/>            <aop:after method="after" pointcut-ref="aspect"/>            <aop:around method="around" pointcut-ref="aspect"/>        </aop:aspect>    </aop:config>

还有一种是注解形式,不需要配置<aop:config>就可以使用,直接创建一个切入的类,和一个引入的类

使用注解时记住在类前加入@Aspect,否则不会执行通知,

@Aspectpublic class Loggs {    //配置切入点    @Before("execution ( * cn.bjsxt.service.impl.*.*(..))")    public void before(){        System.out.println("方法执行前");    }    @After("execution ( * cn.bjsxt.service.impl.*.*(..))")    public void after(){        System.out.println("方法执行后");    }    @Around("execution ( * cn.bjsxt.service.impl.*.*(..))")    public Object aroud(ProceedingJoinPoint jp ) throws Throwable{        System.out.println("环绕前");        System.out.println("签名:"+jp.getSignature());        Object obj = jp.proceed();        System.out.println("环绕后");        return obj;    }}

注解的配置

<!-- 配置自定义注解方式 -->    <!-- 配置srvice -->    <bean id="userService" class="cn.bjsxt.service.impl.UserServiceImpl"/><bean id="loggs" class="cn.bjsxt.log.Loggs"></bean><aop:aspectj-autoproxy/>

补充:expression=”execution( * cn.bjsxt.service.impl.* .* (..))”,这个属性值中的 * 类似正则中的 * ,是全部的意思,也可以写为print* ,这代表切面中只要是以print开头的方法都会拦截到


以上是个人的认识,不足之处请之处,欢迎补充 互相学习 谢谢