SSM(一):spring-ioc依賴註入筆記

来源:https://www.cnblogs.com/kuaizifeng/archive/2018/12/14/10116540.html
-Advertisement-
Play Games

一、java代理模式 java代理模式是ioc的前置知識。代理模式非常簡單,看代碼就一目瞭然了。 public interface role { public void makeMoney(); } public class Boss implements role { @Override publ ...


一、java代理模式

  java代理模式是ioc的前置知識。代理模式非常簡單,看代碼就一目瞭然了。

public interface role {
    public void makeMoney();
}
role
public class Boss implements role {
    @Override
    public void makeMoney() {
        System.out.println("老闆正在談項目...");
    }
}
boss
public class Secretary implements Role {

    private Role subject = new Boss();

    @Override
    public void makeMoney() {
        before();
        subject.makeMoney();
        after();
    }

    private void before(){
        System.out.println("你可以先和秘書談一下合作意向...");
    }
    private void after(){
        System.out.println("秘書把老闆簽過的合同給你...");
    }
}
secretary

  上面是極簡的java靜態代理(也是介面代理模式),定義了介面、介面的實現類和代理類。或者稱實現類為目標類,代理類為封裝類。

  一般我們先找老闆秘書,說一下合作意向(Secretary的makeMoney),可以的話(before)調用老闆真正談項目Boss的makeMoney),談妥了就可以簽合同了(after)。可以做一下測試:

public class Test {

    public static void main(String[] args){
        // 測試靜態代理
        /*
            所謂靜態代理,是對介面實現類的某些方法進行了一次封裝
            這個代理類需要實現介面的同樣方法,併在內部初始化一個實現類
            在該方法中調用了實現類的方法
         */
        Secretary proxy = new Secretary();
        proxy.makeMoney();
    }
}
Test

  可見,與其直接訪問一個目標類,更多的是訪問封裝類。這樣做的好處在於:

    1.不操作目標類,不對目標類造成破壞;

    2.自定義封裝類,具有充分的控制權;

    3.可以增強目標類的特定方法,達到增強和擴展的目的;

  但是,目標類和封裝類必須實現介面定義的同一個方法

  更多地,java使用動態代理來實現功能擴展。舉個例子:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        final Role role = new Boss();
        Role secretary = (Role) Proxy.newProxyInstance(
                role.getClass().getClassLoader(), // 目標類的類載入器
                role.getClass().getInterfaces(),  // 目標類所實現的所有介面
                new InvocationHandler() {         // 內部匿名類 -- InvocationHandler介面類的實現類,必須重寫介面方法invoke
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // proxy: 代理對象
                        // method: 目標方法
                        // args: 目標方法的參數列表
                        before();
                        Object result = method.invoke(role, args);
                        after();
                        return result;
                    }
                    private void before(){
                        System.out.println("先和秘書接洽約時間...");
                    }
                    private void after(){
                        System.out.println("秘書送來合同書...");
                    }
                }
        );
        secretary.makeMoney();
    }
}
jdk動態代理

  jdk動態代理同樣是介面代理模式,new InvocationHandler()實現類中的invoke方法被Proxy.newProxyInstance自動調用。secretary對象相當於上面的secretary對象,只不過不需要我們自己手動創建了。jdk動態代理幫助我們創建了一個代理對象,我們只需寫介面和實現類即可

  當然,上面的new InvocarionHandler()實現類可以單獨拿出來重寫:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkProxy implements InvocationHandler {

    private Object impl;
    JdkProxy(Object impl){
        super();
        this.impl = impl;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(impl, args);
        after();
        return result;
    }

    private void before(){
        System.out.println("先和秘書接洽約時間...");
    }
    private void after(){
        System.out.println("秘書送來合同書...");
    }
}
JdkProxy

  做一下測試:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class JdkProxyTest {
    public static void main(String[] args) {
        Role role = new Boss();
        InvocationHandler invocationHandler = new JdkProxy(role);
        Role boss = (Role) Proxy.newProxyInstance(
                role.getClass().getClassLoader(),
                role.getClass().getInterfaces(),
                invocationHandler
                );
        boss.makeMoney();
    }
}
test

  除了jdk動態代理外,還有cglib動態代理。與jdk動態代理不同的是,cglib動態代理沒有介面類,它是通過子類繼承目標類,在子類同名方法內部調用目標類方法來實現的。這種實現方法的性能要優於jdk動態代理。

  總結一下,jdk代理幫助我們在目標類的基礎上做了一次功能擴展,因此,對於日誌、事務等系統級業務,可以通過動態代理交給別的組件管理,自己則專註寫應用級業務,也就是上面的實現類(對應bean)和Test類(對應service)。這就是IOC控制反轉。

 二、applicationContext.xml和ApplicationContext

  “解耦(業務邏輯代碼和實體類分離)”、“功能擴展和托管(系統級業務交給框架處理,開發者只專註應用級業務處理)”是理解spring容器的兩個重要思想。

  動態代理解決了“功能擴展和托管”這個問題,通過xml解析,spring容器(讀取applicationContext.xml生成的ApplicationContext實例)對目標類和封裝類、業務代碼兩者進行了拆解。

  我們可以將目標類註入(DI)到applicationContetx.xml文件中,由spring容器(ApplicationContext實例)讀取applicationContext.xml來自動生成目標類對應的介面類和封裝類。在業務代碼中,只需獲取ApplicationContext實例,並從中獲取響應的封裝類即可。這個過程實現即是IOC控制反轉

  假設項目環境(IDEA)已經搭建完成(主要是JDK配置、maven配置、tomcat-->local伺服器配置、項目Open Modules Settings配置、以及maven下文件添加及屬性配置Mark Directory as)。

