中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

用dw個人網站怎么做優(yōu)化網站的軟件下載

用dw個人網站怎么做,優(yōu)化網站的軟件下載,做電商網站,wordpress文章附件文章目錄 spring61. 一些基本的概念、優(yōu)勢2. 入門案例實現(xiàn)maven聚合工程創(chuàng)建步驟分析實現(xiàn)過程 3. IoC(Inversion of Control)基于xml的bean環(huán)境搭建獲取bean獲取接口創(chuàng)建實現(xiàn)類依賴注入 setter注入 和 構造器注入原生方式的setter注入原生方式的構造器注…

文章目錄

  • spring6
    • 1. 一些基本的概念、優(yōu)勢
    • 2. 入門案例實現(xiàn)
      • maven聚合工程創(chuàng)建步驟
      • 分析實現(xiàn)過程
    • 3. IoC(Inversion of Control)
      • 基于xml的bean環(huán)境搭建
      • 獲取bean
      • 獲取接口創(chuàng)建實現(xiàn)類
      • 依賴注入 setter注入 和 構造器注入
        • 原生方式的setter注入
        • 原生方式的構造器注入
        • 使用bean的方式進行依賴注入步驟
      • 對象類型屬性賦值步驟
      • 引入外部屬性文件步驟
        • bean的八大生命周期(重要)
      • 基于xml的bean自動裝配
      • 基于注解的bean自動裝配(極其重要)
        • 使用注解創(chuàng)建對象
      • 手動實現(xiàn)IoC
        • 實現(xiàn)過程
        • 手動實現(xiàn)一個IoC反射
        • 手動實現(xiàn)屬性注入過程
    • 4. 面向切面編程
          • 靜態(tài)代理圖解
          • 靜態(tài)代理術語
          • 動態(tài)代理
          • 動態(tài)代理的實現(xiàn)過程
      • 概念
      • 相關術語
      • AOP思想綜述
      • 基于注解方式配置AOP
          • 通知類型及其使用
          • bean配置文件
          • 測試文件
          • 報錯信息解決
          • 重用切入點表達式
    • 5. 單元測試
    • 6. 事務
      • JdbcTemplate對象操作Mysql數據庫(Spring事務處理前置知識)
          • 準備環(huán)境
          • 利用JdbcTemplate對象完成對數據庫的增加、修改、刪除操作
          • 利用JdbcTemplate對象完成對數據庫的查詢操作
          • 報錯信息解決
      • 事務處理
        • 概念
        • 特性
      • 聲明式事務管理(基于注解實現(xiàn))
        • 準備環(huán)境(基于上面的JDBC示例進行添加,目錄結構如下)
            • BookController
            • BookDao
            • BookDaoImpl
            • BookService
            • BookServiceImpl
            • TestBookTx
          • 分析
        • 開啟事務步驟(使用注解的方式)
          • 事務注解的一些屬性
    • Resoource資源操作
          • UrlResource實現(xiàn)類訪問url資源
          • ClassPathResourceDemo訪問本地資源(項目內資源)
          • loadAndReadUrlResource訪問磁盤內文件
    • 國際化:i18n
    • 數據校驗
          • 通過Validator接口實現(xiàn)數據校驗
          • 基于注解校驗
          • 基于方法校驗
          • 自定義校驗
    • AOT

導語:感覺自己記得可能不全,所以我找了一篇優(yōu)秀的筆記

(雙手)奉上鏈接:https://lixx.cn/archives/spring6-2
https://lixx.cn/categories/spring6

spring6

1. 一些基本的概念、優(yōu)勢

主流,輕量級開源 框架

廣義劃分:以SpringFirework為核心的Spring技術棧

狹義劃分:專指SpringFireWork

兩大核心:Ioc和AOP

特點:

  • 非侵入式:功能組件使用注解進行標記,簡潔。
  • 控制反轉:自動創(chuàng)建對象,讓我們享受資源注入。
  • 面向切面編程:在不修改源代碼的基礎上增強代碼功能。
  • 組件化:拆分復雜應用為多個組件共同實現(xiàn)
  • 一站式:在集合許多依賴

2. 入門案例實現(xiàn)

maven聚合工程創(chuàng)建步驟

  • 父工程 - spring6
  • 子模塊 - spring-first

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

  1. 引入spring相關依賴
<dependencies><!--引入spring-context依賴--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.10</version></dependency><!--測試依賴:junit--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.2</version></dependency>
</dependencies>
  1. 創(chuàng)建類,定義屬性和方法
package com.itchen.spring6;public class User {public void add(){System.out.println("add ...");}public static void main(String[] args) {User user = new User();user.add();}
}
  1. 按照spring要求創(chuàng)建一個配置文件.xml

  2. 在配置文件中創(chuàng)建相應的配置信息

