Spring---AOP面向切面编程

我查阅了一些资料 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,否则不会执行通知,

@Aspect
public 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开头的方法都会拦截到


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值