Spring AOP

AOP:面向切面编程,相对于OOP面向对象编程
Spring AOP使用动态代理技术在运行期织入增强的代码,Spring AOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以使用两种代理机制,很大程度是因为JDK本身只提供接口的代理,而不支持类的代理。
Demo:
新建一个注解类@interface Action:

package com.example.mavenspringmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
    String name();
}

这里讲下注解:注解本身是没有功能的,就和xml一样,注解之一种元数据,即解释数据的数据,这就是所谓配置。注解的功能来自于用这个注解的地方

编写使用注解的被拦截类:AspectService.java

package com.example.mavenspringmvc.aspect;
import org.springframework.stereotype.Service;
import com.example.mavenspringmvc.annotation.Action;

@Service
public class AspectService {
    @Action(name = "注解action的name属性")
    public void printHelloWorld(){
        System.out.println("Hello World");
    }
}

编写使用方法规则被拦截类:TestAspectService.java

package com.example.mavenspringmvc.service.test;
import org.springframework.stereotype.Service;

@Service
public class TestAspectService {

    public void printOrderInfo(String orderID,String checkTime){
        System.out.println("订单号: " + orderID + ", 复核时间: " + checkTime);
    }
}

编写切面:LogAspect.java

package com.example.mavenspringmvc.aspect;

import com.example.mavenspringmvc.annotation.Action;
import org.apache.log4j.spi.LoggerFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.logging.Logger;

@Aspect //通过该注解声明一个切面
@Component //通过@Component,让切面成为spring容器管理的bean
public class LogAspect {

    @Pointcut("@annotation(com.example.mavenspringmvc.annotation.Action)")
    public void actionAnnotationPointCut(){}

    @After("actionAnnotationPointCut()")
    public void after(JoinPoint joinPoint){
        //获取连接点的方法签名对象
        MethodSignature methodSignature =  (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Action action = method.getAnnotation(Action.class);
        System.out.println("注解式拦截:" + action.name());
    }

    @Before("execution(* com.example.mavenspringmvc.service.test.TestAspectService.*(..))")
    public void before(JoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        String methodName = method.getName();
        Object[] argsArray = joinPoint.getArgs();
        System.out.print("执行方法参数: ");
        for(Object o : argsArray){
            System.out.print(o + ", ");
        }
        System.out.println(methodName);
    }
}

代码解释:
通过@Aspect注解声明一个切面
通过@Component让此切面成为Spring容器管理的Bean
通过@PointCut注解声明切点
通过@After注解声明一个增强类型,并使用@PointCut定义的切点

编写配置类:AppConfig.java

package com.example.mavenspringmvc.config;
import org.springframework.context.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(basePackages = {"com.example.mavenspringmvc"})
@Import({TaskExecutorConfig.class, ConditionConfig.class})
public class AppConfig {

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
        return mapping;
    }
}

使用@EnableAspectJAutoProxy(proxyTargetClass = true)注解开启Spring对AspectJ的支持。

maven配置:

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjrtartifactId>
    <version>1.8.9version>
dependency>
    <dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.8.9version>
dependency>

测试类:TestAspectController.java

package com.example.mavenspringmvc.controller.test;
import com.example.mavenspringmvc.aspect.AspectService;
import com.example.mavenspringmvc.service.test.TestAspectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestAspectController {

    @Autowired
    private TestAspectService testAspectServiceMethod;

    @Autowired
    private AspectService aspectServiceAnnotation;

    @RequestMapping("testAspect")
    public void testAspect(){
        testAspectServiceMethod.printOrderInfo("DPO201711011101","2017-11-11 00:00:");
        aspectServiceAnnotation.printHelloWorld();
    }
}

测试结果:
这里写图片描述

对Demo中用到的知识点进行说明:

切点表达式函数:
spring支持9个@AspectJ切点表达式函数,他们用不同的方式描述目标类的链接点,根据描述对象的不同,可以大致分为4中类型:
方法切点函数:通过描述目标类方法信息定义连接点;execution();
@annocation();
方法入参切点函数:通过描述目标类方法入参的信息定义链接点;args(); @args();
目标类切点函数:通过描述目标类类型信息定义连接点;within(); target(); @within(); @target();
代理类切点函数:通过描述目标类的代理类的信息定义连接点。this();

Demo中使用到execution()和@annotation()来定义切点信息:
@Before(“execution(* com.example.mavenspringmvc.service.test.TestAspectService.*(..))”)
TestAspectService类下的所有方法,第一个号代表方法可以是任何返回值,第二个号代表该类下的所有方法,(..)代表方法的参数可以是任意的;

@Pointcut(“@annotation(com.example.mavenspringmvc.annotation.Action)”)
将程序中任何使用到Action注解的方法定义为切点。

不同的增强类型:
@Before: 前置增强,在切点之前执行
@AfterReturning: 后置增强,在切点之后执行
@Around:环绕增强
@AfterThrowing: 抛出增强
@After: Final增强,不管是抛出异常还是正常退出,该增强都会执行
@DeclareParents: 环绕增强

访问连接点信息:
AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:
1)JoinPoint
 java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
 Signature getSignature() :获取连接点的方法签名对象;
 java.lang.Object getTarget() :获取连接点所在的目标对象;
 java.lang.Object getThis() :获取代理对象本身;
2)ProceedingJoinPoint
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
 java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
 java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

你可能感兴趣的:(spring-aop-案例,spring)