当前位置: 首页 > news >正文

青岛建网站哪个好新乡商城网站建设

青岛建网站哪个好,新乡商城网站建设,想做网站要学什么,连云港seospring入门1.0 一 小黑子对spring基础进行概述1.1 spring导论1.2 传统Javaweb开发困惑及解决方法1.3 三大的思想提出1.3.1 IOC入门案例1.3.2 DI入门案例 1.4 框架概念1.5 初识spring1.5.1 Spring Framework 1.6 BeanFactory快速入门1.7 ApplicationContext快速入门1.8 BeanFact… spring入门1.0 一 小黑子对spring基础进行概述1.1 spring导论1.2 传统Javaweb开发困惑及解决方法1.3 三大的思想提出1.3.1 IOC入门案例1.3.2 DI入门案例 1.4 框架概念1.5 初识spring1.5.1 Spring Framework 1.6 BeanFactory快速入门1.7 ApplicationContext快速入门1.8 BeanFactory与ApplicationContext的关系1.9 BeanFactory的继承体系1.10 ApplicationContext的继承体系 二 小黑子基于xml的SpringBean配置详解2.1 Bean的配置概述2.2 beanName和别名配置2.3 Bean的作用范围scope配置2.4 Bean的延迟加载2.5 Bean的初始化方法和销毁方法2.6 InitializingBean方式2.7 实例化Bean的方式-构造方法方式2.7.1 spring bean异常的看法2.7.2 静态工厂方法该方式去实例化Bean2.7.3 实例工厂方法该方式去实例化Bean2.7.4 有参数的静态工厂和实例工厂方法2.7.5 实现FactoryBean规范延迟实例化Bean 2.8 Bean的注入2.8.1 Bean的注入方式2.8.2 Bean的注入数据类型2.8.3 自动装配 2.9 命名空间的种类2.9.1 beans的profile属性切换环境2.9.2 import标签2.9.3 alias标签2.9.4 自定义命名空间的使用步骤 三 小黑子Spring的get方法3.1 spring常用的三种getBean的API 四 小黑子配置Spring非自定义的Bean4.1 DruidDatasource4.2 Connection4.3 Date4.4 SqlSessionFactory 五 小黑子Bean实例化的基本流程5.1 BeanDefinition5.2 单例池和流程总结 六 小黑子Spring的Bean工厂后处理器6.1 入门6.2 注册BeanDefinition6.3 BeanDefinitionRegistryPostProcessor6.4 完善实例化流程图6.5 自定义Component 七 Spring的Bean后处理器7.1 BeanPostProcessor7.2 before和after方法的执行时机7.3 案例-对Bean方法执行日志功能增强7.4 再次完善实例化基本流程图 八 小黑子Spring Bean的生命周期8.1 概述8.2 初始化阶段执行步骤8.3 初始化阶段注入属性信息封装8.4 属性注入的三种情况8.5 注入单向对象的代码验证8.6 循环依赖概念及解决方案8.7 三级缓存的设计原理8.8 循环依赖源码流程剖析8.9 Aware接口 九 小黑子对Spring IoC整体流程总结十 小黑子进行Spring xml方式整合第三方框架10.1 Mybatis整合Spring实现10.2 Mybatis整合Spring源码解析10.3 加载外部properties文件10.4 自定义空间步骤10.5 自定义命名空间案例 一 小黑子对spring基础进行概述 1.1 spring导论 1.2 传统Javaweb开发困惑及解决方法 问题一:层与层之间紧密耦合在了一起接口与具体实现紧密耦合在了一起 解决思路:程序代码中不要手动new对象第三方根据要求为程序提供需要的Bean对象 问题二:通用的事务功能耦合在业务代码中通用的日志功能耦合在业务代码中 解决思路:程序代码中不要手动new对象第三方根据要求为程序提供需要的Bean对象的代理对象 1.3 三大的思想提出 现今代码书写现状 耦合度偏高在项目上线部署之后项目要更新代码就要更新假如说数据层要换个实现那么业务层也要跟着换个实现对象然后重新测试、部署、发布成本高。 解决方案使用对象时在程序中不要主动使用new产生对象转换为由外部提供对象这种对象创建控制器由程序转移到外部——称为IoC IoC思想 lnversion of Control控制反转强调的是原来在程序中创建Bean的权利反转给第三方。使用对象时由主动new产生对象转换为由外部提供对象此过程对象创建控制权由程序转移到外部该思想叫做控制反转。 spring提供了一个容器——IoC容器spring容器用来充当思想中的外部 IoC容器负责对象的创建、初始化等一系列工作呗创建或被管理的对象在IoC容器中统称为Bean DI思想 Dependency Injection依赖注入强调的Bean之间关系这种关系第三方负责去设置。在容器中建立bean与bean之间的依赖关系的整个过程被称为依赖注入 AOP思想 Aspect Oriented Programming面向切面编程,功能的横向抽取主要的实现方式就是Proxy 1.3.1 IOC入门案例 IoC入门案例思路分析 管理什么?( Service与Dao )如何将被管理的对象告知IoC容器?配置被管理的对象交给IoC容器如何获取到IoC容器?接口)IoC容器得到后如何从容器中获取bean ?接口方法)使用Spring导入哪些坐标?( pom.xml ) 在IDEA下创建maven项目在resources文件下导入Spring坐标 发现没有spring这种结构选这就需要导入包 在pom文件里面的导包 之后就出现spring的配置文件 目录 定义spring管理的类接口 创建spring配置文件配置对应类作为spring管理的bean 初始化IoC容器spring核心容器/spring容器通过容器获取bean 1.3.2 DI入门案例 DI入门案例思路分析 基于IoC管理beanService中使用new形式创建的Dao对象是否保留?(否)Service中需要的Dao对象如何进入到service中?提供方法)Service与Dao间的关系如何描述?配置) 删除使用new的形式创建对象的代码 提供依赖对象对应的setter方法 配置service与dao之间的关系 1.4 框架概念 框架(Framework)是基于基础技术之上从众多业务中抽取出的通用解决方案;框架是一个半成品使用框架规定的语法开发可以提高开发效率可以用简单的代码就能完成复杂的基础业务;框架内部使用在这里插入代码片大量的设计模式、算法、底层代码操作技术如反射、内省、xml解析、注解解析等;框架一般都具备扩展性;有了框架我们可以将精力尽可能的投入在纯业务开发上而不用去费心技术实现以及一些辅助业务。 Java中常用的框架: 不同语言不同领域都有属于自己的框架使用框架开发是作为程序员的最基础的底线。Java语言中的框架可以分为基础框架和服务框架: 基础框架:完成基本业务操作的框架如MyBatis、Spring、SpringMVC、Struts2、Hibernate等服务框架:特定领域的框架一般还可以对外提供服务框架如MQ、ES、Nacos等 1.5 初识spring spring是一个开源的轻量级Java开发应用框架可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题提供了功能强大IOC、AOP及Web MVC等功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善不管是Spring哪个领域的解决方案都是依附于在Spring Framework基础框架的。 spring官网 Spring发展到今天已经形成了一种开发的生态圈spring提供了若干给项目每个项目用于完成特定的功能 Spring框架的历史 Jsp 默默扛下所有;MVC三层架构分工明确但开发成本及其高;EJB重量级框架出现走出一个困境有进入另一个困境;Spring春天来到随之SSH风生水起、称霸武林;Spring 稳住江湖大哥位置SSM开始上位;、Spring本着“拿来主义”的思维快速发展生态不断健全;SpringBoot 又一里程碑崛起把“约定大于配置“思想玩儿的炉火纯青;SpringCloud打包了微服务众多解决方案应对互联网项目更加easy! Spring Boot 使用spring boot技术可以在简化开发的基础上加速开发它是用来提高开发速度的会让原先的spring开发变得更加简单代码写得更少 Spring cloud 分布式开发的相关技术 1.5.1 Spring Framework 这是spring里面的第一门技术也是最早出现的在整个全家桶的地位即其他的所有技术都是依赖它执行的。它是Spring生态圈中最基础的项目是其他项目的根基 Spring Framework技术栈图示 第一要学习的内容是Core Container核心容器部分它是管理对象的。所有的一切都是基于对象的一定要先学这spring核心部分的内容第二学数据访问已集成的部分可以整合mybatis。还有一个东西需要重点学习——Transactions事务开发效率非常高的事务控制方案第三学习spring中的第二个核心东西AOP技术——其也是一种编程的思想其具体是可以在不惊动原始程序的基础上给它增强功能。而Aspects也是对AOP思想进行了实现 1.6 BeanFactory快速入门 根据下图分析一下Spring的BeanFactory的开发步骤 在pom文件中导入Spring的jar包或Maven坐标; dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.3.7/version /dependency定义UserService接口及其UserServiceImpl实现类 public interface UserService { }public class UserServiceImpl implements UserService { }创建beans.xml配置文件将UserServiceImpl的信息配置到该xml中 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!-- 配置UserServiceImpl--bean iduserService classcom.itheima.service.impl.UserServiceImpl/bean/beans编写测试代码创建BeanFactory加载配置文件获取UserService实例对象 //创建工厂对象 DefaultListableBeanFactory BeanFactory new DefaultListableBeanFactory(); //创建一个读取器XML文件 XmlBeanDefinitionReader Reader new XmlBeanDefinitionReader(BeanFactory); //读取配置文件给工厂 Reader.loadBeanDefinitions(beans.xml); //根据id获取Bean实例对象 UserService userService (UserService)BeanFactory.getBean(UserService);能够打印出其对象 实现DI依赖注入 在dao包下定义UserDao接口及其UserDaolmpl实现类; 修改UserServicelmpl代码添加一个setUserDao(UserDao userDao)用于接收注入的对象; public class UserServiceImpl implements UserService {private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println(BeanFactory去调用该方法获得userDao设置到此处userDao);this.userDao userDao;} } 修改beans.xml配置文件在UserDaolmpl的bean中嵌入property其name和UserServicelmpl里面的创建变量的userDao该属性名是相互对应的其ref同引入的哪个bean的id相互对应配置注入; bean idUserService classcom.Smulll.service.Impl.UserServiceImplproperty nameuserDao refUserDao/ /bean bean idUserDao classcom.Smulll.Dao.Impl.UserDaoImpl/bean修改测试代码获得UserService时setUserService方法执行了注入操作 //创建工厂对象 DefaultListableBeanFactory BeanFactory new DefaultListableBeanFactory(); //创建一个读取器XML文件 XmlBeanDefinitionReader Reader new XmlBeanDefinitionReader(BeanFactory); //读取配置文件给工厂 Reader.loadBeanDefinitions(beans.xml); //根据id获取Bean实例对象 UserService userService (UserService)BeanFactory.getBean(UserService);1.7 ApplicationContext快速入门 ApplicationContext 称为Spring容器内部封装了BeanFactory比BeanFactory功能更丰富更强大使用ApplicationContext进行开发时xml配置文件的名称习惯写成applicationContext.xml //创建ApplicationContext加载配置文件实例化容器ApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);//根据beanName获得容器中的Bean实例UserService userService (UserService) applicationContext.getBean(userService);System.out.println(userService);1.8 BeanFactory与ApplicationContext的关系 BeanFactory是Spring的早期接口称为Spring的Bean工厂 ApplicationContext是后期更高级接口称之为Spring容器;ApplicationContext在BeanFactory基础上对功能进行了扩展例如:监听功能、国际化功能等。 BeanFactory的API更偏向底层ApplicationContext的API大多数是对这些底层API的封装;Bean创建的主要逻辑和功能都被封装在BeanFactory中ApplicationContext不仅继承了BeanFactory而且ApplicationContext内部还维护着BeanFactory的引用所以,ApplicationContext与BeanFactory既有继承关系又有融合关系。Bean的初始化时机不同原始BeanFactory是在首次调用getBean时才进行Bean的创建 ApplicationContext则是配置文件加载容器一创建就将Bean都实例化并初始化好。 ApplicationContext除了继承了BeanFactory外还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源等。但是ApplicationContext的核心功能还是BeanFactory。 1.9 BeanFactory的继承体系 BeanFactory是核心接口项目运行过程中肯定有具体实现参与这个具体实现就是DefaultListableBeanFactory而ApplicationContext内部维护的Beanfactory的实现类也是它 1.10 ApplicationContext的继承体系 只在Spring基础环境下即只导入spring-context坐标时此时ApplicationContext的继承体系 只在Spring基础环境下常用的三个ApplicationContext作用如下 实现类功能描述ClassPathXmlApplicationContext加载类路径下的xml配置的ApplicationContextFileSystemXmlApplicationContext加载磁盘路径下的xml配置的ApplicationContextAnnotationConfigApplicationContext加载注解配置类的ApplicationContext 如果Spring基础环境中加入了其他组件解决方案如web层解决方案即导入spring-web坐标此时ApplicationContext的继承体系 dependencygroupIdorg.springframework/groupIdartifactIdspring-web/artifactIdversion5.3.7/version /dependency只在Spring的Web环境下常用的两个ApplicationContext作用如下 实现类功能描述XmlWebApplicationContextweb环境下加载类路径下的xml配置的ApplicationContextAnnotationConfigWebApplicationContextweb环境下加载磁盘路径下的xml配置的ApplicationContext 二 小黑子基于xml的SpringBean配置详解 2.1 Bean的配置概述 Spring开发中主要是对Bean的配置Bean的常用配置: Xml配置方式功能描述bean id classBean的id和全限定名配置bean name通过name设置Bean的别名通过别名也能直接获取到Bean实例bean scopeBean的作用范围 BeanFactory作为容器时取值singleton和prototypebean lazy-initBean的实例化时机是否延迟加载。BeanFactory作为容器时无效bean init-methodBean实例化后自动执行的初始化方法method指定方法名bean destroy-methodBean实例销毁前的方法method指定方法名bean autowirebyType设置自动注入模式常用的有按照类型byType按照名字byNamebean factory-bean factory-method/指定哪个工厂Bean的哪个方法完成Bean的创建 2.2 beanName和别名配置 一、Bean的基础配置 例如配置UserDaolmpl由Spring容器负责管理 bean iduserDao classcom.itheima.dao .impl.UserDaoImp! /此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao值是UserDaolmpl对象可以根据beanName获取Bean实例 applicationContext.getBean(userDao);如果不配置id则Spring会把当前Bean实例的全限定名作为beanName applicationContext.getBean(com.itheima.dao.impl.UserDaoImpl);二、Bean的别名配置 类别描述名称name类型属性所属bean标签功能定义bean的别名可定义多个使用逗号,分号;空格 分隔范例 可以为当前Bean指定多个别名根据别名也可以获得Bean对象 bean iduserDao nameaaa,bbb classcom.itheima.dao.impl.UserDaoImp1 /此时多个名称都可以获得UserDaolmpl实例对象 applicationContext.getBean(userDao); applicationContext.getBean(aaa); applicationContext.getBean(bbb);注意 获取bean无论是通过id还是name获取如果无法获取到将抛出异常NoSuchBeanDefinitionException NoSuchBeanDefinitionException: No bean named ‘bookServiceImpl’ available 所以抛出这个异常检查id或者name有没有对上即可 2.3 Bean的作用范围scope配置 默认情况下单纯的Spring环境Bean的作用范围有两个: Singleton和Prototype singleton单例默认值Spring容器创建的时候就会进行Bean的实例化并存储到容器内部的单例池中每次getBean时都是从单例池中获取相同的Bean实例;prototype非单例Spring容器初始化时不会创建Bean实例当调用getBean时才会实例化Bean每次getBean都会创建一个新的Bean实例。 为什么bean默认为单例 为适合交给spring容器进行管理的bean 表现层对象业务层对象serivce数据层对象dao工具对象utils 不适合交给容器进行管理的bean 封装实体的域对象 2.4 Bean的延迟加载 当lazy-init设置为true时为延迟加载也就是当Spring容器创建的时候不会立即创建Bean实例等待用到时在创建Bean实例并存储到单例池中去后续在使用该Bean直接从单例池获取即可本质上该Bean还是单例的 bean iduserDao class com.itheima.dao.impl.UserDaoImpl lazy-inittrue/2.5 Bean的初始化方法和销毁方法 格式一 格式二 Bean在被实例化后可以执行指定的初始化方法完成一些初始化的操作Bean在销毁之前也可以执行指定的销毁方法完成一些操作初始化方法名称和销毁方法名称通过 bean iduserService classcom.itheima.service.impl.UserServiceImpl init-methodinit destroy-methoddestroyproperty nameuserDao refuserDao/property/beanpackage com.itheima.service.impl;import com.itheima.dao.UserDao; import com.itheima.service.UserService;public class UserServiceImpl implements UserService {public void init(){ System.out.println(初始化方法...);}public void destroy(){ System.out.println(销毁方法...);}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println(BeanFactory去调用该方法获得userDao设置到此处userDao);this.userDao userDao;} } 测试 Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService (UserService) applicationContext.getBean(userService);System.out.println(userService);applicationContext.close();}bean的消耗和bean销毁的方法的调用是两回事有的时候容器已经没有了对应内部维护的哪些bean对象也没有了这时方法没有被调用的画——是因为spring没有执行到调用销毁方法那个步骤这个容器就挂掉了 步骤 bean销毁时机 2.6 InitializingBean方式 扩展除此之外我们还可以通过实现InitializingBean接口完成一些Bean的初始化操作如下 package com.itheima.service.impl;import com.itheima.dao.UserDao; import com.itheima.service.UserService; import org.springframework.beans.factory.InitializingBean;public class UserServiceImpl implements UserService, InitializingBean {public void init(){ System.out.println(初始化方法...);}public void destroy(){ System.out.println(销毁方法...);}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println(BeanFactory去调用该方法获得userDao设置到此处userDao);this.userDao userDao;}//执行时机早于init-method配置的方法Overridepublic void afterPropertiesSet() throws Exception {System.out.println( afterPropertiesSet方法执行...);} } 2.7 实例化Bean的方式-构造方法方式 提供可访问的构造方法 配置 Bean的实例化配置 Spring的实例化方式主要如下两种: 构造方式实例化: 底层通过构造方法对Bean进行实例化工厂方式实例化: 底层通过调用自定义的工厂方法对Bean进行实例化 测试 Testpublic void BeanTest3(){ApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService (UserService) applicationContext.getBean(userService);System.out.println(userService);}构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化Spring中配置的bean几乎都是无参构造该方式此处不在赘述。下面讲解有参构造方法实例化Bean public class UserServiceImpl implements UserService {public UserServiceImpl(){System.out.println(UserServiceImpl无参构造方法执行);}public UserServiceImpl(String name,int age){System.out.println(UserServiceImpl有参构造方法执行);}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){this.userDao userDao;}}有参构造在实例化Bean时需要参数的注入通过constructor-arg标签其name为有参构造里面的形参名字value值随便写。该嵌入在bean标签内部提供构造参数如下: bean iduserService classcom.itheima.service.impl.UserServiceImplconstructor-arg namename valuehaohao/constructor-argconstructor-arg nameage value18/constructor-argproperty nameuserDao refuserDao/property/bean大部分的情况下用的都是无参构造 无参构造方法如果不存在将会抛出异常BeanCreationException 2.7.1 spring bean异常的看法 报错异常建议从最下面开始看起解决看不懂再到上一层逐层递增 大部分的情况下解决最后一个异常就解决问题了 2.7.2 静态工厂方法该方式去实例化Bean 工厂方式实例化Bean又分为如下三种: 静态工厂方法实例化Bean实例工厂方法实例化Bean实现FactoryBean规范延迟实例化Bean 使用该静态工厂方法的优点 可以执行一些创建对象之前其他的逻辑操作可以生成一些其他工具类或jar包使用静态方法创造的对象 public class MyBeanFactory1 {public static UserDao userDao(){//Bean创建之前可以进行一些其他业务逻辑操作return new UserDaoImpl();} }bean标签中有了factory-method就不是把MyBeanFactory1当做一个对象而是找它内部的这个userDao方法之后把该方法的返回值当做对象。再以你指定的id作为nam存储到spring容器当中 bean iduserDao1 classcom.itheima.factory.MyBeanFactory1 factory-methoduserDao/bean测试 Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object userDao1 applicationContext.getBean(userDao1);System.out.println(userDao1);}2.7.3 实例工厂方法该方式去实例化Bean 区别静态工厂就是不加static 使用实例工厂方法的优点 可以执行一些创建对象之前其他的逻辑操作可以生成一些其他工具类或jar包通过方法来创造的对象 public class MyBeanFactory2 {public UserDao userDao(){//Bean创建之前可以进行一些其他业务逻辑操作return new UserDaoImpl();} }实例工厂就要调用对象 !--配置实例工厂方法-- !--先设置工厂对象--bean idmyBeanFactory2 classcom.itheima.factory.MyBeanFactory2/bean !--在设置工厂方法--bean iduserDao2 factory-beanmyBeanFactory2 factory-methoduserDao/bean如果是静态工厂就不用传递对象直接调用方法即可 测试 Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object userDao2 applicationContext.getBean(userDao2);System.out.println(userDao2);}2.7.4 有参数的静态工厂和实例工厂方法 bean idfactoryUser factory-beanfactoryDemo2 factory-methodCreateUserServiceconstructor-arg namename valuezhangsan/constructor-arg nameage value18/ /beanconstructor-arg该参数是工厂方法构造Bean方法的参数 2.7.5 实现FactoryBean规范延迟实例化Bean 使工厂类继承FactoryBeanUserDao 通过getObject()方法返回对象 public class MyBeanFactory3 implements FactoryBeanUserDao {Overridepublic UserDao getObject() throws Exception {return new UserDaoImpl();}Overridepublic Class? getObjectType() {return UserDao.class;} }该bean获取的不是MyBeanFactory3而是MyBeanFactory3内部的getObject()返回的这个对象 bean iduserDao3 classcom.itheima.factory.MyBeanFactory3/bean该方法有延迟功能只有调用类时才会将getObject()方法返回的类储存到FactoryBean缓存当中 测试 Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object userDao3 applicationContext.getBean(userDao3);System.out.println(userDao3);}2.8 Bean的注入 2.8.1 Bean的注入方式 Bean的依赖注入又两种方式 注入方式配置方式通过Bean的set方法又叫setter注入property nameuserDao refuserDao / property nameuserDao valuehaohao/通过构造Bean的方法进行注入constructor-arg namename refuserDao/ constructor-arg namename valuehaohao/ 其中ref是reference的缩写形式翻译为:涉及参考的意思用于引用其他Bean的id。value用于注入普通属性值。 2.8.2 Bean的注入数据类型 依赖注入的数据类型有如下三种 普通数据类型例如: String、int、 boolean等通过value属性指定 引用数据类型例如: UserDaolmpl、DataSource等通过ref属性指定。 集合数据类型例如: List、Map、Properties等。 注property和constructor-arg的name属性和参数的名字必须要一样 public class UserServiceImpl implements UserService {//注入Listprivate ListString stringList;public void setStringList(ListString stringList) {this.stringList stringList;}private ListUserDao userDaoList;public void setUserDaoList(ListUserDao userDaoList) {this.userDaoList userDaoList;}private SetString strSet;public void setStrSet(SetString strSet){this.strSet strSet;}private SetUserDao userDaoSet;public void setUserDaoSet(SetUserDao userDaoSet){this.userDaoSet userDaoSet;}private MapString,UserDao map;public void setMap(MapString, UserDao map) {this.map map;}private Properties properties;public void setProperties(Properties properties) {this.properties properties;}public void show(){System.out.println(stringList);System.out.println(userDaoList);System.out.println(userDaoSet);System.out.println(strSet);System.out.println(map);System.out.println(properties);}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){this.userDao userDao;}}bean容器 property namestringListlistvalueaaa/valuevaluebbb/valuevalueccc/value/list/propertyproperty nameuserDaoListlistref beanuserDao1/refref beanuserDao2/refref beanuserDao3/ref/list/propertyproperty namestrSetsetvaluexxxx/valuevalueyyyy/value/set/propertyproperty nameuserDaoSetsetref beanuserDao1/refref beanuserDao2/refref beanuserDao3/ref/set/propertyproperty namemapmapentry keyd1 value-refuserDao1/entryentry keyd2 value-refuserDao2/entry/map/propertyproperty namepropertiespropsprop keyp1pp1/propprop keyp2pp2/prop/props/property /beanbean iduserDao1 classcom.itheima.dao.impl.UserDaoImpl/beanbean iduserDao2 classcom.itheima.dao.impl.UserDaoImpl/beanbean iduserDao3 classcom.itheima.dao.impl.UserDaoImpl/bean接口的方法创建 public interface UserService {public void show(); }测试 Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService (UserService) applicationContext.getBean(userService);userService.show();}2.8.3 自动装配 扩展自动装配方式 如果被注入的属性类型是Bean引用的话那么可以在bean标签中使用autowire属性去配置自动注入方式属 性值有两个: byName通过属性名自动装配即去匹配 setXxx 与idxxx (namexxx)是否一致; public void show(){System.out.println(userDao);}private UserDao userDao;public void setUserDao(UserDao userDao){this.userDao userDao;}bean iduserService classcom.itheima.service.impl.UserServiceImpl autowirebyName/beanbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/beanbyType通过Bean的类型从容器中匹配匹配出多个相同Bean类型时报错。 2.9 命名空间的种类 Spring的xml标签大体上分为两类一种是默认标签一种是自定义标签 默认标签:就是不用额外导入其他命名空间约束的标签例如bean标签自定义标签:就是需要额外引入其他命名空间约束并通过前缀引用的标签例如context:property-placeholder/标签 Spring的默认标签用到的是Spring的默认命名空间 ?xml version1.0encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp: //www.w3.org/2001/xMschema-instancexsi:schemalocationhttp: //www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd /beans该命名空间约束下的默认标签如下 标签作用beans一般作为xml配置根标签其他标签都是该标签的子标beanBean的配置标签上面已经详解了此处不再阐述import外部资源导入标签alias指定Bean的别名标签使用较少 2.9.1 beans的profile属性切换环境 beans标签除了经常用的做为根标签外还可以嵌套在根标签内使用profile属性切换开发环境 !--配置测试环境下需要加载的Bean实例-- beans profiletest/beans !--配置开发环境下需要加载的Bean实例-- beans profiledev/beans 可以使用以下两种方式指定被激活的环境 使用命令行动态参数虚拟机参数位置加载 -Dspring.profiles.activetest使用代码的方式设置环境变量 System.setProperty(spring.profiles.active,test) 案例 beans profiledevbean iduserService1 classcom.itheima.service.impl.UserServiceImpl/bean/beansbeans profiletestbean iduserDao1 classcom.itheima.dao.impl.UserDaoImpl/bean/beansTestpublic void BeanTest4(){//指定环境System.setProperty(spring.profiles.active,test);//System.setProperty(spring.profiles.active,dev);ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml); // UserService userService1 (UserService) applicationContext.getBean(userService1);UserDao userDao1 (UserDao) applicationContext.getBean(userDao1); // System.out.println(userService1);System.out.println(userDao1);}2.9.2 import标签 import标签用于导入其他配置文件项目变大后就会导致一个配置文件内容过多可以将一个配置文件根据业务某块进行拆分拆分后最终通过import标签导入到一个主配置文件中项目加载主配置文件就连同import导入的文件一并加载了 !--导入用户模块配置文件--import resourceclasspath:applicationContext-user.xml/importimport resourceclasspath:applicationContext-orders.xml/import案例 user的bean ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduserService classcom.itheima.service.impl.UserServiceImpl/bean /beansorders的bean ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean /beans测试 Testpublic void BeanTest4(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService (UserService) applicationContext.getBean(userService);UserDao userDao (UserDao) applicationContext.getBean(userDao);System.out.println(userService);System.out.println(userDao);}2.9.3 alias标签 alias标签是为某个Bean添加别名与在bean标签上使用name属性添加别名的方式一样我们为UserServicelmpl指定四个别名: aaa、bbb、xxx、yyy 格式alias alias自定义名字 namebean的id !--配置UserService-- bean iduserService nameaaa,bbb classcom.itheima.service.imp1.UserserviceImp1property nameuserDao refuserDao/ /bean !--指定别名-- alias nameuserService aliasxxx/ alias nameuserDao aliasyyy/测试 Testpublic void BeanTest4(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService (UserService) applicationContext.getBean(xxx); // UserDao userDao (UserDao) applicationContext.getBean(userDao);System.out.println(userService); // System.out.println(userDao);}2.9.4 自定义命名空间的使用步骤 Spring的自定义标签需要引入外部的命名空间并为外部的命名空间指定前缀使用前缀:标签形式的标签称之为自定义标签自定义标签的解析流程也是Spring xml扩展点方式之一 第一步先再pom文件里面引入坐标 第二步去找或者抄对应的这个命名空间xmlns例如 xmlns:mvchttp://www.springframework.org/schema/mvc 第三步去找或者抄对应的xsi这个schema的位置例如http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 第四步通过前缀来引入自定义标签 !--默认标签-- bean iduserDao classcom.itheima. dao .imp1.userDaoImpl / !--自定义标签-- mvc:annotation-driven/mvc:annotation-driven三 小黑子Spring的get方法 3.1 spring常用的三种getBean的API 方法定义返回值和参数Object getBean (String beanName)根据beanName从容器中获取Bean实例要求容器中Bean唯一返回值为Object需要强转T getBean (Class type)根据Class类型从容器中获取Bean实例要求容器中Bean类型唯一返回值为Class类型实例无需强转T getBean (String beanNameClass type)根据beanName从容器中获得Bean实例返回值为Class类型实例无需强转 //根据beanName获取容器中的Bean实例需要手动强转 UserService userService (UserService)applicationContext.getBean(userService); //根据Bean类型去容器中匹配对应的Bean实例如存在多个匹配Bean则报错 UserService userService2 applicationContext.getBean(UserService.class); //根据beanName获取容器中的Bean实例指定Bean的Type类型 UserService userService3 applicationContext.getBean(userService,UserService.class) ;四 小黑子配置Spring非自定义的Bean 以上在xml中配置的Bean都是自己定义的例如: UserDaolmplUserServicelmpl。但是在实际开发中有些功能类并不是我们自己定义的而是使用的第三方jar包中的那么这些Bean要想让Spring进行管理也需要对其进行配置 配置非自定义的Bean需要考虑如下两个问题 被配置的Bean的实例化方式是什么无参构造、有参构造、静态工厂方式还是实例化工厂方式被配置的Bean是否需要注入必要属性 4.1 DruidDatasource 配置Druid数据源交由Spring管理 导入Druid坐标 !--mysql驱动-- dependencygroupIdmysql/groupidartifactIdmysql-connector-java/artifactIdversion5.1.49/version /dependency !-- druid数据源-- dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.23/version /dependency配置数据源信息 bean iddataSource classcom.alibaba.druid.pool.DruidDataSourceproperty namedriverClassName valuecom.mysql.cj.jdbc.Driver/propertyproperty nameurl valuejdbc:mysql://localhost:3306/mybatis/propertyproperty nameusername value你的数据库名字/propertyproperty namepassword value你的数据库密码/property /bean测试 Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object dataSource applicationContext.getBean(dataSource);System.out.println(dataSource);}4.2 Connection Connection的产生是通过DriverManager的静态方法getConnection获取的所以我们要用静态工厂方式配置 bean idclazz classjava.lang.Class factory-methodforNameconstructor-arg nameclassName valuecom.mysql.jdbc.Driver //beanbean idconnection classjava.sql.DriverManager factory-methodgetConnection scopeprototypeconstructor-arg nameurl valuejdbc:mysql:///mybatis /constructor-arg nameuser valueroot/constructor-arg namepassword valueroot//bean测试 Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object connection applicationContext.getBean(connection);System.out.println(connection);}4.3 Date 产生一个指定日期格式的对象原始代码如下 String currentTimeStr 2023-08-27 07:20:00 ; SimpleDateFormat simpleDateFormat new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); Date date simpleDateFormat.parse(currentTimeStr);可以看成是实例工厂方式使用Spring配置方式产生Date实例 !--配置日期对象-- bean idsimpleDateFormat classjava.text.SimpleDateFormatconstructor-arg namepattern valueyyyy-MM-dd HH:mm:ss / /bean bean iddate factory-beansimpleDateFormat factory-methodparseconstructor-arg namesource value2023-08-27 07:20:00/ /bean测试 Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object date applicationContext.getBean(date);System.out.println(date);}4.4 SqlSessionFactory mybatis官方文档 配置SqlSessionFactory交由Spring管理 导入Mybatis相关坐标 dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.10/version /dependency dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.30/version /dependency配置bean参考的原代码 //静态工厂方法方式InputStream is Resources.getResourceAsStream(mybatis-config.xml);//无参构造实例化SqlSessionFactoryBuilder builder new SqlSessionFactoryBuilder();//实例工厂方法SqlSessionFactory sqlSessionFactory builder.build(is);!-- class路径是复制对象的路径 id可以是原本命名的id-- !-- 工厂对象--!--静态工厂--bean idis classorg.apache.ibatis.io.Resources factory-methodgetResourceAsStreamconstructor-arg nameresource valuemybatis-config.xml !-- 该name使用ctrl点击左键查看getResourceAsStream方法的形参可知--/constructor-arg/bean !-- 无参构造实例化--bean idbuilder classorg.apache.ibatis.session.SqlSessionFactoryBuilder/bean!--在设置实例工厂方法--!--实例工厂--bean idsqlSessionFactory factory-beanbuilder factory-methodbuildconstructor-arg nameinputStream refis/constructor-arg/bean测试 Testpublic void BeanTest5() throws {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);SqlSessionFactory sqlSessionFactory (SqlSessionFactory) applicationContext.getBean(sqlSessionFactory);System.out.println(sqlSessionFactory);}在拿到一个bean时去配置让spring去管要分清它是属于哪种方式产生的是属于无参构造还是静态工厂方法还是实例工厂方法。确定之后再看需不需要参数 五 小黑子Bean实例化的基本流程 5.1 BeanDefinition Spring容器在进行初始化时会将xml配置的bean的信息封装成一个BeanDefinition对象;所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去;Spring框架在对该Map进行遍历使用反射创建Bean实例对象创建好的Bean对象存储在一个名为singletonObjects的Map集合中;当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。 DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap public class DefaultListableBeanFactory extends ... implements ... {//存储bean标签对应的BeanDefinition对象//key:是Bean的beanName,value:是Bean定义对象BeanDefinitionprivate final MapString,BeanDefinition beanDefinitionMap; }Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息反射构造方法或调用指定的工厂方法生成Bean实例对象所以只要将BeanDefinition注册到beanDefinitionMap这个Map中Spring就会进行对应的Bean的实例化操作 5.2 单例池和流程总结 Bean实例及单例池singletonObjectsbeanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象存储到单例池singletonObjects中去在DefaultListableBeanFactory的上四级父类 DefaultSingletonBeanRegistry中维护着singletonObjects 源码如下: public class DefaultSingletonBeanRegistry extends ... implements ... { //存储Bean实例的单例池 key :是Bean的beanName,value:是Bean的实例对象 private final MapString,object singleton0bjects new ConcurrentHashMap(256)基本流程 加载xml配置文件解析获取配置中的每个bean的信息封装成一个个的BeanDefinition对象;将BeanDefinition存储在一个名为beanDefinitionMap的MapString,BeanDefinition中;ApplicationContext底层遍历beanDefinitionMap创建Bean实例对象;创建好的Bean实例对象被存储到一个名为singletonObjects的MapString,Object中;当执行applicationContext.getBean(beanName)时从singletonObjects去匹配Bean实例返回。 六 小黑子Spring的Bean工厂后处理器 6.1 入门 Spring的后处理器是Spring对外开发的重要扩展点允许我们介入到Bean的整个实例化流程中来以达到动态注册BeanDefinition动态修改BeanDefinition以及动态修改Bean的作用。Spring主要有两种后处理器: BeanFactoryPostProcessor: Bean工厂后处理器在BeanDefinitionMap填充完毕Bean实例化之前执行;BeanPostProcessor: Bean后处理器一般在每个Bean实例化之后填充到单例池singletonObjects之前执行。 Bean工厂后处理器——BeanFactoryPostProcessor BeanFactoryPostProcessor是一个接口规范实现了该接口的类只要交由Spring容器管理的话那么Spring就会回调该接口的方法用于对BeanDefinition注册和修改的功能。 BeanFactoryPostProcessor定义如下 public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory); }6.2 注册BeanDefinition 使用这种方法可以不用在spring容器内再创建一个类的bean标签 public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){System.out.println(beandefinitionMap填充完毕后回调该方法);//1.注册一个beandefinition 创建一个RootBeanDefinition()对象BeanDefinition BeanDefinition new RootBeanDefinition();BeanDefinition.setBeanClassName(com.itheima.dao.impl.PersonDaoImpl);//2.将beanFactory强转成DefaultListableBeanFactory类型DefaultListableBeanFactory defaultListableBeanFactory (DefaultListableBeanFactory) beanFactory;defaultListableBeanFactory.registerBeanDefinition(personDao,BeanDefinition);} }Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);PersonDao personDao applicationContext.getBean(PersonDao.class);System.out.println(personDao);}bean classcom.itheima.processor.MyBeanFactoryPostProcessor/bean6.3 BeanDefinitionRegistryPostProcessor Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作 public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {System.out.println(MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法);//创建一个RootBeanDefinition()对象BeanDefinition beanDefinition new RootBeanDefinition();beanDefinition.setBeanClassName(com.itheima.dao.impl.PersonDaoImpl);//不需要强转就可以创建一个BeanbeanDefinitionRegistry.registerBeanDefinition(personDao,beanDefinition);}Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println(MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法);} }public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){System.out.println(MyBeanFactoryPostProcessor的postProcessBeanFactory方法); // //1.注册一个beandefinition 创建一个RootBeanDefinition()对象 // BeanDefinition BeanDefinition new RootBeanDefinition(); // BeanDefinition.setBeanClassName(com.itheima.dao.impl.PersonDaoImpl); // //2.将beanFactory强转成DefaultListableBeanFactory类型 // DefaultListableBeanFactory defaultListableBeanFactory (DefaultListableBeanFactory) beanFactory; // defaultListableBeanFactory.registerBeanDefinition(personDao,BeanDefinition);} }bean classcom.itheima.processor.MyBeanFactoryPostProcessor/beanbean classcom.itheima.processor.MyBeanDefinitionRegistryPostProcessor/bean测试 可见执行顺序 6.4 完善实例化流程图 BeanFactoryPostProcessor在SpringBean的实例化过程中的体现 记图非常重要 6.5 自定义Component 要求 自定义MyComponent注解使用在类上;使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;自定义BeanFactoryPostProcessor完成注解MyComponent的解析解析后最终被Spring管理。 utils包下BaseClassScanUtils package com.itheima.utils;import com.itheima.anno.MyComponent; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils;import java.util.HashMap; import java.util.Map;public class BaseClassScanUtils {//设置资源规则private static final String RESOURCE_PATTERN /**/*.class;public static MapString, Class scanMyComponentAnnotation(String basePackage) {//创建容器存储使用了指定注解的Bean字节码对象MapString, Class annotationClassMap new HashMapString, Class();//spring工具类可以获取指定路径下的全部类ResourcePatternResolver resourcePatternResolver new PathMatchingResourcePatternResolver();try {String pattern ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX ClassUtils.convertClassNameToResourcePath(basePackage) RESOURCE_PATTERN;Resource[] resources resourcePatternResolver.getResources(pattern);//MetadataReader 的工厂类MetadataReaderFactory refractory new CachingMetadataReaderFactory(resourcePatternResolver);for (Resource resource : resources) {//用于读取类信息MetadataReader reader refractory.getMetadataReader(resource);//扫描到的classString classname reader.getClassMetadata().getClassName();Class? clazz Class.forName(classname);//判断是否属于指定的注解类型if(clazz.isAnnotationPresent(MyComponent.class)){//获得注解对象MyComponent annotation clazz.getAnnotation(MyComponent.class);//获得属value属性值String beanName annotation.value();//判断是否为if(beanName!null!beanName.equals()){//存储到Map中去annotationClassMap.put(beanName,clazz);continue;}//如果没有为,那就把当前类的类名作为beanNameannotationClassMap.put(clazz.getSimpleName(),clazz);}}} catch (Exception exception) {}return annotationClassMap;}public static void main(String[] args) {MapString, Class stringClassMap scanMyComponentAnnotation(com.itheima);System.out.println(stringClassMap);} }anno包下 Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) public interface MyComponent {String value(); } beans包下 MyComponent(otherBean) public class OtherBean { }bean容器 bean classcom.itheima.processor.MyBeanDefinitionRegistryPostProcessor/beanprocessor包下的MyComponentBeanFactoryPostProcessor public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {//通过扫描工具去扫描指定包及其子包下的所有雷手机使用Mycomponent的注解的类MapString, Class myComponentAnnotationMap BaseClassScanUtils.scanMyComponentAnnotation(com.itheima);//遍历Map 组装BeanDefinition进行注册myComponentAnnotationMap.forEach((beanName,clazz)-{//获得beanClassNameString beanClassName clazz.getName();//com.itheima.beans.OtherBean//创建BeanDefinitionBeanDefinition beanDefinition new RootBeanDefinition();beanDefinition.setBeanClassName(beanClassName);//注册beanDefinitionRegistry.registerBeanDefinition(beanClassName,beanDefinition);});}Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {} }测试 Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);OtherBean otherBean applicationContext.getBean(OtherBean.class);System.out.println(otherBean);}七 Spring的Bean后处理器 7.1 BeanPostProcessor Bean被实例化后到最终缓存到名为singletonObjects单例池之前中间会经过Bean的初始化过程例如:属性的填充、初始方法init的执行等其中有一个对外进行扩展的点BeanPostProcessor我们称为Bean后处理。跟上面的Bean工厂后处理器相似它也是一个接口实现了该接口并被容器管理的BeanPostProcessor会在流程节点上被Spring自动调用。 public class MyBeanPostProcessor implements BeanPostProcessor {Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanNamepostProcessBeforeInitialization);return bean;}Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanNamepostProcessAfterInitialization);return bean;} }public class UserDaoImpl implements UserDao {private String username;public void setUsername(String username) {this.username username;}public UserDaoImpl(){System.out.println(userDao实例化);} }!-- bean iduserService classcom.itheima.service.impl.UserServiceImpl/bean--bean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean!-- bean classcom.itheima.processor.MyBeanFactoryPostProcessor/bean-- !-- bean classcom.itheima.processor.MyBeanDefinitionRegistryPostProcessor/bean--!-- bean classcom.itheima.processor.MyComponentBeanFactoryPostProcessor/bean--bean classcom.itheima.processor.MyBeanPostProcessor/bean测试 Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);}postProcessBeforeInitialization方法和postProcessAfterInitialization方法需要进行手动创建接口中实现的是null返回值的方法两者方法在bena创建之后执行 7.2 before和after方法的执行时机 先执行bean的构造方法执行before方法执行InitializingBean接口中的afterPropertiesSet()方法执行在xml文件中设置的Bean的init-method方法执行after方法 public class UserDaoImpl implements UserDao, InitializingBean {private String username;public void setUsername(String username) {this.username username;}public UserDaoImpl(){System.out.println(userDao实例化);}public void init(){System.out.println(init初始化方法执行。。。);}Overridepublic void afterPropertiesSet() throws Exception {System.out.println(属性在设置之后执行。。。);} }!-- bean iduserService classcom.itheima.service.impl.UserServiceImpl/bean--bean iduserDao classcom.itheima.dao.impl.UserDaoImpl init-methodinit/bean!-- bean classcom.itheima.processor.MyBeanFactoryPostProcessor/bean-- !-- bean classcom.itheima.processor.MyBeanDefinitionRegistryPostProcessor/bean--!-- bean classcom.itheima.processor.MyComponentBeanFactoryPostProcessor/bean--bean classcom.itheima.processor.MyBeanPostProcessor/bean测试 Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);Object userDao applicationContext.getBean(userDao);}7.3 案例-对Bean方法执行日志功能增强 Processor类 public class TimeLogBeanPostProcessor implements BeanPostProcessor {Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {//使用动态代理对目标Bean进行增强返回proxy对象进而存储到singletonObjects中Object beanProxy Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy,method,args)-{//1.输出开始时间System.out.println(方法method.getName()-开始时间new Date());//2.执行目标方法Object result method.invoke(bean, args);//3.输出结束时间System.out.println(方法method.getName()-结束时间new Date());return result;});return beanProxy;} } 接口实现类 public interface UserDao {void show(); }public class UserDaoImpl implements UserDao{Overridepublic void show() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(show);} }xml文件 bean iduserDao classcom.itheima.dao.impl.UserDaoImpl/beanbean classcom.itheima.processor.TimeLogBeanPostProcessor/bean测试 Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserDao userDao (UserDao) applicationContext.getBean(userDao);userDao.show();}7.4 再次完善实例化基本流程图 BeanPostProcessor在 SpringBean的实例化过程中的体现 八 小黑子Spring Bean的生命周期 8.1 概述 Spring Bean的生命周期是从Bean 实例化之后即通过反射创建出对象之后导Bean成为一个完整对象最终储存到单例池中这个过程被称为SpringBean的生命周期。Spring Bean的生命周期大体上分为三个阶段: Bean的实例化阶段: Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的是否不是FactoryBean等最终将一个普通的singleton的Bean通过反射进行实例化Bean的初始化阶段∶Beane创建之后还仅仅是个”半成品“还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段Aop增强功能后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的Bean的完成阶段:经过初始化阶段Bean就成为了一个完整的Spring Bean被存储到单例池singletonObjects中去了即完成了Spring Bean的整个生命周期。 8.2 初始化阶段执行步骤 由于Bean的初始化阶段的步骤比较复杂所以着重研究Bean的初始化阶段 Spring Bean的初始化过程涉及如下几个过程: Bean实例的属性填充 Aware接口属性注入 BeanPostProcessor的before()方法回调 lnitializingBean接口的初始化方法回调 自定义初始化方法init回调 BeanPostProcessor的after()方法回调 8.3 初始化阶段注入属性信息封装 BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储 bean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserDao refuserDao/propertyproperty nameusername valuehaohao/property/beanbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean例如UserService的属性信息 Bean实例的属性填充 8.4 属性注入的三种情况 Spring在进行属性注入时会分为如下几种情况: 注入普通属性String、int或存储基本类型的集合时直接通过set方法的反射设置进去;注入单向对象引用属性时从容器中getBean获取后通过set方法反射设置进去如果容器中没有则先创建被注入对象Bean实例完成整个生命周期后在进行注入操作注入双向对象引用属性时就比较复杂了涉及了循环引用循环依赖问题下面会详细阐述解决方案。 8.5 注入单向对象的代码验证 public class UserServiceImpl implements UserService {public UserServiceImpl(){System.out.println(userService创建);}private UserDao userDao;private String username;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println(userService执行注入userDao的操作setUserDao方法执行);this.userDao userDao;}public void setUsername(String username) {this.username username;} } public class UserDaoImpl implements UserDao{public UserDaoImpl(){System.out.println(userDao创建);}Overridepublic void show() {System.out.println(show);} } 1、user的bean在userService的前面 bean iduserDao classcom.itheima.dao.impl.UserDaoImpl/beanbean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserDao refuserDao/propertyproperty nameusername valuehaohao/property/bean测试 Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService bean applicationContext.getBean(UserService.class);}2、user的bean在userService的后面时 bean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserDao refuserDao/propertyproperty nameusername valuehaohao/property/beanbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean8.6 循环依赖概念及解决方案 多个实体之间相互依赖并形成闭环的情况就叫做 “循环依赖”也叫做 “循环引用” 8.7 三级缓存的设计原理 Spring提供了三级缓存存储完整Bean实例和半成品Bean实例用于解决循环引用问题 在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map: public class DefaultsingletonBeanRegistry ... {//1、最终存储单例Bean成品的容器即实例化和初始化都完成的Bean称之为一级缓存MapString,Object singletonObjects new ConcurrentHashMap(256);//2、早期Bean单例池缓存半成品对象且当前对象已经被其他对象引用了称之为二级缓存MapString,Object earlySingletonObjects new ConcurrentHashMap(16);//3、单例Bean的工厂池缓存半成品对象对象未被引用使用时在通过工厂创建Bean称之为三级缓存MapString,ObjectFactory? singletonFactories new HashMap(16); }8.8 循环依赖源码流程剖析 UserService和UserDao循环依赖的过程结合上述三级缓存描述一下 UserService 实例化对象但尚未初始化将UserService存储到三级缓存;UserService 属性注入需要UserDao从缓存中获取没有UserDao;UserDao 实例化对象但尚未初始化将UserDao存储到到三级缓存;UserDao 属性注入需要UserService从三级缓存获取UserServiceUserService从三级缓存移入二级缓存;UserDao执行其他生命周期过程最终成为一个完成Bean存储到一级缓存删除二三级缓存;UserService注入UserDao;UserService执行其他生命周期过程最终成为一个完成Bean存储到一级缓存删除二三级缓存。 1. 2. 3. 8.9 Aware接口 Aware接口是一种框架辅助属性注入的一种思想其他框架中也可以看到类似的接口。框架具备高度封装性我们接触到的一般都是业务代码一个底层功能API不能轻易的获取到但是这不意味着永远用不到这些对象如果用到了就可以使用框架提供的类似Aware的接口让框架给我们注入该对象。 Aware接口回调方法作用ServletContextAwaresetServletContext(ServletContext context)Spring框架回调方法注入ServletContext对象,web环境下才生效BeanFactoryAwaresetBeanFactory(BeanFactory factory)Spring框架回调方法注入beanFactory对象BeanNameAwaresetBeanName(String beanName)Spring框架回调方法注入当前Bean在容器中的beanNameApplicationContextAwaresetApplicationContext(ApplicationContext applicationContext)Spring框架回调方法注入applicationContext对象 九 小黑子对Spring IoC整体流程总结 十 小黑子进行Spring xml方式整合第三方框架 xml整合第三方框架有两种整合方案 不需要自定义名空间不需要使用Spring的配置文件配置第三方框架本身内容例如: MyBatis; 需要引入第三方框架命名空间需要使用Spring的配置文件配置第三方框架本身内容例如Dubbo 10.1 Mybatis整合Spring实现 Spring整合MyBatis之前已经在Spring中简单的配置了SqlSessionFactory但是这不是正规的整合方式,MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。 Spring整合MyBatis的步骤如下: 导入MyBatis整合Spring的相关坐标; dependencygroupIdorg.mybatis/groupIdartifactIdmybatis-spring/artifactIdversion3.0.1/version /dependency dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion5.2.25.RELEASE/version /dependency编写Mapper和Mapper.xml;配置SqlSessionFactoryBean和MapperScannerConfigurer; !-- 配置数据源信息--bean iddataSource classcom.alibaba.druid.pool.DruidDataSourceproperty namedriverClassName valuecom.mysql.jdbc.Driver/propertyproperty nameurl valuejdbc:mysql://localhost:3306/mybatis/propertyproperty nameusername valueroot/propertyproperty namepassword valueroot/property/bean !-- 配置SqlSessionFactoryBean作用将SqlSessionFactory储存导spring容器中--bean classorg.mybatis.spring.SqlSessionFactoryBeanproperty namedataSource refdataSource/property/bean !-- 作用扫描指定的包产生Mapper对象储存导spring容器当中--bean classorg.mybatis.spring.mapper.MapperScannerConfigurerproperty namebasePackage valuecom.itheima.mapper/property/beanbean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserMapper refuserMapper/property/beanbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean编写测试代码 public class UserServiceImpl implements UserService {//需要Mapperprivate UserMapper userMapper;public void setUserMapper(UserMapper userMapper) {this.userMapper userMapper;}Overridepublic void show() {ListUser all userMapper.selectAll();for(User user : all){System.out.println(user);}}}package com.itheima.pojo;public class User {private Long id;private String username;private String password;private String gender;private String addr;Overridepublic String toString() {return User{ id id , username username \ , password password \ , gender gender \ , addr addr \ };}public Long getId() {return id;}public void setId(Long id) {this.id id;}public String getUsername() {return username;}public void setUsername(String username) {this.username username;}public String getPassword() {return password;}public void setPassword(String password) {this.password password;}public String getGender() {return gender;}public void setGender(String gender) {this.gender gender;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr addr;} } package com.itheima.mapper;import com.itheima.pojo.User;import java.util.List;public interface UserMapper {ListUser selectAll(); } ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.itheima.mapper.UserMapperselect idselectAll resultTypecom.itheima.pojo.Userselect * from tb_user/select /mapper?xml version1.0 encodingUTF-8 ? !DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttps://mybatis.org/dtd/mybatis-3-config.dtd configurationenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.jdbc.mybatis/property nameurl valuejdbc:mysql://localhost:3306/mybatis/property nameusername valueroot/property namepassword valueroot//dataSource/environment/environmentsmapperspackage namecom.itheima.mapper//mappers/configurationTestpublic void BeanTest6(){ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(beans.xml);UserService userService applicationContext.getBean(UserService.class);userService.show();}10.2 Mybatis整合Spring源码解析 整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象SqlSessionFactoryBean一旦被实例化就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类: SqlSessionFactoryBean需要进行配置用于提供SqlSessionFactory;MapperScannerConfigurer需要进行配置用于扫描指定mapper注册BeanDefinition;MapperFactoryBean:Mapper的FactoryBean获得指定Mapper时调用getObject方法;ClassPathMapperScannerdefinition.setAutowireMode(2)修改了自动注入状态所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。 MapperScanerConfiguer源码分析 10.3 加载外部properties文件 Spring整合其他组件时就不像MyBatis这么简单了例如Dubbo框架在于Spring进行整合时要使用Dubbo提供的命名空间的扩展方式自定义了一些Dubbo的标签 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:dubbohttp://dubbo.apache.org/schema/dubboxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbohttp://dubbo.apache.org/schema/dubbo/dubbo.xsd!-- 配置Dubbo应用信息 --dubbo:application nameyour-application-name /!-- 配置注册中心 --dubbo:registry addresszookeeper://127.0.0.1:2181 /!-- 配置服务提供者 --dubbo:protocol namedubbo port20880 /dubbo:service interfacecom.example.YourServiceInterface refyourServiceBean /!-- 配置其他Bean --!-- 消费者配置 --dubbo:consumer checkfalse timeout1000 retries0/ /beans 通过配置context文件来加载外部properties文件 jdbc.properties jdbc.driver com.mysql.cj.jdbc.Driver jdbc.url jdbc:mysql://localhost:3306/mybatis jdbc.username root jdbc.password rootbean.xml !-- 加载properties文件--context:property-placeholder locationclasspath:jdbc.properties/context:property-placeholder !-- 配置数据源信息--bean iddataSource classcom.alibaba.druid.pool.DruidDataSourceproperty namedriverClassName value${jdbc.driver}/propertyproperty nameurl value${jdbc.url}/propertyproperty nameusername value${jdbc.username}/propertyproperty namepassword value${jdbc.password}/property/bean自定义命名空间解析原理 10.4 自定义空间步骤 将自定义标签的约束与物理约束文件与网络约束名称的约束以键值对形式存储到一个spring.schemas文件里该文件存储在类加载路径的META-INF里Spring会自动加载到;将自定义命名空间的名称与自定义命名空间的处理器映射关系以键值对形式存在到一个叫spring.handlers文件里该文件存储在类加载路径的 META-INF里Spring会自动加载到;准备好NamespaceHandler如果命名空间只有一个标签那么直接在parse方法中进行解析即可一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签那么可以在init方法中为每个标签都注册一个BeanDefinitionParser在执行NamespaceHandler的parse方法时在分流给不同的 BeanDefinitionParser进行解析(重写doParse方法即可)。 10.5 自定义命名空间案例 步骤分析 确定命名空间名称、schema虚拟路径、标签名称;编写schema约束文件haohao-annotation.xsd在类加载路径下创建META目录编写约束映射文件spring.schemas和处理器映射文件spring.handlers编写命名空间处理器HaohaoNamespaceHandler在init方法中注册HaohaoBeanDefinitionParser编写标签的解析器HaohaoBeanDefinitionParser在parse方法中注册HaohaoBeanPostProcessor编写HaohaoBeanPostProcessor 以上五步是框架开发者写的以下是框架使用者写的 在applicationContext.xml配置文件中引入命名空间在applicationContext.xml配置文件中使用自定义的标签
文章转载自:
http://www.morning.qcnk.cn.gov.cn.qcnk.cn
http://www.morning.gjtdp.cn.gov.cn.gjtdp.cn
http://www.morning.rcdmp.cn.gov.cn.rcdmp.cn
http://www.morning.txrq.cn.gov.cn.txrq.cn
http://www.morning.kjcll.cn.gov.cn.kjcll.cn
http://www.morning.cbndj.cn.gov.cn.cbndj.cn
http://www.morning.ryzgp.cn.gov.cn.ryzgp.cn
http://www.morning.rrxgx.cn.gov.cn.rrxgx.cn
http://www.morning.dfckx.cn.gov.cn.dfckx.cn
http://www.morning.phzrq.cn.gov.cn.phzrq.cn
http://www.morning.3ox8hs.cn.gov.cn.3ox8hs.cn
http://www.morning.rcntx.cn.gov.cn.rcntx.cn
http://www.morning.kzslk.cn.gov.cn.kzslk.cn
http://www.morning.xkwyk.cn.gov.cn.xkwyk.cn
http://www.morning.thxfn.cn.gov.cn.thxfn.cn
http://www.morning.tlpgp.cn.gov.cn.tlpgp.cn
http://www.morning.zztkt.cn.gov.cn.zztkt.cn
http://www.morning.pjjkz.cn.gov.cn.pjjkz.cn
http://www.morning.dnls.cn.gov.cn.dnls.cn
http://www.morning.syznh.cn.gov.cn.syznh.cn
http://www.morning.klltg.cn.gov.cn.klltg.cn
http://www.morning.jrqcj.cn.gov.cn.jrqcj.cn
http://www.morning.c7501.cn.gov.cn.c7501.cn
http://www.morning.ylph.cn.gov.cn.ylph.cn
http://www.morning.lyhrg.cn.gov.cn.lyhrg.cn
http://www.morning.gtwtk.cn.gov.cn.gtwtk.cn
http://www.morning.rkzb.cn.gov.cn.rkzb.cn
http://www.morning.wrlxy.cn.gov.cn.wrlxy.cn
http://www.morning.bpmth.cn.gov.cn.bpmth.cn
http://www.morning.tnmmp.cn.gov.cn.tnmmp.cn
http://www.morning.ryxyz.cn.gov.cn.ryxyz.cn
http://www.morning.jqrp.cn.gov.cn.jqrp.cn
http://www.morning.xbmwm.cn.gov.cn.xbmwm.cn
http://www.morning.gybnk.cn.gov.cn.gybnk.cn
http://www.morning.yrflh.cn.gov.cn.yrflh.cn
http://www.morning.xzlp.cn.gov.cn.xzlp.cn
http://www.morning.ccsdx.cn.gov.cn.ccsdx.cn
http://www.morning.kqbjy.cn.gov.cn.kqbjy.cn
http://www.morning.ckhry.cn.gov.cn.ckhry.cn
http://www.morning.zlcsz.cn.gov.cn.zlcsz.cn
http://www.morning.wmgjq.cn.gov.cn.wmgjq.cn
http://www.morning.wmrgp.cn.gov.cn.wmrgp.cn
http://www.morning.xjnjb.cn.gov.cn.xjnjb.cn
http://www.morning.ypxyl.cn.gov.cn.ypxyl.cn
http://www.morning.rmtmk.cn.gov.cn.rmtmk.cn
http://www.morning.kngx.cn.gov.cn.kngx.cn
http://www.morning.cltrx.cn.gov.cn.cltrx.cn
http://www.morning.jzfrl.cn.gov.cn.jzfrl.cn
http://www.morning.yllym.cn.gov.cn.yllym.cn
http://www.morning.mnslh.cn.gov.cn.mnslh.cn
http://www.morning.hkpyp.cn.gov.cn.hkpyp.cn
http://www.morning.nnhfz.cn.gov.cn.nnhfz.cn
http://www.morning.tcylt.cn.gov.cn.tcylt.cn
http://www.morning.qztdz.cn.gov.cn.qztdz.cn
http://www.morning.rjrlx.cn.gov.cn.rjrlx.cn
http://www.morning.qswws.cn.gov.cn.qswws.cn
http://www.morning.pyzt.cn.gov.cn.pyzt.cn
http://www.morning.txrq.cn.gov.cn.txrq.cn
http://www.morning.tntgc.cn.gov.cn.tntgc.cn
http://www.morning.yrjfb.cn.gov.cn.yrjfb.cn
http://www.morning.pxjp.cn.gov.cn.pxjp.cn
http://www.morning.pxwzk.cn.gov.cn.pxwzk.cn
http://www.morning.gtnyq.cn.gov.cn.gtnyq.cn
http://www.morning.kcbml.cn.gov.cn.kcbml.cn
http://www.morning.nnwmd.cn.gov.cn.nnwmd.cn
http://www.morning.kqqk.cn.gov.cn.kqqk.cn
http://www.morning.wrtw.cn.gov.cn.wrtw.cn
http://www.morning.zgnng.cn.gov.cn.zgnng.cn
http://www.morning.mfjfh.cn.gov.cn.mfjfh.cn
http://www.morning.bgqr.cn.gov.cn.bgqr.cn
http://www.morning.ldqzz.cn.gov.cn.ldqzz.cn
http://www.morning.hchrb.cn.gov.cn.hchrb.cn
http://www.morning.bloao.com.gov.cn.bloao.com
http://www.morning.fdrwk.cn.gov.cn.fdrwk.cn
http://www.morning.zdxinxi.com.gov.cn.zdxinxi.com
http://www.morning.zlnmm.cn.gov.cn.zlnmm.cn
http://www.morning.knscf.cn.gov.cn.knscf.cn
http://www.morning.lszjq.cn.gov.cn.lszjq.cn
http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn
http://www.morning.cdrzw.cn.gov.cn.cdrzw.cn
http://www.tj-hxxt.cn/news/241749.html

