Java—RPC:远程过程调用(1)

标签: java  rpc

Java—RPC:远程过程调用(1)

在我们学习RPC的过程中,首先我们先认识一下项目结构在发展中干的变化

一、项目结构变化

1、单体结构

​ 单体结构又叫单一项目,在我们所认识的传统项目基本上都是单一项目,j可是在互联网逐步发展的过程中,逐渐的淘汰 。单一项目的架构过于简单,改一个东西都要重新的启动,重新编译。

​ 在单体架构项目中,团队都是通过包进行区分每个模块。

优点:

​ 部署简单、维护方便、开发成本低

缺点:

​ 当项目规模过大,用户的访问量频率高,并发量大;数据量大,会大大的降低执行效率,甚至会出现宕机。

适用项目:

​ 传统管理项目、小型互联网项目。

2.分布式架构

​ 分布式架构就是将一个项目按照特定的情况和要求(或者按照模块和功能)拆分成若干个项目,把每一个项目分别的部署到不同的服务器中。

优点:

​ 增了系统可用性、增加了重用性、增加了可扩展性、增加每个模块的负载能力。

缺点:

​ 成本高、架构更加复杂、整体响应时间变长、吞吐量更大。吞吐量=请求数/秒

适用项目:

​ 中、大型互联网项目。客户多、数据多、并高发、压力大,吞吐量高的项目。

​ 通过以上的项目结构的分析,我们可以很清楚的看出每个项目结构的优缺点,在项目选择上可以更加清楚的做出选择。

​ 通过分布式架构的简介我们可以看出的是:分布式架构中的各个模块是如何进行通信?

​ 在现阶段中我们可以使用Http协议,也可以使用RPC协议通信,也可以使用其他的通信方式,在这里我们着重的介绍使用RPC协议,因为它比Http协议更适合内部通信。

二、RPC简介

​ 2.1、RFC:互联网工程任务组(IEIF)发布的文件集,每一个文件集都有自己唯一的编号,其中我们所介绍的RPC就收集在rfc 1831中。 可以通过下面的网址查看:

​ https://datatracker.ietf.org/doc/rfc1831/

​ 2.2、RPC:协议规定允许互联网中一台主机程序调用另一台主机程序,而程序员无需对这个交互过程进行编程。在RPC协议中强调的是:当X程序调用Y程序的 更能或方法时,X是不知道同时也不需要知道Y中方法具体的实现。

​ RPC是上层协议,但是它的底层是基于TCP协议,也可以基于HTTP协议。一般我们说RPC都是基于RPC的具体实现。

​ RPC框架一般都带有丰富事务服务治理功能,更加的适合企业内部接口调用。而HTTP协议更适合多平台之间相互调用。

​ 在这里我们借用HttpClient来实现RPC协议。HttpClient起初是Apache Jakarta Common 的子项目。是用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持最新的HTTP版本。到2007年的时候已经成为了顶级项目。

下面我们就HttpClient来简单的实现RPC从协议:

首先第一步就是创建一个项目以便我们进行测试:

在这里插入图片描述

第二步导依赖:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

第三步书写代码:

3.1:创建一个启动类:

package com.bjsxt.httpclicentserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @SpringBootApplication:是一个组合注解,用于快捷配置启动类
 *
 * 此注解等同于@[email protected][email protected]的合集
 */
@SpringBootApplication
public class ServerApp {
    public static void main(String[] args) {
        SpringApplication.run(ServerApp.class,args);
    }
}
/*
@SpringBootApplication:是一个组合注解,用于快捷的培训hi启动类可配置多个启动类,但启动时需要选择以那个类作为启动类来启动项目
*/

3.2:创建一个控制类:

package com.bjsxt.httpclicentserver.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

    @RequestMapping(value="/test" ,produces={"application/json;charset=UTF-8"})
    @ResponseBody
    public  String test(){
        return "{\"msg\":\"处理返回\"}";
    }
}
/*
@Controller注解:是Spring框架提供的注解,Controller标识类,该类代表控制器(控制层/表现层)。
@RequestMapping注解:是一个用来处理请求地址映射的注解,可以用于类或方法上。用在类上表示类中所有请求的方法都是以该地址作为父路径。
    value属性:指定请求的实际地址
    method属性:指定请求方式,get\post
    consumes属性:指定处理请求的提交内容类型
    produces属性:指定返回内容类型,可以防止中文乱码
	params属性:指定request中必须包含某些参数值时,才让该方法处理
	headers属性:指定request中必须包含某些指定的header值
 @ResponseBody注解:就是将返回的对象通过MessageConverter处理之后,写入response的outputStream中返回。
*/

