前面说到了使用注解的方式把目标类和切面放到spring容器中,那么我们现在使用注解的形式把aop配置到spring容器中。
为了突出区别,我们把前面“springAOP的入门例子“拿出来重构。关于具体的需求以及需求分析,可以查看前面的文章。
spring配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--
在这里的scheme约束比例子中多了几行,因为这里要用到类的扫描,也要通过注解的形式把目标类和切面放到spring容器中。
-->
<!--
context:component-scan 扫描相应的包,把目标类和切面放到spring容器中。
-->
<context:component-scan base-package="cn.ansel.spring.aop.annotation.sh"></context:component-scan>
<!--
aop:aspectj-autoproxy 让spring自动产生代理对象,这个标签的作用是aop的注解解析器
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
person类没有改变,修改一下person的映射文件中Person的引用以及hibernate配置文件中person映射文件的引用。
切面:
/**
* Component:代表把这个类放到spring容器中
* @Aspect:相当于spring配置文件中的<aop:config>
*
*
*/
@Component("myTransaction")
@Aspect
public class MyTransaction extends hibernateUitls{
private Transaction transaction;
/**
* @Pointcut:放在了方法中,详情看源代码,spring的规定
* 其中一共有2个属性,一个是value,另外一个是arguments
* 这里我们用到的是value,它代表切入点表达式。
* 切入点表达式的写法跟我们在使用xml形式的写法一样。
* private void aa(){}:
* private:我们把有@Pointcut的方法叫注解方法,这个方法由于不希望被
* 继承的类使用,所以要用private
*
* void :因为这个方法只是做签名方法,所以函数主体中有内容没有什么意义
*
* aa() :相当于配置文件中
* <aop:pointcut expression=
* "execution(* cn.ansel.spring.aop.annotation.PersonDaoImpl.*(..))"
id="perform" />
id的取值。
*/
@Pointcut("execution(* cn.ansel.spring.aop.annotation.sh.PersonDaoImpl.*(..))")
private void aa(){}
//代表前置通知
@Before("aa()")
public void beginTransaction(){
transaction=sessionFactory.getCurrentSession().beginTransaction();
}
/**
* 代表后置通知,returning相当于
* <aop:after-returning
* method="commit"
* returning="val"/>
* 中的returning
*/
@AfterReturning(value="aa()",returning="val")
public void commit(Object val){
System.out.println(val);
transaction.commit();
}
}
//把personDaoImpl(目标类)放到spring容器中,并且表明它属于Mvc的持久化层
@Repository("personDaoImpl")
public class PersonDaoImpl extends hibernateUitls {
//(目标方法),操作hibernate框架来保存对象,并返回一个字符串
public String savePerson(Person person) {
sessionFactory.getCurrentSession().save(person);
return "helloAOP";
}
}
测试类只是把spring配置文件的引用修改了一下。
运行结果:
数据库:
aop注解的运行流程:
1、由于运行的是测试类,那么首先读取spring的配置文件
2、在配置文件里面读取到了类的扫描器和aop的注解解析器
3、从类的扫描中配置的相应包中扫描,看看哪个类上有component及其子类的注解,扫描到就创建它们的对象
4、读取到aop的注解解析器:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
这句话时,spring容器再次扫描类扫描器相应包下的类,看看哪个类上面有@aspect注解,然后再查询@pointcut注解在哪里,并且根据里面的切入点表达式寻找相应的目标类,为目标类创建代理对象,然后再读取其中的所有通知,把目标方法跟通知整合。
5、由客户端调用目标方法,然后执行代理方法(目标方法+通知)
6、关闭spring容器
从运行流程可以看出,spring容器要做一层层的扫描,如果相应包中的类很多的话,扫描会降低效率