相关文章:

  • 医院客户做网站找谁怎么做网站的界面
  • 网站设计怎么做图片透明度wordpress 明星主题
  • 涿州建设局网站阳新县建设局网站
  • 电脑 手机网站建站网站服务器防护
  • 官方网站建设的公司门户网站模板html5
  • 观音桥网站建设网站建设将html嵌入wordpress
  • 教育教学成果展示网站建设wordpress检索
  • 网站ico添加f福州网站建设公司
  • wordpress中文网站殷氏科技网站建设工作室
  • 自己做网站能否赚钱6企业网站建设制作公司哪家好
  • 片网站无法显示网站建设好公司
  • 创意做网站公司苏州的建筑公司网站
  • 免费门户网站模板下载昆山设计网站公司
  • 广告平台网站有哪些wordpress修改后台管理地址=
  • 专业做网站全包提高网站打开速度
  • 网站开发折旧开发电子商务系统的五个步骤
  • 网站超市网站建设怎样接业务
  • 网站维护页面 下载建设网上银行登录
  • 简约网站程序网站源码下载炫酷
  • 有什么网站可以接单做兼职的企业铭做网站
  • 狼雨seo网站专做老酒的网站
  • 查看网站有没有备案微信公众号管理平台登录
  • 建立网站可以赚钱吗域名购买是什么意思
  • 石嘴山网站定制开发建设成都最差的十大物业公司
  • 邢台网站设计华北冶建工程建设有限公司网站
  • 网站开发和网页制作湖南自驾旅游与房车协会
  • 西安推荐企业网站制作平台韩雪冬推荐网站
  • 洱源县建设局门户网站物流网站推广怎么做
  • 石家庄新钥匙建站手机网站 用户体验
  • 网站优化自己可以做吗c2c平台是洗钱吗