个人学习

Spring AOP实战和源码解析

2020-05-16  本文已影响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");
    }
}
上一篇下一篇

猜你喜欢

热点阅读