3.3:测试代码

以上的代码完成后,我们需要通过启动类进行测试返回的结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4NsmY5h-1596204049432)(D:\桌面文件\RPC\05.png)]

现在我们暂时是用浏览器进行访问,将来我们就需要用代码进行访问

3.4:创建一个客户端

httpclient_rpc_client

在这里插入图片描述

3.4.1:导入httpClient依赖

<dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>
    </dependencies>

在我们拥有了这个代码以后我们就可以尝试的写客户端代码了

3.4.2:GET无参数客户端代码

package com.bjsxt.httpclient;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 *使用Main方法,测试HttpClient技术
 */
public class TestHttpClient {
    public static void main(String[] args) throws IOException {
        //调用无参数GET请求
        testGetNoParams();
    }
/**
 *在这里我们创建一个静态的没有返回值的类
 * 无参数GET请求
 * 在我们用HttpClient发送请求的时候我们回忆一下
 * 使用浏览器访问网站的过程
 * 1、打开浏览器
 * 2、输入网址
 * 3、访问
 * 4、看结果
 * 使用HttpClient,访问WEB服务器的过程
 * 1、创建客户端,相当于打开浏览器
 * 2、创建请求地址,相当于输入网址
 * 3、发送请求,相当于访问王网址(回车键)
 * 4、返回处理结果,相当于浏览器返回结果
 *通过以上的对比我们可以看出两者之间的相似的程度
 */
    public static void testGetNoParams() throws IOException {
        /*
        注意的是不要导错包了,我们所导入的是
        import org.apache.http.client.HttpClient;包下的HttpClient
         */
        //创建客户端对象
        HttpClient  client = HttpClients.createDefault();

        /*
        创建请求地址
        我们所请求的地址是以字符串形式展现的
        IP:localhost
        端口号:8080
        请求路径:test
         */
        HttpGet get = new HttpGet("http://localhost:8080/test");
        //发起请求,接收响应对象
        HttpResponse response = client.execute(get);
        /*
        获取响应体。响应数据是一个基于HTTP协议标准字符串封装的对象
        响应体和响应头,都是封装的HTTP协议数据。直接使用可能会出现乱码或解析错误.
        所以我们要对数据进行转换。
         */
        HttpEntity entity = response.getEntity();

        /*
        通过HTTP实体类工具,转换响应体数据,使用的字符集是UTF-8
        UTF-8,也可以省略不写,因为默认的就是UTF-8字符集
         */
        String responseString = EntityUtils.toString(entity, "UTF-8");

        System.out.println("服务器响应的结果是-[" + responseString +"]");

        //回收资源
        client = null;
    }
}

3.4.3:返回结果


服务器响应的结果是-[{"msg":"处理返回"}]

Process finished with exit code 0

以上的代码是我们运用的是无参数的请求方式。

3.5:有参数的客户端代码

3.5.1:首先到控制器中增加一个有参数的控制类

 @RequestMapping(value="/params",produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String params(String name,String password){
        System.out.println("name - "+name +";password -" +password);
        return "{\"msg\":\"登录成功\",\"user\"{\"name\":\""+name+"\";\"password\":\""+password+"\"}}";
    }

有参数的控制器写完之后,重新启动一下启动类。

3.5.2:GET有参数客户端代码

//调用有参数GET请求
        testGetParams();
    }

    /*
    有参数的GET请求
     */
    public static void testGetParams() throws URISyntaxException, IOException {
        //创建客户端对象
        HttpClient client = HttpClients.createDefault();

        //基于Builder构建请求地址,有异常直接抛出
        URIBuilder builder = new URIBuilder("http://localhost:8080/params");

        /*//基于单参数传递,构建请求地址
        builder.addParameter("name","bjsxt");
        builder.addParameter("password","123456");
        URI uri= builder.build();*/

        /*基于多参数传递,构建请求地址
        在导入NameValuePair的时候需要注意不要导错包了
        我们所导入的是一个接口在http下:import org.apache.http.NameValuePair;
        NameValuePair是一个接口,自己构建的话直接return返回字符串就行。
        也可以使用BasicNameValuePair,BasicNameValuePair是NameValuePair接口的实现类,
        可以直接new对象,也可以直接传name和value

        或者直接在http请求地址的后面直接加上?
         */
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("name","bjsxt"));
        nvps.add(new BasicNameValuePair("password","123456"));
        builder.addParameters(nvps);
        URI uri = builder.build();
        System.out.println(uri.toASCIIString());

        /*
        在无参数中我们分开请求的,在这里我们是一起进行处理的。
         */
        String result = EntityUtils.toString(client.execute(new HttpGet(uri)).getEntity());
        System.out.println(result);
    }

