1、概念术语
?
在开始之前,需要理解Spring aop 的一些基本的概念术语(总结的个人理解,并非Spring官方定义):
?
切面(aspect):用来切插业务方法的类。
?
连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。
?
通知(advice):在切面类中,声明对业务方法做额外处理的方法。
?
切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。
?
目标对象(target object):被代理对象。
?
AOP代理(aop proxy):代理对象。
?
通知:
?
前置通知(before advice):在切入点之前执行。
?
后置通知(after returning advice):在切入点执行完成后,执行通知。
?
环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。
?
异常通知(after throwing advice):在切入点抛出异常后,执行通知。
?
Spring AOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供基于接口的代理,不支持类的代理。
基于JDK的代理和基于CGLib的代理是Spring AOP的核心实现技术
?
?
2、AOP接口实现方式
利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知?MethodBeforeAdvice ,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice 。
a、业务接口
class="java" name="code">package org.lazyzhong.dao; public interface BossSpeak { public void speak(); }
?b、业务实现类
package org.lazyzhong.daoimpl; import org.lazyzhong.dao.BossSpeak; public class BossSpeakImpl implements BossSpeak{ @Override public void speak() { System.out.println("老板——————大家工作辛苦啦。。。我决定给大家发奖金1毛。。。"); } }
?c、前置通知
package org.lazyzhong.util; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class BeforeInter implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("今天是个好日子啊。。。看到上个月的销售额,老板感到很开心。。。于是召集大家开会。。。"); } }
?d、后置通知
package org.lazyzhong.util; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class AfterInter implements AfterReturningAdvice{ @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("会议结束。。。大家各回各的位置继续工作。。。"); } }
?e、环绕通知
package org.lazyzhong.util; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AroundInter implements MethodInterceptor{ @Override public Object invoke(MethodInvocation arg0) throws Throwable { //speak()方法开始。。。进入环绕方法 System.out.println("老板要开始讲话了。。。各位员工找个位置坐好。。。"); arg0.proceed(); //speak()方法结束。。。进入环绕方法 System.out.println("听到这个消息。。各位员工感到灰常开心。。。于是。。啪啪啪的声音响起。。"); return null; } }
f、切点
package org.lazyzhong.util; import java.lang.reflect.Method; import org.springframework.aop.support.NameMatchMethodPointcut; public class Pointcut extends NameMatchMethodPointcut { @Override public boolean matches(Method arg0, Class arg1) { //配置单个方法 this.setMappedName("speak"); // 配置多个方法 // String[] methods = { "xxx", "yyy" }; // this.setMappedNames(methods); return super.matches(arg0, arg1); } }
?
g、配置
<bean id="beforeAdvice" class="org.lazyzhong.util.BeforeInter" /> <bean id="afterAdvice" class="org.lazyzhong.util.AfterInter" /> <bean id="aroundAdvice" class="org.lazyzhong.util.AroundInter" /> <bean id="bossSpeak" class="org.lazyzhong.daoimpl.BossSpeakImpl" /> <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedNames"> <list> <value>speak</value> </list> </property> </bean> <!-- 指定切点匹配类 --> <bean id="pointcut" class="org.lazyzhong.util.Pointcut" /> <bean id="matchBeforeAdvice" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut"> <ref bean="pointcut" /> <!-- 也可以替换成pointcutBean --> </property> <property name="advice"> <ref bean="beforeAdvice" /> </property> </bean> <bean id="bossProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.lazyzhong.dao.BossSpeak</value> </property> <property name="target"> <ref bean="bossSpeak"/> </property> <property name="interceptorNames"> <list> <value>matchBeforeAdvice</value> <value>afterAdvice</value> <value>aroundAdvice</value> </list> </property> </bean>
?h、测试类
package org.lazyzhong.test; import org.lazyzhong.dao.BossSpeak; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest1 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); BossSpeak boss=(BossSpeak)context.getBean("bossProxy"); boss.speak(); } }
?i、输出:
今天是个好日子啊。。。看到上个月的销售额,老板感到很开心。。。于是召集大家开会。。。 老板要开始讲话了。。。各位员工找个位置坐好。。。 老板——————大家工作辛苦啦。。。我决定给大家发奖金1毛。。。 听到这个消息。。各位员工感到灰常开心。。。于是。。啪啪啪的声音响起。。 会议结束。。。大家各回各的位置继续工作。。。
?
?
?
3、方式二——schema方式
a、业务类
package org.lazyzhong.dao; public class EmployeeSpeak { public void speak(){ System.out.println("员工——老板真抠门"); } }
?b、切面类:切面类中包含所有通知
package org.lazyzhong.util; import org.aspectj.lang.JoinPoint; public class AspectAdvice { public void doBefore(JoinPoint jp) { System.out.println("员工们有话要讲。。。。"); } public void doAfter(JoinPoint jp) { System.out.println("员工们被炒鱿鱼了。。。"); } }
?c、配置
<bean id="employee" class="org.lazyzhong.dao.EmployeeSpeak" /> <!-- 声明通知类 --> <bean id="aspectAdvice" class="org.lazyzhong.util.AspectAdvice" /> <aop:config> <aop:aspect id="TestAspect" ref="aspectAdvice"> <aop:pointcut id="point_cut" expression="execution(* org.lazyzhong.dao.*.*(..))" /> <aop:before pointcut-ref="point_cut" method="doBefore"/> <aop:after pointcut-ref="point_cut" method="doAfter"/> </aop:aspect> </aop:config>
d、测试类
package org.lazyzhong.test; import org.lazyzhong.dao.EmployeeSpeak; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest2 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); EmployeeSpeak employee=(EmployeeSpeak)context.getBean("employee"); employee.speak(); } }
?
e、输出
员工们有话要讲。。。。 员工——老板真抠门 员工们被炒鱿鱼了。。。
?
?
切入点表达式:
?
execution(modifiers-pattern??ret-type-pattern?declaring-type-pattern??name-pattern(param-pattern)?throws-pattern?)?
?
?
?
modifiers-pattern:方法的操作权限
?
ret-type-pattern:返回值
?
declaring-type-pattern:方法所在的包
?
name-pattern:方法名
?
parm-pattern:参数名
?
throws-pattern:异常
其中,除ret-type-pattern和name-pattern之外,其他都是可选的。
?
4、注解方式——
?
?