spring中的AOP

spring的两大特其中就存在一个AOP,AOP就是面向切面编程,在开发中,可以把一些公共功能拆分出来,然后做成一个切面,这个每个经过切面的功能就都会进行增强,例如日志收集、权限拦截、spring自身提供的事务等等。

一、动态代理

说到AOP,就不得不说一个重要的概念,就是动态代理,动态代理是指代理类对象在程序运行时由 JVM 根据反射机制动态生成的,可以再不关心和改变原来方法功能的基础上,进行功能的增强。现在各大主流框架中都使用到动态代理。比较流行的两种动态代理的方式分别是:JDK原生动态代理、CGLIB动态代理。

JDK动态代理

在使用jdk动态代理之前需要说明几点:

1、代理类必须实现接口

2、两个比较重要的接口和方法 InvocationHandler、Proxy,InvocationHandler主要运用反射机制调用代理类的方法,同时可以在方法之前和之后进行增强。Proxy方法负责创建代理对象。

3、因为jdk的动态代理是基于接口的,就会出现代理对象不能用子类进行接受(也就是猴子是动物但不是斑马)

下面看代码演示:

接口:

实现类:

 

InvocationHandler实现类:

PS:需要注意 Proxy.newProxyInstance方法的参数和InvocationHandler中invoke的参数

package com.tes.mgf.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class AnimalProxy implements InvocationHandler {

    private Object target;

    public AnimalProxy(Object target){
        this.target = target;
    }

    public <T> T getPorxy(){
        /**
         * 使用jdk Proxy.newProxyInstance 方法创建动态代理对象
         *  参数
         *  1、类加载器
         *  2、类的接口
         *  3、invocationhandler的实现类
         */
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    /**
     * 实现方法
     * @param proxy 代理对象
     * @param method 调用的方法
     * @param args 调用方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    public void before(){
        System.out.println("方法执行之前!go");
    }

    public void after(){
        System.out.println("方法执行之后!");
    }
}

 测试:

 Cglib动态代理

在使用Cglib动态代理需要注意一下几点:

1、Cglib动态代理使用的是类代理(原理为继承),所以相对JDK动态代理而言更加灵活,但是需要引入第三方jar包

2、重要的两个类:MethodInterceptor接口和Enhancer类,MethodInterceptor接口相当于InvocationHandler,而Enhancer相当于Proxy用于创建代理对象。

3、Cglib是无法代理final的方法的,也可以理解上去因为使用的继承代理类的方式。

下面看下实例:

代理类:

 MethodInterceptor 实现类:

package com.tes.mgf.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    private Object target;

    public CglibProxy(Object target){
        this.target = target;
    }

    public <T> T getProxy(){
        Enhancer enhancer = new Enhancer();
        //回调函数  拦截器
        enhancer.setCallback(this);
        //设置代理对象的父类
        enhancer.setSuperclass(this.target.getClass());
        return (T) enhancer.create();
    }

    /**
     * @param o 代理对象
     * @param method 代理对象的方法
     * @param objects 代理对象的参数
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }

    public void before(){
        System.out.println("方法执行之前!go");
    }

    public void after(){
        System.out.println("方法执行之后!");
    }
}

测试:

FastClass机制:

代理类和被代理类个生成一个class文件,会给方法生成一个index(位置)参数,这样FastClass可以直接调用方法,而不需要进行反射。

两种代理方式的区别:

1、JDK代理是接口代理(也就是被代理的类必须实现接口),Cglib是集成代理

2、JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,JDK效率更高

3、JDK使用反射机制,Cglib使用的是FastClass机制,Cglib相对相率更高

二、AOP

基本概念:传送门

spring aop实现依然有两种方式,第一种是基于配置文件的,第二种是基于注解的,下面我们采用基于注解方式简单演示一下

代码演示:

1、开启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:p="http://www.springframework.org/schema/p"
       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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.tes.mgf"/>
    <!-- 开启spring aop注解方式 -->
    <aop:aspectj-autoproxy/>

</beans>

2、引入maven坐标:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.13</version>
        </dependency>

3、编写代码:

package com.tes.mgf.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {

    /**
     * 切点 - 根据表达式可以进行自定义的拦截
     */
    @Pointcut("execution(* com.tes..*.*(..))")
    public void pointCut(){}

    /**
     * 增强方法,在利用代理调用原方法的前后,可以进行增强
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("执行前。。。。。。。。。。。。");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("执行后。。。。。。。。。。。。");
        return result;
    }

}

切点的拦截分为以下(其他人总结直接拿过来了):

  • execution():用于匹配方法执行的连接点
  • args(): 用于匹配当前执行的方法传入的参数为指定类型的执行方法
  • this(): 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
  • target(): 用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
  • within(): 用于匹配指定类型内的方法执行;
  • @args():于匹配当前执行的方法传入的参数持有指定注解的执行;
  • @target():用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
  • @within():用于匹配所以持有指定注解类型内的方法;
  • @annotation:用于匹配当前执行方法持有指定注解的方法;

需要注意的是,execution是一种表达式,可以根据自己的需要,对不通的方法、类、包等进行拦截。

使用注解作为切点:

package com.tes.mgf.aop;

import com.tes.mgf.annotation.LogAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {

    /**
     * 切点 - 根据表达式可以进行自定义的拦截
     */
    @Pointcut("@annotation(com.tes.mgf.annotation.LogAnnotation)")
    public void pointCut(){}

    /**
     * 增强方法,在利用代理调用原方法的前后,可以进行增强
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointCut() && @annotation(logAnnotation)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, LogAnnotation logAnnotation) throws Throwable {
        System.out.println("执行前。。。。。。。。。。。。" + logAnnotation.value());
        Object result = proceedingJoinPoint.proceed();
        System.out.println("执行后。。。。。。。。。。。。");
        return result;
    }

}

@Around 通知的注解,编写方式也有很多,类似于表达式,上面第一个是切点 && 后面就是切点的注解。

也可以简写,省略切点:

package com.tes.mgf.aop;

import com.tes.mgf.annotation.LogAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {

    /**
     * 增强方法,在利用代理调用原方法的前后,可以进行增强
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.tes.mgf.annotation.LogAnnotation) && @annotation(logAnnotation)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, LogAnnotation logAnnotation) throws Throwable {
        System.out.println("执行前。。。。。。。。。。。。" + logAnnotation.value());
        Object result = proceedingJoinPoint.proceed();
        System.out.println("执行后。。。。。。。。。。。。");
        return result;
    }

}

AOP这里面写法还是比较多的,没必要全部都记住,眼熟即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值