三、Spring AOP (2)

本文详细介绍了SpringAOP的两种实现方式:基于代理类的AOP,包括Spring的通知类型如前置、后置、环绕通知等,以及使用ProxyFactoryBean创建代理对象的步骤。另外,文章还探讨了AspectJ的开发,包括基于XML的声明式AspectJ的切面、切入点和通知配置,以及基于注解的声明式AspectJ,展示了如何通过注解简化AOP配置。
摘要由CSDN通过智能技术生成

目录

一、基于代理类的AOP实现

1、spring的通知类型

2、ProxyFactoryBean

二、AspectJ开发

1、基于XML的声明式AspectJ

 1.配置切面

2.配置切入点

3.配置通知

4、案例

2、基于注解的声明式AspectJ

 


一、基于代理类的AOP实现

1、spring的通知类型

Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:
org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知)
在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。.
org.springframework.aop.ThrowsAdvice(异常抛出通知)
在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
org.springframework.aop.IntroductionInterceptor (引介通知)
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。

2、ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。

( 1 )在核心JAR包的基础上,再向chapter03项目的 lib目录中导入AOP的JAR包spring-aop-4.3.6.RELEASE.jar 和 aopalliance-1.0.jar
aopalliance-1.0.jar:是AOP联盟提供的规范包,该JAR包可以通过地址“http://mvnrepository.com/artifact/aopalliance/aopalliance/1.0”下载。

(2在src目录下,创建一个com.itheima.factorybean包,在该包中创建切面类MyAspect。由于实现环绕通知需要实现
org.aopalliance.intercept. Methodlnterceptor接口,所以MyAspect类需要实现该接口,并实现接口中的 invoke()方法,来执行目标方法

import org.aopalliance.intercept.MethodInterceptor;//注意路径
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor{

 @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //调用权限检查
        check_Permissionss();
        //调用目标方法
        Object object = methodInvocation.proceed();
        //调用日志记录
        log();
        return object;
    }
    //权限检查
        public void check_Permissionss() {
            System.out.println("模拟权限检查....");
        }
        //记录日志
        public void log() {
                System.out.println("模拟记录日志...");
            }

}

(3)在com.itheima.factorybean包中,创建配置文件 applicationContext.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
     <!-- 1.创建目标类实例 -->
     <bean id="userDao" class="com.itheima.jdk.UserDaoImpl"></bean>
     <!-- 2.创建切面类实例 -->
     <bean id="myAspect" class="com.itheima.factorybean.MyAspect"></bean>
     <!-- 3.使用Spring代理工程定义一个代理对象,然后对代理对象的属性注入-->
     <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
             <!-- 3.1指定代理实现的接口 -->
            <property name="proxyInterfaces" value="com.itheima.jdk.UserDao"></property>
            <!--3.2指定目标对象  -->
            <property name="target" ref="userDao"></property>
            <!-- 3.3指定切面,植入环绕通知 -->
            <property name="interceptorNames" value="myAspect"></property>
            <!-- 3.4指定代理方式,默认false用jdk, true用cglib -->
            <property name="proxyTargetClass" value="true"></property>
    </bean>
      </beans>  

( 4)在com.itheima.factorybean包中,创建测试类 ProxyFactoryBeanTest,在类中通过Sprina容器获取代理对象的实例.并执行目标方法

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itheima.jdk.UserDao;

public class ProxyFactoryBeanTest {
    public static void main(String[] args) {
        String xmlPath = "com/itheima/factorybean/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
        userDao.addUser();
    }

}

执行程序后,控制台的输出结果

二、AspectJ开发

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,SpringAOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ

1、基于XML的声明式AspectJ

基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。

 1.配置切面

在Spring 的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。定义完成后,通过<aop:aspect>元素的ref 属性即可引用该 Bean。
配置<aop:aspect>元素时,通常会指定id和ref两个属性

2.配置切入点

