码迷,mamicode.com
首页 > 其他好文 > 详细

四、AOP

时间:2020-09-12 21:44:19      阅读:36      评论:0      收藏:0      [点我收藏+]

标签:expr   system   version   instance   img   java   解决   一个   RoCE   

1.AOP相关术语

  1. Joinpoint(连接点)

    • 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
  2. Pointcut(切入点)

    • 所谓切入点是指我们要对哪些 Joinpoint进行拦截的定义。
  3. Advice(通知/增强)

    • 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
    • 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

    技术图片

  4. Introduction(引介)

    • 引介是种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一 些方法或Field。
  5. Target(目标对象)

    • 代理的目标对象。
  6. Weaving(织入)

    • 是指把增强应用到目标对象来创建新的代理对象的过程。
      spring采用动态代理织入,而Aspect采用编译期织入和类装载期织入。
  7. Proxy(代理)

    • 一个类被AOR织入增强后,就产生一个结果代理类。
  8. Aspect(切面)

    • 是切入点和通知(引介)的结合。

2.spring中基于xml的AOP配置步骤

  1. 把通知Bean也交给spring来管理
  2. 使用aop:config标签表明开始AOP的配置
  3. 使用aop:aspect标签表明配置切面
    • id属性:是给切面提供一个唯一标识
    • ref属性:是指定通知类bean的Id。
  4. 在aop:aspect标签的内部使用对应标签来配置通知的类型
    • 我们现在示例是让printLog方法在切入点方法执行之前执行:所以是前置通知
    • aop:before:
      表示配置前置通知
    • method属性:用于指定Logger类中哪个方法是前置通知

3.切入点表达式的写法

  1. 关键字:execution(表达式)

  2. 表达式:

    • 访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
  3. 标准的表达式写法

    • public void com. itheima. service . impl . Ac countServiceImpl . saveAccount()
    <?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">
       <!--    配置代理Logger类-->    
       <bean id="logger" class="com.itheima.utils.Logger"/>
       <!--配置AOP-->    
       <aop:config>
          <!--配置切面-->        
          <aop:aspect id="logAdvice" ref="logger">
           <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->           
             <aop:before method="pringLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"/>        
          </aop:aspect>    
       </aop:config>    
       <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" /></beans>
    
  4. 通配符的使用

    • 访问修饰符可以省略

      • void com.itheima.service.impl.AccountServiceImpl.saveAccount()
    • 返回值可以使用通配符,表示任意返回值

      • * com.itheima.service.impl.AccountServiceImpl.saveAccount()
    • 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*

      • * *.*.*.*.AccountServiceImpl.saveAccount()
    • 包名可以使用..表示当前包及其子包

      • * *.. AccountServiceImpl. saveAccount ()
    • 类名和方法名都可以使用*来实现通配

      • * *..*.*()
    • 参数列表:

      • 可以直接写数据类型:
    • 基本类型直接写名称
      int

    • 引用类型写包名.类名的方式
      java.lang .String

    • 可以使用通配符表示任意类型,但是必须有参数

    • 可以使用..表示有无参数均可,
      有参数可以是任意类型

    • 全通配写法:

      • * *..*.*(..)
      <!--    配置代理Logger类-->    
      <bean id="logger" class="com.itheima.utils.Logger"/>
      <!--配置AOP-->    
      <aop:config>
            <!--        配置切面-->        
         <aop:aspect id="logAdvice" ref="logger">
            <!--            配置通知的类型,并且建立通知方法和切入点方法的关联-->            				<aop:before method="pringLog" pointcut="execution(* *..*.*(..))"/>        		</aop:aspect>    
      </aop:config>
      
    • 实际开发中切入点表达式的通常写法:

      • 切到业务层实现类下的所有方法
    • * com.itheima.service.impl.*.*(..)

   
   <!--配置AOP-->
    <aop:config>
   <!--        配置切面-->
           <aop:aspect id="logAdvice" ref="logger">
   <!--            配置通知的类型,并且建立通知方法和切入点方法的关联-->
               <aop:before method="pringLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
           </aop:aspect>
       </aop:config>

