Spring AOP面向切面编程实现原理方法详解

作者:弥漫s 时间:2021-07-22 00:26:07 

1. 什么是AOP

AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期 * 实现在不修改源代码的情况下,给程序动态统一添加功能的一种技术,可以理解成 * 。是Spring框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率

2. Spring AOP

①. AOP 在Spring中的作用

提供声明式事务;允许用户自定义切面

②. AOP 的基本概念

横切关注点:跨越应用程序多个模块的方法或功能。即与我们业务逻辑无关,但需要我们关注的部分就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....

  • Aspect(切面):横切关注点被模块化的特殊对象。通常是一个类,里面可以定义切入点和通知

  • Weaving(织入):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时,类加载时和运行时完成。Spring和其它纯Java AOP框架一样,在运行时完成织入

  • Advice(通知):AOP在特定的切入点上执行的增强处理,是切面必须要完成的工作,也是类中的一个方法

  • Target(目标):被通知对象

  • AOP(代理):AOP框架创建的对象,代理就是目标对象的加强。Spring中的 AOP 代理可以是 JDK * ,也可以是 CGLIB 代理,前者基于接口,后者基于子类

  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

  • Pointcut(切入点):就是带有通知的连接点,与切入点匹配的执行点

③. 使用Spring实现Aop

前提

使用AOP织入,需要导入一个依赖包


<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.5</version>
</dependency>

实现Aop的三种方式

方式一:通过 Spring API 实现【主要是springAPI接口实现】

首先编写业务接口和实现类


public interface UserService {
 public void add();
 public void delete();
 public void update();
 public void search();
}

 


public class UserServiceImpl implements UserService{
 public void add() {
   System.out.println("增加了一个用户");
 }

public void delete() {
   System.out.println("删除了一个用户");
 }

public void update() {
   System.out.println("更新了一个用户");
 }

public void select() {
   System.out.println("查询了一个用户");
 }
}

接着编写增强类,这里写两个:前置增强Log和后置增强AfterLog


import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
 //method: 要执行的目标对象的方法
 //args: 参数
 //target: 目标对象
 public void before(Method method, Object[] args, Object target) throws Throwable {
   System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
 }
}

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
 //returnValue;返回值
 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
   System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
 }
}

最后在Spring的文件中注册( applicationContext.xml ),并实现AOP切入,注意导入约束


<?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
   https://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop
   https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
 <bean id="userService" class="com.lf.service.UserServiceImpl"/>
 <bean id="log" class="com.lf.log.Log"/>
 <bean id="afterLog" class="com.lf.log.AfterLog"/>

<!--方式一:使用原生Spring API接口 -->
 <!--配置aop:需要导入aop的约束-->
 <aop:config>
 <!--切入点:expression:表达式,execution(要执行的位置! * * * * *) -->
 <aop:pointcut id="pointcut" expression="execution(* com.lf.service.UserServiceImpl.*(..))"/>
 <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
 <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
 <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
 </aop:config>

</beans>

进行测试:


import com.lf.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
 @Test
 public void test(){
 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 UserService userService1 = (UserService) context.getBean("userService");
 UserService userService = (UserService) context.getBean("userService");
 userService.add();
 }
}

运行结果:

com.lf.service.UserServiceImpl的add被执行了
增加了一个用户
执行了add方法,返回结果为:null

方式二:自定义类实现AOP【主要是切面定义】

目标业务类不变,还是方式一中的UserServiceImpl

写入一个切入类


public class DiyPointCut {
 public void before(){
   System.out.println("========方法执行前=========");
 }

public void after(){
   System.out.println("========方法执行后=========");
 }
}

