Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

学习视频:【孙哥说Spring5:从设计模式到基本应用到应用级底层分析,一次深入浅出的Spring全探索。学不会Spring?只因你未遇见孙哥】

第四章、AOP编程

1.AOP概念

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

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

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

AOP的概念:本质就是Srping的动态代理开发,通过代理类为原始类增加额外功能。好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。

2.AOP编程的开发步骤

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

3.切面的名词解释

切面 = 切入点 + 额外功能

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

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

第五章、AOP的底层实现原理

1.核心问题

  1. AOP如何创建动态代理类(动态字节码技术)

  2. Spring工厂如何加工创建代理对象

    通过原始对象的id值,获得的是代理对象

2.动态代理类的创建

2.1 JDK的动态代理

  • Proxy.newProxyInstance方法参数详解

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

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

        //2 JDK创建动态代理
        /*
        参数详解:
            ClassLoader:借用类加载器,哪个都行,TestJDKProxy UserServiceImpl....
            interfaces:原始对象所实现的接口
            handler:实现invoke方法(额外方法加到原始方法中)
        注意.JDK8.x前
                创建原始对象要加 final
         */
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-----proxy log-------");
                // 1.原始方法运行  原始对象和参数
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        userServiceProxy.login("suns", "123456");
        userServiceProxy.register(new User());
    }
}

2.2 CGlib的动态代理

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

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

package com.baizhi.cglib;
import com.baizhi.proxy.User;
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 TestCglib {
    public static void main(String[] args) {
        // 1.创建原始对象
        UserService userService = new UserService();
        /*
            2.通过cglib方式创建动态代理对象
              JDK:Proxy.newProxyInstance(classloader,interface,invocationhandler)

              Enhancer,setClassLoader()
              Enhancer.setSuperClass()
              Enhancer.setCallback(); --->MethodInterceptor(cglib)
              Enhancer.create() ---> 代理
         */
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            // 等同于 InvocationHandler ---- invoke
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("----cglib log-----");
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("suns","12346");
        userServiceProxy.register(new User());
    }
}
  • 总结(如果有接口就可以使用JDK动态代理,没有使用Cglib继承父类)
    1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类
    2. Cglib动态代理 Enhancer 通过继承父类创建的代理类

3.Spring工厂如何加工代理对象

  • 思路分析

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

  • 编码
public class ProxyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Override
    /*
        Proxy.newProxyInstance(); 基于接口创建代理
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---------- new Log ------------");
                Object ret = method.invoke(bean, args);
                return ret;
            }
        };
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
    }
}
<bean id="userService" class="com.baizhi.factory.UserServiceImpl"/>
<!--
    1.实现BeanPostProcessor 进行加工
    2.配置文件中对BeanPostProcessor进行配置
-->
<bean id="proxyBeanPostProcessor" class="com.baizhi.factory.ProxyBeanPostProcessor"/>

第六章、基于注解的AOP编程

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

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

最大的区别是在2、3、4步,因为是由切面类定义额外功能(@Around)

定义了切入点@Around(”execution( login(..)))”*

@Aspect
/*
    构建切面所需的2个点
    1.额外功能
        public class MyArround implements MethodInterceptor{
            public Object invoke(MethodInvocation invocation){
                Object ret = invocation.proceed();
                return ret;
            }
        }
    2.切入点
        <aop:config
            <aop:pointcut id="" expression="execution(*login(..))"/>
 */
public class MyAspect {
    @Around("execution(* login(..))") // 1. 切入点
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {  // 2.额外功能 等同于 上面的 invoke
        System.out.println("-----aspect log-------");
        Object ret = joinPoint.proceed();

        return ret;
    }
}
<!-- 1. 原始对象-->
<bean id="userService" class="com.baizhi.aspect.UserServiceImpl"/>
<!--
   切面:
      1.额外功能
      2.切入点
      3.组装切面
-->
<!-- 2. 切面-->
<bean id="arround" class="com.baizhi.aspect.MyAspect"/>
<!-- 告知Spring基于注解进行AOP编程-->
<aop:aspectj-autoproxy/>

2.细节

2.1 切入点复用

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

@Aspect
public class MyAspect {
    @Pointcut("execution(* login(..))") // 复用切入点,解耦合
    public void myPointcut(){}

    @Around("myPointcut()") // 1. 切入点
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {  // 2.额外功能 等同于 上面的 invoke
        System.out.println("-----aspect log-------");
        Object ret = joinPoint.proceed();

        return ret;
    }

    @Around("myPointcut()") // 1. 切入点
    public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {  // 2.额外功能 等同于 上面的 invoke
        System.out.println("-----aspect tx-------");
        Object ret = joinPoint.proceed();

        return ret;
    }
}

2.2动态代理的创建方式

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

  1. JDK 通过实现接口 做新的实现类方式 创建代理对象
  2. Cglib通过继承父类 做新的子类 创建代理对象

默认情况 AOP编程 底层应用JDK动态代理创建方式

如果切换Cglib方式

1. 基于注解AOP开发
<aop:aspectj-autoproxy proxy-target-class="true"/>
2.传统的AOP开发
<aop:config proxy-target-class="true">
</aop>

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

第七章、AOP开发中的一个坑

坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过this的方式调用,获得的是原始对象),如果想让内层的this.方法也调用代理对象的方法,就需要实现ApplicationContextAware接口获取。

@Service
public class UserServiceImpl implements UserService, ApplicationContextAware {
    private ApplicationContext ctx;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }
    @Log
    @Override
    public void register(User user) {

        System.out.println("UserServiceImpl.register 注册成功");
//        throw new RuntimeException("测试异常");
        /*
            调用的是原始对象的login方法 ---》核心功能,没有额外功能
            设计目的:代理对象的login方法 ---》 额外功能+核心功能
            所以应该调取代理对象的login方法,要获得工厂,而工厂通过ApplicationContextAware获得
         */
        UserService userService = (UserService) ctx.getBean("userService");
        userService.login("suns", "123456");
    }

    @Override
    public boolean login(String name, String password) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("UserServiceImpl.login登录成功");
        return true;
    }
}

第八章、AOP阶段知识总结

Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)

下一章:Spring5学习随笔-整合MyBatis(持久层)、连接池、Mapper文件

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/190530.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!