在Spring 的配置文件中,切入点是通过<aop:pointcut>元素来定义的。当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。

在上述配置代码片段中,execution(* com.itheima.jdk.*.*(..))就是定义的切入点表达式,该切入点表达式的意思是匹配 com.itheima.jdk包中任意类的任意方法的执行。其中 execution()是表达式的主体,第1个*表示的是返回类型,使用*代表所有类型;com.itheima.jdk表示的是需要拦截的包名,后面第2个*表示的是类名,使用*代表所有的类;第3个*表示的是方法名,使用*表示所有方法;后面(..)表示方法的参数,其中的“..”表示任意参数。需要注意的是,第1个*与包名之间有一个空格。

3.配置通知

使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:

4、案例

(1)导入AspectJ框架相关的JAR包,具体如下。
 spring-aspects-4.3.6.RELEASE.jar:Spring 为AspectJ提供的实现,Spring 的包中已经提供。
aspectjweaver-1.8.10.jar:是AspectJ框架所提供的规范,读者可以通过网址“http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.10”下载。


(2)在chapter03项目的src目录下,创建一个 com.itheima.aspectj.xml包,在该包中创建切面类MyAspect,并在类中分别定义不同类型的通知

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
*切面类,在该类中编写通知
*通知也就是要增强的功能的那些方法*
*/
public class MyAspect {
    //1.前置通知
    public void myBefore(JoinPoint joinPoint) {
        System.out.println("前置通知:模拟执行权限检查...");
        System.out.println("目标类:"+joinPoint.getTarget());//输出一下目标类
        //被植入增强处理的目标方法名
        System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }
    //2.后置通知
    public  void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("后置通知:模拟记录日志...");
        System.out.println("被植入增强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    //3.环绕通知
    /**
    *环绕通知有几个限定条件
    *1.必须是0bject类型的返回值
    *2.必须接收一个参数,类型为ProceedingJoinPoint
    *3.必须throws Throwable
    */
    public Object myArroud(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {
        //开始
        System.out.println("坏绕开始:执行目标方法前,模拟开始事务...");
        //执行当前目标方法
        Object object = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束:执行目标方法之后,模拟关闭事务");
        return object;
    }
    //4.异常通知
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
        System.out.println("异常通知:出错了"+e.getMessage());
    }
    //5.最终通知
    public void myAfter() {
        System.out.println("最终通知:最终通知:模拟方法结束后的释放资源...");
    }

}

在文中,分别定义了5种不同类型的通知,在通知中使用了JoinPoint 接口及其子接口ProceedingJoinPoint 作为参数来获得目标对象的类名、目标方法名和目标方法参数等。
需要注意的是,环绕通知必须接收一个类型为ProceedingJoinPoint的参数,返回值也必须是Object类型,且必须抛出异常。异常通知中可以传入Throwable类型的参数来输出异常信息。


( 3 )在com.itheima.aspectj.xml包中,创建配置文件applicationContext.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">
     <!-- 1.目标类  -->
     <bean id="userDao" class="com.qingcheng.ssm.jdk.UserDaoImpl"></bean>
     <!-- 2.切面类 -->
     <bean id="myAspect" class="com.qingcheng.ssm.aspectj.xml.MyAspect"></bean>
     <!-- 3.进行aop配置 -->
     <aop:config>
           <!-- 配置切面-->
           <aop:aspect ref="myAspect">
                <!-- 3.1配置切入点,通知最后增强哪些方法-->
                <aop:pointcut expression="execution(* com.qingcheng.ssm.jdk.*.*(..))" id="myPointcut"/>
                <!-- 3.2关联通知和切入点-->
                <!-- 3.2.1前置通知-->
                <!--myBefore是切面类中的前置方法名  -->
                <aop:before method="myBefore" pointcut-ref="myPointcut"/>
                <!-- 3.2.2后置通知,在方法返回之后执行,就可以获得返回值
                 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
                 <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
                <!-- 3.2.3环绕通知 -->
                <aop:around method="myArroud" pointcut-ref="myPointcut"/>
                <!--3.2.4抛出异常通知,用于处理程序发生异常,如果程序没有异常,将不执行
                throwing属性,是用于设置通知第二个参数的名称,类型是Throwable  -->
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
                <!-- 3.2.5最终通知:无论程序发生任何事情,都将执行 -->
                <aop:after method="myAfter" pointcut-ref="myPointcut"/>
          </aop:aspect>
     </aop:config>
 </beans>  

(4)在 com.itheima.aspectj.xml包下,创建测试类TestXmIAspectj,在类中为了更加清晰地演示几种通知的执行情况,这里只对 addUser()方法进行增强测试

public class TestAspectjXml {
    public static void main(String[] args) {
        String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml";
        ApplicationContext applicationContext =

                                         new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.addUser();
    
    }

}

执行程序后,控制台的输出结果


要查看异常通知的执行效果,可以在 UserDaolmpl类的 addUser()方法中添加错误代码,如int i = 10/0;”,重新运行测试类,将可以看到异常通知的执行

2、基于注解的声明式AspectJ

