WordPress默认注册问题:网站前台建设时如何解决用户注册难题?

摘要:做网站前台用什么问题,wordpress默认注册,网站建设工具品牌,北京哪个网站最好文章目录Spring的循环依赖1.循环依赖的定义&&原因2.循环依赖的场景1.构造器注入引起循环依赖2.Field属性set
做网站前台用什么问题,wordpress默认注册,网站建设工具品牌,北京哪个网站最好文章目录Spring的循环依赖1.循环依赖的定义原因2.循环依赖的场景1.构造器注入引起循环依赖2.Field属性setter注入的循环依赖3.循环依赖解决思路4.三级缓存5.面试题[三级缓存]AOP源码深度剖析概述Spring AOP的前世今生实现机制**JDK 动态代理****CGLIB 代理**流程总结… 文章目录Spring的循环依赖1.循环依赖的定义原因2.循环依赖的场景1.构造器注入引起循环依赖2.Field属性setter注入的循环依赖3.循环依赖解决思路4.三级缓存5.面试题[三级缓存]AOP源码深度剖析概述Spring AOP的前世今生实现机制**JDK 动态代理****CGLIB 代理**流程总结MVC流程源码剖析Servlet生命周期DispatcherServlet 类图源码剖析-根容器初始化【父容器】Web应用部署初始化过程 (Web Application Deployement)ContextLoaderListener的初始化过程ServletContextListener接口源码源码剖析-DispatcherServlet初始化【子容器9大组件】DispatcherServlet类图为什么需要多个IOC容器呢DispatcherServlet初始化1.Spring中核心组件 2.IOC流程bean对象是如何创建出来的 3.Bean生命周期 4.循环依赖循坏依赖为什么要通过三级缓存来解决 5.AOP 6.事务 7.MVCSpring的循环依赖 1.循环依赖的定义原因 定义一个或多个对象实例之间存在直接或间接的依赖关系这种依赖关系构成了一个环形调用闭环 在Spring中一个对象并不是简单new出来的而是会经过一系列的Bean的生命周期就是因为Bean的生命周期所以才出现了循环依赖的问题 在Bean生命周期的属性赋值阶段A依赖了B从单例池中找B对象没有则创建创建B的途中依赖了A此处形成了环形调用。 2.循环依赖的场景 1.构造器注入引起循环依赖 2.Field属性setter注入的循环依赖 对于以上两种场景的循环依赖Spring下测试效果 1.构造器注入引起的循环依赖不能解决 2.单例Bean的Setter注入产生的循环依赖能解决 补充多例Bean的Setter注入产生的循环依赖不能解决 3.循环依赖解决思路 Spring的循环依赖的理论依据是基于Java的引用传递 即可以先实例化对象实例化对象之后在内存中就有了该对象的内存地址我们就可以先从内存中获取到该对象而对象的具体属性是可以延后设置的 核心创建和属性赋值分开达到对象的提前暴露效果 A创建时先创建A原始对象放入缓存中 因为依赖于B在创建B时需要A从缓存中取A缓存中有A则B创建完成赋值给A 4.三级缓存 一级缓存 存放完整的Bean Bean执行了一系列生命周期存入单例池SingletonObjects中 二级缓存 存放半成品Bean 类似于A{bnull}实例化但未赋值的状态存入earlySingletonObjects 三级缓存 存放ObjectFactory对象 将Bean进行包装成ObjectFactory存入singletonFactories 取对象时可能取A的原始对象/A的代理对象 bean将A的地址传入进来如果现在针对A进行AOP配置的话A对象的方法增强则在三级缓存中取A的时候会提前生成A对象的代理对象proxy如果没有配置则返回A的原始对象。 当有AOP配置时Spring根据beanName在已注册的Advisor集合中找匹配到的拦截面生成代理对象把生成的代理对象存入二级缓存并删除三级缓存: this.singletonFactories.remove(beanName) 5.面试题[三级缓存] 1.构造器注入引起的循环依赖能够解决吗 不能。因为创建A对象时候是通过A的有参构造去构建的——new A(B)这个对象的创建和属性赋值是没有分开来的所以没有办法进行A对象的提前暴露。 2.多例Bean对象setter注入产生的循环依赖能够解决吗 不能。多例Bean的生命周期不由Spring管理 3.循环依赖只有一级缓存能够解决循环依赖吗 单从循环依赖的角度能解决但使用过程会有问题因为此时成品对象、半成品对象都存到一个map中如果另一个请求需要调用A对象的B属性的方法他并不知道此时A对象是成品对象还是半成品对象如果是半成品对象调用B属性方法此时B为null则会报空指针异常。 4.循环依赖只有一级缓存、二级缓存能够解决循环依赖吗 如果不存在AOP代理的情况是可以解决的。 如果对A进行了AOP配置要生成A的代理对象A在初始化通过BeanPostProcessor#after方法生成proxy代理对象并存储缓存但B的A属性还是指向A的原始对象这里就产生了问题。 针对以上问题解决思路在A放缓存之前判断是否需要生成代理对象如果需要则基于原始对象生成代理对象再把代理对象存入缓存中等同于违背了Spring的设计原则无论Bean是否产生循环依赖问题所有Bean对象产生代理对象的时机都提前了所有的Bean都得多一个判断 5.循环依赖只有一级缓存、三级缓存能够解决循环依赖吗 简单的A、B循环依赖没问题但如果此时出现复杂的循环依赖会出现问题比如A、B、C相互依赖场景 B依赖于A生成了A的代理对象Proxy1C依赖于A又生成了A的代理对象Proxy2此时B、C引用了不同的A对象地址。 通过二级缓存解决这种现象Spring先查二级缓存发现有A对象代理生成则不会再去生成新的A对象代理。 生成代理对象的时候有判断该bean之前是否生成过代理不重复生成通过集合判断 6.三级缓存为什么存ObjectFactory为什么需要三级缓存解决循环依赖问题 本身违背了Spring设计原则Spring很多拓展点设计都无法应用只有产生循环依赖才会把产生循环依赖的对象要生成的代理对象做一个提前操作否则就把没有循环依赖但需要产生代理对象按照Spring的设计规范生成 总结 三级缓存作用在没有循环依赖的情况下能够包装bean的初始化的最后阶段再生成代理对象遵循Spring设计原则。 个人理解三级缓存就是判断对象是否有AOP配置生成proxy对象将生成代理对象提前了但Spring找缓存对象时是按照顺序一级缓存 - 二级缓存 - 三级缓存 查找的不一定每个Bean都走到三级缓存所以不违背Spring设计原则。 AOP源码深度剖析 概述 AOPAspect Orient Programming面向切面编程 用途用于系统中的横切关注点比如日志管理事务管理 实现利用代理模式通过代理对象对被代理的对象增加功能。 所以关键在于AOP框架自动创建AOP代理对象代理模式分为静态代理和动态代理 框架 ​ AspectJ使用静态代理编译时增强在编译期生成代理对象 ​ SpringAOP使用动态代理运行时增强在运行时动态生成代理对象 Spring AOP的前世今生 目前 Spring AOP 一共有三种配置方式Spring 做到了很好地向下兼容所以可以放心使用。 Spring 1.2 基于接口的配置最早的 Spring AOP 是完全基于几个接口的Spring 2.0 schema-based 配置Spring 2.0 以后使用 XML 的方式来配置使用 命名空间 Spring 2.0 AspectJ 配置使用注解的方式来配置这种方式感觉是最方便的还有这里虽然叫做 AspectJ但是这个和 AspectJ 其实没啥关系。 要说明的是这里介绍的 Spring AOP 是纯的 Spring 代码和 AspectJ 没什么关系但是 Spring 延用了 AspectJ 中的概念包括使用了 AspectJ 提供的 jar 包中的注解但是不依赖于其实现功能。 如 Aspect、Pointcut、Before、After 等注解都是来自于 AspectJ但是功能的实现是纯 Spring AOP 自己实现的。 实现机制 Spring AOP 底层实现机制目前有两种JDK 动态代理、CGLIB 动态字节码生成。在阅读源码前对这两种机制的使用有个认识有利于更好的理解源码。 JDK 动态代理 Proxy.newProxyInstance()第三个参数实现InvocationHandler接口重写invoke方法在每次调用方法时都会进入invoke方法实现增强。 Spring为Bean对象创建代理时会判断当前Bean对象有没有实现接口如果实现接口使用JDK动态代理生成对象否则使用CGLIB代理。 如果proxy-target-class“true”则再判断当前对象是否为接口对象是接口的话依然使用JDK动态代理不是接口使用CGLIB如果proxy-target-class不设置或者为false则默认为JDK动态代理 CGLIB 代理 new Enhance对象设置属性调用create()方法创建proxy对象每次调用方法时会进入intercept方法可以在proxy.invokeSuper()前后进行逻辑增强。 流程 Spring对标签aop:aspectj-autoproxy EnableAspectJAutoProxy/的解析作用 ​ 注册AnnotationAwareAspectJAutoProxyCreator后置处理器 AnnotationAwareAspectJAutoProxyCreator类图实现了BeanPostProcessor AnnotationAwareAspectJAutoProxyCreator 实现了几个重要的扩展接口可能是在父类中实现 1实现了 BeanPostProcessor 接口实现了 postProcessAfterInitialization 方法。 2实现了 InstantiationAwareBeanPostProcessor 接口实现了 postProcessBeforeInstantiation 方法。 3实现了 SmartInstantiationAwareBeanPostProcessor 接口实现了 predictBeanType 方法、getEarlyBeanReference 方法。 4实现了 BeanFactoryAware 接口实现了 setBeanFactory 方法。 对于 AOP 来说postProcessAfterInitialization 是我们重点分析的内容因为在该方法中会对 bean 进行代理该方法由父类 AbstractAutoProxyCreator 实现。 AbstractAutoProxyCreator#postProcessAfterInitialization Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean ! null) {Object cacheKey getCacheKey(bean.getClass(), beanName);// 1.判断当前bean是否需要被代理如果需要则进行封装if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean; }AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor#after方法执行的时候经过wrapIfNecessary方法来判断是否需要针对当前类生成代理对象 wrapIfNecessary protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 1.判断当前bean是否在targetSourcedBeans缓存中存在已经处理过如果存在则直接返回当前beanif (beanName ! null this.targetSourcedBeans.contains(beanName)) {return bean;}// 2.在advisedBeans缓存中存在并且value为false则代表无需处理if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 3.bean的类是aop基础设施类 || bean应该跳过则标记为无需处理并返回if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 4.获取当前bean的Advices和AdvisorsObject[] specificInterceptors getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 5.如果存在增强器则创建代理if (specificInterceptors ! DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 5.1 创建代理对象这边SingletonTargetSource的target属性存放的就是我们原来的bean实例也就是被代理对象// 用于最后增加逻辑执行完毕后通过反射执行我们真正的方法时使用method.invoke(bean, args)Object proxy createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));// 5.2 创建完代理后将cacheKey - 代理类的class放到缓存this.proxyTypes.put(cacheKey, proxy.getClass());// 返回代理对象return proxy;}// 6.标记为无需处理this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean; }getAdvicesAndAdvisorsForBean Override protected Object[] getAdvicesAndAdvisorsForBean(Class? beanClass, String beanName, TargetSource targetSource) {// 1.找到符合条件的AdvisorListAdvisor advisors findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {// 2.如果没有符合条件的Advisor则返回nullreturn DO_NOT_PROXY;}return advisors.toArray(); }getAdvicesAndAdvisorsForBean针对当前程序中所有切面类Aspect进行解析找到切面类中Before/After/Around注解标注的方法把这些方法创建成Advice对象并且针对Before(“pointcut()”)中的切点信息进行解析成Pointcut对象将Advice对象和Pointcut对象进行封装形成Advisor对象。 总结 1.标签aop:aspectj-autoproxy EnableAspectJAutoProxy/ 注册了一个AnnotationAwareAspectJAutoProxyCreator后置处理器当代理对象生成的时候是调用AnnotationAwareAspectJAutoProxyCreator后置处理器的after方法生成。 2.创建代理对象时after方法对于当前Bean对象去找对应的Advisor如果有对应的Advisor则生成代理对象判断对象有没有实现接口如果实现接口采用JDK动态代理否则采用CGLIB动态代理当代理对象调用接口中任意方法时都是执行底层的invoke方法invoke方法执行时涉及拦截器链的依次执行如果是后置通知/最终通知先放行再在方法返回时执行对应逻辑。 MVC流程源码剖析 * 问题1Spring和SpringMVC整合使用时会创建一个容器还是两个容器父子容器 答会创建两个容器对象并且是有父子容器关系对于Spring容器主要管理业务层/持久层/事务层对象对于SpringMVC容器主要负责Web层对象的维护SpringMVC容器为子容器Spring容器为父容器其实就是设置了SpringMVC的一个parent属性* 问题2DispatcherServlet初始化过程中做了什么 答在DispatcherServlet初始化init方法时首先构建了子容器对象并且根据spring-mvc.xml进行文件解析这里关注mvc:annotation-driven标签开启注解模式驱动它向当前的BeanDefinitionMap中注册了一些Bean定义其中有RequestMappingHandlerMapping、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver以支持对使用了RequestMapping、ExceptionHandler及其他注解的控制器方法的请求处理。RequestMappingHandlerMapping生命周期中父类实现了initializingBean#AfterPropertiesSet,这里进行了映射关系的注册1.获取容器中所有的BeanName根据BeanName获得对应的BeanType判断有没有Controller/RequestMapping注解如果有 说明是处理器Handler对象将这些Bean封装到Map集合中keyMethodvalueRequestMappingInfoRequestMappingInfo是针对RequestMapping注解的属性解析封装2.根据Map注册映射关系keyRequestMappingInfovalueHandlerMethod这里HandlerMethod是上一步Method的封装比如还封装了这个Method的所在类的对象3.将RequestMappingInfo再封装到urlLookup中keyurlvalueRequestMappingInfo RequestMappingHandlerAdapter在它的initializingBean#AfterPropertiesSet方法中初始化了参数解析器、返回值处理器* 问题3请求的执行流程是怎么样的 答1.根据请求地址定位到Controller中的方法可能有拦截器拦截所以返回的是执行器链根据顺序拦截器目标方法、拦截器后置方法执行2.参数如何绑定通过不同的参数解析器完成参数值的解析再反射调用方法3.不同返回值如何处理通过不同的返回值处理器将结果变为ModelAndView对象再进行视图渲染最终以转发的形式完成视图的跳转SpringMVC是基于Servlet和Spring容器设计的Web框架 Servlet生命周期 1/ //将随着服务的启动进行实例化并调用init方法且只调用一次。 ServletConfig 是一个和 Servlet 配置相关的接口: 在配置 Spring MVC 的 DispatcherServlet 时会通过 ServletConfig 将配置文件的位置告知 DispatcherServlet。 例 servletservlet-namedispatcher/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-classinit-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-param /servlet如上标签内的配置信息最终会被放入 ServletConfig 实现类对象中。DispatcherServlet 通过 ServletConfig 接口中的方法就能获取到 contextConfigLocation 对应的值。 DispatcherServlet 类图 请求入口DispatcherServlet类的dpDispatch()方法 (找service()方法一步一步找到的核心方法) 源码剖析-根容器初始化【父容器】 Web应用部署初始化过程 (Web Application Deployement) Web应用部署初始化流程执行图 可以发现在tomcat下web应用的初始化流程是先初始化listener接着初始化filter最后初始化servlet当我们清楚认识到Web应用部署到容器后的初始化过程后就可以进一步深入探讨SpringMVC的启动过程。 ContextLoaderListener的初始化过程 首先定义了context-param标签用于配置一个全局变量context-param标签的内容读取后会被放进application中做为Web应用的全局变量使用接下来创建listener时会使用到这个全局变量因此Web应用在容器中部署后进行初始化时会先读取这个全局变量之后再进行上述讲解的初始化启动过程。 接着定义了一个ContextLoaderListener类的listener。查看ContextLoaderListener的类声明源码如下图: ServletContextListener接口源码 public interface ServletContextListener extends java.util.EventListener {void contextInitialized(javax.servlet.ServletContextEvent servletContextEvent);void contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent); }该接口只有两个方法contextInitialized和contextDestroyed这里采用的是观察者模式也称为为订阅-发布模式实现了该接口的listener会向发布者进行订阅当Web应用初始化或销毁时会分别调用上述两个方法。 继续看ContextLoaderListener该listener实现了ServletContextListener接口因此在Web应用初始化时会调用该方法该方法的具体实现如下 /*** Initialize the root web application context.*/Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}ContextLoaderListener的contextInitialized()方法直接调用了initWebApplicationContext()方法这个方法是继承自ContextLoader类通过函数名可以知道该方法是用于初始化Web应用上下文即IoC容器这里使用的是代理模式 源码剖析-DispatcherServlet初始化【子容器9大组件】 DispatcherServlet类图 Web应用启动的最后一个步骤就是创建和初始化相关Servlet我们配置了DispatcherServlet类前端控制器前端控制器作为中央控制器是整个Web应用的核心用于获取分发用户请求并返回响应。 其类图如下所示 通过类图可以看出DispatcherServlet类的间接父类实现了Servlet接口因此其本质上依旧是一个Servlet 为什么需要多个IOC容器呢 答父子容器类似于类的继承关系子类可以访问父类中的成员变量而父类不可访问子类的成员变量同样的子容器可以访问父容器中定义的Bean但父容器无法访问子容器定义的Bean。 根IoC容器做为全局共享的IoC容器放入Web应用需要共享的Bean而子IoC容器根据需求的不同放入不同的Bean这样能够做到隔离保证系统的安全性。 DispatcherServlet类的子IoC容器创建过程如果当前Servlet存在一个IoC容器则为其设置根IoC容器作为其父类并配置刷新该容器用于构造其定义的Bean这里的方法与前文讲述的根IoC容器类似同样会读取用户在web.xml中配置的中的值用于查找相关的xml配置文件用于构造定义的Bean这里不再赘述了。如果当前Servlet不存在一个子IoC容器就去查找一个如果仍然没有查找到则调用 createWebApplicationContext()方法去创建一个查看该方法的源码如下图所示: protected WebApplicationContext createWebApplicationContext(Nullable WebApplicationContext parent) {return createWebApplicationContext((ApplicationContext) parent); }protected WebApplicationContext createWebApplicationContext(Nullable ApplicationContext parent) {Class? contextClass getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException(Fatal initialization error in servlet with name getServletName() : custom WebApplicationContext class [ contextClass.getName() ] is not of type ConfigurableWebApplicationContext);}ConfigurableWebApplicationContext wac (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);String configLocation getContextConfigLocation();if (configLocation ! null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac; } 该方法用于创建一个子IoC容器并将根IoC容器做为其父容器接着进行配置和刷新操作用于构造相关的Bean。至此根IoC容器以及相关Servlet的子IoC容器已经配置完成子容器中管理的Bean一般只被该Servlet使用因此其中管理的Bean一般是“局部”的如SpringMVC中需要的各种重要组件包括Controller、Interceptor、Converter、ExceptionResolver等。 DispatcherServlet初始化 DispatcherServlet的内置组件及其作用。 如 Aspect、Pointcut、Before、After 等注解都是来自于 AspectJ但是功能的实现是纯 Spring AOP 自己实现的。颜色 颜色 颜色 颜色 颜色 颜色 颜色 颜色 颜色 颜色