15.Spring的增强类型(各种通知以及理解)
1.结构图
2.业务层接口IAccountService:
package com.itheima.service; /*业务层接口*/ public interface IAccountService { /*保存账户*/ void saveAccount(); /*更新账户*/ void updateAccount(int i); /*删除账户*/ int deleteAccount(); }
3.业务层实现类 AccountServiceImp:
package com.itheima.service.Imp; import com.itheima.service.IAccountService; public class AccountServiceImp implements IAccountService { @Override public void saveAccount() { System.out.println("保存账户"); /* int i=1/0;*/ } @Override public void updateAccount(int i) { System.out.println("更新账户"); } @Override public int deleteAccount() { System.out.println("删除账户"); return 0; } }
4.切面类Logger(使调用方法之前先执行这个):
package com.itheima.utils; import org.aspectj.lang.ProceedingJoinPoint; public class Logger { /*前置通知*/ public void beforePintLogger(){ System.out.println("前置通知:Logger中的方法beforePintLogger开始执行记录日志。。。"); } /*后置通知通知*/ public void afterReturningPrintLogger(){ System.out.println("后置通知:Logger中的方法afterReturningPrintLogger开始执行记录日志。。。"); } /*异常通知*/ public void afterThrowingPrintLogger(){ System.out.println("异常通知:Logger中的方法afterThrowingPrintLogger开始执行日志。。。"); } /*最终通知*/ public void afterFinalPrintLogger(){ System.out.println("最终通知:Logger中的方法afterFinalPrintLogger开始执行日志。。。"); } /*环绕通知 * 问题: * 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了 * 分析: * 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确切入点的方法调用,,而我们的代码中没有 * 解决: * Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed,此方法就相当于明确调用切入点方法 * 该接口可以作为环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的的实现类供我们使用。 * Spring中环绕通知: * 它是spring框架为我们提供的一种可以在代码中手动控制增强的方法何时执行的方式。 * 【也就是说:不仅可以靠配,还可以靠代码来进行实现】 * * * * */ public Object aroundPrintLogger(ProceedingJoinPoint pjp){ Object reValue = null; try{ Object args = pjp.getArgs();/*得到方法所执行的参数*/ System.out.println("Logger环绕通知中的。。。前置"); reValue = pjp.proceed((Object[]) args);//明确调用业务层的方法(切入点方法) System.out.println("Logger环绕通知中的。。。后置"); return reValue; }catch (Throwable t) { System.out.println("Logger环绕通知中的。。。异常"); throw new RuntimeException(t); }finally { System.out.println("Logger环绕通知中的。。。最终"); } } } /*总结:环绕通知就是:自己编码的方式就是环绕通知,而还有其他的方式就是通过配置的方式*/
5.测试类AdviceTypeTest
package com.itheima; import com.itheima.service.IAccountService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AdviceTypeTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); IAccountService as = ac.getBean("accountService",IAccountService.class); as.saveAccount(); as.updateAccount(1); as.deleteAccount(); } }
6.配置文件bean.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"> <bean id="accountService" class="com.itheima.service.Imp.AccountServiceImp"></bean> <bean id="logger" class="com.itheima.utils.Logger"></bean> <aop:config> <!--这一个放后面不可以,但是前面可以,引申出来这个和约束有关--> <aop:pointcut id="pt1" expression="execution(public void com.itheima.service.Imp.AccountServiceImp.saveAccount())"/> <aop:aspect id="loggerAdvice" ref="logger"> <!--配置前置通知 :在切入点方法执行之前执行--> <!-- <aop:before method="beforePintLogger" pointcut="execution(public void com.itheima.service.Imp.AccountServiceImp.saveAccount())"></aop:before>--> <!--<aop:before method="beforePintLogger" pointcut-ref="pt1"></aop:before>--> <!--配置后置通知 :在切入点方法正常执行之后执行 ,永远只能与异常通知执行一个--> <!--<aop:after-returning method="afterReturningPrintLogger" pointcut-ref="pt1"></aop:after-returning>--> <!--配置异常通知 :在切入点方法执行产生异常之后执行--> <!--<aop:after-throwing method="afterThrowingPrintLogger" pointcut-ref="pt1"></aop:after-throwing>--> <!--配置最终通知 :无论切入点方法是否正常执行他都会在其最后面执行--> <!--<aop:after method="afterFinalPrintLogger" pointcut-ref="pt1"></aop:after>--> <!--配置切面表达式:通过使切面地址赋值给一个变量,然后通过pointcut-ref来引用--> <!--当然,这个切面还可以放外面,它的顺序如果放到后面去不可以的话,前面可以,那就是和环境的约束有关--> <!--<aop:pointcut id="pt1" expression="execution(public void com.itheima.service.Imp.AccountServiceImp.saveAccount())"/>--> <!--配置环绕通知--> <aop:around method="aroundPrintLogger" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config> </beans>
7.依赖pom.xml
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <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.9.2</version> </dependency> </dependencies>
总结:普通配置的通知只需要通过配置文件,然后通过标签配置通知即可;环绕通知可以通过写入代码的方式配置通知
当然还有通过注释的方式进行方法的增强(下一案例)。