Spring 中 AOP 的实现
1,440 total views, 6 views today
AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代理,你以为操作的这个类,实际上操作的是它的一个代理类。动态代理用到两种机制,一直是基于 JDK 的动态代理,另一种是基于 CGLib 的动态代理。网上有很多介绍这种原理的文章,深入了解可以参考这些文章。
AOP 中有很多术语,比如连接点,切入点,有必要解释一下:
术语解释
Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点。
Pointcut(切入点): 类中有很多连接点,但是我们真正增强的那个连接点,即那个方法,称之为所谓切入点。
Advice(通知/增强): 通知/增强,指的是增强某个方法而实现的逻辑。通知/增强 有几个类型。分为:
- 前置增强,在方法之前执行。
- 后置增强,在方法之后执行。
- 异常增强,在方法异常的时候执行。
- 最终增强,在后置之后执行。
- 环绕增强,在方法之前和之后执行。
- Aspect(切面): 把增强应用到具体方法上,这个过程称之为切面。
捋一下 AOP 的相关术语,白话的说如下:
一个类有好多个连接点(方法),当去增强某个连接点时,这个连接点就称之为切入点。有好几种方式增强某个切入点(就是扩展一些逻辑功能),分别是:
- 方法执行之前执行。
- 方法执行 之后执行 。
- 在方法异常的时候执行。
- 在后置之后执行,无论目标方法是否出现异常都会 执行。
- 在方法之前和之后执行。
这个扩展功能的过程又称之为切面。
代码演示
使用 AOP 的时候,需要配置 AOP,分好几步。
1,配置切入点,就是要对哪个方法增强。用到 execution 表达式写法。
2,实现需要增强的逻辑,这个逻辑通常是写在某个方法中,这个方法可以用来增强切入点。
3,配置切面,即配置一下,把增强和切入点关联起来。确定了哪个方法需要哪方面的增强,增强方式是前置增强,或者后置增强,或者其他类型的增强。
配置可以是用 XML 配置,也可以基于注解配置。这里仅演示基于注解配置,本质上都是一样的。
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?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:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 开启注解扫描 --> <context:component-scan base-package="com.learn" /> <!-- 开启aop注解方式,此步骤s不能少,这样java类中的aop注解才会生效 --> <aop:aspectj-autoproxy/> </beans> |
需要被增强的方法,即所谓的切入点:
1 2 3 4 5 6 7 8 9 |
package com.learn.demo; import org.springframework.stereotype.Component; @Component public class HelloWorld { public void sayHello(){ System.out.println("Hello!"); } } |
配置 AOP 的类,AOP 的增强功能在这里实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.learn.demo; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class AopUtils { //前置通知 @Before("execution(* com.learn.demo.HelloWorld.*(..))") public void before() { System.out.println("before doing....."); } // 后置通知 @After("execution(* com.learn.demo.HelloWorld.*(..))") public void after() { System.out.println("after doing....."); } // 环绕通知。注意要有ProceedingJoinPoint参数传入。 @Around("execution(* com.learn.demo.HelloWorld.*(..))") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around..begin"); pjp.proceed();// 执行方法 System.out.println("around..end"); } } |
上面的代码中,有形如 execution 这样的表达式,这称之为切入点表达式。其语法格式:
execution(< 访问修饰符>?< 返回类型>< 方法名>(< 参数>)< 异常>)
这个表达式和定义方法的写法很类似,举个例子:
- 匹配精确的方法:execution(public String org.baeldung.dao.FooDao.findById(Long))。
- 匹配指定包下的所有方法:execution(* com.myjava.dao.*(..)) 不包含子包。
上述代码中,每个注解上都有 execution 表达式。其实可以定义一个切入点函数,用该函数代替每个 execution 表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.learn.demo; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class AopUtils { //定义切点 @Pointcut("execution(* com.learn.demo.HelloWorld.*(..))") public int point(){return 0;} @Before("point()") public void before(){ System.out.println("before doing....."); } //后置通知 @After("point()") public void after(){ System.out.println("after doing....."); } //环绕通知。注意要有ProceedingJoinPoint参数传入。 @Around("point()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around..begin"); pjp.proceed();//执行方法 System.out.println("around..end"); } } |
调用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.learn; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.learn.demo.HelloWorld; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); HelloWorld helloWorld = ( HelloWorld) context.getBean("helloWorld"); helloWorld.sayHello(); } } |
运行结果如下:
around..begin
before doing…..
Hello!
around..end
after doing…..
原创文章,转载请注明出处!http://www.javathings.top/spring中aop的实现/