Spring AOP学习笔记


Spring AOP

Spring 中 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。一些AOP的概念:

  • 切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
  • 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
  • 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
  • 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
  • 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
  • AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
  • 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

一、基于代理的AOP实现

1. 代理类

public class ServiceHelper implements MethodBeforeAdvice, AfterReturningAdvice {
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		System.out.println("方法执行开始!");
	}
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		System.out.println("方法执行结束!");
	}
}

2. XML配置

<!-- 定义被代理者 -->
<bean id="user" class="com.aop.service.UserServiceImpl"></bean>

<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="serviceHelper" class="com.aop.helper.ServiceHelper"></bean>

<!-- 定义切入点位置 -->
<bean id="servicePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*"></property>
</bean>

<!-- 使切入点与通知相关联,完成切面配置 -->
<bean id="serviceHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="serviceHelper"></property>   	
   <property name="pointcut" ref="servicePointcut"></property>
</bean>

<!-- 设置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的对象,有记录方法开始结束能力 -->
<property name="target" ref="user"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="serviceHelperAdvisor"></property>
<!-- 代理接口,service接口 -->
<property name="proxyInterfaces" value="com.aop.service.IUserService"></property> 
</bean>

二、通过AspectJ提供的注解实现AOP

1. 切面代码

@Aspect
public class ServiceHelper{
    @Pointcut("execution(* *.*())")
    public void userPointcut(){}
    
    @Before("userPointcut()")
    public void before(){
        System.out.println("方法执行开始!");
    }
    
    @AfterReturning("userPointcut()")
    public void after(){
        System.out.println("方法执行结束!");
    }
}

2. XML配置

<aop:aspectj-autoproxy />
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="serviceHelper" class="com.aop.helper.ServiceHelper"></bean>
<!-- 定义被代理者 -->
<bean id="user" class="com.aop.service.UserServiceImpl"></bean>

三、使用Spring来定义纯粹的POJO切面

1.

public class ServiceHelper{
    public void before(){
        System.out.println("方法执行开始!");
    }
    
    public void after(){
        System.out.println("方法执行结束!");
    }
}

2. XML配置

2.1 写法一
<aop:config>
	<aop:aspect ref="serviceHelper">
		<aop:before method="before" pointcut="execution(* *.*(..))" />
		<aop:after method="after" pointcut="execution(* *.*(..))" />
	</aop:aspect>
</aop:config>
2.2. 写法二
<aop:config>
	<aop:aspect ref="serviceHelper">
        <aop:pointcut id="serviceHelpers" expression="execution(* *.*(..))" />
        <aop:before pointcut-ref="serviceHelper" method="before" />
        <aop:after pointcut-ref="serviceHelper" method="after" />       	
    </aop:aspect>
</aop:config>

Springboot 实现AOP

其实搞明白AOP是什么之后(基于动态代理实现面向切面编程的一种思想),无论在哪里使用都很简单,但是由于最近做的好多项目都使用springboot,所以就在补充一下,超级无敌简单

依赖的包,pom中引入即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

springboot实现AOP有两种方式:一是申明的方式(基于XML),二是注解的方式(基于AspectJ),由于个人喜好只说说注解的方式实现AOP,

一、实现方式一(编写切入点表达式)

@Service
public class LogService {
    private final Logger logger = LoggerFactory.getLogger(LogService.class);

    public void search(){
        logger.info("查询方法被执行");
    }
}

@Aspect
@Component
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
    
    @Pointcut("execution(public * com.dafeng.service.LogService.*(..))")//切入点描述 这个是切入点
    public void logPointcut(){}//签名,可以理解成这个切入点的一个名称

    @Before("logPointcut()") //在切入点的方法执行之前
    public void before(JoinPoint joinPoint) {
        logger.info("方法执行前。。。");
    }

    @After("logPointcut()") //在切入点的方法执行之后
    public void after(JoinPoint joinPoint) {
        logger.info("方法执行后。。。");
    }
}

执行结果:

2020-03-01 13:40:38.894  INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogAspect             : 方法执行前。。。
2020-03-01 13:40:38.903  INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogService            : 查询方法被执行
2020-03-01 13:40:38.903  INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogAspect             : 方法执行后。。。

二、 实现方式二(自定义注解)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

@Service
public class LogService {
    private final Logger logger = LoggerFactory.getLogger(LogService.class);

    @Log // 此处添加自定义注解
    public void search(){
        logger.info("查询方法被执行");
    }
}

@Aspect
@Component
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
    
    @Pointcut("@annotation(com.dafeng.annotation.Log)")//切入点描述使用自定义注解 这个是切入点
    public void logPointcut(){}//签名,可以理解成这个切入点的一个名称

    
    @Before("logPointcut()") //在切入点的方法执行之前要干的
    public void before(JoinPoint joinPoint) {
        logger.info("方法执行前。。。");
    }

    @After("logPointcut()") //在切入点的方法执行之后要干的
    public void after(JoinPoint joinPoint) {
        logger.info("方法执行后。。。");
    }
}

执行结果:

2020-03-01 14:12:35.434  INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogAspect             : 方法执行前。。。
2020-03-01 14:12:35.441  INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogService            : 查询方法被执行
2020-03-01 14:12:35.442  INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogAspect             : 方法执行后。。。
原文地址:https://www.cnblogs.com/dafengdeai/p/12389730.html