美文网首页
Spring | AOP

Spring | AOP

作者: EclipseO2 | 来源:发表于2018-07-06 14:52 被阅读6次

    一、什么是AOP?

    AOP:即面向切面编程,主要编程对象是切面,通俗的来说,就是在一系列控制流程中,把那些相同的子流程提取成一个横向的面,在这个面中,你可以做很多操作。

    在应用 AOP 编程时,仍然需要定义公共功能,但是可以将这个公共功能应用在任意你想要应用的流程中

    二、AOP术语

    • 切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
    • 通知(Advice):切面必须要完成的工作
    • 目标(Target):被通知的对象
    • 代理(Proxy):向目标对象应用通知之后创建的对象
    • 连接点(JoinPoint):程序执行的某个特定位置,可以在核心代码前、后、或者抛出异常后等等
    • 切点(Pointcut):每个类拥有多个连接点。目标的接口中的每个方法都是连接点

    三、Spring AOP

    3.1 简介

    AspectJ:Java 社区里最完整和流行的 AOP 框架,此处我们使用基于 AspectJ 注解和基于 XML 配置的 AOP。但是,基于注解的声明要优先于基于 XML 的声明。

    3.2 所需架包
    • aopalliance-1.0.jar
    • aspectjweaver.jar
    • commons-logging-1.2.jar
    • spring-aop-5.0.7.RELEASE.jar
    • spring-aspects-5.0.7.RELEASE.jar
    • spring-beans-5.0.7.RELEASE.jar
    • spring-context-5.0.7.RELEASE.jar
    • spring-core-5.0.7.RELEASE.jar
    • spring-expression-5.0.7.RELEASE.jar

    3.3 使用注解

    要在 Spring IOC 容器中启用 AspectJ 注解支持,只要在 Bean 配置文件中定义一个空的 xml 元素:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>,当容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时,会自动为与 AspectJ 切面匹配的 Bean 创建代理

    3.4 用AspectJ注解声明切面
    1. 要在 Spring 中声明 AspectJ 切面,只需要在 IOC 容器中将切面声明为 Bean 实例。
    2. 在 AspectJ 注解中,切面只是一个带有 @Aspect 注解的类
    3. AspectJ 支持 5 种类型的通知注释:@Before@After@AfterRunning@AfterThrowingAround。其中 @AfterRunning 表面在方法返回结果之后执行
    @Before("execution(public int *(int, int))")
    @After("execution(public int *(int, int))")
    
    3.5 指定切面的优先级

    由于同一个连接点上的应用不止一个切面,除非因此他们的优先级时不确定的。切面的优先级可以通过 @Order 注解指定,注解中的值越小,优先级越大

    3.6 重用切入点
    • 配置切面时,由于同一个切面表达式可能在多个通知中重复出现,因此可以重复定义。
    • 在 AspectJ 切面中,可以通过 @Pointcut 注解将一个切入点声明成简单的方法。切入点的方法通常是空的
    @Pointcut("execution(public int *(..))")
    public void declareJointPointExpression() {}
    

    此注解表示,切入点可以用于类似于 public int add(int i, int j) 这种样式的方法,前提是方法已经在 IOC 容器中

    四、代码实例

    基于注解的方式

    ArithmeticCalculator.java

    public interface ArithmeticCalculator {
        int add(int i, int j);
        int sub(int i, int j);
        int mul(int i, int j);
        int div(int i, int j);
    }
    

    ArithmeticCalculatorImpl.java

    @Component("arithmeticCalculator")
    public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int mul(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    
    }
    

    声明此继承类为 Bean 实例

    LoggingAspect.java:日志切面,用于在匹配到的类中的每个方法种添加说明

    @Order(0)   //order: 可以使用 @Order 注解指定切面的优先级, 值越小, 优先级越高
    @Aspect         
    @Component      //切面一定实在 IOC 容器中
    public class LogginAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式, 一般该方法中不需要再填入其他的代码
         * 使用 @Pointcut 来声明切入点表达式, 后面的其他通知直接引用方法名引用当前的切入点表达式
         */
        @Pointcut("execution(public int *(..))")
        public void declareJointPointExpression() {}
        
        /**
         * 在 edu.just.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object []args = joinPoint.getArgs();
            System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
        }
        
        /**
         * 后置通知: 在方法执行之后执行的代码, 无论该方法是否出现异常. 但是访问不到方法的返回值
         * @param joinPoint
         */
        @After("declareJointPointExpression()")
        public void afterMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object []args = joinPoint.getArgs();
            System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
        }
        
        /**
         * 返回通知: 在方法正常结束后执行的代码, 返回通知是可以访问到方法的返回值的
         *             如果代码执行过程中有异常, 则无法返回
         * @param joinPoint
         */
        @AfterReturning(value="declareJointPointExpression()", returning="result")
        public void afterReturning(JoinPoint joinPoint, Object result) {
            String method = joinPoint.getSignature().getName();
            System.out.println("The method " + method + " " + result);
        }
        
        /**
         * 异常通知: 再目标方法出现异常时会执行的代码. 可以访问异常对象, 且可以指定在出现特定异常时在执行通知代码
         * @param joinPoint
         * @param ex
         */
        @AfterThrowing(value="declareJointPointExpression()", throwing="ex")
        public void afterThrowing(JoinPoint joinPoint, Exception ex) {
            String method = joinPoint.getSignature().getName();
            System.out.println("The method " + method + " " + ex);
        }
    
    }
    

    Mian.java

    public static void main(String[] args) {
            
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);
        
        int result = arithmeticCalculator.add(1, 2);
        System.out.println(result);
        
        result = arithmeticCalculator.div(100, 2);
        System.out.println(result);
    }
    

    applicationContext.xml

    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="edu.just.spring.aop"></context:component-scan>
    
    <!-- 配置自动为匹配 aspectJ 注解的 java 类生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

    输出

    The method add begins with[1, 2]
    validate: [1, 2]
    The method add begins with[1, 2]
    The method add 3
    3
    The method div begins with[100, 2]
    validate: [100, 2]
    The method div begins with[100, 2]
    The method div 50
    50
    
    基于xml的方式

    接口类和接口实现类和上面一样,不过接口实现类不需要使用 @Component 注释

    applicationContext-xml.xml

    <!-- 配置 bean -->
    <bean id="arithmeticCalculator" class="edu.just.spring.aop.xml.ArithmeticCalculatorImpl"></bean>
    
    <!-- 配置切面的 bean -->
    <bean id="validationAspect" class="edu.just.spring.aop.xml.ValidationAspect"></bean>
    
    <bean id="loggingAspect" class="edu.just.spring.aop.xml.LoggingAspect"></bean>  
    
    <!-- 配置 AOP -->
    <aop:config>
        <!-- 配置切点表达式 -->
        <aop:pointcut expression="execution(public int *(..))" id="pointcute"/>
        
        <!-- 配置切面及通知 -->
        <aop:aspect ref="loggingAspect" order="2">
            <aop:before method="beforeMethod" pointcut-ref="pointcute"/>    
            <aop:after method="afterMethod" pointcut-ref="pointcute"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcute" throwing="ex"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pointcute" returning="result"/>
        </aop:aspect>
            
        <aop:aspect ref="validationAspect">
            <aop:after method="validateArgs" pointcut-ref="pointcute"/>
        </aop:aspect>
    </aop:config>
    

    <aop:pointcut>:声明一些公用的部分
    <aop:config>:所有的 Spring AOP 配置都必须定义在其内部,对所有切面有效
    <aop:aspect>:为切面实现具体的配置,支队当前切面有效
    <pointcut-ref>:引用切入点

    LoggingAspect.java

    public class LoggingAspect {
        
        public void beforeMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object []args = joinPoint.getArgs();
            System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
        }
        
        public void afterMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object []args = joinPoint.getArgs();
            System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
        }
        
        public void afterReturning(JoinPoint joinPoint, Object result) {
            String method = joinPoint.getSignature().getName();
            System.out.println("The method " + method + " " + result);
        }
        
        public void afterThrowing(JoinPoint joinPoint, Exception ex) {
            String method = joinPoint.getSignature().getName();
            System.out.println("The method " + method + " " + ex);
        }
    
    }
    

    因为测试的方法和之前的方法一致,因此不在写出。

    相关文章

      网友评论

          本文标题:Spring | AOP

          本文链接:https://www.haomeiwen.com/subject/ziivuftx.html