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:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。