3.5.3:返回的结果

http://localhost:8080/params?name=bjsxt&password=123456
{"msg":"登录成功""user"{"name":"bjsxt";"password":"123456"}}

Process finished with exit code

上面的代码操作的都是GET请求的数据,下面我么们开始介绍POST请求

3.6:POST无参数客户端代码

post请求和get请求唯一的区别就是在于我们请求的对象,其它的区别上不大。

public static void testPost() throws IOException {
        //创建客户端对象
        HttpClient client = HttpClients.createDefault();

        /*
        无参的post请求,test对应的是无参的请求
        与get请求唯一的区别就是这里用的是HttpPost
         */
        HttpPost post = new HttpPost("http://localhost:8080/test");
        /*
        有异常直接抛出
         */
        HttpResponse response = client.execute(post);
        System.out.println(EntityUtils.toString(response.getEntity(),"UTF-8"));
    }

3.6:POST有参数的客户端代码:

/*
        有参数的post请求。请求头携带参数。和get请求携带的参数的方式一致
       有异常直接抛出,咱们的对象不用重新创建,就像浏览器页面一样,不需要关了在启动。
       在这里还可以在请求的地址后面加上?参数&参数
         */
        URIBuilder builder = new URIBuilder("http://localhost:8080/params");
        builder.addParameter("name","post");
        builder.addParameter("password","post123456");
        HttpResponse postResponse = client.execute(new HttpPost(builder.build()));
        System.out.println(EntityUtils.toString(postResponse.getEntity(),"UTF-8"));

        /*
        请求体传递参数
        在我们使用post请求的时候,我们一般都是使用请求体进行传参,在非专业人事的眼中是隐藏路径的
         */
        HttpPost bodyParamsPost = new HttpPost("http://localhost:8080/params");

        /*
        定义请求协议体,设置请求参数,这是传字符串形式
        使用请求体传递参数的时候。需要定义请求格式,默认的是表单格式,
        通常我们都会把默认的请求格式进行修改成阶层格式
        使用URIbuilder构建的URI对象,就是请求体传递参数
         */
        HttpEntity entity = new StringEntity("name=bjsxt&password=123");

        /*
        给响应体是同一个类型,它所代表的就是协议体,当两个子类型分别是响应协议体和请求协议体
        面向父类的方式进行开发的
         */
        bodyParamsPost.setEntity(entity);

        //输出
        System.out.println(EntityUtils.toString(client.execute(bodyParamsPost).getEntity(),"UTFa-8"));


    }

返回结果

{"msg":"处理返回"}
{"msg":"登录成功""user"{"name":"post";"password":"post123456"}}
{"msg":"登录成功""user"{"name":"null";"password":"null"}}

Process finished with exit code 0

其中第三的参数属于没有传过来,原因是请求体格式不符合标准要求。这种的请求方式对应的是另一种格式。

3.7:解决参数为空

3.7.1在控制器类增加一个控制类

 //使用请请求体传递请求参数
 @RequestMapping( value = "/bodyParams",produces ="application/json;charset=UTF-8")
    @ResponseBody
    @CrossOrigin
//在这里我先传入了实体类User,这个实体类应该在实体类创建完毕后再传入的。注意一下。
    public String bodyParams(@RequestBody List<User> users){
        System.out.println(users);
        return users.toString();
    }
