**AOP概念:**面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的动态延续。利用AOP可以对业务逻辑各个部分进行隔离,从而使得各业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
简单是的说他就是把我们程序的重复代码抽离出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对已有的方法进行增强。
**作用:**在程序运行期间,在不修改源码的情况下对方法进行增强
**优势:**减少重复代码,提高开发效率,维护方便,
**AOP的实现方式:**使用动态代理技术。
通过配置的方式实现上一个转账案例
AOP术语:
- target目标类:需要被代理的类。accountService
- Joinpoint连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
- PointCut切入点:已经被增强的连接点。例如:addAccount()
- advice通知/增强,增强代码。例如:动态代理中invoke()方法具有拦截的功能,拦截被代理对象中的所有方法。然后通知TransactionManager,因为拦截之后就开启的事务的支持(开启、执行、提交、回滚、释放),所以在这里TransactionManager就是通知。通知的类型:前置通知,异常通知、后置通知、最终通知、环绕通知。那些每个方法里面的有关事物的重复代码提取出来,如下:
- Weaving织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
- proxy代理类:return Proxy…
- Aspect切面:是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
SpringAOP案例:
接口类IAccountService:
public interface IAccountService {
/*
* 模拟保存*/
void saveAccount();
/*
* 模拟更新账户*/
void updateAccount(int i);
/*
* 删除账户*/
int deleteAccount();
}
业务层AccountServiceImpl:
/*
* 账户的业务层实现类*/
public class AccountServiceImpl implements IAccountService{
@Override
public void saveAccount() {
System.out.println("执行了保存");
}
@Override
public void updateAccount(int i) {
System.out.println("执行了更新");
}
@Override
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
用于提供日志的工具类Logger:
/*
* 用于记录日志的工具类,它里面提供了公共的代码*/
public class Logger {
/*
* 前置通知*/
public void beforPrintLog(){
System.out.println("前置通知Logger类中的printLog方法开始记录日志。。。");
}
/*
* 后置通知*/
public void afterReturanPrintLog(){
System.out.println("后置通知Logger类中的afterReturanPrintLog方法开始记录日志。。。");
}
/*
* 异常通知*/
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志。。。");
}
/*
* 最终通知*/
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志。。。");
}
/*
* 问题:
* 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用
* 解决:
* Spring框架为我们提供了一个接口:proceedingJoinPoint。该接口有一个方法procced()。此方法就相当于明确调用切入点方法
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
* Spring中的环绕通知:
* 他是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式*/
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
Object[] args = pjp.getArgs();//得到方法执行所需要的参数
System.out.println("Logger类中的aroundPrintLog方法开始记录日志。。。前置");
rtValue =pjp.proceed(args); //明确调用业务层方法(切入点方法)
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方法开始记录日志。。。最终");
}
}
}
//如果让你自己用编码的方式实现在业务层中的方法执行之前先来执行Logger类中的printLog()方法;
//只需要创建IAccountService接口的代理类在执行业务层之前先调用一下Logger类就可以了。
配置文件:
<?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">
<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.selan.service.impl.AccountServiceImpl"></bean>
<!--配置Loger类也就是通知类-->
<bean id="logger" class="com.selan.util.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式,id属性用于指定表达式的唯一标志,
expression属性用于指定表达式内容
此标签只能写在aop:aspect标签内部只能在当前标签使用,如果有新的切面还需要配置
还可以写在aop:aspect外面=======一定要放在aop:aspect前面因为有约束的要求-->
<aop:pointcut id="pointCut1" expression="execution(* com.selan.service.impl.*.*(..))"></aop:pointcut>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置前置通知,在切入点方法执行之前执行
<aop:before method="beforPrintLog" pointcut-ref="pointCut1"></aop:before>-->
<!--配置后置通知,在切入点方法正常执行之后,后置通知和异常通知只能执行一个
<aop:after-returning method="afterReturanPrintLog" pointcut-ref="pointCut1"></aop:after-returning>-->
<!--配置异常通知,在切入点方法执行产生异常之后执行,异常通知和后置通知只能执行一个
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pointCut1"></aop:after-throwing>-->
<!--配置最终通知,无论切入点方法正常执行他都会在最后面执行
<aop:after method="afterPrintLog" pointcut-ref="pointCut1"></aop:after>-->
<!--配置环绕通知-->
<aop:around method="aroundPrintLog" pointcut-ref="pointCut1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
测试类:
public class AOPTest {
public static void main(String[] args) {
//获取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService)applicationContext.getBean("accountService");
accountService.saveAccount();
}
}