开始SpringAOP前先解释一些术语:
Jointpoint:在Spring中指Method
Pointcut:一组Jointponit的集合,可以通过Spring中的表达式配置
Advice:在Jointpoint和Pointcut上的执行的动作,比如before advice、after advice、around advice
Aspect:Jointpont+Advice的集合,实行业务拦截的那个切面组件
Introduction:特殊的Advice可以对拦截类添加Field、Method
Weaving:将Advice应用到Jointcut或Pointcut的过程
Target:目标对象,要拦截的对象
Proxy:通过对Target拦截事实Weaving后产生的Proxy
案例一:对接口实现AOP,使用before advice、after finaly advide
创建接口、实现类作为Target
public interface IAOPService {
public void hiAOP();
}
public class AOPServiceImpl implements IAOPService {
public void hiAOP() {
System.out.println("AOPServiceImpl hiAOP()");
}
}
创建一个Advice类,用于对Target进行增强的操作
public class AOPAdvice {
public void beforeAdvide() {
System.out.println("AOPAdvice beforeAdvide()");
}
public void afterFinalyAdvide() {
System.out.println("AOPAdvice afterAdvide()");
}
}
对pointcut和advice进行配置,简单理解就是weaving的过程
<!-- Target --> <bean id="aopServiceImpl" class="aop.service.impl.AOPServiceImpl"></bean> <!-- AOP Advice --> <bean id="aopAdvice" class="aop.advice.AOPAdvice"></bean> <aop:config> <!-- 配置pointcut,可以被其它aspect重用 --> <aop:pointcut id="pointcuts" expression="execution(* aop.service.*.*(..))" /> <!-- 配置Asepct,可以定义多种advice和多个pointcut --> <aop:aspect ref="aopAdvice"> <aop:before method="beforeAdvide" pointcut-ref="pointcuts" /> <aop:after method="afterFinalyAdvide" pointcut-ref="pointcuts" /> </aop:aspect> </aop:config>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext-aop.xml")
public class SpringAOPTest {
@Autowired
private IAOPService aopService;
@Test
public void testAdvice() {
aopService.hiAOP();
}
}
/**
AOPAdvice beforeAdvide()
AOPServiceImpl hiAOP()
AOPAdvice afterAdvide()
*/
案例二:匿名切入点
配置切入点还有一种方式成为匿名切入点,该切入点只能被afterFinalyAdvice所使用
<aop:aspect ref="aopAdvice"> <aop:after method="afterFinalyAdvide" pointcut="execution(* aop.service.*.*(..))"/> </aop:aspect> pointcut 和pointcut-ref:二者选一,指定切入点; method:指定前置通知实现方法名,如果是多态需要加上参数类型,多个用“,”隔开,如beforeAdvice(java.lang.String); arg-names:指定通知实现方法的参数名字,多个用“,”分隔,可选,在class 文件中没生成变量调试信息是获取不到方法参数名字的, 因此只有在类没生成变量调试信息时才需要使用arg-names 属性来指定参数名,如arg-names="param"表示通知实现方法的参数列表的第一个参数名字为“param”。
案例三:advice的配置
before advice:前置声明通知的完整配置,pointcut和pointcut-ref二选一
<aop:before pointcut="切入点表达式" pointcut-ref="切入点Bean引用" method="前置通知实现方法名" arg-names="前置通知实现方法参数列表参数名字"/>
after returning:后置返回通知
<aop:after-returning pointcut="切入点表达式" pointcut-ref="切入点Bean引用" method="后置返回通知实现方法名" arg-names="后置返回通知实现方法参数列表参数名字" returning="返回值对应的后置返回通知实现方法参数名" /> pointcut 和pointcut-ref:同前置通知同义; method:同前置通知同义; arg-names:同前置通知同义; returning:定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法执行正常返回后,将把目标方法返回值传给通知方法;returning 限定了只有目标方法返回值匹配与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning 对应的通知方法参数为Object 类型将匹配任何目标返回值。
after throwing:在切入点方法抛出异常时执行
<aop:after-throwing pointcut="切入点表达式" pointcut-ref="切入点Bean引用" method="后置异常通知实现方法名" arg-names="后置异常通知实现方法参数列表参数名字" throwing="将抛出的异常赋值给的通知实现方法参数名"/>around advice:环绕通知
<aop:around pointcut="切入点表达式" pointcut-ref="切入点Bean引用"
method="后置最终通知实现方法名" arg-names="后置最终通知实现方法参数列表参数名字"/>
pointcut 和pointcut-ref:同前置通知同义;
method:同前置通知同义;
arg-names:同前置通知同义;
案例四:后置返回通知
接口及实现类
public interface IAOPService {
public boolean returnTrue();
}
public class AOPServiceImpl implements IAOPService {
public boolean returnTrue() {
System.out.println("AOPServiceImpl returnTrue()");
return true;
}
}
通知类中的方法要定义参数,用于接收Target执行完成后的返回值
public class AOPAdvice {
public void afterRetuning(Object value){
System.out.println("AOPAdvice afterRetuning +"+value);
}
}
配置
<bean id="aopServiceImpl" class="aop.service.impl.AOPServiceImpl"></bean> <bean id="aopAdvice" class="aop.advice.AOPAdvice"></bean> <aop:config> <aop:pointcut id="pointcuts" expression="execution(* aop.service.*.*(..))" /> <aop:aspect ref="aopAdvice"> <aop:after-returning method="afterRetuning" pointcut-ref="pointcuts" arg-names="value" returning="value"/> </aop:aspect> </aop:config>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext-aop.xml")
public class SpringAOPTest {
@Autowired
private IAOPService aopService;
@Test
public void testAdvice() {
aopService.returnTrue();
}
}
/**output:
AOPServiceImpl returnTrue()
AOPAdvice afterRetuning +true
*/
案例五:后置异常通知
创建接口及实现
public class AOPAdvice {
public void afterThrowing(Exception exception) {
System.out.println("AOPAdvice afterThrowing +" + exception);
}
}
public class AOPServiceImpl implements IAOPService {
public void throwExcetpion() {
System.out.println("AOPServiceImpl throwExcetpion()");
throw new RuntimeException("throwExcetpion");
}
}
定义advice,当Target执行有异常后advice接收的参数
public class AOPAdvice {
public void afterThrowing(Exception exception) {
System.out.println("AOPAdvice afterThrowing +" + exception);
}
}
配置
<!-- Target --> <bean id="aopServiceImpl" class="aop.service.impl.AOPServiceImpl"></bean> <!-- AOP Advice --> <bean id="aopAdvice" class="aop.advice.AOPAdvice"></bean> <aop:config> <!-- 配置pointcut,可以被其它aspect重用 --> <aop:pointcut id="pointcuts" expression="execution(* aop.service.*.*(..))" /> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcuts" arg-names="exception" throwing="exception" /> </aop:aspect> </aop:config>测试
@Test
public void testThrowExcetpion() {
aopService.throwExcetpion();
}
/**
AOPServiceImpl throwExcetpion()
AOPAdvice afterThrowing +java.lang.RuntimeException: throwExcetpion
*/
案例六:环绕通知
定义接口和实现
public interface IAOPService {
public void around();
}
定义advice
public class AOPAdvice {
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOPAdvice aroundAdvice before()");
pjp.proceed();
System.out.println("AOPAdvice aroundAdvice after()");
}
}
配置
<!-- Target -->
<bean id="aopServiceImpl" class="aop.service.impl.AOPServiceImpl"></bean>
<!-- AOP Advice -->
<bean id="aopAdvice" class="aop.advice.AOPAdvice"></bean>
<aop:config>
<aop:around method="aroundAdvice" pointcut-ref="pointcuts" />
</aop:aspect>
</aop:config>
测试
@Test
public void testAround() {
aopService.around();
}
/**
AOPAdvice aroundAdvice before()
AOPServiceImpl around()
AOPAdvice aroundAdvice after()
*/
JDK动态代理
接口
public interface IService {
public void method();
}
实现类
public class ServiceImpl implements IService {
@Override
public void method() {
System.out.println("ServiceImpl--->method()");
}
}
proxy
public class JDKProxy implements InvocationHandler {
private Object target;
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().toString());
System.out.println("before...");
Object invoke = method.invoke(target, args);
System.out.println("after...");
return invoke;
}
}
测试
@Test
public void demo01() {
IService service = new ServiceImpl();
JDKProxy proxy = new JDKProxy();
IService serviceProxy = (IService) proxy.getProxy(service);
serviceProxy.method();
}
/**
class $Proxy4
before...
ServiceImpl--->method()
after...
*/
JDK的这种动态代理必须基于接口,代理后的对象也必须转换为接口才能操作
CGLIB代理
接口,同上
实现类,同上
proxy
public class CGLIBProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
//这只代理目标类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(proxy.getClass().toString());
System.out.println("before...");
Object invoke = methodProxy.invokeSuper(proxy, args);
System.out.println("after...");
return invoke;
}
}
测试
@Test
public void demo2() {
IService service = new ServiceImpl();
CGLIBProxy proxy = new CGLIBProxy();
ServiceImpl serviceProxy = (ServiceImpl) proxy.getProxy(service.getClass());
serviceProxy.method();
}
/**
class demo01.ServiceImpl$$EnhancerByCGLIB$$6cb2203b
before...
ServiceImpl--->method()
after...
*/
Spring AOP 底层,会判断用户是根据接口代理还是目标类代理,如果针对接口代理 使用JDK代理,如果针对目标类代理 使用Cglib代理
JDK动态代理是针对目标接口生成一个实现类该类为代理对象,目标对象和代理对象没有任何关系
CGLIB根据目标对象生成一个继承目标对象的子类,代理对象和目标对象是继承关系