1、Spring只支持方法通知,也就是说,只能在方法的前后进行通知,而不能在属性前后进行通知。
2、Spring支持四种通知类型:目标方法调用前(before),目标方法调用后(after),目标方法调用前后(around),以及目标方法抛出异常(throw)。
3、前置通知的类必须实现MethodBeforeAdvice接口,实现其中的before方法。
4、后置通知的类必须实现AfterReturningAdvice接口,实现其中的afterReturning方法。
5、环绕通知的类必须实现MethodInterceptor接口,实现其中的invoke方法。前后通知是唯一可以控制目标方法是否被真正调用的拦截类型,也可以控制返回对象。而前置拦截或后置拦截不能控制,它们不能印象目标方法的调用和返回。
6、异常通知 要实现 ThrowsAdvice ,ThrowAdvice 接口只是一个标示接口,它没有任何的方法,但是在使用时我们需要实现afterThrowing 方法来实现通知的具体内容。
ProBeginAdvice.java
package com.lgh.spring.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class ProBeginAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置通知:开启事务");
}
}
ProAfterAdvice
package com.lgh.spring.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class ProAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("后置通知:提交事务");
}
}
ExecpetionAdvice.java
package com.lgh.spring.advice;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class ExecpetionAdvice implements ThrowsAdvice {
public void afterThrowing(Method m,Object[] os,Object target,Exception throwable)
{
System.out.println("异常通知: 程序出问题了:" + throwable.getMessage());
}
}
AroundInterceptor
package com.lgh.spring.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/*
* 环绕通知
* */
public class AroundInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕通知被调用:调用方法前执行 ");
Object obj = arg0.proceed();
System.out.println("环绕通知被调用:调用方法后执行 ");
return obj;
}
}
被代理的类
ProductDaoImpl
package com.lgh.spring.dao.impl;
public class ProductDaoImpl {
public void save(){
System.out.println("保存商品");
}
public void del(){
System.out.println("删除商品");
//int i = 9/0;
}
}
在applicationContext.xml中配置
<bean id="productDao" class="com.lgh.spring.dao.impl.ProductDaoImpl" lazy-init="true">bean>
<bean id="proBegin" class="com.lgh.spring.advice.ProBeginAdvice" lazy-init="true">bean>
<bean id="proAfter" class="com.lgh.spring.advice.ProAfterAdvice" lazy-init="true">bean>
<bean id="proThrow" class="com.lgh.spring.advice.ExecpetionAdvice">bean>
<bean id="proAround" class="com.lgh.spring.advice.AroundInterceptor">bean>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean" >
<property name="target" ref="productDao">property>
<property name="interceptorNames">
<list>
<value>proBeginvalue>
<value>proAftervalue>
<value>proThrowvalue>
<value>proAroundvalue>
list>
property>
SpringTest02
package com.lgh.spring.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.lgh.spring.biz.UserBiz;
import com.lgh.spring.dao.impl.ProductDaoImpl;
public class SpringTest02 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
/*
* 通过代理获取ProductDaoImpl
* 因为ProductDaoImpl本身并没有与通知类有联系
*
* */
ProductDaoImpl productDao = (ProductDaoImpl) ac.getBean("proxy");
System.out.println(productDao.getClass());
productDao.save();
System.out.println("------------------");
productDao.del();
}
}
结果:
class com.lgh.spring.dao.impl.ProductDaoImpl$$EnhancerBySpringCGLIB$$e5e8be14
前置通知:开启事务
环绕通知被调用:调用方法前执行
保存商品
环绕通知被调用:调用方法后执行
后置通知:提交事务
前置通知:开启事务
环绕通知被调用:调用方法前执行
删除商品
环绕通知被调用:调用方法后执行
后置通知:提交事务
我们在ProductDaoImpl的del方法中添加一个除零异常
public void del(){
System.out.println("删除商品");
int i = 9/0;
}
运行结果:
class com.lgh.spring.dao.impl.ProductDaoImpl$$EnhancerBySpringCGLIB$$e5e8be14
前置通知:开启事务
环绕通知被调用:调用方法前执行
保存商品
环绕通知被调用:调用方法后执行
后置通知:提交事务
------------------
前置通知:开启事务
环绕通知被调用:调用方法前执行
删除商品
异常通知: 程序出问题了:/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.lgh.spring.dao.impl.ProductDaoImpl.del(ProductDaoImpl.java:12)
at com.lgh.spring.dao.impl.ProductDaoImpl$$FastClassBySpringCGLIB$$c7e39ca2.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...
我们看到配置的异常通知起作用了
这时,我们所配置的代理只对一个对象起作用,如果有多个对象时,使用起来比较麻烦,
我们在这里可以使用自动代理bean 定义
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<value>pro*,userDaovalue>
property>
<property name="interceptorNames">
<list>
<value>proBeginvalue>
<value>proAftervalue>
<value>proThrowvalue>
<value>proAroundvalue>
list>
property>
bean>
<bean id="userDao" class="com.lgh.spring.dao.impl.UserDaoMysqlImpl" lazy-init="true" >
bean>
<bean id="userBiz" class="com.lgh.spring.biz.impl.UserBizImpl" lazy-init="true">
<property name="userDao" ref="userDao">property>
bean>
测试
package com.lgh.spring.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.lgh.spring.biz.UserBiz;
import com.lgh.spring.dao.impl.ProductDaoImpl;
public class SpringTest03 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductDaoImpl productDao = (ProductDaoImpl) ac.getBean("productDao");
productDao.save();
System.out.println("----------------------------------");
UserBiz ub = (UserBiz) ac.getBean("userBiz");
ub.save();
System.out.println("----------------------------------");
productDao.del();
}
}
结果:
前置通知:开启事务
环绕通知被调用:调用方法前执行
保存商品
环绕通知被调用:调用方法后执行
后置通知:提交事务
----------------------------------
UserDaoMysqlImpl 构造方法
前置通知:开启事务
环绕通知被调用:调用方法前执行
UserDaoMysqlImpl save方法
环绕通知被调用:调用方法后执行
后置通知:提交事务
----------------------------------
前置通知:开启事务
环绕通知被调用:调用方法前执行
删除商品
异常通知: 程序出问题了:/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.lgh.spring.dao.impl.ProductDaoImpl.del(ProductDaoImpl.java:12)
at com.lgh.spring.dao.impl.ProductDaoImpl$$FastClassBySpringCGLIB$$c7e39ca2.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)