spring-AOP

👉 spring AOP 💎

csdn

1  何为 Spring–AOP


AOP(Aspect-oriented-programming),面向切面编程。它与 OOP(面向对象编程)相辅相成。AOP中的基本单元是 Aspect (切面)

术语


Aspect(切面)

aspect 由 point cut 和 advice 组成,它既包含了横切逻辑的定义,也包括了连接点的定义。 AOP的工作重心在于如何将增强织入目标对象的连接点上,这里包含两个工作:
1.如何通过 point cut 和 advice 定位到特定的 join point上。
2.如何在 advice 中编写切面的代码。
可以简单地认为:使用了 @Aspect 注解的类就是切面。

advice(增强)

由 aspect 添加到特定的 join point (即满足 point cut 规则的 join point )的一段代码。许多AOP框架,包括Spring AOP,会将advice模拟为一个拦截器(interceptor),并且在 join point 上维护多个 advice ,进行层层拦截。

join point(连接点)

程序运行中的一些时间点,在Spring AOP 中 join point 总是方法的执行点,即只有方法的连接点。

point cut (切入点)

在 Spring 中,所有的方法都可以认为是 join point ,但是我们并不希望在所有的方法上都添加 Advice ,而 point cut 的作用就是提供一组规则来匹配 join point ,给满足规则的 join point 添加 Advice。

introduction (引入)

在不修改代码的前提下,引入可以在运行期为类动态的添加额外的方法。

weaving (织入)

将切面应用到目标对象并导致代理对象创建的过程。

Advice 的类型

  • Before Advice(前置通知) : 在join point前被执行的advice.虽然before advice是在join point前被执行,但是它并不能够阻止join point的执行,除非发生了异常(即我们在before advice代码中,不能人为地决定是否继续执行join point中的代码)
  • After Return Advice(后置返回通知) : 在一个join point正常返回后执行的advice
  • After Throwing Advice(异常通知) : 当一个join point 抛出异常后执行的advice
  • After(Final) Advice(最终通知) : 无论一个join point是正常退出还是发生了异常,都会被执行的advice
  • Around Advice(环绕通知) : 在join point前和joint point退出后都执行的advice.这个是最常用的advice

彻底理解 aspect 、join point 、point cut、advice

看完了上面的理论部分知识,我相信还是会有不少朋友感觉到AOP的概念还是很模糊,对AOP中的各种概念理解的还不是很透彻.其实这很正常,因为AOP中的概念是在是太多了,我当时也是花了老大劲才梳理清楚的.下面我以一个简单的例子来比喻一下AOP中 aspect , join point, point cut 与 advice 之间的关系。

让我们来假设一下,从前有一个叫爪哇的小县城,在一个月黑风高的晚上,这个县城中发生了命案。作案的凶手十分狡猾,现场没有留下什么有价值的线索。不过万幸的是,刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程,但是由于天色已晚,加上凶手蒙着面,老王并没有看清凶手的面目,只知道凶手是个男性,身高约七尺五寸。爪哇县的县令根据老王的描述,对守门的士兵下命令说:凡是发现有身高七尺五寸的男性,都要抓过来审问。士兵当然不敢违背县令的命令,只好把进出城的所有符合条件的人都抓了起来。来让我们看一下上面的一个小故事和AOP到底有什么对应关系.首先我们知道,在Spring AOP中 join point 指代的是所有方法的执行点,而point cut是一个描述信息,它修饰的是join point,通过point cut,我们就可以确定哪些 join point 可以被织入Advice。对应到我们在上面举的例子,我们可以做一个简单的类比, join point 就相当于爪哇的小县城里的百姓, point cut就相当于老王所做的指控,即凶手是个男性,身高约七尺五寸,而advice则是施加在符合老王所描述的嫌兼疑人的动作:抓过来审问。为什么可以这样类比呢?

  • join point --> 爪哇的小县城里的百姓:因为根据定义, join point 是所有可能被织入advice的候选的点,在Spring AOP中,则可以认为所有方法执行点都是 join point。而在我们上面的例子中,命案发生在小县城中,按理说在此县城中的所有人都有可能是嫌疑人。
  • point cut --> 男性,身高约七尺五寸:我们知道,所有的方法(joint point)都可以织入 advice,但是我们并不希望在所有方法上都织入advice,而 pointcut 的作用就是提供—组规则来匹配 join point 给满足规则的 join point 添加 advice .同理,对于县令来说,他再昏庸,也知道不能把县城中的所有百姓都抓起来审问,而是根据凶手是个男性,身高约七尺五寸,把符合条件的人抓起来。在这里凶手是个男性,身高约七尺五寸就是一个修饰谓语,它限定了凶手的范围,满足此修饰规则的百姓都是嫌疑人,都需要抓起来审问.
  • advice --> 抓过来审问, advice是一个动作,即一段 Java 代码,这段Java代码是作用于 point cut 所限定的那些 join point 上的。同理,对比到我们的例子中,抓过来审问这个动作就是对作用于那些满足男性,身高约七尺五寸的爪哇的小县城里的百姓。
  • aspect --> aspect是 point cut与advice的组合,因此在这里我们就可以类比:"根据老王的线索,凡是发现有身高七尺五寸的男性,都要抓过来审问”这一整个动作可以被认为是一个aspect

