侧边栏壁纸
  • 累计撰写 98 篇文章
  • 累计创建 85 个标签
  • 累计收到 9 条评论

spring系列笔记 - 第⼗七章 AOP概念和AOP底层实现原理

bearjun
2021-07-14 / 0 评论 / 0 点赞 / 553 阅读 / 4,289 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-07-14,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

AOP编程的概念

AOP的概念

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


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


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

AOP的概念:本质上就是 Spring动态代理开发,有益于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。

切面名词解释

81267-eq2mdiczt1q.png

切面= 切入点 + 额外功能
几何学:面 = 点 + 相同的功能

AOP底层实现原理

核心问题

1、AOP如何创建字节代理类(动态字节码技术)
2、Spring是如何通过原始对象的ID值获取的代理对象那?
通过原始对象的id值,获得的是代理对象

动态代理的创建

动态代理的创建的创建方式有两种,一种是基于jdk的动态代理;另外一种是基于cglib的动态代理。

JDK的动态代理

60564-u75jamqhvzn.png
49455-wjjjuutakfh.png
由上图可以知道:
代理创建需要三要素:1 原始对象 2 额外功能 3 代理对象实现相同的接口。
Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)需要三个参数:
1、ClassLoader:完成代理类的创建,可以去借一个

通过类加载器把对应类的字节码加载到JVM中
通过类加载器创建class对象,进而创建这个类的对象
如何获得类加载器?
每个类的.class文件 自动分配与之对应的ClassLoder
在动态代理创建的过程中,需要ClassLoader创建代理类的Class对象,可是动态代理类没有对应的.class文件,JVM也不会为其分配ClassLoader,但是又需要怎么办?
借用一个ClossLoader
2、interfaces:原始对象所实现的接口
创建代理类的class对象,进而完成代理类的创建
3、invocationHandler:额外需要增加的功能
用于书写额外功能 额外功能:原始方法执行前后 抛出异常

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
  • Object proxy 忽略掉,表示的是代理对象
  • Method method 额外功能所增加给的那个原始方法
  • Object[] args 原始方法的参数
  • method.invoke(object, args) 原方法运行

最终完成所有的数据的组合:

//注意:类加载器是借用来的 可以随表找一个借用
public class JDKProxy {
    public static void main(String[] args) {
        // 在JDK8.0之前 内部变量访问外部变量需要加final
        final UserService  userService = new UserServiceImpl();
        InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-----login-------");
                //原方法运行
                Object obj = method.invoke(userService, args);
                return obj;
            }
        };
        UserService service = (UserService) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        service.login("bearjun","hello");
        service.register(new User());
    }
}
CGlib动态代理

CGlib创建动态代理的原理:通过父子继承关系创建代理对象,原始类作为父类,代理类作为 > 子类,这样既可以保证2者方法⼀致,同时在代理类中可以提供新的实现(额外功能+原始方法)。
81388-kozcxbh5eu.png
有上图可以知道:

# 对于一些没有实现接口的方法
public class UserServiceImpl(){
	login();
	register();
}

# 代理类 继承你要代理的类
public clss Proxy extends UserServiceImpl(){
	login(){
	 	额外功能
	 	super.login();
	}
}

最终cglib的动态代理实现:

public class TestCglib {
    public static void main(final String[] args) {
        //创建原始对象
       final UserService userService = new UserService();
    /*
        通过cglib方式创建动态代理对象
        Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
        
     */
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            //等同于 InvocationHandler -- invoke
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("hello world");
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };
        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login();
        userServiceProxy.register();
    }
}

cglib同样也需要做这些:

  • enhancer.setClassLoader(); 类加载器,可以借
  • enhancer.setSuperclass(); 原始方法继承父类的class
  • enhancer.setCallback(); --> MethodInterceptor(cglib包下) 增强方法
  • enhancer.create() 原方法运行

刚才在网上看到说:Cglib invoke以及invokeSuper的一点区别: 简而言之,invoke方法调用的对象没有增强过,invokeSuper方法调用的对象已经是增强了的,所以会再走一遍 MyMethodInterceptor的 interceptor方法,如果是个拦截器链条,就会重新在走一次拦截器链;
相关链接:https://www.cnblogs.com/lvbinbin2yujie/p/10284316.html

总结:

  • JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理类
  • Cglib动态代理 Enhancer 通过继承父类创建的代理类
Spring工厂加工代理对象

刚才说的第2个问题:Spring是如何通过原始对象的ID值获取的代理对象那?
通过原始对象的id值,获得的是代理对象。那通过id为什么获取的是代理对象呢?
89569-vq1hhqw1xp.png
模拟编码:

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 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.yusael.factory.UserServiceImpl"/>
<!--1. 实现 BeanPostProcessor 进行加工-->
<!--2. 配置文件中对 BeanPostProcessor 进行配置-->
<bean id="proxyBeanPostProcessor" class="com.bearjun.factory.ProxyBeanPostProcessor"/>

原来spring在初始化的时候,利用了动态代理的技术,在初始化的时候,把原始的bean换成了代理对象。

0

评论区