D:.
├─src
│  ├─main
│  │  ├─java               // Sources Root
│  │  │  └─com
│  │  │      └─boke
│  │  ├─resources          // Resources Root,可以手動添加並右鍵設置Mark Directory as --> Resources Root
│  │  │  ├─applicationContext.xml
│  │  │  └─log4j.properties
│  │  └─webapp             // Sources Root,可以手動添加並右鍵設置Mark Directory as --> Sources Root
│  │      └─WEB-INF
│  └─test                 
│      └─java             // Test Resources Root,可以手動添加並右鍵設置Mark Directory as --> Test Resources Root
│          └─com
│              └─node
│                  ├─boke
└─target                   // Excluded,自動生成
備註:這些都可以在項目配置中設置。IDEA右上角"搜索"圖標左側第一個圖標,或者項目文件目錄上右鍵Open Module Settings設置。
maven項目文件配置

  pom.xml需要引入的依賴:junit4.11、spring-core、spring-beans、spring-context、spring-aop、spring-expression、log4j1.2.17、spring-test、commons-logging1.1.1。springframework版本為4.2.1.RELEASE。

  現在跑一遍applicationContext.xml-->ApplicationContext的整個流程。

  定義介面類和實現類。

// MyInterface類
public interface MyInterface {
    public void doSome();
}

// MyImplement類
public class MyImplement implements MyInterface {
    public MyImplement(){
        super();
    }
    public void doSome() {
        System.out.println("--------執行doSome()方法...----------");
    }
}
介面類和實現類

  將實現類註冊到applicationContext.xml文件中。通常,實現類是一個POJO(plain old java object),對應applicationCOntext.xml的某個bean標簽,因此也可稱為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">

    <!--
        註冊MyImplement
        它相當於:MyInterface implement = (MyInterface) new MyImplement();
    -->
    <bean id="implement" class="com.boke.MyImplement" />
</beans>
依賴註入

  測試。

package com.node.boke;

import com.boke.MyInterface;
import com.boke.MyImplement;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

public class MyTest {

    /**
     * 示例一:傳統的開發模式:介面-->實現-->調用,都綁定在了一起,耦合度比較高
     */
    @Test
    public void test01(){
        MyInterface implement = new MyImplement();
        implement.doSome();
    }

    /**
     * 示例二:spring容器解耦
     */
    @Test
    public void test02(){
        /*
            ClassPathXmlApplicationContext:類路徑
            把resources設置為source root,然後直接從其文件夾下讀取applicationContext.xml即可
            解耦:看不到MyInterface實現類
         */
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyInterface implement = (MyInterface)ac.getBean("implement");
        implement.doSome();
    }
    /**
     * 示例三:文件路徑
     */
    @Test
    public void test03(){
        /*
            FileSystemXmlApplicationContext:文件路徑,預設是項目根路徑下,也可以從其他路徑讀取applicationContext.xml文件
            解耦:看不到ISomeimplementImpl實現類
         */
        ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
        MyInterface implement = (MyInterface)ac.getBean("implement");
        implement.doSome();
    }

