Spring 详解(二)------- AOP症结观点和两种完成体


玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。

目次

  • 1. AOP 关键词
  • 2. AOP 的作用
  • 3. AOP 的关照范例
  • 4. 基于 xml 的设置装备摆设体式格局
  • 5. 基于注解的设置装备摆设体式格局
  • 6. 切面的优先级
  • 7. 重用切点表达式
  • 8. 两种体式格局的对照(摘自 spring 官方文档)


1. AOP 关键词


  • target:目的类,须要被署理的类。比方:ArithmeticCalculator
  • Joinpoint(连接点):所谓连接点是指那些能够被阻拦到的要领。比方:一切的要领
  • PointCut 切入点:已被加强的连接点。比方:add()
  • advice:关照/加强,加强代码。比方:showRaram、showResult
  • Weaving(织入):是指把加强 advice 应用到目的对象 target 来建立新的署理对象proxy的历程.
  • proxy 署理类:关照 切入点
  • Aspect(切面)::是切入点 pointcut 和关照 advice 的连系

2. AOP 的作用


当我们为体系做参数考证,登录权限考证或许日记操纵等,为了完成代码复用,我们能够把日记处置惩罚抽离成一个新的要领。然则如许我们依然必需手动插进去这些要领,如许的话模块之间高耦合,不利于后期的保护和功用的扩大,有了 AOP 我们能够将功用抽成一个切面,代码复用好,低耦合。

3. AOP 的关照范例



Spring 依照关照 Advice 在目的类要领的连接点地位,能够分为5类

  • 前置关照[Before advice]:在连接点前面实行,前置关照不会影响连接点的实行,除非此处抛出非常。
  • 一样平常返回关照[After returning advice]:在连接点一样平常实行完成后实行,若是连接点抛出非常,则不会实行。
  • 非常返回关照[After throwing advice]:在连接点抛出非常后实行。
  • 返回关照[After (finally) advice]:在连接点实行完成后实行,不论是一样平常实行完成,照样抛出非常,都邑实行返回关照中的内容。
  • 围绕关照[Around advice]:围绕关照围绕在连接点前后,好比一个要领挪用的前后。这是最壮大的关照范例,能在要领挪用前后自界说一些操纵。围绕关照还须要卖力决议是继承处置惩罚join point(挪用ProceedingJoinPoint的proceed要领)照样中缀实行。


    Spring 中运用五种关照
1. 前置关照
    <aop:before method="" pointcut="" pointcut-ref=""/>
        method : 关照,及要领名
        pointcut :切入点表达式,此表达式只能今后关照运用。
        pointcut-ref : 切入点援用,能够与其他关照同享切入点。
    关照要领花样:public void myBefore(JoinPoint joinPoint){
        参数1:org.aspectj.lang.JoinPoint  用于形貌连接点(目的要领),取得目的要领名等

2. 后置关照  目的要领后实行,取得返回值
    <aop:after-returning method="" pointcut-ref="" returning=""/>
        returning 关照要领第二个参数的称号
   关照要领花样:public void myAfterReturning(JoinPoint joinPoint,Object result){
        参数1:连接点形貌
        参数2:范例Object,参数名 returning="result" 设置装备摆设的

3. 非常关照  目的要领发作非常后
    <aop:after-throwing method="testException" throwing="e"
    pointcut="execution(* com.anqi.testAop.ArithmeticCalculator.div(..))"/>
        throwing 发作的非常
   关照要领花样:public Object testRound(ProceedingJoinPoint pjp){
        参数1:ProceedingJoinPoint
        返回值为 reslut


4. 基于 xml 的设置装备摆设体式格局

xml 设置装备摆设文件

<context:component-scan base-package="com.anqi">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--1、 建立目的类 -->
<bean id="arithmeticCalculator" class="com.anqi.testAop.ArithmeticCalculatorImpl"></bean>
<!--2、建立切面类(关照)  -->
<bean id="logAspect" class="com.anqi.testAop.MyLogger"></bean>
<aop:config>
    <aop:aspect ref="logAspect">
        <!-- 切入点表达式 也能够在关照内部离别设置切入点表达式 -->
        <aop:pointcut expression="execution(* com.anqi.testAop.*.*(..))" id="myPointCut"/>
        <!-- 设置装备摆设前置关照,注重 method 的值要和 对应切面的类要领称号雷同 -->
        <aop:before method="before" pointcut-ref="myPointCut" />
        <aop:after method="after" pointcut-ref="myPointCut" />
        <aop:after-returning method="testAfterReturn" returning="result" pointcut-ref="myPointCut"/>
        <aop:after-throwing method="testException" throwing="e" pointcut="execution(* com.anqi.testAop.ArithmeticCalculator.div(..))"/>
        <!--<aop:around method="testRound"  pointcut-ref="myPointCut"  /> 最壮大,然则一样平常不运用-->
    </aop:aspect>
</aop:config>


目的类

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);
}

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;
    }
}



