zoukankan      html  css  js  c++  java
  • 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阶段知识总结

     

  • 相关阅读:
    React元素渲染
    初识JSX
    微信小程序复制文本到剪切板
    微信小程序报错request:fail url not in domain list
    小程序,通过自定义编译条件,模拟推荐人功能
    积分抵扣逻辑
    微信小程序 switch 样式
    tomcat 配置开启 APR 模式
    tomcat8 传输json 报错 Invalid character found in the request target. The valid characters are defined in RFC 3986
    c++数组初始化误区
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/14198176.html
Copyright © 2011-2022 走看看