<?xml version="1.0" encoding="UTF-8"?>
<!--下面是一些對標簽的約束-->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--完成對User對象的創(chuàng)建id屬性:唯一標識 (為了方便一般使用類的首字母小寫)class:要創(chuàng)建的類的對象的全路徑(包名稱 + 類名稱)--><bean id="user" class="com.itchen.spring6.User"></bean>
</beans>
  1. 最終測試
package com.itchen.spring6;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {@Testpublic void testUserObject(){// 加載spring配置文件,對象創(chuàng)建ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 獲取創(chuàng)建的對象User user = (User)context.getBean("user");System.out.println(user);// 使用對象的調用方法進行測試user.add();}
}
  1. 輸出了user對象的全路徑和user的地址

分析實現(xiàn)過程

  1. 執(zhí)行了實體類的無參數構造方法
  2. 如何返回創(chuàng)建的對象
    • 加載bean.xml配置文件
    • 對xml文件進行解析操作
    • 獲取標簽的屬性值
    • 使用反射根據類的全路徑創(chuàng)建對象
    • 對象存放在項目的一個Map集合中;格式<key,value>

3. IoC(Inversion of Control)

Spring通過loC容器來管理所有Java對象的實例化和初始化,控制對象與對象之間的依賴關系。我們將由loC容器管理的Java對象稱為Spring Bean,它與使用關鍵字new創(chuàng)建的Java對象沒有任何區(qū)別。

將對象的創(chuàng)建和對象與對象的關系都交給了容器進行管理。

基于xml的bean環(huán)境搭建

  1. 將子模塊pom文件中的依賴放在父模塊中(子模塊依然可以調用相應的依賴)
  2. 創(chuàng)建資源文件(.xml)和java實體類

獲取bean

  1. 實體類user.java
package com.itchen.spring6.iocxml;public class User {private String name;private Integer age;public void run(){System.out.println("run ...");}
}
  1. bean資源文件獲取
    • 注意:此時的class類型的bean只能有一個
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.itchen.spring6.iocxml.User"></bean>
</beans>
  1. 測試獲取bean的三種方式
package com.itchen.spring6.iocxml;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 根據對象獲取beanUser user = (User)context.getBean("user");System.out.println("根據id獲取bean:" + user);// 根據類型獲取beanUser user2 = (User)context.getBean(User.class);System.out.println("根據類型獲取bean:" + user2);// 根據id和類型獲取beanUser user3 = (User)context.getBean("user", User.class);System.out.println("根據id和類型獲取bean:" + user3);}
}

獲取接口創(chuàng)建實現(xiàn)類

  1. 創(chuàng)建接口,聲明需要實現(xiàn)的方法;在實體類中實現(xiàn)接口。
  2. 在bean.xml中配置相應的impl實現(xiàn)類
<bean id="USerDaoImpl" class="com.itchen.spring6.iocxml.bean.USerDaoImpl"></bean>

注意:這里的bean必須是唯一的。一個接口只能由一個impl實體類來實現(xiàn)

依賴注入 setter注入 和 構造器注入

原生方式的setter注入

先在類中定義基本的set和get方法,然后在其他類中創(chuàng)建類的對象,調用其setget方法來傳值。

原生方式的構造器注入

先定義基本的有參構造器和無參構造器,然后在創(chuàng)建對象的時候在構造器中傳入相對應的值。

使用bean的方式進行依賴注入步驟
  1. 創(chuàng)建一個類,在其中定義基本屬性,生成set方法
  2. 在spring中進行配置
<bean id="book" class="com.itchen.spring6.iocxml.di.Book"><property name="bname" value="java后端開發(fā)"></property><property name="author" value="陳志偉"></property>
</bean>
  1. 測試
@Test
public void testSetter(){// 1. 加載配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");Book book = context.getBean("book", Book.class);System.out.println(book);
}

or

  1. 創(chuàng)建類,定義屬性,生成有參數的構造方法
  2. 進行配置
<bean id="BookConstructor" class="com.itchen.spring6.iocxml.di.Book"><constructor-arg name="bname" value="前端開發(fā)"></constructor-arg><constructor-arg name="author" value="陳志偉"></constructor-arg>
</bean>
  1. 測試

注意:setter注入調用的是無參構造方法,而構造器注入調用的是有參構造方法

對象類型屬性賦值步驟

  1. 準備工作:創(chuàng)建兩個類(班和學生),學生中定義班的對象。定義其中的構造方法、和get、set方法
  2. 創(chuàng)建bean.xml(名字任意)
