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实战》(第四版)