Spring 温习笔记(四)spring AOP
AOP 相关概念
Spring 的AOP (Aspect Oriented Programming)实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
- Target(目标对象):代理的目标对象
- Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
- Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
基于XML的AOP开发
切面表达式和及其抽取
切点表达式的写法
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号* 代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
例如:execution(public void com.feitian.aop.Target.method())
execution(void com.feitian.aop.Target.(..))
execution( com.feitian.aop..(..))
execution( com.feitian.aop...(..))
execution( ...*(..))
通知的类型
通知织入的写法:
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。
<aop:config>
<!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">
<aop:pointcut id="myPointcut" expression="execution(* com.feitian.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
</aop:aspect>
</aop:config>
图解spring aop
小案例
在导开始小案例之前,需要映入若干jar包;
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
</dependencies>
采用目标类和切面类实现切面编程,增强目标方法
方法一 采用xml配置方式(bean 通过在xml配置文件中注入)
目标类
public class AopTarget implements AopTargetInterface {
public void save() {
System.out.println("AOP target saving .......");
//int i = 1/0;
}
}
目标接口
public interface AopTargetInterface {
public void save();
}
切面类
public class AopAspect {
public void before(){
System.out.println("前值增强......");
}
public void after(){
System.out.println("后置增强......");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("this is before around method....");
Object proceed = pjp.proceed();
System.out.println("this is after around method....");
return proceed;
}
public void afterThrowing(){
System.out.println("exception is throwing");
}
}
配置文件
<?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.xsd">
<!--配置目标对象-->
<bean id="aopTarget" class="com.feitian.spring.aop.AopTarget"/>
<!--配置切面对象-->
<bean id="aopAspect" class="com.feitian.spring.aop.AopAspect"/>
<!--配置织入: 告诉spring框架 那些方法(切点)需要进行那些增强(前置、后置)-->
<aop:config>
<!--申明切面-->
<aop:aspect ref="aopAspect">
<!--抽取切点-->
<aop:pointcut id="myExecution" expression="execution(* com.feitian.spring.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myExecution"/>
<aop:after method="after" pointcut-ref="myExecution"/>
<aop:around method="around" pointcut="execution(public void com.feitian.spring.aop.AopTarget.save())"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="myExecution"/>
</aop:aspect>
</aop:config>
</beans>
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringAop01Demo {
@Autowired
private AopTargetInterface aopTarget;
@Test
public void test1(){
aopTarget.save();
}
}
方法二 采用注解配置方式(bean 直接通过扫描注入)
接口类:
public interface AnnAopTargetInterface {
public void save();
}
目标类 :
@Component("annAopTarget")
public class AnnAopTarget implements AnnAopTargetInterface {
public void save() {
System.out.println("AOP target saving .......");
//int i = 1/0;
}
}
切面类
@Component("myAnnAspect")
@Aspect //标注当前MyAspect是一个切面类
public class AnnAopAspect {
@Before("execution(* com.feitian.spring.anno.*.*(..))")
public void before(){
System.out.println("前值增强......");
}
@After("execution(* com .feitian.spring.anno.*.*(..))")
public void after(){
System.out.println("后置增强......");
}
@Around("execution(* com .feitian.spring.anno.*.*(..)))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("this is before around method....");
Object proceed = pjp.proceed();
System.out.println("this is after around method....");
return proceed;
}
@AfterThrowing("execution(* com .feitian.spring.anno.*.*(..)))")
public void afterThrowing(){
System.out.println("exception is throwing");
}
}
xml配置文件(在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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.feitian.spring.anno"/>
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
</beans>
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_ann.xml")
public class SpringAop02Demo {
@Autowired
private AnnAopTargetInterface annAopTarget;
@Test
public void test1(){
annAopTarget.save();
}
}
测试结果达到基本预期。
在这里小猿抛出一个问题,为什么开启自动注解后各个通知的执行顺序会发生变化呢?若有童鞋知道答案请及时告知小猿,谢谢各位了。
还没有评论,来说两句吧...