<!--第一種方式:引入外部bean的方式-->
<bean id="class" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="241班"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Aim"></property><property name="age" value="22"></property><property name="cls" ref="class"></property>
</bean>
<!--第二種方式:引入內部bean的方式-->
<bean id="student2" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Mary"></property><property name="age" value="20"></property><!--內部bean--><property name="cls"><bean id="class2" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="242班"></property></bean></property>
</bean>
<!--第三種方式:級聯(lián)屬性賦值-->
<bean id="cls3" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="144班"></property></bean><bean id="student3" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Tom"></property><property name="age" value="28"></property><property name="cls" ref="class"></property><!--級聯(lián)再賦值--><property name="cls.cname" value="141班"></property></bean>
<!--第四種方式:數組注入-->
<!--注入數組類型的屬性-->
<bean id="clss" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="143班"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.ditest.Student"><!--普通--><property name="sname" value="Aim"></property><property name="age" value="22"></property><!--對象--><property name="cls" ref="clss"></property><!--數組--><property name="loves"><array><value>吃飯</value><value>睡覺</value><value>玩jj</value></array></property>
</bean>
<!--第五種方式:集合類型屬性的注入-->
<bean id="studen1" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Aim"></property><property name="age" value="19"></property>
</bean>
<bean id="studen2" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Mary"></property><property name="age" value="19"></property>
</bean>
<bean id="studen3" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Tom"></property><property name="age" value="19"></property>
</bean>
<bean id="cls" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="142班"></property><property name="stuList"><list><ref bean="studen1"></ref><ref bean="studen2"></ref><ref bean="studen3"></ref></list></property>
</bean>

注意:因為這里的包含關系變了,需要修改一下測試方法

public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean-diarr.xml");// 學生的對象Student stu = context.getBean("student",Student.class);stu.work();
}
<!--第六種方式:map類型屬性注入-->
<!--1. 創(chuàng)建兩個對象:用bean標簽進行配置2. 注入它的普通類型的屬性3. 在學生的bean中注入map集合的屬性
-->
<bean id="teacher" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tid" value="1000"></property><property name="tname" value="小李"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.dimap.Student"><property name="sid" value="2000"></property><property name="sname" value="小明"></property><property name="teacherMap"><map><entry><key><value>10010</value></key><!--這里的(鍵值對的值)值如果是一個普通的值,可以使用value--><ref bean="teacher"></ref></entry></map></property>
</bean>
<!--第七種方式:集合類型的屬性注入-->
<!--1. 創(chuàng)建三個對象2. 注入普通類型的屬性3. 使用“util:類型” 定義4. 在學生bean中引入3.的bean
--><bean id="student" class="com.itchen.spring6.iocxml.dimap.Student"><property name="sid" value="10000"></property><property name="sname" value="張三"></property><!--注入list和map類型屬性--><property name="lessonList" ref="lessonList"></property><property name="teacherMap" ref="teacherMap"></property>
</bean><util:list id="lessonList"><ref bean="lesson1"></ref><ref bean="lesson2"></ref>
</util:list>
<util:map id="teacherMap"><entry><key><value>10010</value></key><ref bean="teacher1"></ref></entry><entry><key><value>10086</value></key><ref bean="teacher2"></ref></entry>
</util:map><bean id="lesson1" class="com.itchen.spring6.iocxml.dimap.Lesson"><property name="lessonName" value="java開發(fā)"></property>
</bean>
<bean id="lesson2" class="com.itchen.spring6.iocxml.dimap.Lesson"><property name="lessonName" value="前端開發(fā)"></property>
</bean><bean id="teacher1" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tname" value="西門慶"></property><property name="tid" value="1000"></property>
</bean>
<bean id="teacher2" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tname" value="歐陽弧"></property><property name="tid" value="2000"></property>
</bean>

注意:第七種方式在使用的時候需要引入標簽相應的命名空間

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">

引入外部屬性文件步驟

  1. 引入外部屬性文件的相關依賴【pom文件】
  2. 創(chuàng)建一個外部的屬性文件。定義數據信息:用戶名、密碼、地址。【.properties文件】
  3. 創(chuàng)建spring的配置文件,引入context的命名空間。引入屬性文件,使用表達式完成注入。
<!--引入外部文件 完成數據庫的信息注入-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.user}"></property><property name="password" value="${jdbc.password}"></property><property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
bean的八大生命周期(重要)

可以在配置文件中【bean.xml文件】使用參數的形式制定bean對象的初始化函數和銷毀函數。

  1. bean對象的創(chuàng)建(調用無參構造方法)
  2. 給bean對象設置相關屬性
  3. bean后置處理器(初始化之前)
  4. bean對象初始化(調用制定的初始化方法)
  5. bean后置處理器(初始化之后)
  6. bean對象創(chuàng)建完成了,可以使用了
  7. bean對象銷毀(配置制定的銷毀方法)
  8. IoC容器關閉

基于xml的bean自動裝配

