preface
how the logic of AOP starts after the AOP function is enabled.
1, Bean life cycle
to talk about AOP, you must understand the life cycle of spring beans. The logic of AOP starts at this stage after Bean initialization. The life cycle of Spring Bean is simple, as shown in the figure above:
2, AOP
(1) @ EnableAspectJAutoProxy
use this annotation in Spring to enable the AOP function. This annotation will register a class related to AutoProxyCreator [AnnotationAwareAspectJAutoProxyCreator, AspectJAwareAdvisorAutoProxyCreator] into the IOC container. The class related to AutoProxyCreator implements the instantiaawarebeanpostprocessor interface, that is, some pre preparation operations of AOP will be performed before and after Bean instantiation.
- AbstractAutoProxyCreator implements the BeanFactoryAware interface, which is the top-level parent class of the post processor imported from our transaction and aop;
In the method before instantiation of abstractautoproxycreator postprocessbeforeinstance, AOP aspect beans will be cached (if they are Advice, PointCut and Advisor basic Bean types, they will be skipped directly without resolution);
/** AbstractAutoProxyCreator * In our bean creation process, we call it before calling the constructor to instantiate the bean (before and after instantiation) * Our aop parsing aspect and transaction annotation are completed here * @param beanClass class object of the bean currently being created * @param beanName beanName * @return * @throws BeansException */ @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { //Build our cache key Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { //If it has been parsed, it will be returned directly if (this.advisedBeans.containsKey(cacheKey)) { return null; } /** * Determine whether it is a basic bean * Judge whether it should be skipped (aop parsing directly parses our facet information (and saves our facet information), while transactions will not be parsed here) */ if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } /** * Generally, proxy objects will not be generated in this place unless there is TargetSourceCreator in our container and our bean needs to be implemented * TargetSource Interface */ TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; }
(2) Create Proxy object
In the method after the initialization of AbstractAutoProxyCreator postProcessAfterInitialization [each Bean will be called when it is created], the AOP proxy object will be created. The main method is wrapIfNecessary;
/** AbstractAutoProxyCreator * In this post method, the proxy object of AOP is generated here * @param bean bean example * @param beanName bean Name of * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { if (bean != null) { //Get cache key Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { //If you find the right one, you will be represented return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
The wrapIfNecessary method is as follows:
/** AbstractAutoProxyCreator wrapIfNecessary * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //It has been processed if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } //No enhancement required if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } //Is it the basic bean that needs to be skipped if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } //If we have a notification, create a proxy object Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //Our appropriate notifier is not empty if (specificInterceptors != DO_NOT_PROXY) { //Indicates that the current object has been processed by proxy mode this.advisedBeans.put(cacheKey, Boolean.TRUE); //Create our real proxy object Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); //Add to cache this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
The core method of creating proxy: createProxy (based on ProxyFactory). The real method to create proxy objects is ProxyFactory Getproxy method;
/** * Create a new proxy according to the settings in this factory. * <p>Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. * <p>Uses the given class loader (if necessary for proxy creation). * @param classLoader the class loader to create the proxy with * (or {@code null} for the low-level proxy facility's default) * @return the proxy object */ public Object getProxy(@Nullable ClassLoader classLoader) { //createAopProxy() is used to get our proxy factory return createAopProxy().getProxy(classLoader); }
The createAopProxy method determines whether to use JDK dynamic proxy or CGLIB
- CGLIB proxy may take a long time to execute AOP for the first time, because it needs to generate a new bytecode file;
- In general, the performance of CGLIB and JDK is similar; However, the performance is worse than using native AspectJ. AspectJ is based on bytecode programming technology and is not implemented with the help of Proxy objects.
/** * Implementation class DefaultAopProxyFactory createAopProxy * @param config Used to specify advisor information for us * This method is used to create our proxy object * So our targetClass object implements the interface, and ProxyTargetClass does not specify a mandatory cglib proxy, so it is to create a jdk proxy * If our proxy class does not implement the interface, we will go directly to the cglib proxy * If ProxyTargetClass is specified as false and the proxy class is an interface, jdk proxy will be used. Is it cglib proxy * @return * @throws AopConfigException */ @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //Judge whether we specify to use cglib proxy proxytargetclass = true fasle 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."); } //The targetClass interface uses the jdk proxy if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } //cglib proxy return new ObjenesisCglibAopProxy(config); } else { //Dynamic agent return new JdkDynamicAopProxy(config); } }
// Getproxy method of implementation class jdkdynamicaopproxy @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } /** * Finds any {@link #equals} or {@link #hashCode} method that may be defined * on the supplied set of interfaces. * @param proxiedInterfaces the interfaces to introspect */ private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) { for (Class<?> proxiedInterface : proxiedInterfaces) { Method[] methods = proxiedInterface.getDeclaredMethods(); for (Method method : methods) { if (AopUtils.isEqualsMethod(method)) { this.equalsDefined = true; } if (AopUtils.isHashCodeMethod(method)) { this.hashCodeDefined = true; } if (this.equalsDefined && this.hashCodeDefined) { return; } } } }
The created proxy object will be saved to singletonObjects in the singleton pool of DefaultSingletonBeanRegistry, and then getBean() will get the proxy object in the singleton pool.
In the follow-up, some optimization measures for AOP, especially surround notification, are basically surround notification when encountering all AOP performance bottlenecks.