SpringAOP(面向切面)编程
SpringAOP是针对不同问题的OOP编程的简单补充,是Spring框架中的一个重要内容。用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
基本术语
1.通知(Advice):切面要完成的工作以及何时完成执行
2.连接点(Join Point):程序中需要动态织入代码的一个点
3.切点(Pointcut):会匹配通知所要织入的一个或多个连接点,解决了在哪些类和方法的连接点上执行通知的问题
4.切面(Aspect):通知和切点的结合
5.引入(Introduction):引入允许我们向现有的类添加新方法或属性,使程序具有动态性。
6.织入(Weaving):把切面应用到目标对象的过程。可以在编译期、类加载期、运行期进行织入。
通知(Advice)常见的几种类型
前置通知:需要实现MethodBeforeAdvice接口
后置通知:需要实现AfterReturningAdvice接口
环绕通知:需要实现MethodIntercept接口
异常通知:需要实现ThrowsAdvice接口
基于XML文件实现的AOP
设置通知(Advice)类,实现相应接口,重写方法。
注意:ThrowsAdvice接口是一个空接口,实现它不需要重写方法,但是异常通知的方法名必须时afterThrowing(参数类型为可能发生异常的类型)
public class LogAdvice implements MethodBeforeAdvice,
AfterReturningAdvice,ThrowsAdvice {
//前置
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----------------------------");
System.out.println("[日志输出]:前置通知");
System.out.println("[方法名称]:"+method.getName());
System.out.println("[方法参数]:"+Arrays.toString(args));
System.out.println("[目标对象]:"+method);
}
//后置
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("[日志输出]:后置通知");
System.out.println("[返回结果]:"+returnValue);
System.out.println("-----------------------------");
}
public void afterThrowing(Exception ex) {
System.out.println("[异常处理]:警报,出现异常!请尽快处理");
}
public void afterThrowing(NullPointerException ex){
System.out.println("[异常处理]:警报,出现空指针异常");
}
}
创建XML文件,声明Bean,使用JDk的反射机制,生成Proxy代理对象(BeanNameAutoProxyCreator自动代理对象创建工厂)
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>logAdviceBean</value>
<value>performanceAdviceBean</value>
</list>
</property>
<!-- 注入Target目标对象Bean的名称匹配规则 -->
<property name="beanNames">
<list>
<value>*ServiceBean</value>
<value>*DaoBean</value>
</list>
</property>
</bean>
设置切点和切面,配置方法匹配规则
<!-- Pointcut切点:确定目标方法 -->
<bean id="daoInsertMethodPointcut"
class="org.springframework.aop.support.NameMatchMethodPointcut">
<!-- 查找所有以Insert结尾的方法为目标方法(切点) -->
<property name="mappedName" value="*Insert"/>
</bean>
<!-- Advisor Advice + Pointcut -->
<bean id="defaultLogAdvisorBean"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="daoInsertMethodPointcut" />
<property name="advice" ref="logAdviceBean" />
</bean>
并且在interceptorNames中注入切面
<property name="interceptorNames">
<list>
<!-- 注入Advisor -->
<value>LogAdvisorBean</value>
</list>
</property>
使用XML实现纯POJO切面
POJO:简单Java对象,通知(Advice)类不用实现指定接口,可自由定义通知方法。
XML配置方式:
<!-- AOP配置 -->
<aop:config>
<!-- 公共切点,可匹配多个切面 -->
<aop:pointcut expression="execution(* com.apesource.dao.*.*Insert(..))" id="daoInsertMethod"/>
<!-- 配置切面1 -->
<aop:aspect ref="logAdviceBean">
<!-- 配置通知“类型” -->
<!-- 前置通知:在pointcut查找到的目标方法执行前,自动执行通知类中的指定method -->
<aop:before method="methodBefore" pointcut-ref="daoInsertMethod"/>
<!-- 后置通知 -->
<aop:after-returning method="methodAfter" pointcut-ref="daoInsertMethod"/>
</aop:aspect>
<!-- 配置切面2 -->
<aop:aspect ref="adminAdviceBean">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="daoInsertMethod"/>
</aop:aspect>
</aop:config>
而在通知(Advice)类中,虽然不用实现特定接口,可自定义通知方法,但方法的参数必须是JoinPoint(org.aspectj.lang.JoinPoint)类型,用于连接目标方法和当前通知方法。
public class LogAdvice{
// "自定义"前置通知方法
public void methodBefore(JoinPoint joinpoint){
System.out.println("[日志输出]:--------前置通知----------------");
}
// "自定义"后置通知方法
public void methodAfter(JoinPoint joinpoint){
System.out.println("[日志输出]:--------后置通知----------------");
}
}
而环绕通知传入的参数为ProceedingJoinPoint类型,该接口继承JoinPoint 接口,可以调用目标方法(joinPoint.proceed()方法)。
public class AdminAdvice {
// "自定义"环绕通知方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("[日志输出]:--------环绕通知开始----------------");
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
Object resultVal = joinPoint.proceed();
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
System.out.println("[日志输出]:--------环绕通知结束----------------");
return resultVal;
}
}