<!--根據類型自動裝配-->
<bean id="userController" class="com.itchen.spring6.iocxml.auto.Controller.UserController" autowire="byType"></bean>
<bean id="userService" class="com.itchen.spring6.iocxml.auto.Service.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.itchen.spring6.iocxml.auto.dao.UserDaoImpl" autowire="byType"></bean>
<!--根據名稱自動裝配-->
<bean id="userController" class="com.itchen.spring6.iocxml.auto.Controller.UserController" autowire="byName"></bean>
<bean id="userService" class="com.itchen.spring6.iocxml.auto.Service.UserServiceImpl" autowire="byName"></bean>
<bean id="userDao" class="com.itchen.spring6.iocxml.auto.dao.UserDaoImpl" autowire="byName"></bean>
<!--這時需要保證類的名字和id的名字一模一樣-->

基于注解的bean自動裝配(極其重要)

使用注解創(chuàng)建對象
  1. 引入依賴
  2. 開啟組件掃描

<context:component-scan base-package="com.itchen.spring6"></context:component-scan>

  1. 被掃描的類用@component修飾;自動注入的屬性用@Autowired修飾;使用注解定義bean
注解說明
@Component該注解用于描述 Spring 中的 Bean,它是一個泛化的概念,僅僅表示容器中的一個組件(Bean),并且可以作用在應用的任何層次,例如 Service 層、Dao 層等。 使用時只需將該注解標注在相應類上即可。
@Repository該注解用于將數據訪問層(Dao 層)的類標識為 Spring 中的 Bean,其功能與 @Component 相同。
@Service該注解通常作用在業(yè)務層(Service 層),用于將業(yè)務層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。
@Controller該注解通常作用在控制層(如SpringMVC 的 Controller),用于將控制層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。

手動實現(xiàn)IoC

主要使用java的反射機制和注解進行實現(xiàn)

實現(xiàn)過程
  1. 創(chuàng)建子模塊
  2. 創(chuàng)建測試類
  3. 創(chuàng)建兩個注解
    • 約定注解@Bean @Di
  4. 創(chuàng)建bean容器的接口ApplicationContext,定義方法,返回對象
  5. 實現(xiàn)bean的容器接口
    • 返回對象
    • 根據包的規(guī)則加載bean(設置包的掃描規(guī)則)
手動實現(xiàn)一個IoC反射

loadBean步驟:

// 1. 判斷當前是否是文件夾

// 2. 獲取文件夾里面的所有內容

// 3. 文件夾里面為空,直接返回

// 4. 如果文件夾里面不為空
// 遍歷得到的每個file對象 重復3. 4.

// 5. 如果是文件,得到包路徑 + 類名稱

// 6. 判斷文件是否是.class類型,如果是,則去掉后綴

// 7. 如果有@Bean注解使用反射實例化

// 將實例化結果放入map集合中

package com.itchen.bean;import com.itchen.annotation.Bean;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext{// 創(chuàng)建map集合用于創(chuàng)建bean的實例對象private static Map<Class,Object> beanFactory = new HashMap<>();private static String rootPath;// 返回對象@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}// 設置包的掃描規(guī)則// 創(chuàng)建有參構造,傳遞包路徑public static void pathDemo1(String basePackage){// 把點替換為反斜杠String packagePath = basePackage.replaceAll("\\.", "\\\\");// 獲取包的絕對路徑try {Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packagePath);while(dirs.hasMoreElements()){URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(), "utf-8");rootPath = filePath.substring(0, filePath.length() - packagePath.length());System.out.println(filePath);loadBean(new File(filePath));}} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}// 包掃描過程,實例化beanprivate static void loadBean(File fileParent) throws ClassNotFoundException {// 1. 判斷當前是否是文件夾if (fileParent.isDirectory()) {// 2. 獲取文件夾里面的所有內容File[] childrenFiles = fileParent.listFiles();// 3. 文件夾里面為空,直接返回if (childrenFiles == null || childrenFiles.length == 0) {return;}// 4. 如果文件夾里面不為空// 遍歷得到的每個file對象  重復3. 4.for (File child : childrenFiles) {if (child.isDirectory()) {loadBean(child);} else {// 5. 如果是文件,得到包路徑 + 類名稱String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);// 6. 判斷文件是否是.class類型,如果是,則去掉后綴String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);// 7. 如果有@Bean注解使用反射實例化if (!aClass.isInterface()) {Bean annotation = aClass.getAnnotation(Bean.class);if (annotation != null) {Object instance = aClass.newInstance();if (aClass.getInterfaces().length > 0) {System.out.println("正在加載【" + aClass.getInterfaces()[0] + "】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);} else {System.out.println("正在加載【" + aClass.getName() + "】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}// 將實例化結果放入map集合中}catch (Exception e){e.printStackTrace();}}}}}    
}
手動實現(xiàn)屬性注入過程
// 1. 實例化對象在beanFactory的map集合里面
// 遍歷map集合// 2. 獲取所有的value值,獲取每個對象的屬性// 3. 遍歷對象屬性的數組,得到每個屬性// 4. 判斷是否有@Di注解// 5. 如果有,將對象注入
package com.itchen.bean;import com.itchen.annotation.Bean;
import com.itchen.annotation.Di;
import com.itchen.bean.ApplicationContext;import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext {//存儲bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根據包掃描加載bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}// 屬性的注入loadDi();}private  void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是個文件夾就繼續(xù)調用該方法,使用了遞歸loadBean(child);} else {//通過文件路徑轉變成全類名,第一步把絕對路徑部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//選中class文件if (pathWithClass.contains(".class")) {//    com.xinzhi.dao.UserDao//去掉.class后綴,并且把 \ 替換成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的類實例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判斷一下有沒有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class當成key,實例對象當成valueSystem.out.println("正在加載【"+ aClass.getInterfaces()[0] +"】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class當成key,實例對象當成valueSystem.out.println("正在加載【"+ aClass.getName() +"】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}// 屬性注入private void loadDi() {for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){//就是咱們放在容器的對象Object obj = entry.getValue();Class<?> aClass = obj.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields){Di annotation = field.getAnnotation(Di.class);if( annotation != null ){field.setAccessible(true);try {System.out.println("正在給【"+obj.getClass().getName()+"】屬性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}}
}

