一 基本概念
通知(Advice):切面的工作被称为通知。定义了切面是什么以及何时使用。spring切面可以应用5种类型的通知(Before、After、After-retrunning,After-throwing, Around)。
连接点(JointPoint):是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
切点(Pointcut):切点定义了切面在何处。切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类和方法名称指定这些切点,或者利用正则表达式定义匹配的类和方法名称模式来指定这些切点。
切面(Aspect): 是通知和切点的结合,通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。
引入(introduction):允许我们向现有的类添加新方法或属性。
织入(Weaving): 将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以织入。(编译器、类加载期、运行期)
二 基于xml配置的使用
目标类:
public interface IHelloWorldService {
public void sayHello();
}
public class HelloWorldService implements IHelloWorldService {
@Override
public void sayHello() {
System.out.println("say hello");
}
}
切面类:
public class HelloWorldAspect {
//前置通知
public void beforAdvice(){
System.out.println("====before=====");
}
//后置通过
public void afterFinallyAdvice(){
System.out.println("====after====");
}
}
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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id = "helloWorldService" class="aopTest.HelloWorldService"/>
<bean id="aspect" class="aopTest.HelloWorldAspect"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* aopTest..*.*(..))"/>
<aop:aspect ref="aspect">
<aop:before pointcut-ref="pointcut" method="beforAdvice"/>
<aop:after pointcut-ref="pointcut" method="afterFinallyAdvice"/>
</aop:aspect>
</aop:config>
</beans>
<aop:config>用户配置aop
<aop:pointcut> 定义切点
<aop:aspect> 定义切面,其中ref用来引用切面支持类的bean。
<aop:before>前置通知 pointcut-ref 切点,引用在前面定义的切点,也可以直接使用pointcut定义一个匿名切点。mehthod 引用切换通知实现类中的方法,即在目标类方法执行之前调用的方法。
<aop:after>后置通知
测试类:
public class AopTest {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop/spring-aop.xml");
IHelloWorldService helloWorldService = applicationContext.getBean(IHelloWorldService.class);
helloWorldService.sayHello();
}
}
输出结果:
====before=====
say hello
====after====
调试时,也可以看到helloWorldService类被spring代理了。
如果是有入参的方法,可以使用如下方式定义:
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--<aop:aspectj-autoproxy/>-->
<bean id = "helloWorldService" class="aopTest.HelloWorldService"/>
<bean id="aspect" class="aopTest.HelloWorldAspect"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* aopTest..*.*(..)) and args(param)"/>
<aop:aspect ref="aspect">
<aop:before pointcut-ref="pointcut" method="beforAdvice" arg-names="param"/>
<aop:after pointcut-ref="pointcut" method="afterFinallyAdvice" arg-names="param"/>
</aop:aspect>
</aop:config>
</beans>
其中需要注意的是
(1) pointCut中的expression中必须包含 args(param),否则会在运行时报错:0 formal unbound in pointcut
args是在用于匹配当前执行的方法传入的参数为指定类型的执行方法,是在运行时切入的,属于动态切入。
(2) 如果pointCut中包含 args(param), 则before和after通知的方法也必须包含该参数。
(3) before和after通知中arg-names可以省略不写。
(4) 可以通过order字段配置通知的优先级。order字段值越大,优先级越高。
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* aopTest..*.*(..)) and args(param)"/>
<aop:aspect ref="aspect" order="2">
......
</aop:aspect>
</aop:config>
三 基于注解的使用
Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置:
<aop:aspectj-autoproxy/>
切面类:
@Aspect
public class HelloWorldAspect2 {
@Pointcut(value = "execution(* aopTest..*.*(..))")
public void pointTest(){};
@Before(value = "pointTest()")
public void before(){
System.out.println("====before===");
}
}
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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy/>
<bean id = "helloWorldService" class="aopTest.HelloWorldService"/>
<bean id = "aspect2" class="aopTest.HelloWorldAspect2"/>
</beans>
测试类:
public class AopTest {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop/spring-aop.xml");
IHelloWorldService helloWorldService = applicationContext.getBean(IHelloWorldService.class);
helloWorldService.sayHello();
}
}
如果是有入参的方法,可以使用如下方式定义:
@Aspect
public class HelloWorldAspect2 {
@Pointcut(value = "execution(* aopTest..*.*(..)) && args(arg)")
public void pointTest(String arg){};
@Before(value = "pointTest(arg)")
public void before(String arg){
System.out.println("====before === + " + arg);
}
}
可以通过order字段制定通知的优先级:
@Aspect
@Order(1)
public class HelloWorldAspect2 {
.....
}
四 其他
aspectJ语法:http://www.importnew.com/17828.html
AspectJ类型匹配的通配符:
* : 匹配任何数量字符
.. : (两个点)匹配任何数量字符的重复。如在类型模式中匹配任何数量子包,而在方法参数模式中匹配任何数量参数
+:匹配执行类型的子类型,仅能作为后缀放在类型模式后边。
java.*.String: 匹配Java包下任何的一级子包的下的String类型,匹配java.lang.String,但不匹配java.lang.ss.String
java..* 匹配java包以及任何子包下的任何类型。匹配java.lang.String,java.lang.ss.String
采用JoinPoint传递参数:
使用方法非常简单,直接在通知方法中增加一个入参,需要注意的是,该入参需要放在第一个位置。
public void beforAdvice(JoinPoint joinPoint,String param){
System.out.println("====beforeAdvice=====" + param);
}
其中JoinPoint有几个属性,getTarget()——获取目标对象
getThis()——获取代理对象,getArgs()——获取被通知对象的入参列表
Spring AOP通过代理模式实现,目前支持两种代理:JDK动态代理、CGLIB代理来创建AOP代理,Spring建议优先使用JDK动态代理.
要采用cglib代码:
对于xml配置的切面:
<aop:config proxy-target-class="true">
</aop:config>
对于基于@Aspect注解的切面:
<aop:aspectj-autoproxy proxy-target-class="true"/>
参考资料: http://www.importnew.com/17828.html
http://www.importnew.com/17813.html
http://www.importnew.com/17755.html