前文了解了代理的概念和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包
一、springAOP的具体加载步骤:
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();
}
}
此时Spring的AOP已经配置好了 写一个客户端程序实现
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、环绕通知可以控制目标方法的执行
通过对通知声明的调用,我们可以实现切面的通知与目标类的方法结合的代理类。