4. AOP 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: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">
<!--    配置service类-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" />
    <!--    配置代理Logger类-->
    <bean id="logger" class="com.itheima.utils.Logger"/>
    <!--配置AOP-->
    <aop:config>
<!--    配置切面-->
        <aop:aspect id="logAdvice" ref="logger">	
<!--         配置前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
<!--        配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
            <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
<!--        配置异常通知:在切入点方法执行产生异常之后执行,它和异常通知永远只能执行一个-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
<!--        配置最终通知:无论切入点方法是否正常执行它都会在其后执行-->
            <aop:after method="afterPrintLog" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
        </aop:aspect>
</aop:config>

</beans>

或者

<?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">

    <!--    配置service类-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" />
    <!--    配置代理Logger类-->
    <bean id="logger" class="com.itheima.utils.Logger"/>
    <!--配置AOP-->
    <aop:config>
        <!--     配置切入点表达式  id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
                    此标签写在aop:aspect标签内部只能当前切面使用
                    它还可以写在aop:aspect外面,此时就变成了所有切面可用
                    -->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        <!--    配置切面-->
        <aop:aspect id="logAdvice" ref="logger">
            <!--         配置环绕通知-->
            <aop:around method="aroundPrintLog" pointcut-ref="pt1" />
        </aop:aspect>
    </aop:config>

</beans>

5.AOP 基于注解的写法

  1. 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:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           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
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--    配置spring创建容器时要扫描的包-->
        <context:component-scan base-package="com.itheima" />
    <!--    配置spring开启注解AOP的支持-->
        <aop:aspectj-autoproxy />
    
    </beans>
    
  2. 常用注解

    @Component("logger")
    @Aspect
    public class Logger {
    
        @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
        private void pt1(){
    
        }
        /**
         * 前置通知
         */
    //    @Before("pt1()")
        public void beforePrintLog(){
            System.out.println("Logger中的beforePrintLog开始记录日志了。。");
        }
        /**
         * 后置通知
         */
    //    @AfterReturning("pt1()")
        public void afterReturningPrintLog(){
            System.out.println("Logger中的afterReturningPrintLog开始记录日志了。。");
        }
        /**
         * 异常通知
         */
    //    @AfterThrowing("pt1()")
        public void afterThrowingPrintLog(){
            System.out.println("Logger中的afterThrowingPrintLog开始记录日志了。。");
        }
        /**
         * 最终通知
         */
    //    @After("pt1()")
        public void afterPrintLog(){
            System.out.println("Logger中的afterPrintLog开始记录日志了。。");
        }
    
        /**
         * 环绕通知
         *问题:
         * 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
         * 分析:
         * 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
         * 解决:
         * Spring框架为我们提供了一个接口: ProceedingJoinPoint 该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
         * 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供接口的实现类供我们使用。
         */
        @Around("pt1()")
        public Object aroundPrintLog(ProceedingJoinPoint pjp){
            Object rtValue = null;
            try{
                Object[] args = pjp.getArgs();//得到方法执行所需的参数
                System.out.println("Logger中的aroundPrintLog开始记录日志了。。前置");
                rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
                //int i = 1/0;
                System.out.println("Logger中的aroundPrintLog开始记录日志了。。后置");
                return rtValue;
            }catch (Throwable t){
                System.out.println("Logger中的aroundPrintLog开始记录日志了。。异常");
                throw new RuntimeException(t);
            }finally {
                System.out.println("Logger中的aroundPrintLog开始记录日志了。。最终");
            }
            //System.out.println("Logger中的aroundPrintLog执行了。。");
        }
    }
    

    若完全用注解实现,则要在配置类前加上@EnableAspectJAutoProxy 注解来开启AOP代理自动配置,相当于xml中的<aop:aspectj-autoproxy />的作用

    例如:

    @ComponentScan({"com.itheima","com.utils"})
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbcConfig.properties")
    @EnableAspectJAutoProxy //开启AOP代理自动配置
    public class SpringConfiguration {
    
    }
    

四、AOP

标签:expr   system   version   instance   img   java   解决   一个   RoCE   

原文地址:https://www.cnblogs.com/TearCheer/p/13593013.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!