zoukankan      html  css  js  c++  java
  • springMVC之AOP

    AOP(Aspect-Oriented Programming,面向切面编程)

    切面(Aepect):横切关注点(跨越应用程序多个模块的功能)被模块化的对象;

    通知(Advice):切面必须要完成的工作;

    目标(Target):被通知的对象;

    代理(Proxy):像目标对象应用通知之后创建的对象;

    连接点(Joinpoint):程序执行的某个特殊位置,如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;想对点表示的方位。

    切点(pointcut):每个类都拥有多个连接点,即连接点是程序类中客观存在的事务;  

    AOP通过切点定位到特定的连接点。

    AOP的主要编程对象是切面(aopect),而切面模块化横切关注点

    在应用AOP编程时,仍需要定义公共功能,但可以明确这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样的话横切关注点就被模块化到特殊的对象(切面)里。

    AOP的好处:

      每个事物逻辑位于一个位置,代码不分散,便于维护和升级;

      业务模块更简洁,值包含核心业务代码

    1、基于AspectJ注解

    (1)Maven引入依赖包

    <dependency>  
      <groupId>org.springframework</groupId>  
      <artifactId>spring-aop</artifactId>  
      <version>2.5.6</version>  
    </dependency>  
    <dependency>  
      <groupId>org.aspectj</groupId>  
      <artifactId>aspectjweaver</artifactId>  
      <version>1.6.1</version>  
    </dependency>  
    <dependency>  
      <groupId>org.aspectj</groupId>  
      <artifactId>aspectjrt</artifactId>  
      <version>1.6.1</version>  
    </dependency>  

    (2)在spring配置文件中添加aop的命名空间,同时配置使得项目支持Aop,配置如下:

    <?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:mvc="http://www.springframework.org/schema/mvc"
        xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-4.0.xsd
                http://www.springframework.org/schema/aop 
                http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                http://www.springframework.org/schema/tx 
                http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
                http://www.springframework.org/schema/mvc 
                http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-4.0.xsd">
        <!--使Spring支持自动检测组件,如注解的Controller -->
        <context:component-scan base-package="com.parry.redisCluster.*" />
        <!--*************** 支持aop **************** -->
        <aop:aspectj-autoproxy proxy-target-class="true" />
    </beans>

    (3)实现一个日志切面

    • 切面首先是IOC容器中的一个bean,即在切面类前加上标签@Component标签
    • 切面还需要添加标签@Aspect标签,声明该类是一个切面
    • 实现需要的通知,在方法之前添加如下的通知类型:
      • @Before:前置通知,在方法前通知;
      • @After :后置通知,在方法执行后通知;
      • @AfterRunning:返回通知,在方法返回结果之后通知;
      • @AfterThrowing:异常通知,在方法抛出异常之后通知;
      • @Around:环绕通知,围绕着方法执行;
    • 切入点表达式的书写:
      • execution(* com.cn21.redisCluster.controller.*(..)) :第一个*表示任意的修饰符(public/private/protected)及任意的返回值(void/Object);第二个*表示任意的方法,‘..’表示任意数量的参数;
      • execution(public * com.cn21.redisCluster.controller.*Controller.*(..)):表示com.cn21.redisCluster.controller包下+以Controller结尾的+公共的方法(public)的方法;
      • execution(public void com.cn21.redisCluster.controller.*Controller.*(..)):表示com.cn21.redisCluster.controller包下+以Controller结尾的+公共的方法(public)+返回类型是void的方法;
      • execution(public void com.cn21.redisCluster.controller.*Controller.*(int,..)):表示com.cn21.redisCluster.controller包下+以Controller结尾+公共的方法(public)+返回类型是void的类+第一个参数是int的方法;
      • execution(public void com.cn21.redisCluster.controller.*Controller.*(int,int)):表示com.cn21.redisCluster.controller包下+以Controller结尾+公共的方法(public)+返回类型是void的类+第一个参数是int+第二个参数是int的方法;
    • 可以在方法中声明一个类型为JoinPoint的参数,然后就可以访问链接细节,如方法名称和参数值
    import java.util.Arrays;
    import java.util.List;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Aspect    //该标签把LoggerAspect类声明为一个切面
    @Order(1)  //设置切面的优先级:如果有多个切面,可通过设置优先级控制切面的执行顺序(数值越小,优先级越高)
    @Component //该标签把LoggerAspect类放到IOC容器中
    public class LoggerAspect {
        
        /**
         * 定义一个方法,用于声明切入点表达式,方法中一般不需要添加其他代码
         * 使用@Pointcut声明切入点表达式
         * 后面的通知直接使用方法名来引用当前的切点表达式;如果是其他类使用,加上包名即可
         */
        @Pointcut("execution(public * com.parry.redisCluster.controller.*Controller.*(..))")
        public void declearJoinPointExpression(){}
        
        /**
         * 前置通知
         * @param joinPoint
         */
        @Before("declearJoinPointExpression()") //该标签声明次方法是一个前置通知:在目标方法开始之前执行
        public void beforMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("this method "+methodName+" begin. param<"+ args+">");
        }
        /**
         * 后置通知(无论方法是否发生异常都会执行,所以访问不到方法的返回值)
         * @param joinPoint
         */
        @After("declearJoinPointExpression()")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("this method "+methodName+" end.");
        }
        /**
         * 返回通知(在方法正常结束执行的代码)
         * 返回通知可以访问到方法的返回值!
         * @param joinPoit
         */
        @AfterReturning(value="declearJoinPointExpression()",returning="result")
        public void afterReturnMethod(JoinPoint joinPoint,Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("this method "+methodName+" end.result<"+result+">");
        }
        /**
         * 异常通知(方法发生异常执行的代码)
         * 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
         * @param joinPoint
         * @param ex
         */
        @AfterThrowing(value="declearJoinPointExpression()",throwing="ex")
        public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("this method "+methodName+" end.ex message<"+ex+">");
        }
        /**
         * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
         * 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
         * 且环绕通知必须有返回值,返回值即目标方法的返回值
         * @param joinPoint
         */
        @Around(value="declearJoinPointExpression()")
        public Object aroundMethod(ProceedingJoinPoint point){
            
            Object result = null;
            String methodName = point.getSignature().getName();
            try {
                //前置通知
                System.out.println("The method "+ methodName+" start. param<"+ Arrays.asList(point.getArgs())+">");
                //执行目标方法
                result = point.proceed();
                //返回通知
                System.out.println("The method "+ methodName+" end. result<"+ result+">");
            } catch (Throwable e) {
                //异常通知
                System.out.println("this method "+methodName+" end.ex message<"+e+">");
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("The method "+ methodName+" end.");
            return result;
        }
    }

    2、基于XML配置的AOP

    (1)在spring配置文件中添加aop的命名空间,配置如下:

    <?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:mvc="http://www.springframework.org/schema/mvc"
        xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-4.0.xsd
                http://www.springframework.org/schema/aop 
                http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                http://www.springframework.org/schema/tx 
                http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
                http://www.springframework.org/schema/mvc 
                http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-4.0.xsd">
        
        <!--使Spring支持自动检测组件,如注解的Controller -->
        <context:component-scan base-package="com.parry.redisCluster.*" />
        
        <!-- 配置切面的Bean -->
        <bean id="validationAspect" class="com.cn21.redisCluster.aspect.ValidationAspect"></bean>
        
        <!-- 配置AOP -->
        <aop:config>
            <!-- 配置切点表达式  -->
            <aop:pointcut id="pointcut" expression="execution(!void com.parry.redisCluster.controller.*.*(..))" />
            <!-- 配置切面及配置 -->
            <aop:aspect order="3" ref="validationAspect">
                <!-- 前置通知 -->
                <aop:before method="beforMethod" pointcut-ref="pointcut"/>
                <!-- 后置通知 -->
                <aop:after method="afterMethod" pointcut-ref="pointcut"/>
                <!-- 返回通知 -->
                <aop:after-returning method="afterReturnMethod" pointcut-ref="pointcut" returning="result"/>
                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/>
            </aop:aspect>
        </aop:config>
    </beans>

    (2)实现切面类:

    package com.parry.redisCluster.aspect;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.aspectj.lang.JoinPoint;
    
    public class ValidationAspect {
        
        /**
         * 前置通知
         * @param joinPoint
         */
        public void beforMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            System.out.println("ValidationAspect this method "+methodName+" begin. param<"+ args+">");
        }
        /**
         * 后置通知(无论方法是否发生异常都会执行,所以访问不到方法的返回值)
         * @param joinPoint
         */
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("ValidationAspect this method "+methodName+" end.");
        }
        /**
         * 返回通知(在方法正常结束执行的代码)
         * 返回通知可以访问到方法的返回值!
         * @param joinPoit
         */
        public void afterReturnMethod(JoinPoint joinPoint,Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("ValidationAspect this method "+methodName+" end.result<"+result+">");
        }
        /**
         * 异常通知(方法发生异常执行的代码)
         * 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
         * @param joinPoint
         * @param ex
         */
        public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("ValidationAspect this method "+methodName+" end.ex message<"+ex+">");
        }
    }
  • 相关阅读:
    安装Mysql或者msi文件时遇到2502/2503错误
    学习Redis之set集合类型详解
    学习Redis之List列表类型详解
    学习Redis之String字符串类型详解
    学习Redis之redis的基础知识
    学习Redis之Benchmark性能测试
    学习Redis之什么是Redis
    学习Redis之为什么要使用Nosql
    Java基础之使用多线程处理多客户端请求
    代码层实现质量属性战术
  • 原文地址:https://www.cnblogs.com/parryyang/p/5881523.html
Copyright © 2011-2022 走看看