/*
 @CrossOrigin注解:是用来处理跨域请求的注解
 @RequestBody注解:作用其实是将json格式的数据转为java对象
*/

​ 在这里导入包的时候请注意包是不是导入错误,在这里我们应该导入的是自己的User,而不是导入catalina的User。这样的话会报一个错误:

控制台报的错误:

{"timestamp":"2020-07-31T12:29:27.686+00:00","status":500,"error":"Internal Server Error","message":"","path":"/bodyParams"}

服务器日志:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.apache.catalina.User` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.apache.catalina.User` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

3.7.2:创建一个实体类项目

在这里插入图片描述

3.7.3:在实体类项目中书写实体类的代码

package com.bjsxt.httpclient;

import java.io.Serializable;
import java.util.Objects;

public class User implements Serializable {
    private String name;
    private String password;

    public User() {
    }

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) &&
                Objects.equals(password, user.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, password);
    }

    /*
    在这里我把toString代码进行了修改
    */
    @Override
    public String toString() {
        return "{\"name\":\""+name+"\", \"password\":\""+password+"\"}";
    }
}

3.7.4:实体类结束之后需要在另外的两个项目中分别增加依赖

	   <dependency>
            <groupId>org.example</groupId>
            <artifactId>httpclient_rpc_pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

书写完毕后重启一下项目。

再做请求的时候我们就会拿请求体传递数据了。

3.7.5:这样的话我们下面就需要做一些代码上的修改,修改如下,暂时有bug还为解决,已解决。

原因:

​ 控制类导包错误,上面已有解释造成的原因。

/*
        请求体传递参数
        在我们使用post请求的时候,我们一般都是使用请求体进行传参,在非专业人事的眼中是隐藏路径的
         */
        HttpPost bodyParamsPost = new HttpPost("http://localhost:8080/bodyParams");

        /*
        定义请求协议体,设置请求参数,这是传字符串形式
        使用请求体传递参数的时候。需要定义请求格式,默认的是表单格式,
        通常我们都会把默认的请求格式进行修改成阶层格式
        使用URIbuilder构建的URI对象,就是请求体传递参数
         */
        User u1 = new User();
        u1.setName("name1");
        u1.setPassword("password1");
        User u2 = new User();
        u2.setName("name2");
        u2.setPassword("password2");

        /*这是一个集合的表达形式json格式字符串,表示请求参数,一个List<User>
        但是这里面的toString方法不能直接用,这里是我修=修改过的toString方法。
         */
        String paramsString = "["+u1.toString()+","+u2.toString()+"]";
        /*
        StringEntity除了可以传字符串(String)、字符集(Charset)和请求体的格式(mimType)
         */
        HttpEntity entity = new StringEntity(paramsString,"application/json","UTF-8");

        /*
        给响应体是同一个类型,它所代表的就是协议体,当两个子类型分别是响应协议体和请求协议体
        面向父类的方式进行开发的
         */
        bodyParamsPost.setEntity(entity);

        //输出
        System.out.println(EntityUtils.toString(client.execute(bodyParamsPost).getEntity(),"UTF-8"));


    }

3.7.6:返回的结果

{"msg":"处理返回"}
{"msg":"登录成功""user"{"name":"post";"password":"post123456"}}
[{"name":"name1", "password":"password1"}, {"name":"name2", "password":"password2"}]

Process finished with exit code 0

jackson和gson在性能和效率上没有区别。Spring底层和json转换对象用的都是jackson。而我们开发的时候基本上都会用到Spring,尽量和他的应用和规则进行匹配。

3.8:下面我们做一下String字符串和Json字符串之间的转换。

3.8.1:首先在实体类中增加数据

