ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

AOP的底层实现原理,基于注解的AOP编程,切入点复用,AOP开发中同一个业务类中方法的相互调用

2021-04-13 22:58:53  阅读:160  来源: 互联网

标签:Object 代理 ret 复用 AOP login public 类中


1.AOP编程

1.1 AOP概念

AOP (Aspect Oriented Programing) 面向切面编程 Spring动态代理开发
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
切面 = 切入点 + 额外功能

OOP (Object Oritened Programing) 面向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建

POP (Producer Oriented Programing) 面向过程(方法、函数)编程 C
以过程(方法、函数) 为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建

1.2 AOP编程的开发步骤

  1. 原始对象
  2. 额外功能 (MethodInterceptor)
  3. 切入点
  4. 组装切面 (额外功能+切入点)

1.3 切面的名词解释

切面 = 切入点 + 额外功能

几何学
面 = 点 + 相同的性质

2. 动态代理类的创建

2.1 JDK的动态代理

在这里插入图片描述

public class TestJDKProxy {

    public static void main(String[] args) {
        //1 创建原始对象
        UserService userService = new UserServiceImpl();

        //2 JDK创建动态代理
       
        InvocationHandler handler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("------proxy  log --------");
                //原始方法运行
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };

        UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler);

        userServiceProxy.login("suns", "123456");
        userServiceProxy.register(new User());
    }
}

2.2 CGlib的动态代理

CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)
在这里插入图片描述

public void test4(){
        AdminService adminService=new AdminService();

        Enhancer enhancer=new Enhancer();

        //设置类加载器
        enhancer.setClassLoader(String.class.getClassLoader());
        //设置父类
        enhancer.setSuperclass(adminService.getClass());


        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("===== cglib  log =====");
                Object ret = method.invoke(adminService,objects);

                return ret;
            }
        };
        //设置额外功能
        enhancer.setCallback(methodInterceptor);
        //通过字节码技术动态创建子类实例
        AdminService adminServiceProxy = (AdminService) enhancer.create();

        adminServiceProxy.login("xiaohahaha","123123");
        adminServiceProxy.register(new User("lilei","666666"));
    }

总结:

  1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类
  2. Cglib动态代理 Enhancer 通过继承父类创建的代理类

3. AOP的底层实现原理(简易版)

3.1 核心问题

  1. AOP如何创建动态代理类
借用了其他类的类加载器,通过动态字节码技术,进行创建
  1. Spring工厂如何加工创建代理对象
   通过原始对象的id值,获得的是代理对象
   底层是Bean的后置处理技术(BeanPostProcessor)对对象进行处理生成代理对象, 且最终是返回的代理对象

3.2 Spring工厂如何加工原始对象

public class ProxyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        InvocationHandler invocationHandler=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("====BeanPostProcessor====");
                Object ret = method.invoke(bean, args);
                return ret;
            }
        };

        Object proxyInstance = Proxy.newProxyInstance(String.class.getClassLoader(), bean.getClass().getInterfaces(), invocationHandler);

        return proxyInstance;
    }
}
<!--1. 实现BeanPostProcessor 进行加工
        2. 配置文件中对BeanPostProcessor进行配置
    -->

    <bean id="studentService" class="com.tcgroup.service.StudentServiceImpl"/>

    <bean id="proxyBeanPostProcessor" class="com.tcgroup.beanPost.ProxyBeanPostProcessor"/>

4、基于注解的AOP编程

4.1 基于注解的AOP编程的开发步骤

  1. 原始对象

  2. 额外功能

  3. 切入点

  4. 组装切面

原来额外功能的实现:通过实现MethodInterceptor接口,在invoke方法里面实现

public class MyArround implements MethodInterceptor{

    public Object invoke(MethodInvocation invocation){
            Object ret = invocation.proceed();
			return ret;
    }
}

原来切入点是在aop:pointcut标签里面定义

 <aop:config
                 <aop:pointcut id=""  expression="execution(* login(..))"/>

通过注解编程:只要在类上面加上@Aspect注解 就可以告知Spring这是一个额外功能的实现类,在方法加上@Around注解,就可以在注解里面定义切入点了,不需要实现任何接口