4. 面向切面編程

核心業(yè)務+任務日志 – > 代碼冗余

解決:用代理模式將其分離(分離任務日志) ----------------- 解耦

靜態(tài)代理圖解

原生的方法調用

增加代理之后的方法調用

靜態(tài)代理術語

代理:將非核心代碼分離出來后,封裝這些非邏輯代理、對象、方法

目標:被代理套用的非核心代碼類、對象、方法

動態(tài)代理

原因:因為靜態(tài)代理不具備代碼的靈活性,所以引入了動態(tài)代理

動態(tài)代理的實現(xiàn)過程
package com.itchen.spring6.aop.example;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;public class ProxyFactory {// 目標對象private Object target;public ProxyFactory(Object obj){target = obj;}// 返回代理對象的方法public Object getProxy(){/*** 參數1. 類加載器* 參數2. 目標對象實現(xiàn)的所有接口* 參數3. 設置代理修改目標函數的方法* */ClassLoader classLoader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler(){@Override/*** 參數1. 代理對象* 參數2. 需要重寫目標對象中的方法啊* 參數3. 目標方法中的參數* */public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[動態(tài)代理][日志] "+method.getName()+",參數:"+ Arrays.toString(args));Object result = method.invoke(target, args);System.out.println("[動態(tài)代理][日志] "+method.getName()+",結果:"+ result);return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);}
}

自行補充計算器的基類和計算器接口類

