spring AOP搭建

    xiaoxiao2022-07-07  145

    1.pom.xml导入坐标

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>sise.cn</groupId> <artifactId>day03_eesy_03springAOP</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!--解析切入点表达式--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies> </project>

    2.IAccountserv AccountServiceImpl

    /** * 账户的业务层接口 */ public interface IAccountService { /** * 模拟保存账户 */ void saveAccount(); /** * 模拟更新账户 */ void updateAccount(); /** * 删除账户 * @return */ int deleteAccount(); } /** * 账户的业务层实现类 */ public class AccountServiceImpl implements IAccountService { public void saveAccount() { System.out.println("执行了保存"); } public void updateAccount() { System.out.println("执行了更新"); } public int deleteAccount() { System.out.println("执行了保存"); return 0; } }

    3.创建utils工具类,提供公共代码

    /** * 用于记录日志的工具类 * 里面提供了公共的代码 */ public class Logger { /** * 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法 ) */ public void printLog(){ System.out.println("Logger类中的printLog方法开始记录日志了..."); } }

    4.bean.xml导入aop约束,并且配置

    <?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"> <!-- bean definitions here --> <!--配置spring的ioc,把service对象配置进来--> <bean id="accountService" class="sise.cn.service.Impl.AccountServiceImpl"></bean> <!--spring中基于XML的AOP配置步骤 1.把通知bean也交给spring来管理 2.使用aop:config标签表明开始aop的配置 3.使用aop:aspect标签表明配置切面 id:给切面提供一个唯一标志 ref:是指定通知类bean的id 4.在aop:aspect标签的内部使用对应标签来配置通知的类型 我们现在的实例是让pringLog方法在切入点方法执行之前执行,所以是前置通知 aop:before表示配置前置通知 method属性:用于指定Logger类中哪个方法是前置通知 pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中的哪些方法增强 切入点表达式的写法: 关键字:execution(表达式) 表达式: 访问修饰符 返回值 包名.包名....类名.方法名(参数列表) 标准的表达式写法: public void sise.cn.service.Impl.AccountServiceImpl.saveAccount() 全通配写法: * *..*.*(..) 实际开发中切入点表达式的通常写法: 切到业务层实现类下的所有方法 * sise.cn.service.Impl.*.*(..) --> <!--配置Logger类--> <bean id="logger" class="sise.cn.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置通知的类型且建立通知方法和切入点方法的关联 用此方法后service中所有的方法都被加强 executio(* sise.cn.service.Impl.*.*(..))--> <aop:before method="printLog" pointcut="execution(public void sise.cn.service.Impl.AccountServiceImpl.saveAccount())"></aop:before> </aop:aspect> </aop:config> </beans>

    test:

    /** * 测试aop的配置 */ public class AOPTest { public static void main(String[] args) { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.获取对象 IAccountService as = (IAccountService) ac.getBean("accountService"); //3.执行方法 as.saveAccount(); } }

    结果:

    Logger类中的printLog方法开始记录日志了... 执行了保存

    四种常用通知类型


    logger

    /** * 用于记录日志的工具类 * 里面提供了公共的代码 */ public class Logger { /** * 前置通知 */ public void beforePrintLog(){ System.out.println("前置Logger类中的beforePrintLog方法开始记录日志了..."); } /** * 后置通知 */ public void afterReturningPrintLog(){ System.out.println("后置Logger类中的afterReturningPrintLog方法开始记录日志了..."); } /** * 异常通知 */ public void afterThrowingPrintLog(){ System.out.println("异常Logger类中的afterThrowingPrintLog方法开始记录日志了..."); } /** * 最终通知 */ public void afterPrintLog(){ System.out.println("最终Logger类中的afterPrintLog方法开始记录日志了..."); } }

    bean.xml

    <!--配置spring的ioc,把service对象配置进来--> <bean id="accountService" class="sise.cn.service.Impl.AccountServiceImpl"></bean> <!--配置Logger类--> <bean id="logger" class="sise.cn.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置前置通知--> <aop:before method="beforePrintLog" pointcut="execution(* sise.cn.service.Impl.*.*(..))"></aop:before> <!--配置后置通知--> <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* sise.cn.service.Impl.*.*(..))"></aop:after-returning> <!--配置异常通知--> <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* sise.cn.service.Impl.*.*(..))"></aop:after-throwing> <!--配置最终通知--> <aop:after method="afterPrintLog" pointcut="execution(* sise.cn.service.Impl.*.*(..))"></aop:after> </aop:aspect> </aop:config>

    优化:切入点表达式

    <!--配置AOP--> <aop:config> <!--配置切入点表达式,id属性用于指定表达式的唯一标识, expression属性用于指定表达式内容--> <aop:pointcut id="pt1" expression="execution(* sise.cn.service.Impl.*.*(..))"/> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置前置通知--> <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before> <!--配置后置通知--> <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning> <!--配置异常通知--> <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing> <!--配置最终通知--> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after> <!--配置切入点表达式,id属性用于指定表达式的唯一标识, expression属性用于指定表达式内容 此标签写在aop:aspect标签内部只能当前切面使用 它还可以写在aop:aspect外面,此时就变成了了所有切面可用(必须在切面之前) --> <!-- <aop:pointcut id="pt1" expression="execution(* sise.cn.service.Impl.*.*(..))"/>--> </aop:aspect> </aop:config>

    环绕通知


    bean.xm

    <aop:config> <!--配置切入点表达式,id属性用于指定表达式的唯一标识, expression属性用于指定表达式内容--> <aop:pointcut id="pt1" expression="execution(* sise.cn.service.Impl.*.*(..))"/> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置环绕通知 详细的注释请看Logger类中--> <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config>

    Logger

    /** * 环绕通知 * 问题: * 当配置了环绕通知后切入点方法没有执行,而通知方法执行了 * * 分析: * 代码中没有明确的切入点方法调用 * 解决: * spring框架为我们提供了一个接口:ProceedingJoinPoint,该接口有一个方法proceed(),此方法就相当于明确调用切入点方法 * 该接口可以作为环绕通知的方法按参数,在程序执行时spring框架会为我们提供该接口的实现类供我们使用 * * spring中的环绕通知: * 它是spring为我们提供的一种可以在代码中手动控制增强方法合适执行的方法 */ public Object aroundPringLog(ProceedingJoinPoint pjp){ Object rtValue = null; try { Object[] args = pjp.getArgs();//得到方法执行所需的参数 System.out.println("Logger类中的aroundPringLog方法开始记录日志了...前置"); rtValue = pjp.proceed(args);//明确调用业务层方法也叫切入点方法 System.out.println("Logger类中的aroundPringLog方法开始记录日志了...后置"); return rtValue; } catch (Throwable throwable) { System.out.println("Logger类中的aroundPringLog方法开始记录日志了...异常"); throw new RuntimeException(throwable); }finally { System.out.println("Logger类中的aroundPringLog方法开始记录日志了...最终"); } }
    最新回复(0)