在Spring中配置(applicationContext.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
   https://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop
   https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
 <bean id="userService" class="com.lf.service.UserServiceImpl"/>
 <bean id="log" class="com.lf.log.Log"/>
 <bean id="afterLog" class="com.lf.log.AfterLog"/>

<!--方式二:自定义类-->
 <bean id="diy" class="com.lf.diy.DiyPointCut"/>

<aop:config>
 <!--自定义切面, ref 要引用的类-->
 <aop:aspect ref="diy">
 <!--切入点-->
 <aop:pointcut id="point" expression="execution(* com.lf.service.UserServiceImpl.*(..))"/>
 <!--通知-->
 <aop:before method="before" pointcut-ref="point"/>
 <aop:after method="after" pointcut-ref="point"/>
 </aop:aspect>
 </aop:config>
</beans>

在上面的 MyTest.java 中测试,得到结果:

========方法执行前=========
增加了一个用户
========方法执行后=========

方式三:使用注解实现【多用】

编写一个注解实现的增强类


package com.lf.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注这个类是一个切面
public class AnnotationPointCut {

@Before("execution(* com.lf.service.UserServiceImpl.*(..))")
 public void before(){
   System.out.println("=====方法执行前======");
 }

@After("execution(* com.lf.service.UserServiceImpl.*(..))")
 public void after(){
   System.out.println("=====方法执行后======");
 }

//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
 @Around("execution(* com.lf.service.UserServiceImpl.*(..))")
 public void around(ProceedingJoinPoint jp) throws Throwable {
   System.out.println("环绕前");
   Signature signature = jp.getSignature();//获得签名
   System.out.println("signature:"+signature);

Object proceed = jp.proceed();  //执行方法
   System.out.println("环绕后");

System.out.println(proceed);
 }

}

在Spring配置文件中,注册bean,并增加支持注解的配置


<?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
   https://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop
   https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
 <bean id="userService" class="com.lf.service.UserServiceImpl"/>
 <bean id="log" class="com.lf.log.Log"/>
 <bean id="afterLog" class="com.lf.log.AfterLog"/>

<!--方式三-->
 <bean id="annotationPointCut" class="com.lf.diy.AnnotationPointCut"/>
 <!--开启注解支持!  JDK(默认 proxy-target-class="false")  cglib(proxy-target-class="true")-->
 <aop:aspectj-autoproxy/>
</beans>

在 MyTest.java 中测试


import com.lf.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
 @Test
 public void test(){
 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 UserService userService = (UserService) context.getBean("userService");
 userService.add();
 }
}

得到结果:

环绕前
signature:void com.lf.service.UserService.add()
=====方法执行前======
增加了一个用户
=====方法执行后======
环绕后
null

来源:https://www.cnblogs.com/lf-637/p/13401297.html

标签:Spring,AOP,编程
0
投稿

猜你喜欢

  • JavaWeb工程中集成YMP框架快速上手

    2023-11-24 12:15:12
  • Spring MVC 使用支付宝接口完成在线支付的示例代码

    2023-11-29 04:07:55
  • 详解springboot读取yml配置的几种方式

    2023-08-21 02:44:54
  • Java事务的个人理解小结

    2023-11-29 12:10:37
  • Java MD5消息摘要算法原理及实现代码

    2022-10-22 17:48:03
  • SpringBoot拦截 器如何获取http请求参数

    2023-11-28 19:40:48
  • Java Swing实现扫雷源码

    2023-11-10 08:16:20
  • 关于Spring Boot内存泄露排查的记录

    2023-06-27 18:46:19
  • Flutter瀑布流仿写原生的复用机制详解

    2023-06-20 17:02:08
  • Spring实战之使用ClassPathResource加载xml资源示例

    2023-11-28 23:00:30
  • Java字符串拼接新方法 StringJoiner用法详解

    2022-06-16 13:02:13
  • Android Studio多渠道打包的配置方法

    2023-06-15 23:19:48
  • 关于@ConditionalOnProperty的作用及用法说明

    2023-11-24 02:39:19
  • SpringMVC + servlet3.0 文件上传的配置和实现代码

    2023-08-08 16:42:43
  • Spring Boot使用Allatori代码混淆的方法

    2023-11-24 16:34:55
  • Java实现按行读取大文件

    2022-11-05 13:56:57
  • java创建子类对象设置并调用父类的变量操作

    2023-10-13 18:16:13
  • Java Validation方法入参校验实现过程解析

    2021-08-04 03:31:50
  • 轻松理解Java面试和开发中的IoC(控制反转)

    2023-08-10 03:00:35
  • C语言实现扫雷游戏(含注释详解)

    2023-11-02 15:25:07
  • asp之家 软件编程 m.aspxhome.com