// 測試類
package com.itchen.spring6.aop.example;public class TestCaculator {public static void main(String[] args) {// 1. 創(chuàng)建代理對象ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());Calculator proxy = (Calculator) proxyFactory.getProxy();proxy.add(1,2);proxy.mul(2,4);}
}

概念

AOP(Aspect Oriented Programming)是一種設計思想,是軟件設計領域中的面向切面編程,它是面向對象編程的一種補充和完善,它以通過預編譯方式和運行期動態(tài)代理方式實現(xiàn),在不修改源代碼的情況下,給程序動態(tài)統(tǒng)一添加額外功能的一種技術。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。

相關術語

  1. 橫切關注點
    • 項目中相同的需要被提取的部分
  2. 通知(增強)
    • 需要增加的功能
    • 通知方法:橫切關注點的實現(xiàn)
    • 通知類型:五種
  3. 切面 > 通知
    • 封裝通知方法的類(eg. 一個切面類包含:前置通知、返回通知、后置通知等)
  4. 目標
    • 被代理的對象
  5. 代理
    • 向目標對象應用通知后創(chuàng)建代理對象
  6. 連接點
    • 能使用通知的地方
  7. 切入點
    • 實際去增強方法的位置

AOP思想綜述

將特定的代碼封裝到切面類中,將切面類用于一個新的目標方法,最終實現(xiàn) 方法的增強(方法的增強好抽象呀)

基于注解方式配置AOP

通知類型及其使用
package com.itchen.spring6.aop.annoaop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;@Aspect // 切面類
@Component // 在spring的ioc容器中進行管理
public class LogAspect {// 設置切入點和通知類型@Before(value = "pointCut()")public void beforeMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());System.out.println("Logger-->前置通知,方法名:"+methodName+",參數:"+args);}@After("execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public void afterMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->后置通知,方法名:"+methodName);}@AfterReturning(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))", returning = "result")public void afterReturningMethod(JoinPoint joinPoint, Object result){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->返回通知,方法名:"+methodName+",結果:"+result);}@AfterThrowing(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->異常通知,方法名:"+methodName+",異常:"+ex);}@Around("execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());Object result = null;try {System.out.println("環(huán)繞通知-->目標對象方法執(zhí)行之前");//目標對象(連接點)方法的執(zhí)行result = joinPoint.proceed();System.out.println("環(huán)繞通知-->目標對象方法返回值之后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("環(huán)繞通知-->目標對象方法出現(xiàn)異常時");} finally {System.out.println("環(huán)繞通知-->目標對象方法執(zhí)行完畢");}return result;}// 重用切入點表達式@Pointcut(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public void pointCut(){}
}
bean配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--開啟組件的掃描--><context:component-scan base-package="com.itchen.spring6.aop.annoaop"></context:component-scan><!--開啟自動代理--><aop:aspectj-autoproxy />
</beans>
測試文件
package com.itchen.spring6.aop.annoaop;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAop {@Testpublic void testAdd(){ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Calculator calculator = context.getBean(Calculator.class);calculator.add(1,1);}
}
報錯信息解決

NoSuchBeanDefinitionException:沒有在實現(xiàn)類中加入@Component注解創(chuàng)建bean

重用切入點表達式

通知注解后面的參數重復書寫,所以可以用注解將參數定義為一個類似宏的東西

5. 單元測試

Junit依賴自動注入方便測試

整合Junit5 和 Junit4

// 不整合的時候測試
public class TestUser {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");UserController userController = context.getBean("userController", UserController.class);userController.addUser();}
}
// 整合Junit5
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5 {@Autowiredprivate User user;// 測試方法@Testpublic void testUser(){System.out.println(user);user.run();}
}
// 整合Junit4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")
public class SpringTestJunit4 {@Autowiredprivate User user;@Testpublic void testUser4(){System.out.println(user);user.run();}
}

6. 事務

JdbcTemplate對象操作Mysql數據庫(Spring事務處理前置知識)

準備環(huán)境
  1. 導入依賴
<!--spring jdbc  Spring 持久化層支持jar包-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.2</version>
</dependency>
<!-- MySQL驅動 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
<!-- 數據源 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.15</version>
</dependency>
<!--spring對junit的支持相關依賴-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>6.0.2</version>
</dependency><!--junit5測試-->
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.0</version>
</dependency>
  1. 配置jdbc.properties配置文件連接數據庫
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false
jdbc.driver=com.mysql.cj.jdbc.Driver
  1. 配置bean.xml配置JdbcTemplate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 導入外部屬性文件 --><context:property-placeholder location="classpath:jdbc.properties" /><!-- 配置數據源 --><bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/></bean><!-- 配置 JdbcTemplate --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!-- 裝配數據源 --><property name="dataSource" ref="druidDataSource"/></bean>
</beans>
利用JdbcTemplate對象完成對數據庫的增加、修改、刪除操作

測試代碼

package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;// 添加、修改、刪除操作@Testpublic void testUpdate(){// 添加數據String sql = "insert into t_emp values(null,?,?,?)";Object[] params = {"洛薩",32,"男"};
//        添加數據
//        int rows = jdbcTemplate.update(sql,params);
//        System.out.println("影響了" + rows + "行");String sql2 = "update t_emp set name=? where id=?";// 修改id為?行數據的名字字段
//        int row = jdbcTemplate.update(sql2,"666",3);
//        System.out.println("影響了" + row + "行");// 刪除id為?的一行String sql3 = "delete from t_emp where id=?";
//        int row3 = jdbcTemplate.update(sql3,3);
//        System.out.println("影響了" + row3 + "行");}
}
利用JdbcTemplate對象完成對數據庫的查詢操作
package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;import java.util.List;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class jdbcTemplateTest2 {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** queryForObject* */// 查詢并獲取數據庫的第一行數據@Testpublic void testSelectObject1(){String sql = "select * from t_emp where id=?";Emp person = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {Emp emp = new Emp();emp.setId(rs.getInt("id"));emp.setName(rs.getString("name"));emp.setAge(rs.getInt("age"));emp.setSex(rs.getString("sex"));return emp;}, 1);System.out.println(person);}/*** queryForObject* */@Testpublic void testSelectObject2(){String sql = "select * from t_emp where id=?";// 利用BeanPropertyRowMapper方法自動創(chuàng)建對象接收數據庫數據Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class),2);System.out.println(emp);}/*** query* */@Testpublic void testSelectList(){String sql = "select * from t_emp";List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class));System.out.println(list);}/*** queryForObject* *///查詢單行單列的值@Testpublic void selectCount(){String sql = "select count(id) from t_emp";Integer count = jdbcTemplate.queryForObject(sql, Integer.class);System.out.println(count);}
}
報錯信息解決

BadSqlGrammarException:沒有按照規(guī)定的sql語法進行傳遞參數信息

事務處理

概念

eg.銀行轉賬,要成功都成功;要失敗,都失敗

特性

原子性:要成功,都成功

一致性:操作前和操作后總量一致

隔離性:多個事務進行操作,對各個事務之間沒有影響

持久性:提交就永久保存

聲明式事務管理(基于注解實現(xiàn))

