前文了解了代理的概念和spring的cglib代理模式,这里要注意的是JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
对于这些概念,这篇文章中帮助我很多http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
了解了代理的概念,那么AOP的学习也就水到渠成了。
首先搭建环境,引入spring-framework-2.5\lib\aspectj下的两个jar包
1、当spring容器启动的时候,加载了spring的配置文件
2、为配置文件中所有的bean创建对象
3、spring容器会解析aop:config的配置
1、解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,不会创建代理对象
4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果代理对象,则返回目标对象
说明:如果目标类没有实现接口,则spring容器会采用cglib的方式产生代理对象,如果实现了接口,会采用jdk的方式
结合hibernate框架,用spring aop 在实现一个事务提交例子,最重要的就是xml文件配置
<?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-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="MyTransaction" class="cn.zjy.SpringAop.MyTransaction"></bean> <bean id="PersonDaoImpl" class="cn.zjy.SpringAop.PersonDaoImpl"></bean> <aop:config> <aop:pointcut id="personimppoint" expression="execution(* cn.zjy.SpringAop.PersonDaoImpl.*(..))"/> <aop:aspect id="myAspect" ref="MyTransaction"> <aop:before method="beginTransaction" pointcut-ref="personimppoint"/> <aop:after-returning method="commit" pointcut-ref="personimppoint"/> </aop:aspect> </aop:config> </beans>从配置中我们配置了一个切入点和切面,切入点是目标类的方法,切面中包含了通知
目标类
package cn.zjy.SpringAop; import java.io.Serializable; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class PersonDaoImpl implements PersonDao{ public void deletePerson(Serializable id) { Person person = (Person)SessionFactoryUtils.sessionfactory.getCurrentSession().get(Person.class, 2L); SessionFactoryUtils.sessionfactory.getCurrentSession().delete(person); } public String savePerson(Person person) { SessionFactoryUtils.sessionfactory.getCurrentSession().save(person); return "success"; } public void updatePerson(Person person) { SessionFactoryUtils.sessionfactory.getCurrentSession().update(person); } }
package cn.zjy.SpringAop; import org.aspectj.lang.JoinPoint; import org.hibernate.Transaction; public class MyTransaction { private Transaction transaction; public void beginTransaction(JoinPoint jp){ System.out.println(jp.getSignature().getName());//选择没有process方法的 this.transaction = SessionFactoryUtils.sessionfactory.getCurrentSession().beginTransaction(); } public void commit(){ this.transaction.commit(); } }
package cn.zjy.SpringAop; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringAoptest { @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("cn/zjy/SpringAop/applicationContext.xml"); PersonDao persondao = (PersonDao)context.getBean("PersonDaoImpl"); Person person = new Person(); person.setPname("haha"); person.setPsex("nan"); persondao.savePerson(person); } }
执行结果
通知:
1、前置通知
1、在目标方法执行之前执行
2、无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常
2、后置通知
1、在目标方法执行之后执行
2、当目标方法遇到异常,后置通知将不再执行
3、后置通知可以接受目标方法的返回值,但是必须注意:
后置通知的参数的名称和配置文件中returning="var"的值是一致的
3、最终通知:
1、在目标方法执行之后执行
2、无论目标方法是否抛出异常,都执行,因为相当于finally
4、异常通知
1、接受目标方法抛出的异常信息
2、步骤
在异常通知方法中有一个参数Throwable ex
在配置文件中
<aop:after-throwing method="throwingMethod"pointcut-ref="perform" throwing="ex"/>
5、环绕通知
1、如果不在环绕通知中调用ProceedingJoinPoint的proceed,目标方法不会执行
2、环绕通知可以控制目标方法的执行
通过对通知声明的调用,我们可以实现切面的通知与目标类的方法结合的代理类。