private Date birth;

    public int getAge(){
        if (birth == null){
            return -1;
        }
        //生日的年份
        int brithYear = birth.getYear();
        //当前的年份
        int currentYear = new Date().getYear();
        //年龄
        return  currentYear - brithYear;
    }

    public void setAge(int age){

    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

3.8.2:控制台代码:

/*
        请求体传递参数
        在我们使用post请求的时候,我们一般都是使用请求体进行传参,在非专业人事的眼中是隐藏路径的
         */
        HttpPost bodyParamsPost = new HttpPost("http://localhost:8080/bodyParams");

        /*
        定义请求协议体,设置请求参数,这是传字符串形式
        使用请求体传递参数的时候。需要定义请求格式,默认的是表单格式,
        通常我们都会把默认的请求格式进行修改成阶层格式
        使用URIbuilder构建的URI对象,就是请求体传递参数
         */
        User u1 = new User();
        u1.setName("name1");
        u1.setPassword("password1");
        User u2 = new User();
        u2.setName("name2");
        u2.setPassword("password2");
        List<User> users = new ArrayList<>();
        users.add(u1);
        users.add(u2);
        // 把集合users -> JSON字符串
        // 创建Jackson中的转换器对象
        ObjectMapper objectMapper = new ObjectMapper();

        /*
        java对象返回json格式字符串,不是所有的java对象都能转换为json格式的字符串
        java字符串转换为json格式字符串的时候是遵循一个原则
        默认get对应的property进行转换拼一个字符串。
        补充:
        property - 类型中getter和setter的命名。去除set|get,剩余部分首字母转小写。
        <bean><property name="abc"></property></bean>
        field - 类型中定义的实例变量。
         */
        String paramsString = objectMapper.writeValueAsString(users);

        System.out.println(paramsString);



        /*这是一个集合的表达形式json格式字符串,表示请求参数,一个List<User>
        但是这里面的toString方法不能直接用,这里是我修=修改过的toString方法。
        String paramsString = "["+u1.toString()+","+u2.toString()+"]";
         */
        /*
        StringEntity除了可以传字符串(String)、字符集(Charset)和请求体的格式(mimType)
         */
        HttpEntity entity = new StringEntity(paramsString,"application/json","UTF-8");

        /*
        给响应体是同一个类型,它所代表的就是协议体,当两个子类型分别是响应协议体和请求协议体
        面向父类的方式进行开发的
         */
        bodyParamsPost.setEntity(entity);
        System.out.println(EntityUtils.toString(client.execute(bodyParamsPost).getEntity(),"UTF-8"));

3.8.3:返回的结果:

{"msg":"处理返回"}
{"msg":"登录成功""user"{"name":"post";"password":"post123456"}}
[{"name":"name1","password":"password1","birth":null,"age":-1},{"name":"name2","password":"password2","birth":null,"age":-1}]
[{"name":"name1", "password":"password1"}, {"name":"name2", "password":"password2"}]

上面是已完成的代码:把String字符串转换为json格式字符串,如果我们想把json格式字符串转换为String对象怎么办?反向处理具体的操作如下:

 /*
        给响应体是同一个类型,它所代表的就是协议体,当两个子类型分别是响应协议体和请求协议体
        面向父类的方式进行开发的
         */
        bodyParamsPost.setEntity(entity);
        /*System.out.println(EntityUtils.toString(client.execute(bodyParamsPost).getEntity(),"UTF-8"));*/
        String responseString  = EntityUtils.toString(client.execute(bodyParamsPost).getEntity(), "UTF-8");

        //这是一个但对象,从下标1开始到,结束。包含开始下标不包含结束下标
        String userstring = responseString.substring(1, responseString.indexOf("},") + 1);

        User responseUser = objectMapper.readValue(userstring,User.class);

        System.out.println(responseUser);

        //构建一个Jackson识别的Java类型映射,以下是集合的转换方式
        JavaType valueType =
                objectMapper.getTypeFactory().constructParametricType(List.class, User.class);
        List<User> list = objectMapper.readValue(responseString, valueType);
        System.out.println(list);

返回的结果如下:

{"msg":"处理返回"}
{"msg":"登录成功""user"{"name":"post";"password":"post123456"}}
[{"name":"name1","password":"password1","birth":null,"age":-1},{"name":"name2","password":"password2","birth":null,"age":-1}]
{"name":"name1", "password":"password1"}
[{"name":"name1", "password":"password1"}, {"name":"name2", "password":"password2"}]

Process finished with exit code 0

System.out.println(responseUser);

    //构建一个Jackson识别的Java类型映射,以下是集合的转换方式
    JavaType valueType =
            objectMapper.getTypeFactory().constructParametricType(List.class, User.class);
    List<User> list = objectMapper.readValue(responseString, valueType);
    System.out.println(list);

返回的结果如下:

```java
{"msg":"处理返回"}
{"msg":"登录成功","user"{"name":"post";"password":"post123456"}}
[{"name":"name1","password":"password1","birth":null,"age":-1},{"name":"name2","password":"password2","birth":null,"age":-1}]
{"name":"name1", "password":"password1"}
[{"name":"name1", "password":"password1"}, {"name":"name2", "password":"password2"}]

Process finished with exit code 0

版权声明:本文为jiu_dian原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jiu_dian/article/details/107722354

智能推荐

Python学习练习6----列表、字典的运用2

range 用法参见http://blog.csdn.net/chiclewu/article/details/50592368 直接在 在线编程工具中练习: https://www.tutorialspoint.com/execute_python_online.php 代码如下,增加range、列表的len()、字典的items()函数,for 函数也有了新变化 练习2: 2的运行结果,注意p...

PoolThreadCache

缓存构成   PoolThreadCache的缓存由三部分构成:tiny、small 和 normal。 tiny   缓存数据大小区间为[16B, 496B]数据,数组长度为32,根据数据大小计算索引的办法:数据大小除以16,如下代码所示: small   缓存数据大小区间为[512B, 4KB]数据,数组长度为4,根据数据大小计算索引的办法:数据大小除以512,然后log2得到指数,如下代码所...

Intellij IDEA 搭建Spring Boot项目(一)

Intellij IDEA 搭建Spring Boot项目 标签(空格分隔): SpringBoot JAVA后台 第一步 选择File –> New –> Project –>Spring Initialer –> 点击Next  第二步 自己修改 Group 和 Artif...

CentOS学习之路1-wget下载安装配置

参考1: https://blog.csdn.net/zhaoyanjun6/article/details/79108129 参考2: http://www.souvc.com/?p=1569 CentOS学习之路1-wget下载安装配置 1.wget的安装与基本使用 安装wget yum 安装软件 默认安装保存在/var/cache/yum ,用于所有用户使用。 帮助命令 基本用法 例子:下载...

深入浅出Spring的IOC容器,对Spring的IOC容器源码进行深入理解

文章目录 DispatcherServlet整体继承图 入口:DispatcherServlet.init() HttpServletBean.init() FrameworkServlet.initServletBean() 首先大家,去看Spring的源码入口,第一个就是DispatcherServlet DispatcherServlet整体继承图 入口:DispatcherServlet....

猜你喜欢

laravel框架的课堂知识点概总

1. MVC 1.1 概念理解 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑 MVC 是一种使用 MVC(Model View Controller ...

Unity人物角色动画系统学习总结

使用动画系统控制人物行走、转向、翻墙、滑行、拾取木头 混合树用来混合多个动画 MatchTarget用来匹配翻墙贴合墙上的某一点,人物以此为支点翻墙跳跃 IK动画类似于MatchTarget,控制两只手上的两个点来指定手的旋转和位置,使得拾取木头时更逼真 创建AnimatorController: 首先创建一个混合树,然后双击 可以看到该混合树有五种状态机,分别是Idle、WalkForward、...

Composer 安装 ThinkPHP6 问题

Composer 安装 ThinkPHP6 问题 先说说问题 一.运行环境要求 二.配置 参考: ThinkPHP6.0完全开发手册 先说说问题 执行ThinkPHP6的安装命令 遇到问题汇总如下: 看提示是要更新版本,执行命令更新。 更新之后,再次安装ThinkPHP,之后遇到如下问题。 尝试了很多方法,依然不能解决。其中包括使用https://packagist.phpcomposer.com...

Spring Boot 整合JDBC

今天主要讲解一下SpringBoot如何整合JDBC,没啥理论好说的,直接上代码,看项目整体结构 看一下对应的pom.xml 定义User.java 定义数据源配置,这里使用druid,所以需要写一个配置类 上面指定druid的属性配置,和用户登录的账号信息以及对应的过滤规则: 下面定义数据访问接口和对应的实现: 数据访问层很简单,直接注入JdbcTemplate模板即可,下面再看对应的servi...

html鼠标悬停显示样式

1.显示小手:     在style中添加cursor:pointer 实现鼠标悬停变成小手样式     实例:         其他参数: cursor语法: cursor : auto | crosshair | default | hand | move | help | wait | tex...