    /* applicationContext與BeanFactory的區別:
        - applicationContext容器在進行初始化時,會將其中的所有Bean(對象)創建;
            缺點:占用系統資源(記憶體,CPU等)
            優點:響應速度快
        - BeanFactory容器中的對象在容器初始化不會創建Bean對象,而是在真正獲取該對象時才創建;
            缺點:響應速度慢
            優點:占用系統資源相對較小
    */
    /**
     * 示例四:XmlBeanFactory介面類的實現
     */
    @SuppressWarnings("deprecation")
    @Test
    public void test04(){
        BeanFactory ac = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        MyInterface implement = (MyInterface)ac.getBean("implement");
        implement.doSome();
    }
}
測試

  總結:

  Spring的主要作用就是為代碼"解耦",降低代碼間的耦合度。根據功能的不同,可以將一個系統中的代碼分為主業務邏輯和系統級業務邏輯兩類。主業務代碼邏輯聯繫緊密,有具體的專業業務應用場景,復用性相對較低;系統級業務邏輯相對功能獨立,沒有具體的專業業務應用場景,為主業務提供系統級服務,復用性強。

  Spring根據代碼的功能特點,將降低耦合度的方式分為兩類:IOC和AOP。IOC使得主業務邏輯在相互調用的過程中不用自己創建對象和調用,而是由Spring容器統一管理,自動"註入"。AOP使得系統級服務得到了服用,由Spring容器統一完成"織入"。

  Spring的特點:
            - 1.非入侵:spring的api不會混入到應用業務開發中。
            - 2.容器:Spring可以管理對象的生命周期以及對象間的依賴關係,並且可以通過配置文件來定義對象,以及設置與其它對象的依賴關係。
            - 3.IOC: 控制反轉(inversion of control),即創建調用者的實例不是由調用者完成的,而是由Spring容器完成,並註入調用者。
                            即不是對象從容器中查找對象,這個事情是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。
            - 4.AOP:面向切麵編程(Aspect Orient Programming),是一種編程思想,是面向對象編程的OOP的補充。可以把日誌、安全、事務管理等服務理解成一個"切麵"。

三、ApplicationContext在bean註入時增擴了哪些功能--bean的生命始末

   IOC(Inversion of Control)是一個概念,是一種思想,其實現方式多種多樣。當前比較流行的實現方式有兩種:依賴查找、依賴註入。依賴查找(Dependency Lookup DL):容器提供回調介面和上下文環境給組件,程式代碼則需要提供具體的查找方式。 依賴註入(Dependecy Injection DI):程式代碼不做定位查找,這些工作由容器自行完成。依賴註入是目前最優秀的解耦方式。依賴註入讓Spring的Bean之間以配置文件的方式組織在一起,而不是以硬編碼的方式耦合在一起。

  通過以下代碼查看bean註入和銷毀的整個流程。

// 介面類
package com.boke;

public interface MyInterface {
    public void doSome();
}

// 實現類
package com.boke;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyImplement implements MyInterface, BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean, BeanPostProcessor {

    private String aDao;
    private String bDao;

    public MyImplement(){
        System.out.println("Step1.執行構造方法...");
    }

    @Override
    public void doSome() {
        System.out.println("Step9:執行真正的業務處理邏輯doSome()方法...");
    }

    public void setUp(){
        System.out.println("Step7:bean生命開始...");
    }
    public void tearDown(){
        System.out.println("Step11:Bean銷毀之前...");
    }

    public String getaDao() {
        return aDao;
    }

    public void setaDao(String aDao) {
        System.out.println("Step2:執行aDao的setter方法...");
        this.aDao = aDao;
    }

    public String getbDao() {
        return bDao;
    }

    public void setbDao(String bDao) {
        System.out.println("Step2:執行bDao的setter方法...");
        this.bDao = bDao;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("Step3:獲取bean的id = " + name);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Step4:獲取到BeanFactory容器...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Step6:標志著bean已經初始化完畢...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Step10:實現介面的銷毀之前...");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        System.out.println("Step5:初始化完畢之前,執行before方法...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
        System.out.println("Step8:初始化完畢之後,執行after方法...");
        return bean;
    }
}
介面類和實現類

  applicationContext.xml註入。

