Spring面向切面
面向切面编程:
即定义一个通用的功能,但是可以通过声明的方式定义这个功能要以何种方式在何处应用,而无需修改受影响的类。
相关术语:
通知(Advice):定义切面是做什么以及什么时候执行,Spring定义了5中类型的通知。
前置通知:在目标方法被调用前执行通知的功能
后置通知:在目标方法完成之后,调用通知
返回通知:在目标方法成功执行之后调用通知
异常通知:在目标方法抛出异常后调用通知
环绕通知:在目标方法被执行之前,之后,执行自定义的通知。
连接点(Join point):即在何处应用通知,即在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,修改一个字段的时候。
切点(Point Cut):何处,匹配通知所要织入的一个或者多个连接点。
切面(Aspect):通知和切点的结合,定义一个在何时,何地,执行什么功能。
引入(Introduction):允许我们向现有的类添加新的方法或属性。
织入(Weaving):把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
Spring对AOP的支持:
不是所有的AOP框架是相同的,Spring提供了4种类型的AOP支持:
⑴基于代理的经典Spring AOP
⑵纯POJO切面
⑶@AspectJ注解驱动的切面
⑷注入式AspectJ切面
Spring提供了更方便的面向切面编程方式,即引入了简单的声明式AOP和基于注解的AOP。
Spring通知使用Java编写,可以使用注解或者Spring配置文件采用XML来编写。
Spring在运行时通知对象:在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中,代理类封装目标类,并拦截被通知方法的调用,把调用转发给真正的目标bean,当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。
Spring只支持方法级别的连接点:因为Spring基于动态代理,所以Spring只支持方法连接点。
切点的编写:
execution(* com.dong.test.method(..))
在方法执行时触发, *表示返回任意类型,com.dong.test方法所属的类,method具体方法,(..)使用任意参数。
使用withIn()指示器来限制匹配
execution(* com.dong.test.method(..)) && within(com.dong.test2.*)),也可以使用||,!来进行操作,因为&在xml中有特殊的含义,因此可以使用and代替&&,or代替||,not代替!
在切点中选择bean
execution( * com.dong.test.method(..) ) and bean('beanId‘)
1.使用注解创建切面:
AspectJ5引入使用注解来创建切面。
@Aspect 定义切面
@Before 前置通知
@After后置通知
@Around 环绕通知
@AfterReturning 返回通知
@AfterThrowing 异常通知
@Pointcut 定义切点
@EnableAspectJAutoProxy 启动自动代理功能,否则被注解的类不会被当做一个 切面,注解不会解析,
处理通知中的参数:
execution ( * com.dong.test.method(int)) && args(argsname)
返回任意类型,方法所属的类型,方法,接受int类型的参数,指定参数,将参数传递给目标方法。
具体案例:
package AnnotationTypeBean;
import org.springframework.stereotype.Component;
@Component
public class EatFood {
public void eatFoods() {
System.out.println("要吃饭啦.......");
}
}
package AnnotationTypeBean;
import org.aspectj.lang.annotation.After;
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
public class Prepared {
@Pointcut("execution(** AnnotationTypeBean.EatFood.eatFoods(..) )")
public void func() {}
@Before("func()")
public void preparedEatFood() {
System.out.println("先洗手,在吃饭");
}
@After("func()")
public void finishEat() {
System.out.println("吃完饭收拾桌子");
}
}
package AnnotationTypeBean;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//创建环绕通知
@Aspect
@Component
public class Prepared2 {
@Pointcut("execution(** AnnotationTypeBean.EatFood.eatFoods(..) )")
public void func() {}
@Around("func()")
public void test(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("先洗手,在吃饭...");
pjp.proceed();
System.out.println("吃完饭收拾桌子.....");
} catch (Exception e) {
// TODO: handle exception
System.out.println("不可以吃饭....");
}
}
}
package AnnotationTypeBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 运行结果:
* 先洗手,在吃饭
* 先洗手,在吃饭...
* 要吃饭啦.......
* 吃完饭收拾桌子.....
* 吃完饭收拾桌子
* @author liumin
*/
@Configuration
@ComponentScan("AnnotationTypeBean")
@EnableAspectJAutoProxy
public class Start {
public static void main(String args[]) {
ApplicationContext context =new AnnotationConfigApplicationContext(AnnotationTypeBean.Start.class);
EatFood eat = (EatFood) context.getBean("eatFood");
eat.eatFoods();
}
}
2.在XML中声明切面:
Spring的aop命名空间,提供了多个元素用来在XML中声明切面
<aop:advisor> 定义AOP通知器
<aop:after> 定义后置通知
<aop:before>定义前置通知
<aop:around>定义环绕通知
<aop:after-returning>定义返回通知
<aop:after-throwing>定义异常通知
<aop:aspect>定义一个切面
<aop:pointcut>定义一个切点
具体代码:xx.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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="eatfood" class="com.dong.XmlTypeAop.EatFood"></bean>
<bean id="prepared" class="com.dong.XmlTypeAop.Prepared"></bean>
<bean id="prepared2" class="com.dong.XmlTypeAop.Prepared2"></bean>
<aop:config>
<aop:aspect ref="prepared2">
<aop:pointcut id="eat" expression="execution (** com.dong.XmlTypeAop.EatFood.eatFoods(..) )" />
<aop:around pointcut-ref="eat" method="test"/>
</aop:aspect>
</aop:config>
</beans>
<!--
后置通知
<aop:after pointcut ="execution (** com.dong.XmlTypeAop.EatFood.eatFoods(..))" method="preparedEatFood"/>
!-->
代码:和上一个例子相似,去掉注解
package com.dong.XmlTypeAop;
public class EatFood {
public void eatFoods() {
System.out.println("要吃饭啦.......");
}
}
package com.dong.XmlTypeAop;
public class Prepared {
public void preparedEatFood() {
System.out.println("先洗手,在吃饭");
}
public void finishEat() {
System.out.println("吃完饭收拾桌子.....");
}
}
package com.dong.XmlTypeAop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
public class Prepared2 {
public void test(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("先洗手,在吃饭...");
pjp.proceed();
System.out.println("吃完饭收拾桌子.....");
} catch (Exception e) {
// TODO: handle exception
System.out.println("不可以吃饭....");
}
}
}
package com.dong.XmlTypeAop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 运行结果:
* 先洗手,在吃饭...
* 要吃饭啦.......
* 吃完饭收拾桌子.....
* @author liuD
*
*/
public class Start {
public static void main(String args[]) {
ApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
EatFood eatfood = (EatFood) context.getBean("eatfood");
eatfood.eatFoods();
}
}
部分内容参考《Spring实战》(第四版)