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

网站开发的背景是指什么软件中国采购与招标网官网

网站开发的背景是指什么软件,中国采购与招标网官网,海淀网站制作,重庆消防网SpringBoot低级 SpringBoot概述 SpringBoot提供了一种快速使用Spring的方式#xff0c;基于约定优于配置#xff0c;可以让开发人员不必配置与逻辑业务之间进行思维的切换#xff0c;全身心的投入到逻辑业务的代码编写中#xff0c;从而大大提高了开发的效率#xff0c;…SpringBoot低级 SpringBoot概述 SpringBoot提供了一种快速使用Spring的方式基于约定优于配置可以让开发人员不必配置与逻辑业务之间进行思维的切换全身心的投入到逻辑业务的代码编写中从而大大提高了开发的效率一定程度上缩短了项目周期。 Spring缺点 配置繁琐依赖繁琐 SpringBoot功能 自动配置 springboot的自动配置是一个运行时的过程考虑了众多因素才决定Spring配置应该用哪个不该用哪个。该过程是SpringBoot自动完成的 起步依赖 就是Maven将具备某种过功能的某些包打包到一起并提供了一些默认的功能然后传给项目 辅助功能 提供了一些大型项目的非功能性特性如嵌入式服务器、安全、指标、健康检测等 SpringBoot并不是对Spring功能的增强而是提供了一种快速使用Spring的方式 SpringBoot快速入门 引导类Demo1Application package com.example.demo;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class Demo1Application {public static void main(String[] args) {SpringApplication.run(Demo1Application.class, args);}}HelloApplication package com.example.demo.demos.web;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class HelloSpringBoot {RequestMapping(/hello)public String hello(){return hello Spring Boot 嘤嘤嘤;} } application.yml server:port: 8080index.html html body h1hello word!!!/h1 pthis is a html page/p /body /html运行项目后我们可以在浏览器网页栏搜索localhost:8080就会打开对应的web程序 小结 SpringBoot在创建项目时使用jar的打包方式SpringBoot的引导类是项目入口运行main方法就可以完全启动项目使用SpringBoot和Spring构建的项目业务代码编写方式完全一样 SpringBoot起步依赖原理分析 SpringBoo;t提供的一种依赖管理机制极大地简化了Spring应用程序的开发过程 起步依赖是一种特殊的依赖它将一组相关的依赖项打包在一起方便开发者快速引入。例如spring-boot-starter-web 包含了开发 Web 应用所需的所有依赖如 Spring MVC、Tomcat 等。 作用 简化依赖管理无需依次添加每个组件秩序引入一个起步依赖即可版本一致性依赖版本由Spring Boot进行维护确保版本兼容自动配置引入岂不依赖后SpringBoot会自动进行相关配置减少手动配置的工作量 原理 Maven依赖传递机制当引入一个起步依赖时Maven 会自动解析并引入该依赖中声明的所有传递依赖。自动配置机制Spring Boot 通过 EnableAutoConfiguration 注解通常由 SpringBootApplication 指定来启用自动配置。 小结 在spring-boor-start-parent中定义了各种技术的版本信息组合了一套最优搭配的技术版本在各种starter中定义了完成该功能需要的坐标集合其中大部分版本信息来自于父工程我们的工程继承parent引入starter后通过依赖传递就可以简单方便的获取jar包并且不会存在版本冲突等问题 SpringBoot配置 配置文件分类 SpringBoot是基于约定的所以很多配置都有默认值但如果想使用自己的配置替换默认配置的话就可以使用application.properties或者application.yml(application.yaml)进行配置 特点 全局作用域配置项对整个应用程序有效自动加载SpringBoot会自动加载application.properties和application.yml中的配置优先级同一级目录下propertiesymlyaml默认配置文件名称application yaml 是一种能够直观的被电脑识别的数据序列化格式并且容易被人类阅读容易和脚本语言交互可以被支持YAML库的不同变成语言导入YAML文件是以数据为核心的比传统的xml方式更加简洁YAML文件的拓展名可以使用.yml或者.yaml 比较各种配置文件 properties: server.port8080 servet.address127.0.0.1xml: serverport8080/portaddress127.0.0.1/address /serveryml: (简洁直观以数据为核心) server:port: 8080address: 127.0.0.1YAML基本语法 大小写敏感数据值前边必须有空格作为分隔符使用缩进表示层级关系缩进下空格数目不重要但必须有并且相同层级的元素左侧对齐即可‘# ’表示注释从这个字符一直到行尾都会被解析器忽略 YAML:数据格式 对象(map): 键值对的集合数组(address): 一组按次序排序的值纯量: 单个的、不可再分的值 server:port: 8080person:name: zhangsan# map的行内写法 person2: {name: zhangsan}address:- beijing- shanghai# 数组的行内写法 address2: [beijing,shanghai]mag1: hello \n world # 单引号忽略转义字符 map2: hello \n world # 双引号识别转义字符# 参数引用 name lisi person:name: ${name}读取配置文件内容 HelloController类 package com.example.demo.demos.web;import com.example.demo.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class HelloController {Value(${name})private String name;Value(${person.name})private String name2;Value(${address[0]})private String address;Value(${mag1})private String mg1;Value(${mag2})private String mg2;Autowiredprivate Environment evn;Autowiredprivate Person person;// 方式1 以Value注解方式进行RequestMapping(/hello3)public String hello3() {System.out.println(name);System.out.println(name2);System.out.println(address);System.out.println(mg1);System.out.println(mg2);System.out.println(person);return hello3;}// 方式2 以Environment类进行RequestMapping(/hello2)public String hello2() {System.out.println(evn.getProperty(name));System.out.println(evn.getProperty(address[0]));System.out.println(evn.getProperty(mag1));System.out.println(evn.getProperty(mag2));System.out.println(evn.getProperty(person.name));return evn.getProperty(person.name);}} application.yml server:port: 8080name: abcperson:name: zhangsamage: 200# map的行内写法 person2: {name: zhangsan}address:- beijing- shanghai# 数组的行内写法 address2: [beijing,shanghai]mag1: hello \n world # 单引号忽略转义字符 mag2: hello \n world # 双引号识别转义字符Person类 package com.example.demo;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;Component ConfigurationProperties(prefix person) public class Person {// 这些属性名要和对应的yaml里面的一一对应private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}Overridepublic String toString() {return Person{ name name \ , age age };} } 小结三种方式获取数据 用Value注解修饰属性使用Autowired注入一个Environment对象然后通过该对象的getProperty属性获取定义一个新Bean容器管理的类在类上添加注解Configuration默认识别第一级的可以通过修改参数prefix定义前缀 profile 我们在开发Spring Boot应用时通常同一套程序会被安装到不同环境比如开发、测试、生产等。其中数据库地址、服务器端口等等配置都不同如果每次打包都要修改配置文件那么非常麻烦profile功能就是来进行动态配置切换的 profile配置方式 多profile文件方式yml多文档方式 prifile激活方式 配置文件虚拟机参数命令行参数 多profile文件方式 application.properties name abc spring.profiles.activedevapplication-dev.properties spring.profiles.activedevapplication-pro.properties spring.profiles.activeproapplication-test.properties spring.profiles.activetestyml多文档方式 # 通过激活不同的 ProfileSpring Boot 会加载与该 Profile 对应的配置文件 --- server:port: 8081 spring:config:activate:on-profile: pro --- server:port: 8082 spring:config:activate:on-profile: dev --- server:port: 8083 spring:config:activate:on-profile: test ---spring:profiles:active: devProfile-小结 profile 是用来完成不同环境下配置动态切换功能的。profile 配置方式 多profile文件方式提供多个配置文件每个代表一种环境。 application-dev.properties/yml 开发环境application-test.properties/yml 测试环境application-pro.properties/yml 生产环境 yml多文档方式 在yml中使用 --- 分隔不同配置 profile 激活方式 配置文件再配置文件中配置spring.profiles.activedev虚拟机参数在VM options 指定-Dspring.profiles.activedev命令行参数java -jar xxx.jar --spring.profiles.activedev 内部配置加载顺序 我们之前的配置文件都是放在配置文件都是放在resources文件夹根据当前目录下优先级的高低判断谁先被加载比如properties yml yaml 实际开发这种我们写的配置文件并不是都放在一起的甚至“东一个西一个”这时候就需要判断谁先被加载 SpringBoot程序启动时会从以下位置加载配置文件 file:/config/: 当前项目下的/config目录下file:/ 当前项目的根目录classpath:/config: classpath的/config目录下classpath: classpath的根目录下 加载顺序为上文的排序顺序高优先级配置的属性会生效 小结 内部配置是寄托于IDEA编辑器实现的。对于编写配置命令实现功能我们不仅可以在IDEA中实现还可以打开黑窗口输入命令在外部实现。例如把spring项目打成jar包在“黑窗口”运行大家可以看我SpringBoot配置–Profile这篇博客的命令行参数内容。 对于内部配置加载顺序记住三点 靠近项目优先被加载 具有config下的文件在同等情况下优先被加载 同一级目录下优先级properties yml yaml 上面配置的第一种和第二种是不会被打包到对应的jar包里面的因此如果我们使用package的jar包运行只会识别classpath路径下的配置文件 外部配置加载顺序 外部配置一般都是通过命令行参数实现我们可以运行 java -jar yourapp.jar --serve.port8090后面那部分就是修改我们的端口号多个参数可以之间空格隔开 java -jar yourapp.jar --serve.port9090 --serve.servelet.context-path/hehe甚至我们可以直接加载外部配置文件 java -jar yourapp.jar --spring.config.location路径当然我们也可以直接将配置属性复制到对应的jar包的同一目录下jar包运行时也会自动加载 SpringBoot官网外部配置加载顺序 默认属性通过设置SpringApplication.setDefaultProperties指定。在你的Configuration类上的PropertySource注释。请注意这些属性源在应用上下文刷新之前不会添加到Environment中。因此这对于配置某些属性如logging.和spring.main.来说为时已晚因为这些属性在应用上下文刷新之前被读取。配置数据例如application.properties文件。随机值属性源RandomValuePropertySource其属性仅在random.*。操作系统环境变量。Java系统属性System.getProperties()。来自java:comp/env的JNDI属性。ServletContext init参数。来自SPRING_APPLICATION_JSON的属性内联JSON嵌入在环境变量或系统属性中。命令行参数。测试中的属性属性。在SpringBootTest和用于测试应用特定切片的测试注释中可用。测试中的DynamicPropertySource注释。测试中的TestPropertySource注释。当devtools激活时在$HOME/.config/spring-boot目录中的Devtools全局设置属性。 越往下优先级越高 SpringBoot整合其他框架 Junit 实现步骤 搭建SpringBoot工程引入starter-test依赖编写测试类添加测试相关注解 RunWith(SpringRunner.class)SpringBootTest(classer 启动类.class) 编写测试方法 如果我们是在源根目录下写的test测试类那么测试类中的注解SpringBootTest的括号中不需要添加任何信息 UserService类 package com.lele.springboottest;import org.springframework.stereotype.Service;Service public class UserService {public void add(){System.out.println(add...);} }测试类UserServiceTest package com.lele.springboottest;import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;/*** UserService的测试类*/SpringBootTest(classes SpringbootTestApplication.class) public class UserServiceTest {Autowiredprivate UserService userService;Testpublic void testadd(){userService.add();System.out.println(hello );} } mybatis 实现步骤 搭建SpringBoot工程引入mybatis起步依赖添加mysql驱动编写DataSource 和MyBatis相关配置定义表和实体类编写dao和mapper文件/纯注解开发测试 首先创建数据库并添加两条记录 create database springboot; use springboot; create table t_user(id int(11) NOT NULL primary key auto_increment,username varchar(32) default null,password varchar(32) default null );insert into t_user (id,username,password) values(1,zhangsan,123),(2,lisi,234);​ 我们在下面实现了两种方式的mybatis的注入注解和xml文件 User类 package com.lele.springbotmybatis.domain;import org.springframework.stereotype.Component;public class User {int id;String name;int age;public User(int id, String name, int age) {this.id id;this.name name;this.age age;}Overridepublic String toString() {return User{ id id , name name \ , age age };}public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;} }UserMapper接口 package com.lele.springbotmybatis.mapper;import com.lele.springbotmybatis.domain.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository;import java.util.List;Mapper Repository public interface UserMapper {Select(select * from t_user)public ListUser findAll(); }UserXmlMapper接口 package com.lele.springbotmybatis.mapper;import com.lele.springbotmybatis.domain.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository;import java.util.List;Mapper Repository public interface UserMapper {Select(select * from t_user)public ListUser findAll(); }启动类省略都一样没任何变化 UserXmlMapper.xml ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mbatis-3-mapper.dtd mapper namespacecom.lele.springbotmybatis.mapper.UserXmlMapperselect idfindAll resultTypeuserselect * from t_user/select /mapperappliaction.yml # dataSource spring:datasource:url: jdbc:mysql://localhost:3306/springbootusername: rootpassword: 159357258zx.driver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpathmapper/*Mapper.xmltype-aliases-package: com.lele.springbotmybatis.domain.User # config-location: # 指定mybatis核心配置文件测试类 package com.lele.springbotmybatis;import com.lele.springbotmybatis.domain.User; import com.lele.springbotmybatis.mapper.UserMapper; import com.lele.springbotmybatis.mapper.UserXmlMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;import java.util.List;SpringBootTest class SpringbotMybatisApplicationTests{Autowiredprivate UserMapper userMapper;Autowiredprivate UserXmlMapper userXmlMapper;Testpublic void testfindALl() {ListUser all userMapper.findAll();for (User user : all) {System.out.println(user);}}Testpublic void testfindALl2() {ListUser all userXmlMapper.findAll2();for (User user : all) {System.out.println(user);}}}SpringBoot高级原理分析 SpringBoot其实是对Spring的高度封装虽然用起来很方便但是我们不知道它为什么这么方便、或者它在哪方面还能进行拓展。通过学习SpringBoot的原理分析我们能够更好的使用SpringBoot同时也能学到很多好的设计思想 自动配置 Condition Condition:在Spring4.0增加的条件判断功能可以实现选择性的创建Bean操作 思考SpringBoot是如何知道要创建哪个依赖的 可以看到当我没有导入redis依赖而直接获取Bean时运行出错 package com.lele.springbootcondition;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;SpringBootApplication public class SpringbootConditionApplication {public static void main(String[] args) {// 启动Springboot项目并返回IOC容器ConfigurableApplicationContext context SpringApplication.run(SpringbootConditionApplication.class, args);// 获取BeanredisTemplateObject redisTemplate context.getBean(redisTemplate);System.out.println(redisTemplate);context.close();} }错误信息No bean named ‘redisTemplate’ available 但是当我添加依赖后成功输出 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency因此我们思考Condition可能与依赖有关但它怎么知道我导入了相应的坐标呢 案例在Spring的IOC容器中有一个User的Bean现要求导入Jedis坐标后加载Bean没导入则不加载 核心配置类UserConfig package com.lele.springbootcondition.config;import com.lele.springbootcondition.Condition.ClassCondition; import com.lele.springbootcondition.Condition.ConditionOnClass; import com.lele.springbootcondition.domain.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import com.alibaba.fastjson.JSON;Configuration public class UserConfig {// 下面可以添加一个注解Conditional,它需要传入一个Condition接口的实现类// 接口Condtion里面定义了一个方法我的实现类里面需要重写该方法// 该方法返回一个boolean类型true表示加载Bean,否则不加载Bean//Conditional(ClassCondition.class)ConditionOnClass(com.alibaba.fastjson.JSON)public User user(){return new User();} }第一种方式 是直接使用直接使用自带的Conditional //Conditional(ClassCondition.class)然后定义一个实现Condition接口的方法 ClassCondition类 package com.lele.springbootcondition.Condition;import com.alibaba.fastjson.JSON; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;import java.util.Map;public class ClassCondition implements Condition {/**** param context 上下文对象用于获取环境、IOC容器、ClassLoader对象* param metadata 注解元对象用于获取注解定义的属性值* return*/Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 1. 需求导入Jedis类则加载Bean,否则不加载/*boolean flag;try {Class? cls Class.forName(com.alibaba.fastjson.JSON);flag true;} catch (ClassNotFoundException e) {flag false;}return flag;*///2. 需求 导入通过注解属性值value指定的坐标后创建Bean// 获取注解属性值 valueMapString, Object map metadata.getAnnotationAttributes(ConditionOnClass.class.getName());// System.out.println(map);String[] value (String[])map.get(value);boolean flag false;try {for (String className : value) {Class? cls Class.forName(className);}flag true;} catch (ClassNotFoundException e) {flag false;}return flag;} }第二种方式是我们自己定义一个注解但还是要用系统的注解Conditino覆盖我们自己的 ConditionOnClass注解 package com.lele.springbootcondition.Condition;import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented Conditional(ClassCondition.class) public interface ConditionOnClass {String[] value(); }启动类SpringbootConditionApplication package com.lele.springbootcondition;import com.lele.springbootcondition.domain.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;SpringBootApplication public class SpringbootConditionApplication {public static void main(String[] args) {// 启动Springboot项目并返回IOC容器ConfigurableApplicationContext context SpringApplication.run(SpringbootConditionApplication.class, args);// // 获取BeanredisTemplate // Object redisTemplate context.getBean(redisTemplate); // System.out.println(redisTemplate);Object bean context.getBean(user);System.out.println(bean);context.close();} }除了Conditional注解外我们能还有其他注解就不一一演示了都放在小结里面 切换内置Web服务器 SpringBoot的web环境中默认使用tomcat作为内置服务器其中SpringBoot提供了4种内置服务器供我们选择我们可以很方便的进行切换 我们可以在外部库/Maven:org:springframework.boot/spring-boot-autoconfigure-2.6.13,jar /org/springframework/boot/autofigure/web/embedded里面 JettyMettyTomcatUndertow 我们默认都是选择tomcat进行切换的但是如果想要切换可以修改配置文件pom.xml在web的大包里面排除Tomcat的然后外面再导入对应其他web内置服务器即可 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdexclusionsexclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactId/exclusion/exclusions/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jetty/artifactId/dependencyEnable*注解 SpringBoot种提供了很多Enable开头的注解这些注解都是用于东岸提开启某些功能的。而其底层原理就是使用Import注解导入一些配置类实现Bean的动态加载 思考 SpringBoot工程是否可以直接获取Jar包种定义的Bean? 答案肯定是不行的SpringBoot无法直接引用别人jar包里的Bean 那么为什么我们之前引入一个Redis的起步依赖就可以直接获取到RedisTemplate呢 演示 接下来我们引入了两个模块工程springboot-enable和springboot-enable-other springboot-enable-other的作用单纯就是提供bean类 User类 package com.xh.config;public class UserConfig { }UserConfig配置类 package com.xh.config;import com.xh.domain.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/** 表示配置类 */ Configuration public class UserConfig {/** 注入 */Beanpublic User user(){return new User();} }然后就是在springboot-enable工程中pom.xml中添加新的依赖 dependencygroupIdcom.xh/groupIdartifactIdspringboot-enable-other/artifactIdversion0.0.1-SNAPSHOT/version/dependency然后修改启动类中添加获取User类 的代码 public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user context.getBean(user);System.out.println(user);}运行项目然后就报错了提示错误信息是No qualfied bean named ‘user’ available 通过演示我们可以看到SpringBoot不能直接获取我们在其他工程中定义的Bean 原因在启动类中SpringBootApplication注解中有一个ComponentScan注解这个注解扫描的范围是当前引导类所在包及其子包 我们项目的引导类包路径为om.xh.springbootenable 而 UserConfig 所在的包路径为com.xh.config ComponentScan(excludeFilters {Filter(type FilterType.CUSTOM,classes {TypeExcludeFilter.class} ), Filter(type FilterType.CUSTOM,classes {AutoConfigurationExcludeFilter.class} )} )方案 使用ComponentScan我们可以在引导类上使用ComponentScan注解扫描配置类所在的包 SpringBootApplication ComponentScan(com.xh.config) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user context.getBean(user);System.out.println(user);} }这样的方案虽然可以解决这个问题但是看起来不太行如果获取的包的特别多的话那么可能太多了所以这种方案不推荐 使用Import注解被Import注解所导入的类都会被Spring创建并放入IOC容器中如图可以看到Import注解的value值是一个数组可以传多个值 // Import的定义 package org.springframework.context.annotation;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented public interface Import {Class?[] value(); }修改引导类 SpringBootApplication //ComponentScan(com.xh.config) Import(UserConfig.class) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user context.getBean(user);System.out.println(user);} }启动类后可以正常运行方案稍微比方案一好一些需要记住很多类的名字所以仍然不是很方便 对Import注解进行封装 对springboot-enable-other工程中编写注解EnableUser在注解中使用Import注解导入userConfig,并且添加Import的元注解 package com.xh.config;import org.springframework.context.annotation.Import; import java.lang.annotation.*;Import(UserConfig.class) Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented public interface EnableUser { } 修改springboot-enable工程的引导类现在可以使用我们自定义的注解 SpringBootApplication //ComponentScan(com.xh.config) //Import(UserConfig.class) EnableUser public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user context.getBean(user);System.out.println(user);} } 这种自定义注解的方式和方案二是一个原理只不过是在使用上简化了一下。 Import注解 Enable*底层依赖于Import注解导入一些类使用Import导入的类会被Spring加载到IOC容器中而Import提供4种用法。SpringbootAppliation中选择用第三种实现方式 导入Bean导入配置类导入ImportSelector实现类一般用于加载配置文件中的类导入ImportBeanDefinitionRegistrat实现类 导入Bean /*** Import 4 种用法* 1. 导入Bean* 2. 导入配置类* 3. 导入 ImportSelector 实现类。一般用于加载配置文件中的类* 4. 导入 ImportBeanDefinitionRegistrar 实现类。*/ SpringBootApplication //ComponentScan(com.xh.config) //Import(UserConfig.class) //EnableUser Import(User.class) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 Bean // Object user context.getBean(user); // System.out.println(user);// 由于使用 Import 注解导入 User.class 获取到的 Bean 名称不叫 user// 所以通过类型获取 BeanUser user context.getBean(User.class);System.out.println(user user);// 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值MapString, User map context.getBeansOfType(User.class);System.out.println(map map);} }导入配置类 这里我们创建了一个新的对象Role看看一个配置类是否可以加载两个对象 Role package com.xh.domain;public class Role { }在UserConfig里面添加对应方法 Beanpublic Role role(){return new Role();}然后在启动类上修改为对应的注解 SpringBootApplication //ComponentScan(com.xh.config) //EnableUser //Import(User.class) Import(UserConfig.class) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 Bean // Object user context.getBean(user); // System.out.println(user);// 由于使用 Import 注解导入 User.class 获取到的 Bean 名称不叫 user// 所以通过类型获取 BeanUser user context.getBean(User.class);System.out.println(user user);Role role context.getBean(Role.class);System.out.println(role role);// 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值 // MapString, User map context.getBeansOfType(User.class); // System.out.println(map map);} }导入ImportSelector实现类 首先我们先来看看ImportSelector接口里面有一个方法selectImports参数是一个注解元对象可以用来获取一些注解的属性等然后返回String类型数组一些类的全限定名 package org.springframework.context.annotation;import java.util.function.Predicate; import org.springframework.core.type.AnnotationMetadata;public interface ImportSelector {String[] selectImports(AnnotationMetadata importingClassMetadata); }然后我们就来实现该接口 package com.xh.config;import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata;public class MyImportSelector implements ImportSelector {Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// 放入 user、role 的全限定名之后就会自动去加载 user 和对应的 role 了// return new String[]{com.xh.domain.User, com.xh.domain.Role};// 或者可以使用方法获取// 或者我们可以直接导入配置文件然后从配置文件上动态获取return new String[]{User.class.getName(), Role.class.getName()};} }最后修改为对应注解 SpringBootApplication //ComponentScan(com.xh.config) //EnableUser //Import(User.class) //Import(UserConfig.class) Import(MyImportSelector.class) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 Bean // Object user context.getBean(user); // System.out.println(user);// 由于使用 Import 注解导入 User.class 获取到的 Bean 名称不叫 user// 所以通过类型获取 BeanUser user context.getBean(User.class);System.out.println(user user);Role role context.getBean(Role.class);System.out.println(role role); // // 获取 Spring 容器中所有 UserBean 的名称以及 Bean 对应的值 // MapString, User map context.getBeansOfType(User.class); // System.out.println(map map);} }导入ImportBeanDefinitionRegistrar实现类 package com.example.springbootenableother.config;import com.example.springbootenableother.domain.User; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata;/*** author XH* create 2021/12/13* since 1.0.0*/ public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//在 IOC 容器中注册 BeanBean 名称为 user类型为 User.class// 获取 beanDefinitionAbstractBeanDefinition beanDefinition BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();// 注入 userregistry.registerBeanDefinition(user, beanDefinition);} }SpringBootApplication //EnableUser //Import(MyImportSelector.class) Import(MyImportBeanDefinitionRegistrar.class) public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(SpringbootEnableApplication.class, args);// 根据类型获取Object user context.getBean(User.class);System.out.println(user);// 根据 Bean 名称获取Object user1 context.getBean(user);System.out.println(user1); // Object role context.getBean(Role.class); // System.out.println(role);} } EnableAutoConfiguration注解 EnableAutoConfiguration注解内部使用Import(AutoConfigurationImportSelector.class)来加载配置类配置文案金位置META/spring.factories该配置文件中定义了大量的配置类当SpringBoot应用启动时会自动加载这些配置类初始化Bean并不是所有的Bean都会被初始化在配置类中使用了Condition注解来加载满足条件的Bean 小结 自定义条件 定义条件类自定义类实现接口重写matches方法在matches方法中进行逻辑判断返回boolean值。matches方法两个参数 context:上下文对象可以获取属性值获取类加载器获取BeanFactory等metadata:元数据对象用于获取注解属性 判断条件在初始化Bean时使用Conditional(条件类.class)注解 SpringBoot提供的其他常用条件注解 ConditionalOnProperty判断配置文件中是否有对应属性和值才初始化BeanConditionalOnClass: 判断环境中是否有对应字节码文件才初始化BeanConditionalOnMissingBean: 判断环境中没有对应Bean才初始化 切换web内置服务器Jetty、Metty、Tomcat、UnderTowSpringBoot 底层是使用 Import 注解导入一些配置类实现 Bean 的动态加载。 例如 SpringBootApplication 其中的 EnableAutoConfiguration 就使用了 Import 来实现导入其他的类EnableAutoConfiguration注解通过注解的方式启动自动配置类 SpringBoot 监听机制 SpringBoot的监听机制其实就是对java提供的事件监听机制的封装 java监听机制 java中的事件监听机制定义了以下几个角色 事件Event继承java.util.EventObject类的对象事件源Source任意对象Object监听器Listener实现java.util.EventListener接口的对象 SpringBoot监听机制 SpringBoot在项目启动时会对几个监听器进行回调我们可以实现这些监听器接口在项目启动时完成一些操作 ApplicationContextInitializer、SpringApplicationRunListener、CommandLineRunner、AapplicationRunner 基于上面四个接口我们分别实现了四个类 MyApplicationContextInitializer package com.lele.springbootlistener.listener;import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component;Component public class MyApplicationContextInitializer implements ApplicationContextInitializer {Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println(ApplicationContextInitializer... initializing...);} }MyApplicationRunner package com.lele.springbootlistener.listener;import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component;Component public class MyApplicationRunner implements ApplicationRunner {Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println(MyApplicationRunner run);} } CommandLineRunner package com.lele.springbootlistener.listener;import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component;Component public class MyCommandLineRunner implements CommandLineRunner {Overridepublic void run(String... args) throws Exception {System.out.println(MyCommandLineRunner.run(String[]));} }MySpringApplicationRunListener package com.lele.springbootlistener.listener;import org.springframework.boot.ConfigurableBootstrapContext; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component;import java.time.Duration; // 这里一开始和前面一样可以使用Component注解并且去掉构造函数 // 但是我们看springboot自己定义的发现需要两个参数和构造但是又不能自动注入所以要驱动Component注解 public class MySpringApplicationRunListener implements SpringApplicationRunListener {public MySpringApplicationRunListener(SpringApplication application, String[] args) {}Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println(MySpringApplicationRunListener starting);}Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println(MySpringApplicationRunListener environmentPrepared);}Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println(MySpringApplicationRunListener contextLoaded);}Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println(MySpringApplicationRunListener contextPrepared);}Overridepublic void started(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println(MySpringApplicationRunListener started);}Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println(MySpringApplicationRunListener ready);}Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println(MySpringApplicationRunListener failed);} }定义好后我们可以启动项目发现只有MyApplicationRunner和MySpringApplicationRunListener成功运行了这表明系统会自动运行但是如果我们想要运行另外两个该如何实现呢 在classpath:(即resources目录下)新建一个META-INF/spring.factories系统会自动调用里面的配置文件 配置文件里面格式context.listener.classes [ 监听器全类名 ]如 org.springframework.context.ApplicationContextInitializer\com.lele.springbootlistener.listener.MyApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener\com.lele.springbootlistener.listener.MySpringApplicationRunListenerSpringBoot启动流程分析 ┌───────────────────────────────────────────┐ │ 1. main() 调用入口 │ │ │ │ public static void main(String[] args) { │ │ SpringApplication.run(App.class, args);│ │ } │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 2. 创建 SpringApplication 实例 │ │ - 绑定 sources、listeners、initializers │ │ - 推断应用类型SERVLET/REACTIVE/NONE │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 3. 预备阶段prepareEnvironment │ │ - 创建并配置 EnvironmentPropertySources│ │ - 应用 EnvironmentPostProcessor │ │ - 触发 ApplicationEnvironmentPreparedEvent │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 4. 创建 ApplicationContext │ │ - 根据应用类型选择 │ │ • AnnotationConfigServletWebServerApplicationContext │ │ • AnnotationConfigReactiveWebServerApplicationContext│ │ • AnnotationConfigApplicationContext │ │ - 触发 ApplicationContextInitializedEvent │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 5. 上下文预处理prepareContext │ │ - 注册 BeanDefinition扫描 SpringBootConfiguration 来源│ │ - 执行 ApplicationContextInitializer │ │ - 触发 ContextRefreshedEvent 前的事件│ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 6. 刷新上下文refresh │ │ • BeanFactory 后处理 │ │ – BeanDefinitionRegistryPostProcessor│ │ – BeanFactoryPostProcessor │ │ • 注册 BeanPostProcessor │ │ • 初始化单例 Bean依赖注入、生命周期回调 │ │ • 触发 ContextRefreshedEvent │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 7. 启动 Web 容器仅 Servlet/Reactive │ │ - 嵌入式 Tomcat/Jetty/Netty 启动 │ │ - 触发 ServletWebServerInitializedEvent │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 8. 执行 RunnerCommandLineRunner、ApplicationRunner│ │ - 按 Order 顺序执行 │ │ - 触发 ApplicationStartedEvent ApplicationReadyEvent │ └───────────────────────────────────────────┘↓ ┌───────────────────────────────────────────┐ │ 9. 应用启动完成进入正常运行态 │ └───────────────────────────────────────────┘SpringBoot监控概述 SpringBoot自带监控功能Actuator可以帮助实现对程序内部运行情况监控比如监控状况、Bean加载情况、配置属性、日志信息等。 使用步骤 导入依赖坐标 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId /dependency访问http://localhost:8080/acruator 可以看到所有支持的连接默认只有 /actuator /actuator/health /actuator/health/{component} /actuator/health/{component}/{instance} /actuator/info具体的使用方法 引入上述的依赖 通过下面的配置启用所有的监控端点这些端点默认是禁用的 management:endpoints:web:exposure:include: *或者启用下面配置开启部分监控端点 management:endpoints:web:exposure:exclude: beans,trace“*”号代表启用所有的监控端点可以单独启用例如healthinfometrics等 Actuator 默认所有的监控点路径都在/actuator/*当然如果有需要这个路径也支持定制。 management:endpoints:web:base-path: /manage #记得要加上/设置完重启后再次访问地址就会变成/manage/* Autuator的REST接口 Actuator监控分为两类原生端点和用户自定义端点 自定义端点主要指拓展性根据自己的实际应用比如Redis、MyBatis等原生端点在应用程序里提供众多Web接口通过它们了解应用程序运行时的内部状态 应用配置类可以查看应用在运行期的静态信息例如自动配置信息、加载的springbean信息、yml文件配置信息、环境信息等度量指标类主要是运行期的动态信息例如堆栈、请求连等操作控制类主要是指shurdown用户可以发送一个请求将应用的控制功能关闭 GET/auditevents显示应用暴露的审计事件 (比如认证进入、订单失败)GET/beans展示了所有 bean 的别名、类型、是否单例、类的地址、依赖等信息GET/conditions使用 conditions 可以在应用运行时查看代码了某个配置在什么条件下生效或者某个自动配置为什么没有生效。GET/configprops描述配置属性(包含默认值)如何注入BeanGET/env获取全部环境属性GET/env/{name}根据名称获取特定的环境属性值GET/flyway提供一份 Flyway 数据库迁移信息GET/liquidbase显示Liquibase 数据库迁移的纤细信息GET/health报告应用程序的健康指标这些值由 HealthIndicator 的实现类提供用来检查应用的运行状态“UP”表示健康,down不健康GET/heapdump返回一个 GZip 压缩的 JVM 堆 dumpGET/httptrace显示HTTP足迹最近100个HTTP request/repsponseGET/info获取我们自己配置在配置文件中以 info 开头的配置信息GET/logfile返回log file中的内容(如果 logging.file 或者 logging.path 被设置)GET/loggers可以查看当前应用的日志级别等信息GET/metrics是一个非常重要的监控端点其监控内容覆盖了 JVM 内存、堆、类加载、处理器和 tomcat 容器等一些重要指标GET/metrics/{name}报告指定名称的应用程序度量值GET/scheduledtasks展示应用中的定时任务信息GET/sessions如果我们使用了 Spring Session 展示应用中的 HTTP sessions 信息POST/shutdown关闭应用程序要求endpoints.shutdown.enabled设置为trueGET/mappings描述全部的 URI路径以及它们和控制器(包含Actuator端点)的映射关系GET/threaddump生成当前线程活动的快照主要展示了线程名、线程ID、线程的状态、是否等待锁资源等信息。 SpringBoot监控-图形化界面使用 这个要针对两个模块springboot-admin-server和springboot-admin-clients进行 对于springboot-admin-server 首先我们要导入依赖坐标admin-starter-server dependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-server/artifactId/dependency然后在引导类上启用监控功能EnableAdminServer即可 package com.lele.springbootadminserver;import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;EnableAdminServer SpringBootApplication public class SpringbootAdminServerApplication {public static void main(String[] args) {SpringApplication.run(SpringbootAdminServerApplication.class, args);}}对于springboot-admin-clients 导入依赖坐标admin-starter-client dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-client/artifactId/dependency配置相关信息server地址、显示health详细信息、开启所有监控 # 执行admin.server地址 spring.boot.admin.client.urlhttp://localhost:9000# 这一句是表示在health那里显示详细信息 management.endpoint.health.show-detailsalways# 开启所有监控 management.endpoints.web.exposure.include*server.port8080SpringBoot项目部署 SpringBoot项目开发支持两种方式部署到服务器 jar包war包 注意为了防止打包出来的名字过于繁琐我们可以先定义一个finalName标签进行改名 buildfinalNamespringboot-package/finalNamepluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion3.8.1/versionconfigurationsource1.8/sourcetarget1.8/targetencodingUTF-8/encoding/configuration/plugin/plugins /buildjar包 利用的是本地的Tomcat进行打包使用命令行mvn clean package进行打包或者到右边Maven的生存期package 然后就可以运行jar包java -jar finalName.jar war包 利用外部的Tomcat进行打包部署到其他Tomcat服务器上 首先要修改配置类 SpringBootApplication() public class SpringbootstudyApplication extends SpringBootServletInitializer {//继承SpringBootServletInitializer 重写 configureOverrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder){return builder.sources(SpringbootstudyApplication.class);}public static void main(String[] args) {SpringApplication.run(SpringbootstudyApplication.class, args);} } 然后还要在pom.xml里面修改打包方式packagewar/package就可以了
文章转载自:
http://www.morning.gjmll.cn.gov.cn.gjmll.cn
http://www.morning.kflbf.cn.gov.cn.kflbf.cn
http://www.morning.rfwrn.cn.gov.cn.rfwrn.cn
http://www.morning.lrmts.cn.gov.cn.lrmts.cn
http://www.morning.duqianw.com.gov.cn.duqianw.com
http://www.morning.pcjw.cn.gov.cn.pcjw.cn
http://www.morning.jwgnn.cn.gov.cn.jwgnn.cn
http://www.morning.fmznd.cn.gov.cn.fmznd.cn
http://www.morning.pghry.cn.gov.cn.pghry.cn
http://www.morning.nbrdx.cn.gov.cn.nbrdx.cn
http://www.morning.hcqpc.cn.gov.cn.hcqpc.cn
http://www.morning.lpzqd.cn.gov.cn.lpzqd.cn
http://www.morning.fbbpj.cn.gov.cn.fbbpj.cn
http://www.morning.fqhbt.cn.gov.cn.fqhbt.cn
http://www.morning.tqbyw.cn.gov.cn.tqbyw.cn
http://www.morning.fpkdd.cn.gov.cn.fpkdd.cn
http://www.morning.lcbt.cn.gov.cn.lcbt.cn
http://www.morning.grxsc.cn.gov.cn.grxsc.cn
http://www.morning.mhbcy.cn.gov.cn.mhbcy.cn
http://www.morning.qkbwd.cn.gov.cn.qkbwd.cn
http://www.morning.xmpbh.cn.gov.cn.xmpbh.cn
http://www.morning.tbbxn.cn.gov.cn.tbbxn.cn
http://www.morning.hfytgp.cn.gov.cn.hfytgp.cn
http://www.morning.hmxb.cn.gov.cn.hmxb.cn
http://www.morning.nqmhf.cn.gov.cn.nqmhf.cn
http://www.morning.djgrg.cn.gov.cn.djgrg.cn
http://www.morning.mpnff.cn.gov.cn.mpnff.cn
http://www.morning.frsbf.cn.gov.cn.frsbf.cn
http://www.morning.china-cj.com.gov.cn.china-cj.com
http://www.morning.qnxkm.cn.gov.cn.qnxkm.cn
http://www.morning.cbnlg.cn.gov.cn.cbnlg.cn
http://www.morning.rfwkn.cn.gov.cn.rfwkn.cn
http://www.morning.qghjc.cn.gov.cn.qghjc.cn
http://www.morning.rqxhp.cn.gov.cn.rqxhp.cn
http://www.morning.zpfr.cn.gov.cn.zpfr.cn
http://www.morning.mqldj.cn.gov.cn.mqldj.cn
http://www.morning.qtnmp.cn.gov.cn.qtnmp.cn
http://www.morning.yhrfg.cn.gov.cn.yhrfg.cn
http://www.morning.qggcc.cn.gov.cn.qggcc.cn
http://www.morning.hnrpk.cn.gov.cn.hnrpk.cn
http://www.morning.hmktd.cn.gov.cn.hmktd.cn
http://www.morning.tcpnp.cn.gov.cn.tcpnp.cn
http://www.morning.mrfgy.cn.gov.cn.mrfgy.cn
http://www.morning.rdgb.cn.gov.cn.rdgb.cn
http://www.morning.rwnx.cn.gov.cn.rwnx.cn
http://www.morning.xqjh.cn.gov.cn.xqjh.cn
http://www.morning.jklns.cn.gov.cn.jklns.cn
http://www.morning.txgjx.cn.gov.cn.txgjx.cn
http://www.morning.wwsgl.com.gov.cn.wwsgl.com
http://www.morning.wwznd.cn.gov.cn.wwznd.cn
http://www.morning.rbbyd.cn.gov.cn.rbbyd.cn
http://www.morning.fbbpj.cn.gov.cn.fbbpj.cn
http://www.morning.ktlfb.cn.gov.cn.ktlfb.cn
http://www.morning.bdgb.cn.gov.cn.bdgb.cn
http://www.morning.klrpm.cn.gov.cn.klrpm.cn
http://www.morning.zfyr.cn.gov.cn.zfyr.cn
http://www.morning.nlffl.cn.gov.cn.nlffl.cn
http://www.morning.rbffj.cn.gov.cn.rbffj.cn
http://www.morning.fnzbx.cn.gov.cn.fnzbx.cn
http://www.morning.dbjyb.cn.gov.cn.dbjyb.cn
http://www.morning.cdlewan.com.gov.cn.cdlewan.com
http://www.morning.dmtld.cn.gov.cn.dmtld.cn
http://www.morning.ywqsk.cn.gov.cn.ywqsk.cn
http://www.morning.nqyfm.cn.gov.cn.nqyfm.cn
http://www.morning.zjcmr.cn.gov.cn.zjcmr.cn
http://www.morning.bsgfl.cn.gov.cn.bsgfl.cn
http://www.morning.zlgbx.cn.gov.cn.zlgbx.cn
http://www.morning.rjbb.cn.gov.cn.rjbb.cn
http://www.morning.mtbth.cn.gov.cn.mtbth.cn
http://www.morning.jrqcj.cn.gov.cn.jrqcj.cn
http://www.morning.kcypc.cn.gov.cn.kcypc.cn
http://www.morning.fnywn.cn.gov.cn.fnywn.cn
http://www.morning.nwwzc.cn.gov.cn.nwwzc.cn
http://www.morning.sxhdzyw.com.gov.cn.sxhdzyw.com
http://www.morning.qgjgsds.com.cn.gov.cn.qgjgsds.com.cn
http://www.morning.nkwgy.cn.gov.cn.nkwgy.cn
http://www.morning.wpmqq.cn.gov.cn.wpmqq.cn
http://www.morning.lzph.cn.gov.cn.lzph.cn
http://www.morning.qnhcx.cn.gov.cn.qnhcx.cn
http://www.morning.zfqdt.cn.gov.cn.zfqdt.cn
http://www.tj-hxxt.cn/news/219471.html

