美文网首页个人学习
Spring AOP实战和源码解析

Spring AOP实战和源码解析

作者: 花醉霜寒 | 来源:发表于2020-05-16 17:09 被阅读0次

    \color{green}{Spring AOP基本概念}

    1. Spring AOP简介

    说起Spring框架,我们最先想到的就是IOC和AOP了,如果说IOC是Spring的核心,那AOP就是Spring最重要的功能之一,AOP,即Aspect Oriented Programming,面向切面编程。我们实际项目中,业务除了像数据库的增删改查等核心功能之外,还有一些业务逻辑相同的切面功能,如日志打印和性能统计等。AOP的目的是将业务中共同调用的逻辑封装起来,减少系统中的重复代码,降低模块间的耦合度,提高代码的可扩展性和可维护性。

    1. Spring AOP 基本术语

    1)连接点(Joinpoint)
    一个类或者一段代码中具有边界性质的特定点,如方法调用前后,抛出异常后或者某个类初始化之前和初始化之后等,是客观存在的边界点;

    2)切点(Pointcut)
    切点是某些我们感兴趣的连接点,通过切点来定位特定的连接点,可以将连接点和切点的关系看做数据库中的记录和查询条件的关系,一个查询条件可以对应多条记录,一个切点也可以对应多个连接点。在Spring中切点用Pointcut接口来描述;

    3)增强(Advice)
    增强是织入目标类连接点上的一段程序代码,Spring中增强除了程序代码之外还有一段和连接点相关的方位信息,如Spring提供的BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等;

    4)目标对象(Target)
    增强逻辑织入的目标类;

    5)引介(Introduction)
    引介是一种特殊的增强,表示为类添加一些属性或者方法,即使一个类没有实现某个接口,也能通过引介为这个类添加该接口的实现逻辑;

    6)织入(Weaving)
    将增强添加到目标类连接点上的过程,织入分为编译期织入、类装载期织入和动态代理织入,Spring AOP选择动态代理织入,在运行期为目标类添加增强生成子类的方式,AspectJ选择编译期织入和类装载期间织入,前者需要特殊的编译器,后者需要特殊的类装载器;

    7)代理(Proxy)
    一个类被AOP增强之后,生成一个结果类,这个类既有目标类的业务逻辑也有增强的逻辑;

    8)切面(Aspect)
    切面由切点和增强组成,包括横切逻辑的定义也包括特定连接点的定义。

    \color{green}{Spring AOP实战}
    使用Spring AOP实现一个环绕通知,在方法执行前后打印日志

    • 自定义注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface LogAnnotation {
    }
    
    • 目标方法
    @Component
    public class UserService {
    
        @LogAnnotation
        public String getSomething() {
            return "some thing";
        }
    }
    
    • 切面
    @Aspect
    @Component
    @Slf4j
    public class LogAspect {
    
        @Around("@annotation(liuxin.kkssyy.annotation.LogAnnotation)")
        public Object aroundOperateLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            Signature signature = proceedingJoinPoint.getSignature();
            if (!(signature instanceof MethodSignature)) {
                return new Object();
            }
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();
            targetMethod.setAccessible(true);
            Object resVal = null;
            log.info("方法执行前,打印执行的方法名,方法名:[{}]", targetMethod.getName());
            try {
                resVal = proceedingJoinPoint.proceed();
                log.info("方法执行成功,打印执行结果,结果为:[{}]", resVal.toString());
            } catch (Throwable throwable) {
                log.error("方法执行失败", throwable.toString());
            }
            return resVal;
        }
    }
    
    
    • 方法调用
    @Component
    public class Client {
    
        @Autowired
        UserService userService;
    
        @PostConstruct
        public void run() {
            userService.getSomething();
        }
    }
    
    • 输出结果
    2020-05-18 16:58:23.680  INFO 8940 --- [           main] liuxin.kkssyy.aop.LogAspect              : 方法执行前,打印执行的方法名,方法名:[getSomething]
    2020-05-18 16:58:23.684  INFO 8940 --- [           main] liuxin.kkssyy.aop.LogAspect              : 方法执行成功,打印执行结果,结果为:[some thing]
    

    \color{green}{Spring AOP源码解析}
    Spring AOP动态代理的过程为:
    创建AnnotationAwareAspectJAutoProxyCreator对象;
    扫描容器中的切面;
    创建PointcutAdvisor对象生成代理类

    在AbstractAutoProxyCreator类中实现BeanPostProcessor中的下面方法中

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       if (bean != null) {
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          if (this.earlyProxyReferences.remove(cacheKey) != bean) {
              //核心方法
             return wrapIfNecessary(bean, beanName, cacheKey);
          }
       }
       return bean;
    }
    

    核心方法为wrapIfNeccessary,其处理过程为:

    1. 如果已经处理过,且该bean没有被代理过,则直接返回该bean;
    2. 如果该bean是内部基础设置类Class 或 配置了该bean不需要代理,则直接返回bean(返回前标记该bean已被处理过);
    3. 获取所有适合该bean的增强Advisor如果增强不为null,则为该bean创建代理对象,并返回结果,标记该bean已经被处理过
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            
            if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
                return bean;
            }
            if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
                return bean;
            }
    
            if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
    
    
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    

    AbstractAutoProxyCreator中的createProxy方法中

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
     
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }
     
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        if (!proxyFactory.isProxyTargetClass()) {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
     
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        //加入增强器
        proxyFactory.addAdvisors(advisors);
        //设置要代理的类
        proxyFactory.setTargetSource(targetSource);
        //定制代理
        customizeProxyFactory(proxyFactory);
        //用来控制代理工厂被设置后是否还允许修改通知,缺省值为false
        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
     
        return proxyFactory.getProxy(getProxyClassLoader());
    }
    

    通过ProxyFactory获取Proxy,方法如下

    public Object getProxy(ClassLoader classLoader) {
       return createAopProxy().getProxy(classLoader);
    }
    

    可以看到进一步通过AopProxy类的getProxy获取代理对象,

    Object getProxy(ClassLoader classLoader);方法有两个实现,即JdkDynamicAopProxy和CglibAopProxy,那么究选择jdk的动态代理还是cglib的动态代理呢?是在DefaultAopProxyFactory类中的createAopProxy方法中处理的

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
       if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
          Class<?> targetClass = config.getTargetClass();
          if (targetClass == null) {
             throw new AopConfigException("TargetSource cannot determine target class: " +
                   "Either an interface or a target is required for proxy creation.");
          }
          if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
             return new JdkDynamicAopProxy(config);
          }
          return new ObjenesisCglibAopProxy(config);
       }
       else {
          return new JdkDynamicAopProxy(config);
       }
    }
    

    \color{green}{JDK动态代理}
    前面介绍过AOP的底层是通过动态代理来实现的,动态代理包括基于JDK的动态代理和CGlib的动态代理,JDK的动态代理的两个核心点是:JDK动态代理是通过继承InvocationHandler接口,通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvacationHandler h)来获取代理对象;从newProxyInstance的方法签名可以看出来,JDK动态代理对象必须实现某一个接口,否则不能通过JDK动态代理来进行增强,下面通过一个简单的例子进行介绍。有一个EmployeeService的接口,接口中定义了一个方法showName(String),打印员工的姓名,现在我们需要在打印员工姓名之前展示公司的信息。
    EmployeeService接口

    public interface EmployeeService {
    
        void showName(String name);
    }
    

    EmployeeServiceImpl

    public class EmployeeServiceImpl implements EmployeeService{
    
        @Override
        public void showName(String name) {
            System.out.println(name);
        }
    }
    

    EmployeeServiceHandler

    public class EmployeeServiceHandler implements InvocationHandler {
    
        private Object target;
    
        public EmployeeServiceHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println("the name of the worker is: ");
            Object result = method.invoke(target, args);
            return result;
        }
    }
    

    EmployeeServiceClient

    public class EmployeeServiceClient {
        public static void main(String[] args) {
            EmployeeService employeeService = new EmployeeServiceImpl();
    
            EmployeeServiceHandler employeeServiceHandler = new EmployeeServiceHandler(employeeService);
    
            EmployeeService employeeService1 = (EmployeeService) Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),employeeService.getClass().getInterfaces(), employeeServiceHandler);
            employeeService1.showName("Tom");
            
            //以下为尝试用另外一种思路实现动态代理,目前还没成功
            Constructor cons = employeeServiceHandler .getClass().getConstructor(constructorParams);
            EmployeeService proxy = (EmployeeService ) cons.newInstance(new Object[]{employeeServiceHandler });
        }
    }
    

    JDK动态代理的原理就是实现InvocationHandler接口,通过Proxy.newProxyInstance方法生成代理对象,jdk动态代理只能代理接口的原因就在这里,该方法的第二个参数需要传入接口数组。该方法如下:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
    
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
    
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);
    
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
    
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //最重要的一行
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
    

    \color{green}{CGlib动态代理}

    CGlib的动态代理实例如下
    继承MethodInterceptor

    public class EmployeeServiceImplInterceptor implements MethodInterceptor{
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("======插入前置通知======");
            Object object = methodProxy.invokeSuper(o, objects);
            System.out.println("======插入后者通知======");
            return object;
        }
    }
    

    调用实例代码如下:

    public class CGLibClient {
        public static void main(String[] args) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(EmployeeServiceImpl .class);
            enhancer.setCallback(new EmployeeServiceImplInterceptor ());
            EmployeeServiceImpl employeeServiceImpl = (EmployeeServiceImpl )enhancer.create();
            employeeServiceImpl ..showName("Tom");
        }
    }
    

    相关文章

      网友评论

        本文标题:Spring AOP实战和源码解析

        本文链接:https://www.haomeiwen.com/subject/jcdrohtx.html