 与基于代理类的AOP实现相比,基于XML的声明式ApectJ要便捷得多,但是它也存在着一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。

( 1)在chapter03项目的src目录下,创建com.itheima.aspectj.annotation包,将文件3-12的切面类MyAspect复制到该包下,并对该文件进行编辑

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.stereotype.Component;

/***
*切面类,在该类中编写通知
*通知也就是要增强的功能的那些方法
*/
@Component  //生成MyAspect的实例,也可以说生成它的bean
@Aspect //生成切面
public class MyAspect {
    /**
     * 定义一个切入点
     * */
    @Pointcut("execution(* com.qingcheng.ssm.jdk.*.*(..))")
    private void myPointCut() {
        
    }
    
    //1.前置通知
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint) {
        System.out.println("前置通知:模拟执行权限检查...");
        System.out.println("目标类:"+joinPoint.getTarget());//输出一下目标类
        //被植入增强处理的目标方法名
        System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }
    
    
    
    //2.后置通知
    @AfterReturning("myPointCut()")
    public  void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("后置通知:模拟记录日志...");
        System.out.println("被植入增强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    
    
    //3.环绕通知
    /**
    *环绕通知有几个限定条件
    *1.必须是0bject类型的返回值
    *2.必须接收一个参数,类型为ProceedingJoinPoint
    *3.必须throws Throwable
    */
    @Around("myPointCut()")
    public Object myArroud(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {
        //开始
        System.out.println("坏绕开始:执行目标方法前,模拟开始事务...");
        //执行当前目标方法
        Object object = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束:执行目标方法之后,模拟关闭事务");
        return object;
    }
    
    //4.异常通知
    @AfterThrowing(value="myPointCut()",throwing="e")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
        System.out.println("异常通知:出错了"+e.getMessage());
    }
    
    //5.最终通知
    @After("myPointCut()")
    public void myAfter() {
        System.out.println("最终通知:最终通知:模拟方法结束后的释放资源...");
    }

 (2)在目标类com.itheima.jdk.UserDaolmpl中,添加注解@Repository("userDao")。
(3)在com.itheima.aspectj.annotation包下,创建配置文件 applicationContext.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">
     
     <!--指定需要扫描的包,使注解生效-->
     <context:component-scan base-package="com.itheima"></context:component-scan>
     <!--启动基于注解的声明式Aspect的支持-->
     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
     
      </beans> 

 ( 4 )在 com.itheima.aspectj.annotation包中,创建测试类TestAnnotation

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.qingcheng.ssm.jdk.UserDao;

public class TestAspectAnnotation {
    public static void main(String[] args) {
        String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.addUser();
        
    }

}

执行程序后,控制台的输出结果

 



 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值