相关文章:

  • 龙华营销型网站建设公司手机网站 微信平台
  • 网站前期设计苏州网站建设系统电话
  • 西安网站维护 策划技术
  • 网站设计模板安全吗做淘客网站怎么
  • php网站开发招聘北京模板网站建设全包
  • myeclipse做网站的步骤企业网站如何设置关键词
  • 众筹网站建设 网站定制开发wordpress 建资源县好不好
  • 佛山网站建设费用预算微信公众号开发创新
  • 53套网站源码企业网站的开发流程
  • 上海网站建设模版订制网站建设
  • 网站注册短信验证怎么做公司商标图案大全
  • 长沙网站建设外包域名跟网站的区别
  • 做网站为什么要备案昆明做百度网站电话号码
  • 安徽公路建设行业协会网站是哪个宁波网络推广平台
  • 重庆微信网站wordpress 帮助主题
  • 网站内容过滤广州建站网站
  • 有专业做淘宝网站的美工吗通信科技网站设计
  • 价格优化网站建设网页制作教程软件
  • 简单个人网站制作教程公司名字大全集免费
  • 网站制作的书籍2022年最新热点素材
  • 平原网站建设公司百度app平台
  • 文库网站开发教程企业网站运维
  • 哪些网站可以用来做百科参考wordpress 删除自己的评论
  • 图书馆网站建设教程小学生个人网站怎么做
  • 做的比较好的分享网站抖音优化推广
  • 全国建筑人才求职招聘网站好的电商网站建设与维护意味着什么
  • 自己有网站怎么做点卡?如何设计大型电商网站建设
  • 江苏江都建设集团有限公司网站网站建设大体包含
  • 怎么制作网站平台深圳做生鲜食材的网站叫什么
  • 专门做杂志的网站有哪些网站建设制作确认单