• 【Spring AOP】Spring AOP的使用方式【Q】


    Spring AOP的三种使用方式

    • 经典AOP使用方式
    • 改进XML配置方式
    • 基于注解的方式

    第1种方式可以作为理解spring配置AOP的基础,是最原始的配置方式,也体现了spring处理的过程。
    使用ProxyFactoryBean配置有些欠优雅,在spring2.0里新的xml配置元素体现了改进。Spring2.0在aop命名空间里提供了一些配置元素,简化了把类转化为切面的操作,即第2 种XML方式,本质的使用同第1种方式,只是简化配置,隐藏细节。
    第3种方式是基于注解的,也是比较推荐的使用方式。

    经典AOP使用方式

    Spring中提供:前置通知     环绕通知     后置通知     异常通知  引入通知
    分别需要实现如下接口:

    MethodBeforeAdvice   MethodInteceptor  AfterReturningAdvice   ThrowsAdvice

    其中引入通知是通过配置的,实现自定义切入点,和上述四个通知配合使用

    被代理的对象(被代理对象需要实现接口)

    以MethodBeforeAdvice(环绕通知)为例

    XML配置如下,

    代理对象:ProxyFactoryBean 这是Spring框架提供出来的,我们直接使用,并配置相关属性。

    改进XML配置方式

    为了简化配置,下面是使用普通类,通过aop标签指定来配置AOP。无需再实现上述经典AOP的几个接口。
    以一个案例的形式对xml的开发形式进行简要分析,定义一个切面类。

    public class MyAspectXML {
    
        public void before(){
            System.out.println("MyAspectXML====前置通知");
        }
    
        public void afterReturn(Object returnVal){
            System.out.println("后置通知-->返回值:"+returnVal);
        }
    
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("MyAspectXML=====环绕通知前");
            Object object= joinPoint.proceed();
            System.out.println("MyAspectXML=====环绕通知后");
            return object;
        }
    
        public void afterThrowing(Throwable throwable){
            System.out.println("MyAspectXML======异常通知:"+ throwable.getMessage());
        }
    
        public void after(){
            System.out.println("MyAspectXML=====最终通知..来了");
        }
    }
    

    通过配置文件的方式声明如下(spring-aspectj-xml.xml):

    <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">
    
        <!--<context:component-scan base-package=""-->
    
        <!-- 定义目标对象 -->
        <bean name="productDao" class="com.zejian.spring.springAop.dao.daoimp.ProductDaoImpl" />
    
        <!-- 定义切面 -->
        <bean name="myAspectXML" class="com.zejian.spring.springAop.AspectJ.MyAspectXML" />
        <!-- 配置AOP 切面 -->
        <aop:config>
            <!-- 定义切点函数 -->
            <aop:pointcut id="pointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.add(..))" />
    
            <!-- 定义其他切点函数 -->
            <aop:pointcut id="delPointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.delete(..))" />
    
            <!-- 定义通知 order 定义优先级,值越小优先级越大-->
            <aop:aspect ref="myAspectXML" order="0">
                <!-- 定义通知
                method 指定通知方法名,必须与MyAspectXML中的相同
                pointcut 指定切点函数
                -->
                <aop:before method="before" pointcut-ref="pointcut" />
    
                <!-- 后置通知  returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
                <aop:after-returning method="afterReturn" pointcut-ref="pointcut"  returning="returnVal" />
    
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pointcut"  />
    
                <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
    
                <!--
                     method : 通知的方法(最终通知)
                     pointcut-ref : 通知应用到的切点方法
                    -->
                <aop:after method="after" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    </beans>
    

    声明方式和定义方式在代码中已很清晰了,了解一下即可,在实际开发中,会更倾向与使用注解的方式开发,毕竟更简单更简洁。

    基于注解AOP使用方式

    Spring在新版本中对AOP功能进行了增强,体现在这么几个方面:
    •在XML配置文件中为AOP提供了aop命名空间
    •增加了AspectJ切点表达式语言的支持
    •可以无缝地集成AspectJ

    那我们使用@AspectJ来玩AOP的话,学什么??其实也就是上面的内容,学如何设置切点、创建切面、增强的内容是什么...

    定义切入点函数
    在案例中,定义过滤切入点函数时,是直接把execution已定义匹配表达式作为值传递给通知类型的如下:

    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
      public void after(){
          System.out.println("最终通知....");
      }
    

    除了上述方式外,还可采用与ApectJ中使用pointcut关键字类似的方式定义切入点表达式如下,使用@Pointcut注解:

    /**
     * 使用Pointcut定义切点
     */
    @Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    private void myPointcut(){}
    
    /**
     * 应用切入点函数
     */
    @After(value="myPointcut()")
    public void afterDemo(){
        System.out.println("最终通知....");
    }
    

    使用@Pointcut注解进行定义,应用到通知函数afterDemo()时直接传递切点表达式的函数名称myPointcut()即可,比较简单,下面接着介绍切点指示符。

    基于注解的AOP实例

    定义目标类接口和实现类

    /**
     * Created by zejian on 2017/2/19.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
     //接口类
    public interface UserDao {
    
        int addUser();
    
        void updateUser();
    
        void deleteUser();
    
        void findUser();
    }
    
    //实现类
    import com.zejian.spring.springAop.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    /**
     * Created by zejian on 2017/2/19.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    @Repository
    public class UserDaoImp implements UserDao {
        @Override
        public int addUser() {
            System.out.println("add user ......");
            return 6666;
        }
    
        @Override
        public void updateUser() {
            System.out.println("update user ......");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("delete user ......");
        }
    
        @Override
        public void findUser() {
            System.out.println("find user ......");
        }
    }
    

    使用Spring 2.0引入的注解方式,编写Spring AOP的aspect 类:

    @Aspect
    public class MyAspect {
    
        /**
         * 前置通知
         */
        @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
        public void before(){
            System.out.println("前置通知....");
        }
    
        /**
         * 后置通知
         * returnVal,切点方法执行后的返回值
         */
        @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
        public void AfterReturning(Object returnVal){
            System.out.println("后置通知...."+returnVal);
        }
    
    
        /**
         * 环绕通知
         * @param joinPoint 可用于执行切点的类
         * @return
         * @throws Throwable
         */
        @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("环绕通知前....");
            Object obj= (Object) joinPoint.proceed();
            System.out.println("环绕通知后....");
            return obj;
        }
    
        /**
         * 抛出通知
         * @param e
         */
        @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
        public void afterThrowable(Throwable e){
            System.out.println("出现异常:msg="+e.getMessage());
        }
    
        /**
         * 无论什么情况下都会执行的方法
         */
        @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
        public void after(){
            System.out.println("最终通知....");
        }
    }
    

    编写配置文件交由Spring IOC容器管理

    <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">
    
        <!-- 启动@aspectj的自动代理支持-->
        <aop:aspectj-autoproxy />
    
        <!-- 定义目标对象 -->
        <bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
        <!-- 定义aspect类 -->
        <bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
    </beans>
    

    编写测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations= "classpath:spring/spring-aspectj.xml")
    public class UserDaoAspectJ {
        @Autowired
        UserDao userDao;
    
        @Test
        public void aspectJTest(){
            userDao.addUser();
        }
    }
    

    简单说明一下,定义了一个目标类UserDaoImpl,利用Spring2.0引入的aspect注解开发功能定义aspect类即MyAspect,在该aspect类中,编写了5种注解类型的通知函数,分别是前置通知@Before、后置通知@AfterReturning、环绕通知@Around、异常通知@AfterThrowing、最终通知@After,这5种通知与前面分析AspectJ的通知类型几乎是一样的,并注解通知上使用execution关键字定义的切点表达式,即指明该通知要应用的目标函数,当只有一个execution参数时,value属性可以省略,当含两个以上的参数,value必须注明,如存在返回值时。当然除了把切点表达式直接传递给通知注解类型外,还可以使用@pointcut来定义切点匹配表达式,这个与AspectJ使用关键字pointcut是一样的,后面分析。目标类和aspect类定义完成后,最后需要在xml配置文件中进行配置,同样的所有类的创建都交由SpringIOC容器处理,注意,使用Spring AOP 的aspectJ功能时,需要使用以下代码启动aspect的注解功能支持:

    <aop:aspectj-autoproxy />
    

    另外,开启AOP也可以通过代码配置。使用 Java Configuration 方式使能@AspectJ

    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig {
    }
    

    参考资料:
    Spring AOP三种配置详细介绍
    关于 Spring AOP (AspectJ) 你该知晓的一切
    Spring AOP就是这么简单啦
    彻底征服 Spring AOP 之 理论篇
    JAVA动态代理 和 Spring AOP 4种通知的简单实现

  • 相关阅读:
    Linux系统安全及应用
    Linux 10 进程和计划的管理任务
    Centos netdata 的安装及配置
    Centos nmon安装及使用
    Python 各种数据类型的了解(上)
    神奇的循环知识
    Pathon的基础知识
    初次遇见Python
    操作系统与编程语言的简介
    计算机硬件基础
  • 原文地址:https://www.cnblogs.com/z00377750/p/11793088.html
Copyright © 2020-2023  润新知