準備環(huán)境(基于上面的JDBC示例進行添加,目錄結構如下)

pkgwcod.png

BookController
package com.itchen.spring.tx.controller;import com.itchen.spring.tx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class BookController {@Autowiredprivate BookService bookService;// 買書的方法: 圖書的id  、  用戶的idpublic void buyBook(Integer bookId,Integer userId){// 調用Service方法bookService.buyBook(bookId,userId);}
}
BookDao
package com.itchen.spring.tx.dao;public interface BookDao {Integer getBookPriceByBookId(Integer bookId);void updateStock(Integer bookId);void updateUserBalance(Integer userId,Integer price);
}
BookDaoImpl
package com.itchen.spring.tx.dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class BookDaoImpl implements BookDao{@Autowiredprivate JdbcTemplate jdbcTemplate;Integer price;@Overridepublic Integer getBookPriceByBookId(Integer bookId) {String sql = "select price from t_book where book_id=?";price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);return price;}@Overridepublic void updateStock(Integer bookId) {String sql = "update t_book set stock=stock-1 where book_id=?";jdbcTemplate.update(sql,bookId);}@Overridepublic void updateUserBalance(Integer userId,Integer price) {String sql = "update t_user set balance=balance-? where user_id=?";jdbcTemplate.update(sql,price,userId);}
}
BookService
package com.itchen.spring.tx.service;public interface BookService {void buyBook(Integer bookId, Integer userId);
}
BookServiceImpl
package com.itchen.spring.tx.service;import com.itchen.spring.tx.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BookServiceImpl implements BookService{@Autowiredprivate BookDao bookDao;@Overridepublic void buyBook(Integer bookId, Integer userId) {// 根據圖書id查詢圖書的價格Integer price = bookDao.getBookPriceByBookId(bookId);// 更新圖書表中的庫存量bookDao.updateStock(bookId);// 更新用戶余額bookDao.updateUserBalance(userId,price);}
}
TestBookTx
package com.itchen.spring.tx;import com.itchen.spring.tx.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class TestBookTx {@Autowiredprivate BookController bookController;@Testpublic void testBuyBook(){bookController.buyBook(1,1);}
}
分析

將買書一個方法作為一個大的方法在Controller中調用。然后再Service將這個方法分解成多個數據庫操作。最后將數據庫的各個操作在dao層進行實現(xiàn)。(揉碎了喂到嘴里)

問題:這里如果用戶的余額不足,仍然會執(zhí)行圖書出庫操作,但是應為沒有事物的回滾,報錯的步驟不會執(zhí)行。

開啟事務步驟(使用注解的方式)
  1. 在bean中添加:開啟事務的注解驅動
  2. 在業(yè)務邏輯層(Service層)添加事務注解@Transactional**【這個注解可以加在方法上,也可以加在類上】**

運行結果:在用戶余額不足的情況下依然報錯,但是不會執(zhí)行圖書出庫操作

事務注解的一些屬性
參數描述
readOnly是否只讀(表示是否只能進行查詢操作)
timeout設置超時時間(-1,表示永不超時;單位是秒)
noRollbackFor事務策略(設置哪些異常不回滾)
(見文章頭連接)隔離級別:解決臟讀、不可重讀、虛讀
(見文章頭連接)傳播行為:事務之間的相互調用,使用哪一個事務進行邏輯處理

臟讀:兩個都未提交,但是都能看見對方的修改

不可重復:一個提交了,另一個修改了沒有提交的能看到修改完成之后的數據

虛讀:一個提交了,另一個做完了添加沒有提交之后能讀到自己添加的數據

Resoource資源操作

Resource接口提供了對低級資源訪問的抽象方法,接口有多個實現(xiàn)類供給給我們來訪問低級資源

UrlResource實現(xiàn)類訪問url資源
// 演示urlResource訪問網絡資源
public class UrlResourceDemo {public static void main(String[] args) {// http前綴loadUrlResource("https://www.baidu.com/");// file前綴 放在項目的根路徑loadUrlResource("file:itchen.txt");}// 訪問前綴httppublic static void loadUrlResource(String path){// 創(chuàng)建Resource接口的實現(xiàn)類的對象try {// 獲取資源UrlResource url = new UrlResource(path);System.out.println(url.getFilename());System.out.println(url.getURI());System.out.println(url.getDescription());System.out.println(url.getInputStream().read());} catch (Exception e) {throw new RuntimeException(e);}// 獲取資源信息}
}
ClassPathResourceDemo訪問本地資源(項目內資源)
// 訪問類路徑下的資源
public class ClassPathResourceDemo {public static void main(String[] args) {loadClasspathResource("itchen.txt");}public static void loadClasspathResource(String path){// 創(chuàng)建的對象ClassPathResource resource = new ClassPathResource(path);System.out.println(resource.getFilename());System.out.println(resource.getDescription());// 獲取文件內容try {InputStream in = resource.getInputStream();byte[] b = new byte[1024];while(in.read(b) != -1){System.out.println(new String(b));}} catch (IOException e) {throw new RuntimeException(e);}}
}
loadAndReadUrlResource訪問磁盤內文件
public class FileSystemResourceDemo {public static void loadAndReadUrlResource(String path) throws Exception{//相對路徑FileSystemResource resource = new FileSystemResource("itchen.txt");//絕對路徑//FileSystemResource resource = new FileSystemResource("D:\\itchen.txt");// 獲取文件名System.out.println("resource.getFileName = " + resource.getFilename());// 獲取文件描述System.out.println("resource.getDescription = "+ resource.getDescription());//獲取文件內容InputStream in = resource.getInputStream();byte[] b = new byte[1024];while(in.read(b)!=-1) {System.out.println(new String(b));}}public static void main(String[] args) throws Exception {loadAndReadUrlResource("itchen.txt");}
}

其他的實現(xiàn)類見文檔頭鏈接

國際化:i18n

調用不同的配置文件示例

@Test
public void test1(){ResourceBundle bundle = ResourceBundle.getBundle("itchen", new Locale("zh", "CN"));String value1 = bundle.getString("test");System.out.println(value1);
}
@Test
public void test2() {ResourceBundle bundle = ResourceBundle.getBundle("itchen", new Locale("en", "GB"));String value1 = bundle.getString("test");System.out.println(value1);
}

數據校驗

通過Validator接口實現(xiàn)數據校驗
public class TestMethod1 {public static void main(String[] args) {//創(chuàng)建person對象Person person = new Person();person.setName("lucy");person.setAge(-1);// 創(chuàng)建Person對應的DataBinderDataBinder binder = new DataBinder(person);// 設置校驗binder.setValidator(new PersonValidator());// 由于Person對象中的屬性為空,所以校驗不通過binder.validate();//輸出結果BindingResult results = binder.getBindingResult();System.out.println(results.getAllErrors());}
}

測試:

public class PersonValidator implements Validator {// 校驗類型確認@Overridepublic boolean supports(Class<?> clazz) {return Person.class.equals(clazz);}// 校驗邏輯@Overridepublic void validate(Object object, Errors errors) {ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");Person p = (Person) object;if (p.getAge() < 0) {errors.rejectValue("age", "error value < 0");} else if (p.getAge() > 110) {errors.rejectValue("age", "error value too old");}}
}
基于注解校驗
基于方法校驗
自定義校驗

其他的實現(xiàn)類見文檔頭鏈接

AOT

AOT先關概念

  1. JIT:動態(tài)編譯,邊運行邊編譯

  2. AOT:運行前編譯,提前編譯

pk2bEzq.md.png

http://www.risenshineclean.com/news/29787.html

相關文章:

  • 上海自建站招聘杭州企業(yè)seo
  • 創(chuàng)建網站向導和模板seo如何提高排名
  • 網站目錄提交谷歌搜索引擎鏡像
  • 西安建設網站排名自媒體發(fā)布軟件app
  • 網站流量怎么查看精準引流客源的方法可靠嗎
  • 鹽城網站開發(fā)效果新聞發(fā)稿軟文推廣
  • 做期貨應該看的網站網奇seo培訓官網
  • 網站開發(fā)招標網絡輿情軟件免費入口
  • 南陽公司做網站市場營銷推廣方案模板
  • 哪里免費做網站網站建設方案模板
  • 鄭州網站制作鄭州網站制作案例優(yōu)化大師官網登錄入口
  • 東莞網站優(yōu)化seo今日全國疫情最新消息
  • 做網站到底要不要備案學生個人網頁制作教程
  • 寶安做棋牌網站建設哪家公司收費合理天津優(yōu)化加盟
  • 淄博網站優(yōu)化資訊在線seo超級外鏈工具
  • 政府網站建設管理意見網絡營銷什么意思
  • 寫出網站建設步驟seo優(yōu)化廠商
  • 銷售網站制作怎么做品牌整合營銷
  • 外貿平臺有哪些是免費的直接可以發(fā)布售賣產品的關鍵詞自動優(yōu)化工具
  • 二手商品交易網站開發(fā)百度搜索引擎廣告
  • wordpress+調用+編輯器長沙官網seo技術
  • 網站空間大小選擇百度怎么投放自己的廣告
  • 免費行情軟件網站有哪些sem競價外包公司
  • 騰訊云可以做網站嗎3百度網盤免費下載
  • 沈陽市網站制作廣東省人大常委會
  • 一級a做爰網站下載代發(fā)軟文
  • 深圳光明廣州網絡seo優(yōu)化
  • 使用免費的代碼做網站品牌廣告
  • 公司企業(yè)網站制作教程推廣專家
  • 網站開發(fā)常見問題總結百度收錄關鍵詞查詢