<?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-->
    <bean
            id="implement"
            class="com.boke.MyImplement"
            init-method="setUp"
            destroy-method="tearDown"
    >
        <!--初始化ISomeServiceImpl的成員變數-->
        <property name="aDao" value="aaa" />
        <property name="bDao" value="bbb" />
    </bean>
</beans>
applicationContext.xml

  測試類。

package com.node.boke;

import com.boke.MyInterface;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test01(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyInterface service = (MyInterface)ac.getBean("implement");
        service.doSome();
        /*
            要執行tearDown,需要兩個條件:
               - scope=singleton
               - 手動關閉ac
        */
        ((ClassPathXmlApplicationContext)ac).close();
    }
}
測試類

  執行test,列印結果如下:

INFO [AbstractApplicationContext.java:prepareRefresh:573]- Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ba4d54: startup date [Fri Dec 14 11:41:09 CST 2018]; root of context hierarchy
INFO [XmlBeanDefinitionReader.java:loadBeanDefinitions:317]- Loading XML bean definitions from class path resource [applicationContext.xml]
Step1.執行構造方法...
Step2:執行aDao的setter方法...
Step2:執行bDao的setter方法...
Step3:獲取bean的id = implement
Step4:獲取到BeanFactory容器...
Step5:初始化完畢之前,執行before方法...
Step6:標志著bean已經初始化完畢...
Step7:bean生命開始...
Step8:初始化完畢之後,執行after方法...
Step9:執行真正的業務處理邏輯doSome()方法...
INFO [AbstractApplicationContext.java:doClose:951]- Closing org.springframework.context.support.ClassPathXmlApplicationContext@ba4d54: startup date [Fri Dec 14 11:41:09 CST 2018]; root of context hierarchy
Step10:實現介面的銷毀之前...
Step11:Bean銷毀之前...
列印結果

  分析:

  在註入一個bean時,大致經過了11個步驟。這個流程對每一個註入的bean都適用。BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean, BeanPostProcessor是spring中自帶的bean見名知意,在bean初始化、調用和銷毀之際被spring自動調用。

  我們可以通過實現(如上面的MyImplement)這些容器中自動調用的bean介面類中的抽象方法來實現功能的擴展,並交由spring自動處理。可見,spring在註入一個我們自寫的bean時,創建了一個動態代理對象,這個對象不僅僅封裝了我們自己的介面實現類,也封裝了spring預設的這些介面實現類,從而完成了一個bean註入的整個生命周期。

  當然,我們可以把Implement實現的這些介面單獨拿出來寫一個實現類,註入到spring容器中。以BeanPostProcessor為例。

package com.boke;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        System.out.println("Step5:初始化完畢之前,執行before方法...");
        return bean;     // 要返回這個bean,它表示當前正在初始化的bean對象
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
        System.out.println("Step8:初始化完畢之後,執行after方法...");
        return bean;     // 要返回這個bean,它表示當前正在初始化的bean對象
    }
}
MyBeanPostProcessor

  將其註入到applicationContext.xml。

<?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-->
    <bean
            id="implement"
            class="com.boke.MyImplement"
            init-method="setUp"
            destroy-method="tearDown"
    >
        <!--初始化ISomeServiceImpl的成員變數-->
        <property name="aDao" value="aaa" />
        <property name="bDao" value="bbb" />
    </bean>

    <bean class="com.node.service.beanlife.MyBeanPostProcessor" />
