当前位置:   article > 正文

Aop进阶(一)_高通aop

高通aop

Aop进阶(一)

1、引言

这篇博客主要记载的是我对Aop的理解,和以前使用Aop的一种模糊理解。Aop底层是动态代理,动态代理的底层又是反射。主要是针对Spring-Aop的理解。需要具备Aop的切入点、切面、链接点、通知等基础知识。

2、反射

反射是Java自身核心的一个知识点,反射具体就是指在程序运行期间,对于任意一个类可以获取这个类的属性和方法,对于任意一个对象,能够调用它的任意一个方法和属性。
简单的代码演示:参考:[简单的反射](https://www.cnblogs.com/haodawang/p/5967185.html),主要是使用Java类库中reflect包中的各种方法。
  • 1
  • 2
import java.lang.reflect.*;
public class Main {
	public static void main(String[] args) throws Exception{
		//返回A的构造方法
		Constructor c = A.class.getConstructor();
		//返回A类的所有为public 声明的构造方法
		Constructor[] cons = A.class.getConstructors();
		//返回A类所有的构造方法,包括private
		Constructor[] cons2 = A.class.getDeclaredConstructors();
		//返回A类的第一个public 方法
		Method m = A.class.getMethod("say");
		//执行
		m.invoke(A.class.newInstance(), null);
		//返回A类所有的public 方法
		Method[] ms = A.class.getMethods();
		//返回A类所有的方法,包括private
		Method[] allMs = A.class.getDeclaredMethods();
		//返回A类的public字段
		Field field = A.class.getField("i");
		System.out.println(field.get(A.class.newInstance()));
		//返回A类的static 字段
		System.out.println(field.get(null));
	}
}
class A{
	public int i = 1;
	public static int b = 2;
	public A(){
		System.out.println("无参构造");
	}
	private A(String s){
		System.out.println("有参构造"+s);
	}
	
	public void say(){
		System.out.println("say");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3、动态代理

​ 在了解动态代理之间,我们需要了解静态代理。静态代理是指实现同一个接口的两个类。一个类是代理类,一个类是被代理类。在代理类可以new一个被代理类,达到代理的效果。就比如房东、中介、和客户的关系。房东和中介都有卖房方法,房东是被代理类,中介是代理类。房东只要提自己的卖房要求,至于签合同、带领客户参加这种活动可以交给中介进行。中介就是代理类。静态代理是属于编译期间,也就是编译期间就有真正的class文件,缺点是它只能针对特定的类。而动态代理却不同,它可以在运行期间,具有普适性。Aop就是用的动态代理。
​ 动态代理是怎么实现的?这就是反射,动态代理依靠的就是反射。在Java种有两种动态代理,1、jdk动态代理(接口)2、cglib动态代理(父类)Spring中默认是jdk动态代理。

jdk 动态代理

在jdk动态代理种,要求代理类和被代理类必须实现共同接口。创建代理对象的步骤:1、实现InvocationHandler接口 2、使用newProxyInstance方法

//创建一个动物接口,接口中就只有一个方法
public interface Animal {
    void eat();
}
===================================================================================
//创建一个猫类实现这个接口,这个小猫作为被代理类
public class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("吃鱼");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

然后书写一个类,这个类主要目的是创建代理类(猫的代理对象),并且执行增强的方法

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

public class ProxyJdk implements InvocationHandler {

    private Object object;

    public Object createProxyCat(Object o){
        this.object=o;
        /**
         * newProxyInstance()方法中有三个参数
         * 第一个参数:loader 是代理类的类加载器
         * 第二个参数:interfaces 代理类实现的接口列表
         * 第三个参数:h,也就是实现InvocationHandler的类,用于将方法分派到的调用程序
         */
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                this);
    }

    /**
     *
     * @param proxy 调用方法的代理实例
     * @param method 代理对象上的接口方法
     * @param args  参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("闻到香味,跑过来!");
        //实现被代理类的方法
        Object invoke = method.invoke(object, args);
        System.out.println("吃完离开");
        return invoke;
    }

    //进行测试
    public static void main(String[] args) {
        ProxyJdk jdk=new ProxyJdk();
        Animal proxyCat = (Animal)jdk.createProxyCat(new Cat());
        proxyCat.eat();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
#运行结果
闻到香味,跑过来!
吃鱼
吃完离开
  • 1
  • 2
  • 3
  • 4

cglib 动态代理

​ cglib 动态代理的思想是,一个类继承父类的所有公开方法就可以重写这些方法然后进行增强。创建代理对象,需要实现MethodInterceptor接口,并使用Enhancer类。

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 ProxyCglib implements MethodInterceptor {

    private Enhancer enhancer=new Enhancer();


    public Object createProxy(Class clazz){
        //设置为公共的类
        enhancer.setSuperclass(clazz);
        //建立关联关系
        enhancer.setCallback(this);
        return 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 {
        System.out.println("问起来鱼不行");
        //相当与jdk代理中的invoke方法()
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("受不了,跑步离开");
        return o1;
    }

    public static void main(String[] args) {
        ProxyCglib proxyCglib=new ProxyCglib();
        //创建代理对象
        //这里传的参数主要是类的,可以是Class.forName("类的全名"),或者是 new Cat().getClass
        Animal proxyCat =(Animal) proxyCglib.createProxy(Cat.class);
        proxyCat.eat();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
#结果
问起来鱼不行
吃鱼
受不了,跑步离开
  • 1
  • 2
  • 3
  • 4

4、SpringBoot Aop

在SpringBoot中Aop的使用非常简单,SpringBoot Aop帮我们封装了很多底层,默认使用的是jdk动态代理,可一配置打开cglib动态代理。

1、导入依赖

可以选择 aspectjweaver 也可以选择Aop starter依赖,建议使用starter依赖,因为starter一个包就包含了很多依赖项,并且容易配置。

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.6.4</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2、创建切面类

切入点的匹配方式使用的是execution进行匹配。

@Component
@Aspect
public class TestAop {
        //pointcut 切入点的集合,即在哪里干的集合
        @Pointcut("execution(* com.andrew.study.service.impl.TestServiceImpl.*(..))")
        private void pointCutMethod(){
        }
        //环绕通知
        @Around("pointCutMethod()")
        public void doAround(ProceedingJoinPoint pip){
            System.out.println("----------");
            System.out.println("1200一个月,可以不");
            System.out.println("不行,起码1800一个月");
            try {
                Object proceed = pip.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            System.out.println("成交");
        }

        //前置通知
        @Before("pointCutMethod()")
        public void doBefore(){
            System.out.println("请问是陈先生吗?");
        }

        @After("pointCutMethod()")
        public void doAfter(){
            System.out.println("入住之后,不准改变房子的格局");
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

在切入点的匹配上选择自定义注解的方式

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Test2 {
}
  • 1
  • 2
  • 3
  • 4
  • 5

修改后的切面类为

@Component
@Aspect
public class Test2Aop {
    @Pointcut("@annotation(com.andrew.study.aspect.Test2)")
    private void test(){
    }

    @Before("test()")
    public void doBefore(){
        System.out.println("今天有什么菜呢?");
    }

    @Around("test()")
    public void doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("开饭了");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("小明洗碗!");
    }

    @After("test()")
    public void doAfter(){
        System.out.println("谁最后一个吃完谁洗碗");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在自定义注解方式下,需要在需要增强的方法上加上注解。

@Service
public class Teste2ServiceImpl  implements ITestService {
    @Test2
    @Override
    public void test1() {
        System.out.println("有番茄炒蛋、水煮肉片");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
# 结果
开饭了
今天有什么菜呢?
有番茄炒蛋、水煮肉片
谁最后一个吃完谁洗碗
小明洗碗!

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对于没有自定义注解的方式切面类,直接使用就行

 	 @RequestMapping("/test1")
    public void test(){
        testService.test1();
    }
  • 1
  • 2
  • 3
  • 4
# 结果
----------
1200一个月,可以不
不行,起码1800一个月
请问是陈先生吗?
这个房子卖给你了
入住之后,不准改变房子的格局
成交
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5、拓展

前面几点,是Aop中比较简单的实现原理,下面介绍Aop在SpringBoot中的工作流程。

​ 在了解Aop的底层原理之前,我们需要了解BeanFactoryPostProcessorBeanPostProcess ,这两个接口都是spring 通过配置文件或xml获取Bean声明生成后的BeanDefinition后允许对生成BeanDefinition进行再次封装的入口。在Spring或者Spring Boot进行校验后,应用程序会根据@Aspect注解切面类进行解析,它会根据PointCut匹配代理类,然后创建对象。核心校验方法是wrapIfNecessary,这个方法中有一个getAdvicesAndAdvisorsForBean,主要是返回一个Advisor数据集合,这个主要是将bean和集合中Advisor的Advice的pointCut匹配到。如果匹配到,就为这个bean创建对象。
具体流程

  1. 从Advisor缓存中获取所有的Advisor
  2. 遍历Advisor集合,从Advisor集合中获取PointCut,获取方法匹配器MethodMatcher,通过match方法匹配一个当前Advisor是否能作用于目标类,如果能放入集合中
  3. 如果第二部集合为空,直接返回,否则根据配置选择jdk代理还是cglib代理
  4. cglib代理需要构建Callback拦截增强器集合,然后创建CallbackFilter过滤器,选择合适的拦截增强器对bean实现增强处理
  5. 返回创建的代理对象
本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/767958
推荐阅读
相关标签
  

闽ICP备14008679号