@Aspect
public class MyAspect {

    @Around("execution(* login(..))")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("----- Aspect  log -----");
        Object ret =joinPoint.proceed();
        return ret;
    }
}

接下来在Spring配置文件里面告知Spring基于注解进行AOP编程

    <bean id="studentService" class="com.tcgroup.service.StudentServiceImpl"/>

<!--    <bean id="proxyBeanPostProcessor" class="com.tcgroup.beanPost.ProxyBeanPostProcessor"/>-->

<!--  创建额外功能的实现类的实例  -->
    <bean id="arround" class="com.tcgroup.aspect.MyAspect"/>

    <!--告知Spring基于注解进行AOP编程-->
    <aop:aspectj-autoproxy />

实现结果和预期一致,跟之前不使用注解的结果是一样的
在这里插入图片描述

4.2 切入点复用 @Pointcut注解

切入点复用:在切面类中定义一个函数 上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用。

  1. 定义一个空参空方法体的方法
  2. 在方法上加上@Pointcut注解,在注解中定义切入点
  3. 可以在其他方法上通过 @Around注解的value属性引用这个切入点
@Aspect
public class MyAspect {

    @Pointcut("execution(* login(..))")
    public void loginPointCut(){}

    @Around(value = "loginPointCut()")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("----- Aspect  log -----");
        Object ret =joinPoint.proceed();
        return ret;
    }

    @Around(value = "loginPointCut()")
    public Object arround2(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("----- 其他功能 -----");
        Object ret =joinPoint.proceed();
        return ret;
    }
}

5. 指定动态代理的创建方式

AOP底层实现有2种代理创建方式

  1. JDK 通过实现接口 做新的实现类方式 创建代理对象
  2. Cglib通过继承父类 做新的子类 创建代理对象
    默认情况下 AOP编程底层都是应用JDK动态代理创建方式
    如果需要切换Cglib通过继承父类的动态代理创建方式
    1. 基于注解AOP开发
      在告知Spring基于注解进行AOP编程时, 通过proxy-target-class=“true” 设定成Cglib的创建方式
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    1. 传统的AOP开发
      aop:config标签里面 同样通过proxy-target-class=“true” 设定成Cglib的创建方式
   <aop:config proxy-target-class="true">
<!--    expression:切入点表达式
        execution(*空格*(..)) 所有类的所有方法都作为切入点-->
        <aop:pointcut id="pc" expression="execution(public boolean com.tcgroup.service.UserService.login(String,String))"/>
<!--     组装:目的:把切入点与额外功能进行整合   -->
        <aop:advisor advice-ref="arrount" pointcut-ref="pc"/>
    </aop:config>

6.AOP开发中的一个坑

坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。
如果想让内层的方法也调用代理对象的方法,就要AppicationContextAware获得工厂,进而获得代理对象。

给StudentService的login和register方法都加了额外功能
在这里插入图片描述
如果从register方法中调用login方法, 应该两个方法都实现额外功能的, 但是实际只有register方法实现了额外功能,但是login方法没有实现, 这是因为当register方法调用login方法时,调用的时原始对象的login方法,而原始对象的login方法是没有实现额外功能的。
在这里插入图片描述
如果想让内层的方法也调用代理对象的方法,就要让原始类实现AppicationContextAware接口 获得工厂,进而获得代理对象。再通过工厂再次getBean拿到对象,这是拿到的还是原来的代理对象,然后在通过代理对象进行调用其他方法

public class StudentServiceImpl implements StudentService, ApplicationContextAware {

    private ApplicationContext applicationContext ;

    @Override
    public void register(User user) {
        System.out.println("=====StudentServiceImpl.register =====");
        StudentService studentService=(StudentService) applicationContext.getBean("studentService");
        studentService.login("zhangsan","999666");
    }

    @Override
    public boolean login(String name, String password) {
        System.out.println("=====StudentServiceImpl.login=====");
        return false;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

7. AOP 总结在这里插入图片描述

标签:Object,代理,ret,复用,AOP,login,public,类中
来源: https://blog.csdn.net/tuc1997/article/details/115665865

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有