</beans>
applicationContext.xml

  效果和直接繼承實現是一樣的。

  在這些步驟中,我們可以定製一些針對所有bean都適用的功能來進行擴展。

  BeanPostProcessor說明:

  Bean後處理器是一種特殊的Bean,容器中所有的Bean在初始化時均會自動執行該類的兩個方法。由於該Bean是由其它Bean自動調用執行,不是程式員手工調用,故此Bean無須id屬性。Bean後處理器是org.springframework.beans.factory.config.BeanPostProcessor,它會被自動載入,並執行它的實現類的兩個方法:

  public Object postProcessBeforeInitialization(Object bean, String beanId) throws BeanException,該方法會在目標Bean初始化完畢之前由容器自動調用。

  public Object postProcessAfterInitialization(Object bean, String BeanId) throws BeansException,該方法會在目標Bean初始化完畢之後由容器自動調用。

  它們的參數是:第一個參數是系統即將初始化的Bean實例,第二個參數是該Bean實例的id屬性值,若Bean沒有id就是name屬性值。實現類需要重寫這兩個方法,並註冊到bean中,spring容器會在每個bean調用之前(之後)自動執行者兩個方法。

四、基於XML的DI

  applicationContext.xml中以bean標簽的方式註入屬於典型的xml方式的註入。上節梳理了bean註入的生命始末,本節記錄註入bean的一些方式。

  1.無參構造註入

// Student類
package com.boke;

public class Student {
    private String name;
    private int age;

    private School school;

    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;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

// School類
package com.boke;

public class School {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}
實體類
<?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並註入成員變數-->
    <bean id="school" class="com.boke.School">
        <property name="name" value="清華大學" />
    </bean>

    <bean id="student" class="com.boke.Student">
        <!--註入成員變數的值-->
        <property name="name" value="Alex" />
        <property name="age" value="20" />
        <!--對於一個對象,稱為域屬性,需要用ref來註入-->
        <property name="school" ref="school" />
    </bean>
</beans>
applicationContext.xml
package com.node.boke;

import com.boke.School;
import com.boke.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test01(){
        ApplicationContext ac = 
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這次使用分散式事務框架過程中了學習了一些分散式事務知識,所以本文我們就來聊聊分散式事務那些事。首先我們先回顧下什麼是事務。 事務 什麼是事務?這個作為後端開發,日常開發中只要與資料庫有交互,肯定就會使用過事務。現在摘抄一段wiki的解釋,解釋下什麼是事務。 是資料庫管理系統執行過程中的一個邏輯單位, ...
  • 好久以前就聽說了dart和flutter,只是一直沒有時間去研究一下,最近有了些時間就簡單的研究了一下,也算是快速的入門了。dart是Google開發的語言,目前最新的版本為2.1,官網地址https://www.dartlang.org/ 官網截圖 下載dart的sdk 下載flutter的sdk ...
  • 當執行一個搜索時,它將這個搜索請求廣播給所有的索引分片。可以通過提供路由參數來控制要搜索哪些分片。例如,當檢索tweets這個索引時,路由參數可以設置為用戶名: 1. Search 查詢可以提供一個簡單的查詢字元串作為參數,也可以用一個請求體。 1.1. URI Search 這種方式用的很少,就不 ...
  • 裝飾器(重點,難點) 開閉原則: 對功能的擴展開放 對代碼的修改是封閉的 在目標函數前和後插入一段新的代碼.不改變原來的代碼 通用裝飾器寫法: # 存在的意義: 在不破壞原有函數調用的基礎上,給韓式添加新的功能 def wrapper(fn): # fn是目標函數 def inner(*args, ...
  • 一、頻率簡介 為了控制用戶對某個url的請求 的頻率,比如 ,一分鐘以內,只能訪問三次 二、自定義頻率類,自定義頻率規則 自定義的邏輯 代碼實現: import time 自定義頻率控制 class MyThrottle(): visitor_dic = {} def __init__(self): ...
  • 報錯信息: 根本原因: 一是配置文件,二是註解。 網上總結的一般原因:Mapper interface和xml文件的定義對應不上,需要檢查包名,namespace,函數名稱等能否對應上。按以下步驟一一執行:1、檢查xml文件所在的package名稱是否和interface對應的package名稱一一 ...
  • 題意 "題目鏈接" Sol $30 \%$dp: $f[i][j]$表示放了$i$個$1$和$j$個$0$的不合法方案 ...
  • 3.從context-param獲取:(視頻下載) (全部書籍)馬克-to-win:用context-param存放的參數,本個web應用中的任何servlet,jsp都可以獲得。例:1.3.1 ServletHello1.java:package com;import java.io.IOExce ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...