2  使用@AspectJ支持


@AspectJ可以用 XML 的方式或以 注解 的方式来使用,无论哪种方式使用都必须要有 AspectJweaver 的依赖

使用Java Configuration 方式使用@AspectJ
@Configuration
@EnableAspectJAutoProxy
public class AppConfig{
}
使用XML方式使用@AspectJ
<aop:aspectJ-autoproxy />
定义切面(aspect)

但使用注解@Aspect 标注一个Bean后,那么spring框架就会自动收集这些bean ,并添加到Spring AOP 中,例如:

@Component
@Aspect
public class MyTest{
}

注意:仅仅使用了@Aspect注解,并不能将一个java对象转换为Bean ,因此还需要使用类似@Component之类的注解

声明切入点(point cut)

一个point cut 的声明由两部分组成:

  • 一个方法签名,包括方法名和相关参数
  • 一个point cut 表达式,用来指定那些方法是我们感兴趣的。(即因此可以织入advice)

在@AspectJ 风格AOP中,我们使用一个方法来描述point cut,即

@Pointcut("execution(* com.yao.service.UserService.*(..))")    //切入点表达式
private void dataAccessOperation(){
}

这个方法必须无返回值

切点标志符(designator)

AspectJ5的切点表达式由标志符(designator)和操作参数组成.如"execution( greetTo(…))”的切点表达式,execution就是标志符,而圆括号里的greetTo(…)就是操作参数。

execution
匹配join point的执行,例如“execution(* hello(…)”表示匹配所有目标类中的hello()方法.这个是最基本的 pointcut标志符.

within
匹配特定包下的所有join point,例如within(com.xys.*)表示 com.xys包中的所有连接点,即包中的所有类的所有方法.而within(com.xys.service.*Service)表示在com.xys.service包中所有以Service结尾的类的所有的连接点.

this与target
this 的作用是匹配一个bean,这个bean(Spring AOP proxy)是一个给定类型的实例(instanceof).而target匹配的是一个目标对象(target object,即需要织入advice的原始的类),此对象是—个给定类型的实例(instance of).

bean
匹配bean名字为指定值的bean下的所有方法,

bean(*Service)  //匹配名字后缀为Service的bean下的所有方法
bean(myService)   //匹配名字是myService的bean的所有方法

args
匹配参数满足要求的方法

@Pointcat("within(com.yao.demo2.*)")
public void pointcut(){
}
@Before(value = "pointcut() && args(name)")
public void doSomething(String name){
	logger.info("-----page: {}-----",name);
}
@Service
public class NormalService {
	private Logger logger = LoggerFactory.getLogger(getlass());
	public void someMethod( {
		logger.info("---NormalService: someMethod invoked---");
	public string test(String name) {
		logger.info("---NormalService: test invoked---");
	return“服务一切正常";
}

当NormalService.test执行时,则advice doSomething就会执行, test方法的参数name就会传递到doSomething中.

常用例子:

@Before(value = "pointcut() && args(name)")
public void doSomething(String name){
	logger.info("-----page: {}-----",name);
}
@Before(value = "pointcut() && args(name,..)")
public void doSomething(String name){
	logger.info("-----page: {}-----",name);
}
@Before(value = "pointcut() && args(*,name,..)")
public void doSomething(String name){
	logger.info("-----page: {}-----",name);
}
常见的切入点表达式

匹配方法签名

//匹配指定包下所有方法
execution(* com.yao.service.*(..))
//匹配指定当前包中指定类的方法
execution(* UserService.*(..))
//匹配指定包中所有public方法
execution(public com.yao.service.*(..))
//匹配指定包中的所有public 方法,并且返回值是int类型的方法
execution(public int com.yao.service.*(..))
//匹配指定包中的所有public方法,并且第一个参数是String,返回值是int资型的方法
execution(public com.yao.service.*(String name,..))
声明Advice(增强)

advice是和一个point uct 表达式关联在一起的,并且会在匹配的join point 的方法执行的前、后、周围运行。point cut表达式可以是简单的一个point cut名字引用,或是完整的表达式

Before advice

@Component
@Aspect
public class BeforeAspectTest{
	@Pointcut("execution(* com.yao.service.*(..))")
	public void dataAccessOperation(){}
}
@Component
@Aspect
public class AdviceDefine{
	//定义advice
	@Before("com.yao.aspect.PointcutDefine.dataAccessOperation")
	public void doBeforeAccessCheck(JoinPoint joinPoint){
	System.out.println("****Before advise,method: " +joinPoint.getSignature().toString()+"******");
	}
}

这里,@Before引用了一个pointcut,即"com.xys.aspect.PointcutDefine.dataAccessOperation()”是一个pointcut的名字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值