spring,springboot,AOP的简介,以及使用

AOP概述 

  1. AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,
  2. 是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
  3. AOP编程操作的主要对象是切面(aspect),而切面模块化横切关注点。
  4. 在传统的OOP编程中,更关注的是继承,继承属于一种自上而下纵向的操作,而面向切面,方法更趋向于横向的操作

AOP的一些概念,描述

切面(Aspect):封装横切关注点信息的类,每个关注点体现为一个通知方法。

通知(Advice):切面必须要完成的各个具体工作

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

代理(Proxy):向目标对象应用通知之后创建的代理对象

连接点(Joinpoint):对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等

切入点(pointcut):具体切入的或者扫描的类或者方法

 

要在Spring中声明AspectJ切面,只需要在IOC容器中将切面声明为bean实例。

当在Spring IOC容器中初始化AspectJ切面之后,Spring IOC容器就会为那些与 AspectJ切面相匹配的bean创建代理。

在AspectJ注解中,切面只是一个带有@Aspect注解的Java类,它往往要包含很多通知。

通知是标注有某种注解的简单的Java方法。

AspectJ支持5种类型的通知注解:

① @Before:前置通知,在方法执行之前执行

② @After:后置通知,在方法执行之后执行(无论方法正常结束还是异常结束,都会正常执行)

③ @AfterRunning:返回通知,在方法返回结果之后执行;

④ @AfterThrowing:异常通知,在方法抛出异常之后执行

⑥ @Around:环绕通知,围绕着方法执行;动态代理,手动推进目标方法运行(joinPoint.procced())

spring中注解使用

引入java包

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>

定义目标方法类

@Component
public class Calculator {

    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }

}

定义一个切面类:JoinPoint一定要出现在参数表的第一位

/**
 * 日志切面
 */
@Component // 标识为一个组件
@Aspect // 标识为一个切面
public class LogAspect {
    /**
     * 声明切入点表达式
     *  @Pointcut("execution(* com.badger.spring.aop.*.*(..))")
     *    第一个* : 任意修饰符 任意返回值
     *    第二个* : 任意类
     *    第三个* : 任意方法
     *    参数列表..: 任意参数列表
     */
    @Pointcut("execution(* com.badger.spring.aop.*.*(..))")
    public void declarePointCut() {
    }

    /**
     * 前置通知: 在目标方法(连接点)执行之前执行. 
     */
    @Before("execution(public int com.badger.spring.aop.Calculator.add(int,int))")
    public void beforeMethod(JoinPoint joinPoint) {
        // 获取方法的参数
        Object[] args = joinPoint.getArgs();
        // 方法的名字
        String methodName = joinPoint.getSignature().getName();
        System.out.println("LogAspect==> The method " + methodName + " begin with " + Arrays.asList(args));
    }

    /**
     * 后置通知: 在目标方法执行之后执行, 不管目标方法有没有抛出异常.  不能获取方法的结果
     * 连接点对象: JoinPoint
     */
    @After("declarePointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        // 方法的名字
        String methodName = joinPoint.getSignature().getName();
        System.out.println("LogAspect==> The method " + methodName + " ends .");

    }

    /**
     * 返回通知: 在目标方法正常执行结束后执行.  可以获取到方法的返回值. 
     * 获取方法的返回值: 通过returning 来指定一个名字, 必须要与方法的一个形参名一致. 
     */
    @AfterReturning(value = "declarePointCut()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result) {
        // 方法的名字
        String methodName = joinPoint.getSignature().getName();
        System.out.println("LogAspect==> The method " + methodName + " end with :" + result);
    }

    /**
     * 异常通知: 在目标方法抛出异常后执行. 
     * 获取方法的异常: 通过throwing来指定一个名字, 必须要与方法的一个形参名一致. 
     * 可以通过形参中异常的类型 来设置抛出指定异常才会执行异常通知. 
     */
    @AfterThrowing(value = "declarePointCut()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, ArithmeticException ex) {
        // 方法的名字
        String methodName = joinPoint.getSignature().getName();
        System.out.println("LogAspect==> Thew method " + methodName + " occurs Exception: " + ex);
    }

    /**
     * 环绕通知: 环绕着目标方法执行. 可以理解是 前置 后置 返回  异常 通知的结合体,更像是动态代理的整个过程. 
     */
    @Around("declarePointCut()")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        // 执行目标方法
        try {
            // 前置
            Object result = pjp.proceed();
            // 返回
            return result;
        } catch (Throwable e) {
            // 异常通知
            e.printStackTrace();
        } finally {
            // 后置
        }
        return null;
    }
}

切面,可以直接在@Before("execution(public int com.badger.spring.aop.Calculator.add(int,int))")上加,也可以一直接定义一个单独的方法

/**
     * 声明切入点表达式
     *  @Pointcut("execution(* com.badger.spring.aop.*.*(..))")
     *    第一个* : 任意修饰符 任意返回值
     *    第二个* : 任意类
     *    第三个* : 任意方法
     *    参数列表..: 任意参数列表
     */
    @Pointcut("execution(* com.badger.spring.aop.*.*(..))")
    public void declarePointCut() {
    }

开起注解式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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 组件扫描 -->
    <context:component-scan base-package="com.badger.spring.aop"></context:component-scan>
    <!-- 基于注解使用AspectJ: 主要的作用是为切面中通知能作用到的目标类生成代理. -->
    <aop:aspectj-autoproxy />
</beans>

spring aop在xml配置中使用

<!-- 目标对象 -->
    <bean id="calculator" class="com.badger.spring.aop.Calculator"></bean>

    <!-- 切面 -->
    <bean id="logAspect" class="com.badger.spring.aop.LogAspect"></bean>
    <!-- AOP: 切面 通知 切入点表达式 -->
    <aop:config>
        <!-- 切面 -->
        <aop:aspect ref="logAspect">
            <!-- 切入点表达式 -->
            <aop:pointcut expression="execution(* com.badger.spring.aop.*.*(..))" id="myPointCut" />
            <!-- 通知 -->
            <aop:before method="beforeMethod" pointcut-ref="myPointCut" />
            <aop:after method="afterMethod" pointcut-ref="myPointCut" />
            <aop:after-returning method="afterReturningMethod" pointcut-ref="myPointCut" returning="result" />
            <aop:after-throwing method="afterThrowingMethod" pointcut-ref="myPointCut" throwing="ex" />
            <!-- <aop:around method="aroundMethod" pointcut-ref="myPointCut"/> -->
        </aop:aspect>
    </aop:config>

先把上述的切面对象com.badger.spring.aop.LogAspect,注解都去掉就可以了

springboot中的aop的使用

创建springboot项目,引用aop的starter

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

剩下的步骤,定义目标类,切面类,就可以了,同上,spring中注解使用;只是不需要使用xml文件的形式,开启注解;而是在配置类中标注@EnableAspectJAutoProxy注解,开始aop

源码如下:org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.class

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {

	}

}

@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class }):先验证是否有这三个类

在上述,标注了@EnableAspectJAutoProxy后

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

会导入一个AspectJAutoProxyRegistrar.class,registry主要给spring容器,注入bean的实例的,后面的就不看了,感兴趣的,可以自己看

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}

}

aop的使用,就到这里

原创文章 83 获赞 155 访问量 36万+

猜你喜欢

转载自blog.csdn.net/qq_28410283/article/details/90741746