切面类

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;

/**
 * 建立日记类
 */
public class MyLogger {

    public void before(JoinPoint joinPoint) {
        System.out.println("前置关照 参数为[" joinPoint.getArgs()[0] "," joinPoint.getArgs()[1] "]");
    }
    public void after(JoinPoint joinPoint) {
        System.out.println("后置关照 "  joinPoint.getSignature().getName());
    }

    public void testException(JoinPoint joinPoint, Throwable e) {
        System.out.println("抛出非常: "  e.getMessage());
    }

    public void testAfterReturn(JoinPoint joinPoint, Object result) {
        System.out.println("返回关照,返回值为 "   result);
    }

    public Object testRound(ProceedingJoinPoint pjp) {
        Object result = null;
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        try {
            //前置关照
            System.out.println("!!!前置关照 --> The Method" methodName " begins"  Arrays.asList(args));
            //实行目的要领
            result = pjp.proceed();
            //返回关照
            System.out.println("!!!返回关照 --> The Method" methodName " ends"  args);

        }catch(Throwable e) {
            //非常关照
            System.out.println("!!!非常关照 --> The Method" methodName " ends with"  result);
        }
        //后置关照
        System.out.println("!!!后置关照 --> The Method" methodName " ends"  args);
        return result;
    }
}



测试

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。-
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
    public static void main(String[] args) {
        ApplicationContext application = new ClassPathXmlApplicationContext("spring-context.xml");
        ArithmeticCalculator a = application.getBean(ArithmeticCalculator.class);
        int result = a.add(1,2);
        System.out.println(result);
        System.out.println(a.div(5,0));
    }
}
/*
    前置关照 参数为[1,2]
    后置关照 add
    返回关照,返回值为 3
    3
    前置关照 参数为[5,0]
    后置关照 div
    抛出非常: / by zero
*/

5. 基于注解的设置装备摆设体式格局

xml

<?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:context="http://www.springframework.org/schema/context"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.anqi">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 使 AspectJ 注解起作用: 自动为婚配的类天生署理对象 -->
    <aop:aspectj-autoproxy/>
</beans>



目的类

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);
}
import org.springframework.stereotype.Service;

@Service
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;
    }
}



切面

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;

/**
 * 建立日记类
 */
@Aspect
@Component
public class MyLogger {

    @Before("execution(* com.anqi.testAop.*.*(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("前置关照 参数为[" joinPoint.getArgs()[0] "," joinPoint.getArgs()[1] "]");
    }
    @After("execution(* com.anqi.testAop.*.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("后置关照 "  joinPoint.getSignature().getName());
    }

    @AfterThrowing(value="execution(* com.anqi.testAop.ArithmeticCalculator.div(..))", throwing = "e")
    public void testException(JoinPoint joinPoint, Throwable e) {
        System.out.println("抛出非常: "  e.getMessage());
    }

    @AfterReturning(value="execution(* com.anqi.testAop.*.*(..))", returning = "result")
    public void testAfterReturn(JoinPoint joinPoint, Object result) {
        System.out.println("返回关照,返回值为 "   result);
    }

    @Around("execution(* com.anqi.testAop.*.*(..))")
    public Object testRound(ProceedingJoinPoint pjp) {
        Object result = null;
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        try {
            //前置关照
            System.out.println("!!!前置关照 --> The Method" methodName " begins"  Arrays.asList(args));
            //实行目的要领
            result = pjp.proceed();
            //返回关照
            System.out.println("!!!返回关照 --> The Method" methodName " ends"  args);

        }catch(Throwable e) {
            //非常关照
            System.out.println("!!!非常关照 --> The Method" methodName " ends with"  result);
        }
        //后置关照
        System.out.println("!!!后置关照 --> The Method" methodName " ends"  args);
        return result;
    }
}



