前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 中 AOP 的实现

Spring 中 AOP 的实现

作者头像
水货程序员
修改2018-12-03 16:22:31
5140
修改2018-12-03 16:22:31
举报
文章被收录于专栏:javathings

Spring 中 AOP 的实现

AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代理,你以为操作的这个类,实际上操作的是它的一个代理类。动态代理用到两种机制,一直是基于 JDK 的动态代理,另一种是基于 CGLib 的动态代理。网上有很多介绍这种原理的文章,深入了解可以参考这些文章。

AOP 中有很多术语,比如连接点,切入点,有必要解释一下:

术语解释

Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点。

Pointcut(切入点): 类中有很多连接点,但是我们真正增强的那个连接点,即那个方法,称之为所谓切入点。

Advice(通知/增强): 通知/增强,指的是增强某个方法而实现的逻辑。通知/增强 有几个类型。分为:

  • 前置增强,在方法之前执行。
  • 后置增强,在方法之后执行。
  • 异常增强,在方法异常的时候执行。
  • 最终增强,在后置之后执行。
  • 环绕增强,在方法之前和之后执行。
  • Aspect(切面): 把增强应用到具体方法上,这个过程称之为切面。

捋一下 AOP 的相关术语,白话的说如下:

一个类有好多个连接点(方法),当去增强某个连接点时,这个连接点就称之为切入点。有好几种方式增强某个切入点(就是扩展一些逻辑功能),分别是:

  • 方法执行之前执行。
  • 方法执行 之后执行 。
  • 在方法异常的时候执行。
  • 在后置之后执行,无论目标方法是否出现异常都会 执行。
  • 在方法之前和之后执行。

这个扩展功能的过程又称之为切面。

代码演示

使用 AOP 的时候,需要配置 AOP,分好几步。

1,配置切入点,就是要对哪个方法增强。用到 execution 表达式写法。

2,实现需要增强的逻辑,这个逻辑通常是写在某个方法中,这个方法可以用来增强切入点。

3,配置切面,即配置一下,把增强和切入点关联起来。确定了哪个方法需要哪方面的增强,增强方式是前置增强,或者后置增强,或者其他类型的增强。

配置可以是用 XML 配置,也可以基于注解配置。这里仅演示基于注解配置,本质上都是一样的。

配置文件:

bean1.xml

代码语言:html
复制
<?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>

需要被增强的方法,即所谓的切入点:

代码语言:java
复制
package com.learn.demo;
import org.springframework.stereotype.Component;
 
@Component
public class HelloWorld {
    public void sayHello(){
        System.out.println("Hello!");
    }
}

配置 AOP 的类,AOP 的增强功能在这里实现 。

代码语言:javascript
复制
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 表达式。

代码语言:javascript
复制
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");
    }
}

调用代码如下:

代码语言:javascript
复制
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…..

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring 中 AOP 的实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档