在spring中,除了IOC(控制反转)之外,还有一个重要特性就是面向切面编程。
AOP=面向切面的编程=Aspect Oriented Programming
实现基本原理关键词:动态代理拓展程序功能、申明式事务管理
1.Spring AOP 的一些术语:
(1)切面(Aspect)
要增加什么功能?例如事务管理,日志管理,权限管理,异常处理
(2)连接点(Joinpoint)
哪个类的哪个方法上增加功能
(3) 通知(Advice)
加在方法的什么位置?前面?后面?产生异常时?环绕(前后都添加)??
(4)切入点(Pointcut)
给连接点命名,便于在多处使用
(5)目标对象(Target Object)
被代理对象,自己的功能不够强大,要通过aop加强
(6)AOP代理
功能已经加强的那个对象
(7)织入(weaving)
把新功能和本身的功能组织在一起执行
2.注解配置AOP的实现
这里还是结合一个注册案例来解释AOP基本注解配置。
功能描述:在点击注册之后,将用户名和密码存入数据库中。现在想通过AOP实现在这个add方法调用之前控制台输出“方法开始”,在方法调用结束后输出“方法结束”。这里不在DAO实现类中添加这个功能,而是注解配置AOP来实现
(1)要想添加日志功能先把日志功能在类中写出来。新建一个包 net.xinqushi.aop ,新建一个Log类
类中写两个方法,表示方法调用之前输出日志,调用之后输出日志
把这个类变成一个切面类,表示这个类是添加到别的地方作为一个切面 注解:@Aspect
这个类也要初始化为一个对象 注解:@Component(value=”log”) 这里要在配置文件中把这个包加入到初始化为对象时搜索到包中。
<context:component-scan base-package=”net.xinqushi.dao.impl,net.xinqushi.service.impl,net.xinqushi.aop”/>
(2)现在考虑打印日志的功能应该加在哪个类的哪个方法上?是加在方法执行之前,还是之后?
在之前,这里就在方法之前添加注解
@Before(“execution(public void net.xinqushi.service.impl.save(net.xinqushi.model.User))”)
这里注意@Before一定要选 org.aspectj.lang.annotation.Aspect包下的
在之后,这里就结束方法之前添加注解
@AfterRunning(“execution(public void net.xinqushi.service.impl.save(net.xinqushi.model.User))”)
(3)修改spring的配置文件 在applicationContex.xml文件中添加配置
添加命名空间 xmlns:aop=”http://www.springframework.org/schema/aop”
添加xsd xml格式约束文件:
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
使配置文件支持aop注解
<aop:aspectj-autoproxy/>
3.这里再介绍一下优化注解配置的通配符法
(1)joinpoint语法:用一些通配符来定义在哪些类和方法上增加aop功能。
语法:
1).execution(public * *(..))
所有public方法
2).execution(* set*(..))
所有以set作为方法名开头的方法
3).exuection(* com.xzy.service.AccountService.*(..))
com.xzy.service.AccountService类中的所有方法
4).execution(* com.xyz.service.*.*(..))
com.xyz.service包中所有类的所有方法
5).execution(* com.xyz.service..*.*(..))
com.xyz.service包及子包下的所有类的所有方法
(2)pointcut:给joinpoint取个名字。 使程序更加整洁
@pointcut(value=”execution( * com.xzy.*.*(..))”)
public void businessService(){}
(3)advice
@Before
@AfterReturning
@AfterThrowing
@After(finally)
@Around
(4)JoinPoint jp 可以为打印日志的before方法和end方法传进去一个参数
这个参数就可以get到很多关于连接点的信息。这里可以试着获得方法签名
这里导入的也是import org.aspectj.lang.JoinPoint;
Log.java
package net.xinqushi.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component(value="log")
public class Log {
@Pointcut(value="execution(* net.xinqushi.service.impl.*.*(..))")
public void myPoint(){}
@Before("myPoint()")
//方法执行之前输出
public void mybefore(JoinPoint jp){
System.out.println(jp.getSignature() +"方法开始执行");
}
@AfterReturning("myPoint()")
//方法执行完后输出
public void myafter(JoinPoint jp){
System.out.println(jp.getSignature()+"方法执行结束");
}
}
5.AOP xml配置方式
(1)先把Log.java中的注解配置注释掉,这里保留@Component(value=”log”)。因为xml方式也是要将这个类初始化为一个对象,当然这里也可以采用xml方式去初始化这个类。(前边博客有讲)
(2)在配置文件中添加aop标签的配置
这里之前在注解配置中引入的命名空间和xsd约束文件都是要保留的
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="net.xinqushi.dao.impl,net.xinqushi.service.impl,net.xinqushi.aop"/>
<aop:config>
<aop:aspect ref="log">
<aop:pointcut
expression="execution(* net.xinqushi.service.impl.*.*(..))" id="mypt"/>
<aop:before method="mybefore" pointcut-ref="mypt"/>
<aop:after method="myafter" pointcut-ref="mypt"/>
</aop:aspect>
</aop:config>
<!-- 注解配置AOP才会用到的 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
其中这段注解大概的意思就是先把log设置为一个切面,然后设置切入点,再根据Log类中的两个打印日志的方法设置其pointcut-ref属性
<aop:config>
<aop:aspect ref="log">
<aop:pointcut
expression="execution(* net.xinqushi.service.impl.*.*(..))" id="mypt"/>
<aop:before method="mybefore" pointcut-ref="mypt"/>
<aop:after method="myafter" pointcut-ref="mypt"/>
</aop:aspect>
</aop:config>