输出结果与第一种体式格局一致,这里就不再赘述了。

6. 切面的优先级



能够运用@Order来指定切面的优先级

//参数考证切面
@Order(1)
@Aspect
@Component
public class ValidateAspect {

@Before("execution(public int com.anqi.spring.aop.order.ArithmeticCalculator.*(int, int))")
public void validateArgs(JoinPoint join) {
    String methodName = join.getSignature().getName();
    Object[] args = join.getArgs();
    System.out.println("validate" methodName Arrays.asList(args));
    }
}

//把这个类声明为一个切面:须要把该类放入到 IOC 容器中, 再声明为一个切面
@Order(2)
@Aspect
@Component
public class LoggingAspect2 {

/**
 * 声明该要领是一个前置关照: 在目的要领最先之前实行
 * @param join
 */
@Before("execution(public int com.anqi.spring.aop.order.ArithmeticCalculator.*(int, int))")
public void beforeMehod(JoinPoint join) {
    String methodName = join.getSignature().getName();
    List<Object> args = Arrays.asList(join.getArgs());
    System.out.println("前置关照 --> The Method" methodName " begins"  args);
    }
}

7. 重用切点表达式

//把这个类声明为一个切面:须要把该类放入到 IOC 容器中, 再声明为一个切面
@Order(2)
@Aspect
@Component
public class LoggingAspect {

    /**
     * 界说一个要领, 用于声明切入点表达式, 一样平常地, 该要领中再不须要填入其他代码
     */
    @Pointcut("execution(public int com.anqi.spring.aop.order.ArithmeticCalculator.*(int, int))")
    public void declareJointPointExpression() {}


    /**
     * 声明该要领是一个前置关照: 在目的要领最先之前实行
     * @param join
     */
    @Before("declareJointPointExpression()")
    public void beforeMehod(JoinPoint join) {
        String methodName = join.getSignature().getName();
        List<Object> args = Arrays.asList(join.getArgs());
        System.out.println("前置关照 --> The Method" methodName " begins"  args);
    }
}


8. 两种体式格局的对照(摘自 spring 官方文档)



若是您挑选运用Spring AOP,则能够挑选@AspectJ或XML款式。须要斟酌种种衡量。

XML款式多是现有Spring用户最熟习的,而且由真正的POJO支撑。当运用AOP作为设置装备摆设企业效劳的对象时,XML多是一个不错的挑选(一个好的测试是你是不是以为切入点表达式是你能够想要自力转变的设置装备摆设的一部分)。运用XML款式,从您的设置装备摆设能够更清楚地相识体系中存在哪些方面。

XML作风有两个瑕玷。起首,它没有完整封装它在一个处所处理的请求的完成。DRY准绳划定,体系中的任何学问都应该有单一,明白,威望的透露表现。运用XML款式时,有关怎样完成需求的学问将分支到支撑bean类的声明和设置装备摆设文件中的XML。运用@AspectJ款式时,此信息封装在单个模块中:方面。其次,XML款式在它所表达的内容方面比@AspectJ款式轻微受限:仅支撑“单例”方面实例化模子,而且不能够组合在XML中声明的定名切入点。比方,

@Pointcut("execution(* get*())")
public void propertyAccess() {}

@Pointcut("execution(org.xyz.Account  *(..))")
public void operationReturningAnAccount() {}

@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}

在XML款式中,您能够声明前两个切入点:

<aop:pointcut id="propertyAccess"
        expression="execution(* get*())"/>

<aop:pointcut id="operationReturningAnAccount"
        expression="execution(org.xyz.Account  *(..))"/>

XML要领的瑕玷是您没法 accountPropertyAccess经由过程组合这些界说来界说切入点。

@AspectJ 款式支撑分外的实例化模子和更雄厚的切入点组合。它具有将方面连结为模块化单位的长处。它还具有以下长处:Spring AOP 和 AspectJ 都能够明白(并因而斲丧)@AspectJ 方面。因而,若是您今后决议须要 AspectJ 的功用来完成其他请求,则能够轻松迁移到基于 AspectJ 的要领。总而言之,只需您的方面不仅仅是简朴的企业效劳设置装备摆设,Spring 团队更喜好 @AspectJ 作风。

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。