• Spring--->aop面向切面编程


    AOP编程

    1. AOP概念

     AOP (Aspect Oriented Programing) 面向切面编程 = Spring动态代理开发
     以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
     切面 = 切入点 + 额外功能
    OOP (Object Oriented Programing) 面向对象编程 Java 以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建 POP (Procedure Oriented Programing) 面向过程(方法、函数)编程 C 以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建

        aop的本质就是spring的动态代理开发

        想要实现动态代理需要满足三个条件:

    1. 目标对象
    2. 接口 代理对象和目标对象的相同接口
    3. 增强功能

    AOP的底层实现原理

    1. 核心问题

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

    • Spring工厂如何加工创建代理对象?通过原始对象的id值,获得的是代理对象。

    2. 动态代理类的创建

    2.1 JDK的动态代理

           TestJDKProxy.java

    package proxy.jdk;
    
    
    import proxy.service.UserService;
    import proxy.service.impl.UserServiceImpl;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    

    public class TestJDKProxy { public static void main(String[] args) { //1.创建原始对象 //注意:由于后面匿名子类的方法中用到了userService,所以应该用final修饰 // 而JDK1.8以后默认加了final,不需要手动加 UserService userService = new UserServiceImpl(); //2.JDK创建代理对象 InvocationHandler handler = new InvocationHandler() {
    /**
    *proxy:代理对象
    *method:额外功能要增加的原始方法
    *args:原始方法的参数
    *
    **/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----------- JDKProxy log -----------"); //目标方法运行: Object ret = method.invoke(userService, args); return ret; } };
                    /**
    *ClassLoader:类加载对象
    *inerface:实现的接口(相同的接口)
    *handler:额外增加的功能
    *
    **/
    UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(),handler); 
    userServiceProxy.login("海绵宝宝", "1111"); userServiceProxy.register(new User());
    }
    }
    在InvocationHandler中invoke的方法中增加额外功能,程序在执行Proxy.newProxyInstance的过程中就是动态字节码加载的过程(没有代理类,这个过程就是通过动态字节码技术在jvm内存中创建代理类,再通过ClassLoad类加载,继而创建代理类对象),借用的类加载器,借谁的都可以


    2.2 CGlib动态代理

     原理:

    1.目标类

    StudentService

    package proxy.cglib;
    
    import proxy.service.User;
    
    
    public class StudentServiceImpl {
        public boolean login(String name,String password){
            System.out.println("StudentService.login");
            return true;
        }
    
        public void register(User user) {
            System.out.println("StudentService.register");
        }
    }

    2.测试类  Enhancer java中已经提供了 

    package com.yuziyan.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 TestCGlibProxy {
        public static void main(String[] args) {
            //1.创建原始对象
            UserServiceImpl userService = new UserServiceImpl();
    
            //2.通过CGlib创建代理对象
            //  2.1 创建Enhancer
            Enhancer enhancer = new Enhancer();
            //  2.2 设置借用类加载器
            enhancer.setClassLoader(TestCGlibProxy.class.getClassLoader());
            //  2.3 设置父类(目标类)
            enhancer.setSuperclass(userService.getClass());
            //  2.4 设置回调,额外功能写在里面
            enhancer.setCallback(new MethodInterceptor() {
                //相当于 InvocationHandler --> invoke()
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    //额外功能:
                    System.out.println("========= CGlibProxy log ========");
                    //目标方法执行:
                    Object ret = method.invoke(userService, objects);
                    return ret;
                }
            });
            //  2.5 通过Enhancer对象创建代理
            UserServiceImpl service = (UserServiceImpl) enhancer.create();
    
            //测试:
            service.register();
            service.login();
    
        }
    }

    jdk和cglib的本质不同

         1. JDK动态代理   Proxy.newProxyInstance()  通过目标类实现的接口创建代理类 

         2. Cglib动态代理 Enhancer                  通过继承目标类创建代理类 


    3. 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 invocation = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("----------- 模拟Spring返回代理对象的方式 log -----------");
    
                    Object ret = method.invoke(bean, args);
    
                    return ret;
                }
            };
    
            return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invocation);
        }
    }

    配置文件

    <!-- 1.配置原始对象 -->
    <bean id="userService" class="com.yuziyan.factory.UserServiceImpl"></bean>
    <!-- 2.配置自己模拟的ProxyBeanPostProcessor -->
    <bean id="proxyBeanPostProcessor" class="com.yuziyan.factory.ProxyBeanPostProcessor"/>

    为什么从spring容器中拿原始id的值,获取的是代理对象

     因为是通过BeanPostProcessor方式拿到的


    基于注解的AOP编程

    1. 开发步骤:

    1. 原始对象

    2. 额外功能

    3. 切入点

    4. 组装切面

    /**
     * 声明切面类     @Aspect
     * 定义额外功能   @Around
     * 定义切入点     @Around("execution(* login(..))")
     *
     */
    @Aspect
    public class MyAspect {
    
        @Around("execution(* login(..))")//组装了切入点和额外功能
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            //额外功能:
            System.out.println("--------- 基于注解的AOP编程 log --------");
            //原始方法执行:
            Object ret = joinPoint.proceed();
    
            return ret;
        }
    }

    配置文件

    <!-- 原始对象 -->
    <bean id="userService" class="com.yuziyan.aspect.UserServiceImpl"></bean>
    
    <!-- 切面 -->
    <bean id="myAspect" class="com.yuziyan.aspect.MyAspect"/>
    
    <!-- 开启基于注解的AOP编程 -->
    <aop:aspectj-autoproxy/>

    2. 细节分析:

    • 切入点复用:

    @Aspect
    public class MyAspect {
    
        /**
         * 切入点复用:定义一个函数,加上@Pointcut注解,通过该注解的value定义切入点表达式,以后可以复用。
         */
        @Pointcut("execution(* login(..))")
        public void myPointcut(){}
    
        @Around("myPointcut()")//组装了切入点和额外功能
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            //额外功能:
            System.out.println("--------- 基于注解的AOP编程 log --------");
            //原始方法执行:
            Object ret = joinPoint.proceed();
    
            return ret;
        }
    
    
        @Around("myPointcut()")
        public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
            //额外功能:
            System.out.println("--------- 基于注解的AOP编程 tx --------");
            //原始方法执行:
            Object ret = joinPoint.proceed();
    
            return ret;
        }
    
    }

    动态代理的创建方式:

    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>

    AOP开发中的一个坑

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

    一个调用的是原始对象,一个是代理对象,spring的aop只会对代理对象有效

    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 业务运算 + DAO ");
            //throw new RuntimeException("测试异常");
    
            //调用的是原始对象的login方法 ---> 核心功能
            /*
                设计目的:代理对象的login方法 --->  额外功能+核心功能
                ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
                UserService userService = (UserService) ctx.getBean("userService");
                userService.login();
    
                Spring工厂重量级资源 一个应用中 应该只创建一个工厂
             */
    
            UserService userService = (UserService) ctx.getBean("userService");
            userService.login("suns", "123456");
        }
    
        @Override
        public boolean login(String name, String password) {
            System.out.println("UserServiceImpl.login");
            return true;
        }
    }

    AOP阶段知识总结

     

  • 相关阅读:
    oracle按用户导出导入表
    一次简单的springboot+dubbo+flume+kafka+storm+redis系统
    数据库索引学习
    springboot-quartz 实现动态添加,修改,删除,暂停,恢复等功能
    springboot + rabbitmq 整合示例
    springboot-quartz普通任务与可传参任务
    eclipse里maven项目An error occurred while filtering resources解决办法
    job调度时间格式
    多线程发送短信和微信公众号推送消息
    SerializeUtil 序列化,反序列化工具类
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/14198176.html
Copyright © 2020-2023  润新知