Spring AOP

来源:互联网 发布:org.apache.tiles 编辑:程序博客网 时间:2024/05/16 01:19

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)@Documentedpublic @interface Action {    String name();}

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

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

package com.example.mavenspringmvc.aspect;import org.springframework.stereotype.Service;import com.example.mavenspringmvc.annotation.Action;@Servicepublic 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;@Servicepublic 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容器管理的beanpublic 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.aspectj</groupId>    <artifactId>aspectjrt</artifactId>    <version>1.8.9</version></dependency>    <dependency>    <groupId>org.aspectj</groupId>    <artifactId>aspectjweaver</artifactId>    <version>1.8.9</version></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;@RestControllerpublic 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:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

原创粉丝点击