01-课程内容介绍
软件架构的演进过程
Apache Dubbo概述
服务注册中心Zookeeper
Dubbo快速入门
Dubbo管理控制台
Dubbo相关配置说明
解决Dubbo无法发布被事务代理的Service问题
dubbo优化后的代码:
https://github.com/itheimaxia/dubbo-demo
软件架构的发展经历了由单体架构、垂直架构、SOA架构到微服务架构的演进过程,下面我们分别了解一下这几个架构。
02-软件架构的演进过程(单体架构)

架构说明:
1、所有的功能集成在一个项目工程中(All in one)。
2、所有的功能打一个war包部署到服务器。
3、应用与数据库分开部署。
4、通过部署应用集群和数据库集群来提高系统的性能。
架构优点:
架构简单,前期开发成本低、开发周期短,适合小型项目。
架构缺点:
全部功能集成在一个工程中,对于大型项目不易开发、扩展和维护。
技术栈受限,只能使用一种语言开发。
系统性能扩展只能通过扩展集群节点,成本高。
03-软件架构的演进过程(垂直架构)

架构说明:
1、以单体结构规模的项目为单位进行垂直划分项目即将一个大项目拆分成一个一个单体结构项目。
2、项目与项目之间的存在数据冗余,耦合性较大,比如上图中三个项目都存在客户信息。
3、项目之间的接口多为数据同步功能,如:数据库之间的数据库,通过网络接口进行数据库同步。
架构优点:
1. 技术栈可扩展(不同的系统可以用不同的编程语言编写)。
2. 项目架构简单,前期开发成本低,周期短,小型项目的首选。
3. 通过垂直拆分,原来的单体项目不至于无限扩大。
架构缺点:
功能集中在一个项目中,不利于开发、扩展、维护。
系统扩张只能通过集群的方式。
项目之间功能冗余、数据冗余、耦合性强。
04-软件架构的演进过程(SOA架构)
SOA全称为Service-Oriented Architecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。
站在功能的角度,把业务逻辑抽象成可复用的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。

架构说明:
将重复功能或模块抽取成组件的形式,对外提供服务,在项目与服务之间使用ESB(企业服务总线)的形式作为通信的桥梁。
架构优点:
1、将重复的功能抽取为服务,提高开发效率,提高系统的可重用性、可维护性。
2、可以针对不同服务的特点制定集群及优化方案。
3、采用ESB减少系统中的接口耦合。
架构缺点:
各系统之间业务不同,很难确认功能或模块是重复的。
抽取服务的粒度大。
系统和服务之间耦合度高。
虽然使用了ESB,但是服务的接口协议不固定,种类繁多,不利于系统维护。
05-软件架构的演进过程(微服务架构)

架构说明:
将系统服务层完全独立出来,抽取为一个一个的微服务。
抽取的粒度更细,遵循单一原则。
采用轻量级框架协议传输。
架构优点:
服务拆分粒度更细,有利于提高开发效率。
可以针对不同服务制定对应的优化方案。
适用于互联网时代,产品迭代周期更短。
架构缺点:
粒度太细导致服务太多,维护成本高。
分布式系统开发的技术成本高,对团队的挑战大。
附:
soa和微服务的区别
| 功能 | SOA | 微服务 |
|---|---|---|
| 组件大小 | 大块业务逻辑 | 单独任务或小块业务逻辑 |
| 耦合 | 通常松耦合 | 总是松耦合 |
| 公司架构 | 任何类型 | 小型、专注于功能交叉的团队 |
| 管理 | 着重中央管理 | 着重分散管理 |
| 目标 | 确保应用能够交互操作 | 执行新功能,快速拓展开发团队 |
06-Apache Dubbo概述(Dubbo简介)
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
什么是RPC?
RPC全称为remote procedure call,即远程过程调用。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。
需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo等。
Dubbo官网地址:http://dubbo.apache.org
Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
拓展:
多种rpc框架之间的对比:
| 开发语言 | 服务治理 | 多种序列化 | 多种注册中心 | 管理中心 | 跨语言通讯 | 整体性能 | |
|---|---|---|---|---|---|---|---|
| Dubbo | Java | ✔ | ✔ | ✔ | ✔ | ✘ | 3 |
| motan | Java | ✔ | ✔ | ✔ | ✔ | ✘ | 4 |
| Thrift | 跨语言 | ✘ | 只支持thrift | ✘ | ✘ | ✔ | 5 |
| Grpc | 跨语言 | ✘ | 只支持protobuf | ✘ | ✘ | ✔ | 3 |
dubbo和dubbox的区别:
Dubbox 和Dubbo本质上没有区别,名字的含义扩展了Dubbo而已,以下扩展出来的功能,也是选择Dubbox很重要的考察点。
支持REST风格远程调用(HTTP + JSON/XML);
支持基于Kryo和FST的Java高效序列化实现;
支持基于Jackson的JSON序列化;
支持基于嵌入式Tomcat的HTTP remoting体系;
升级Spring至3.x;
升级ZooKeeper客户端;
支持完全基于Java代码的Dubbo配置;
注:上述指的是dubbo 2.5.7版本。dubbo在17年已经开始继续更新版本。
esb和dubbo的区别:
https://blog.csdn.net/michaelwubo/article/details/78195185
07-Apache Dubbo概述(Dubbo架构)
Dubbo架构图(Dubbo官方提供)如下:
节点角色说明:
| Provider | 暴露服务的服务提供方 |
|---|---|
| 节点 | 角色名称 |
| Consumer | 调用远程服务的服务消费方 |
| Registry | 服务注册与发现的注册中心 |
| Monitor | 统计服务的调用次数和调用时间的监控中心 |
| Container | 服务运行容器 |
虚线都是异步访问,实线都是同步访问蓝色虚线:在启动时完成的功能红色虚线(实线)都是程序运行过程中执行的功能
调用关系说明:
服务容器负责启动,加载,运行服务提供者。
服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者在启动时,向注册中心订阅自己所需的服务。
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
08-服务注册中心Zookeeper(介绍)
通过前面的Dubbo架构图可以看到,Registry(服务注册中心)在其中起着至关重要的作用。Dubbo官方推荐使用Zookeeper作为服务注册中心。
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
为了便于理解Zookeeper的树型目录服务,我们先来看一下我们电脑的文件系统(也是一个树型目录结构):
我的电脑可以分为多个盘符(例如C、D、E等),每个盘符下可以创建多个目录,每个目录下面可以创建文件,也可以创建子目录,最终构成了一个树型结构。通过这种树型结构的目录,我们可以将文件分门别类的进行存放,方便我们后期查找。而且磁盘上的每个文件都有一个唯一的访问路径,例如:C:\Windows\itcast\hello.txt。
流程说明:
服务提供者(Provider)启动时: 向
/dubbo/com.foo.BarService/providers
目录下写入自己的 URL 地址服务消费者(Consumer)启动时: 订阅
/dubbo/com.foo.BarService/providers
目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers
目录下写入自己的 URL 地址监控中心(Monitor)启动时: 订阅
/dubbo/com.foo.BarService
目录下的所有提供者和消费者 URL 地址
zookeeper 一般是部署成奇数个节点 3、5、7
09-服务注册中心Zookeeper(安装)
下载地址:http://archive.apache.org/dist/zookeeper/
本课程使用的Zookeeper版本为3.4.6,下载完成后可以获得名称为zookeeper-3.4.6.tar.gz的压缩文件。
安装步骤:
第一步:安装 jdk(略)第二步:把 zookeeper 的压缩包(zookeeper-3.4.6.tar.gz)上传到 linux 系统 第三步:解压缩压缩包 tar -zxvf zookeeper-3.4.6.tar.gz第四步:进入zookeeper-3.4.6目录,创建data目录 mkdir data第五步:进入conf目录 ,把zoo_sample.cfg 改名为zoo.cfg cd conf mv zoo_sample.cfg zoo.cfg第六步:打开zoo.cfg文件, 修改data属性:dataDir=/root/zookeeper-3.4.6/data
扩展:
windows下安装zookeeper
第一步:安装 jdk(略)第二步:解压缩压缩包 winrar解压 zookeeper-3.4.6.tar.gz第三步:进入zookeeper-3.4.6目录,创建data目录第四步:进入conf目录 ,把zoo_sample.cfg 改名为zoo.cfg第五步:打开zoo.cfg文件, 修改data属性,根据安装目录修改:dataDir=D:/tools/zookeeper-3.4.6/data/
第六步:执行zkServer.cmd
10-服务注册中心Zookeeper(启动、停止)
进入Zookeeper的bin目录,启动服务命令 ./zkServer.sh start
停止服务命令./zkServer.sh stop
查看服务状态:./zkServer.sh status
11-Dubbo入门案例(服务提供方开发)
Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用。本小节就是要创建两个应用,一个作为服务的提供方,一个作为服务的消费方。通过Dubbo来实现服务消费方远程调用服务提供方的方法。
开发步骤:
(1)创建maven工程(打包方式为war)dubbodemo_provider,在pom.xml文件中导入如下坐标
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- dubbo相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<!-- 指定端口 -->
<port>8081</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
(2)配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
(3)创建服务接口
package com.itheima.service;
public interface HelloService {
public String sayHello(String name);
}
(4)创建服务实现类
package com.itheima.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.service.HelloService;
@Service
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
注意:服务实现类上使用的Service注解是Dubbo提供的,用于对外发布服务
(5)在src/main/resources下创建applicationContext-service.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
<dubbo:application name="dubbodemo_provider" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!-- 注册 协议和port 端口默认是20880 -->
<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
<!-- 扫描指定包,加入@Service注解的类会被发布为服务 -->
<dubbo:annotation package="com.itheima.service.impl" />
</beans>
(6)启动服务
tomcat7:run
注意:示例代码中的applicationContext-service.xml中有mybatis相关代码,注释掉就可以了。
12-Dubbo入门案例(服务消费方开发)
开发步骤:
(1)创建maven工程(打包方式为war)dubbodemo_consumer,pom.xml配置和上面服务提供者相同,只需要将Tomcat插件的端口号改为8082即可
(2)配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
(3)将服务提供者工程中的HelloService接口复制到当前工程
(4)编写Controller
package com.itheima.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.service.HelloService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/demo")
public class HelloController {
@Reference
private HelloService helloService;
@RequestMapping("/hello")
@ResponseBody
public String getName(String name){
//远程调用
String result = helloService.sayHello(name);
System.out.println(result);
return result;
}
}
注意:Controller中注入HelloService使用的是Dubbo提供的@Reference注解
(5)在src/main/resources下创建applicationContext-web.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
<dubbo:application name="dubbodemo-consumer" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://192.168.134.129:2181"/>
<!-- 扫描的方式暴露接口 -->
<dubbo:annotation package="com.itheima.controller" />
</beans>
(6)运行测试
tomcat7:run启动
在浏览器输入http://localhost:8082/demo/hello.do?name=Jack,查看浏览器输出结果
注意:这里没有添加
<context:component-scan base-package="com.itheima.controller"/>
但是却能正常注入,代表dubbo:annotation这个标签已经帮我们处理了这个问题。
13-Dubbo入门案例(测试)
tomcat7:run启动provider
tomcat7:run启动consumer
通过页面访问http://localhost:8082/hello/sayHello.do?name=ttt可以打印出ttt
14-Dubbo入门案例(问题思考)
思考一:上面的Dubbo入门案例中我们是将HelloService接口从服务提供者工程(dubbodemo_provider)复制到服务消费者工程(dubbodemo_consumer)中,这种做法是否合适?还有没有更好的方式?
答:这种做法显然是不好的,同一个接口被复制了两份,不利于后期维护。更好的方式是单独创建一个maven工程,将此接口创建在这个maven工程中。需要依赖此接口的工程只需要在自己工程的pom.xml文件中引入maven坐标即可。
思考二:在服务消费者工程(dubbodemo_consumer)中只是引用了HelloService接口,并没有提供实现类,Dubbo是如何做到远程调用的?
答:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用是通过此代理对象完成的。可以通过开发工具的debug功能查看此代理对象的内部结构。另外,Dubbo实现网络传输底层是基于Netty框架(NIO)完成的。
思考三:上面的Dubbo入门案例中我们使用Zookeeper作为服务注册中心,服务提供者需要将自己的服务信息注册到Zookeeper,服务消费者需要从Zookeeper订阅自己所需要的服务,此时Zookeeper服务就变得非常重要了,那如何防止Zookeeper单点故障呢?
答:Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障。
拓展:
zookeeper部署节点一般为奇数个(至少3个),防止出现脑裂情况。一般中小企业部署为3个。
参考资料:https://www.cnblogs.com/shoufeng/p/10591526.html
15-Dubbo管理控制台(安装)
我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。
安装步骤:
(1)将资料中的dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下
(2)启动tomcat,此war文件会自动解压
(3)修改WEB-INF下的dubbo.properties文件,注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
dubbo.registry.address=zookeeper://localhost:2181 dubbo.admin.root.password=root dubbo.admin.guest.password=guest
(4)重启tomcat
dubbo monitor 监控服务调用情况
apm分布式调用链系统取代
16-Dubbo管理控制台(使用)
操作步骤:
(1)访问http://localhost:8080/dubbo-admin-2.6.0/,输入用户名(root)和密码(root)
(2)启动服务提供者工程和服务消费者工程,可以在查看到对应的信息
备注:
此版本的dubbo admin无法统计调用次数,也无法跟踪调用链。目前微服务架构下,有更好的分布式调用链监控系统。比如:CAT、ZIPKIN、Skywalking等。
17-加入log4j日志
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=${WORKDIR}/logs/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
如果要输出到文件,
log4j.rootLogger=debug, stdout,file
18-Dubbo相关配置说明(包扫描)
<dubbo:annotation package="com.itheima.service" />
服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类。
如果不使用包扫描,也可以通过如下配置的方式来发布服务:
<bean id="helloService" class="com.itheima.service.impl.HelloServiceImpl" />
<dubbo:service interface="com.itheima.api.HelloService" ref="helloService" />
作为服务消费者,可以通过如下配置来引用服务:
下一行可以代替controller注解
<context:component-scan base-package="com.wyc"/>
<!-- 生成远程服务代理,可以和本地bean一样使用helloService -->
<dubbo:reference id="helloService" interface="com.itheima.api.HelloService" />
上面这种方式发布和引用服务,一个配置项(dubbo:service、dubbo:reference)只能发布或者引用一个服务,如果有多个服务,这种方式就比较繁琐了。推荐使用包扫描方式。
19-Dubbo相关配置说明(协议)
<dubbo:protocol name="dubbo" port="20880"/>
一般在服务提供者一方配置,可以指定使用的协议名称和端口号。
其中Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。
推荐使用的是dubbo协议。
dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,例如:
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.itheima.api.HelloService" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.itheima.api.DemoService" ref="demoService" protocol="rmi" />
20-Dubbo相关配置说明(启动时检查)
<dubbo:consumer check="false"/>
上面这个配置需要配置在服务消费者一方,如果不配置默认check值为true。Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题。可以通过将check值改为false来关闭检查。
建议在开发阶段将check值设置为false,在生产环境下改为true。
21-Dubbo相关配置说明(负载均衡)
负载均衡(Load Balance):其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。
在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询(按顺序)、最少活跃调用数(在最近的一段时间内,哪个节点被调用最少)、一致性Hash(固定到某个节点上)),缺省为random随机调用。
配置负载均衡策略,既可以在服务提供者一方配置,也可以在服务消费者一方配置,如下:
@Controller
@RequestMapping("/demo")
public class HelloController {
//在服务消费者一方配置负载均衡策略
@Reference(check = false,loadbalance = "random")
private HelloService helloService;
@RequestMapping("/hello")
@ResponseBody
public String getName(String name){
//远程调用
String result = helloService.sayHello(name);
System.out.println(result);
return result;
}
}
//在服务提供者一方配置负载均衡
@Service(loadbalance = "random")
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
可以通过启动多个服务提供者来观察Dubbo负载均衡效果。
注意:因为我们是在一台机器上启动多个服务提供者,所以需要修改tomcat的端口号和Dubbo服务的端口号来防止端口冲突。
在实际生产环境中,多个服务提供者是分别部署在不同的机器上,所以不存在端口冲突问题。
参考:
https://blog.csdn.net/shengqianfeng/article/details/82314269
22-解决Dubbo无法发布被事务代理的Service问题(问题展示)
前面我们已经完成了Dubbo的入门案例,通过入门案例我们可以看到通过Dubbo提供的标签配置就可以进行包扫描,扫描到@Service注解的类就可以被发布为服务。
但是我们如果在服务提供者类上加入@Transactional事务控制注解后,服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致Dubbo在发布服务前进行包匹配时无法完成匹配,进而没有进行服务的发布。
在入门案例的服务提供者dubbodemo_provider工程基础上进行展示
操作步骤:
(1)在pom.xml文件中增加maven坐标
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
(2)在applicationContext-service.xml配置文件中加入数据源、事务管理器、开启事务注解的相关配置
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
上面连接的数据库可以自行创建
(3)在HelloServiceImpl类上加入@Transactional注解
(4)启动服务提供者和服务消费者,并访问
上面的错误为没有可用的服务提供者
查看dubbo管理控制台发现服务并没有发布,如下:
可以通过断点调试的方式查看Dubbo执行过程,Dubbo通过AnnotationBean的postProcessAfterInitialization方法进行处理
要扫描的包是:com.itheima.service.impl
HelloServiceImpl -> proxy代理对象 Proxy@42 JDKDynamicAOPProxy 获取包全路径 com.sun
@Transactional 注解加上之后,用AOP方式使用代理对象去实现
1.spring component-scan包扫描
先去扫描包路径,然后取到字节码文件获取注解,@Controller,如果有的话,放到spring 容器里
2.dubbo扫描
先去生成对象,然后获得对象的包路径,与扫描包去比对,如果匹配,把信息注册到注册中心,如果不匹配,结束
先去生成对象,同时代理对象,然后把对象给注册到注册中心
23-解决Dubbo无法发布被事务代理的Service问题(解决方案)
1.包去扫描需要注入到spring容器的对象 HelloServiceImpl包->com.itheima.service.impl
2.再次检测包路径是否和你配置的路径一致 生成动态代理类包->com.sun.proxy
通过上面的断点调试可以看到,在HelloServiceImpl类上加入事务注解后,Spring会为此类基于JDK动态代理技术创建代理对象,创建的代理对象完整类名为com.sun.proxy.$Proxy35,导致Dubbo在进行包匹配时没有成功(因为我们在发布服务时扫描的包为com.itheima.service),所以后面真正发布服务的代码没有执行。
解决方式操作步骤:
(1)修改applicationContext-service.xml配置文件,开启事务控制注解支持时指定proxy-target-class属性,值为true。其作用是使用cglib代理方式为Service类创建代理对象
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
(2)修改HelloServiceImpl类,在Service注解中加入interfaceClass属性,值为HelloService.class,作用是指定服务的接口类型
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
此处也是必须要修改的,否则会导致发布的服务接口为SpringProxy,而不是HelloService接口,如下:
dubbo面试题:
https://blog.csdn.net/Y0Q2T57s/article/details/83005376
dubbo直连:
<dubbo:reference id="xxxService"interface="com.alibaba.xxx.XxxService"url="dubbo://localhost:20890"/>
被cglib增强之后的bean,包路径不会发生变化
扩展:
如何进行本地的服务调试?
在服务消费方配置:
@Reference(url="dubbo://localhost:20881")
就可以直接连接本地的服务。
但是注意,此种情况下可以把提供方和测试方向zookeeper注册都改为false:
<dubbo:registry address="zookeeper://localhost:2181" register="false"></dubbo:registry>
如果采用配置文件方式可以这样写:
<dubbo:service interface="com.itheima.api.service.HelloService" ref="helloService" url="dubbo://localhost:20881"/>
1. Vue概述
前端演变的过程
1.随着JavaScript的诞生,我们可以操作页面的DOM元素及样式,页面有了一些动态的效果,但是依然是以静态为主。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/jquery.min.js"></script>
<script>
function change(){
var str = document.getElementById("testh2").value
if(str === '文字内容'){
document.getElementById("testh2").value = "改变文字内容"
}else{
document.getElementById("testh2").value = "文字内容"
}
}
</script>
</head>
<body>
<input id="testh2" value="文字内容">
<button onclick="change()">点我</button>
</body>
</html>
上图中的代码,通过操作dom元素来改变dom显示的内容
== equality 等同,=== identity 恒等。
==, 两边值类型不同的时候,要先进行类型转换,再比较。
下面分别说明:先说 ===,这个比较简单。下面的规则用来判断两个值是否===相等:
1、如果类型不同,就[不相等]
2、如果两个都是数值,并且是同一个值,那么[相等];(!例外)的是,如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
3、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。
4、如果两个值都是true,或者都是false,那么[相等]。
5、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。
6、如果两个值都是null,或者都是undefined,那么[相等]。
再说 ==,根据以下规则:
1、如果两个值类型相同,进行 === 比较。
2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
a、如果一个是null、一个是undefined,那么[相等]。
b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。js核心内置类,会尝试valueOf先于toString;例外的是Date,Date利用的是toString转换。非js核心的对象,令说(比较麻 烦,我也不大懂)
e、任何其他组合,都[不相等]。
= 赋值运算符
== 等于
=== 严格等于
例:var a = 3;
var b = "3";
a==b 返回 truea===b 返回 false
因为a,b的类型不一样===用来进行严格的比较判断
2.开发人员通过ajax与后端交互,然后通过JS操作Dom元素来实现页面动态效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/jquery.min.js"></script>
<script>
function change(){
var str = $('#testh2').val()
str==='文字内容'? $('#testh2').val("改变文字内容"):$('#testh2').val("文字内容")
}
</script>
</head>
<body>
<input id="testh2" value="文字内容">
<button onclick="change()">点我</button>
</body>
</html>
通过jquery来操作dom元素,会简单很多。这个阶段ajax流行起来,通过ajax调用后端接口,然后使用jquery刷新dom对象显示。
3.MVVM(Model-View-ViewModel)中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响
只要Model发生了改变,View上自然就会表现出来。
当用户修改了View,Model中的数据也会跟着改变。
data : {
mvvm : '文字内容'
},
changeMvvm(){
if(this.mvvm === '文字内容'){
this.mvvm = '改变文字内容'
}else{
this.mvvm = '文字内容'
}
}
不需要操作dom元素,只需要操作数据,框架会自动处理dom元素和数据的绑定关系
也就是不需要关系model(数据)和view(dom元素)的关系
vue介绍
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
前端框架三巨头:Vue.js、React.js、AngularJS,vue.js以其轻量易用著称,vue.js和React.js发展速度最快。渐进式:可以选择性的使用该框架的一个或一些组件,这些组件的使用也不需要将框架全部组件都应用;而且用了这些组件也不要求你的系统全部都使用该框架。
2. 搭建示例工程
目标:使用IDEA创建示例工程并在工程中通过npm安装下载vue.js
分析:
vue是一个前端框架,也是其实是一个js文件;下载vue.js文件并在页面中引入该js文件。
vue.js的下载方式:
可以引用在线的vue.js
<!-- 开发环境版本,包含了用帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>可以离线下载vue.js;
下载地址:https://github.com/vuejs/vue可以下载2.6.10版本https://github.com/vuejs/vue/archive/v2.6.10.zip 或资料文件夹中也已下载下载解压,在dist 可得到vue.js文件。
<script src="js/vue-2.6.10.js"></script>npm包资源管理器,可以下载vue.js
使用了npm的方式安装vue模块:
#初始化
npm init -y
#下载vue模块
npm install vue --save注意:
刚安装完之后idea需要重启才可以在terminal使用npm命令进行操作。
引入vue:
<script src="node_modules/vue/dist/vue.js">
nodejs和npm安装方式(后续有课程会讲到,这节课我们可以简单的引入js文件直接使用):
https://www.jianshu.com/p/96f2f01a4f3e
3. 演示双向绑定与事件处理
目标:创建01-demo.html页面并初始化Vue实例,通过console修改Vue数据实现双向绑定效果和创建按钮实现点击即自增
分析:
创建页面,初始化vue;
{{}}获取显示数据;
v-model实现双向绑定;
v-on演示事件处理
小结:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="num"><button v-on:click="num++">点我</button>
<h2>{{name}} 非常酷!有{{num}}个学科。</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
name:"黑马",
num: 1
}
});
</script>
</body>
</html>
初始化代码中el里标注vue生效的div
data里包含了要和dom元素进行数据绑定的内容
拓展:
当一个 Vue 实例被创建时,它将 data
对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data
中的属性才是响应式的。也就是说如果你添加一个新的属性,比如:
vm.b = 'hi'
那么对 b
的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $
,以便与用户定义的属性区分开来。例如:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
4. Vue实例生命周期及钩子函数
目标:了解Vue实例生命周期,生命周期的钩子函数及created函数常用场景
分析:
在创建vue实例的时候可以指定模板id、数据和方法;而如果要在实例化、模板渲染的过程中需要执行一些其它操作的话;那么可以使用钩子函数。
小结:
模板或元素
每个Vue实例都需要关联一段Html模板,Vue会基于此模板进行视图渲染;可以通过el属性来指定。例如一段html模板:
<div id="app">
</div>
然后创建Vue实例,关联这个div
var vm = new Vue({
el:"#app"
})
数据
当Vue实例被创建时,它会尝试获取在data中定义的所有属性,用于视图的渲染,并且监视data中的属性变化,当data发生改变,所有相关的视图都将重新渲染,这就是“响应式“系统。
<div id="app">
<input type="text" v-model="name"/>
</div>
var vm = new Vue({
el:"#app",
data:{
name:"黑马"
}
})
name的变化会影响到input 的值
input中输入的值,也会导致vm中的name发生改变
方法
var vm = new Vue({
el:"#app",
data:{
},
methods:{
//add:function(){
add(){
console.log("点我了...233")
}
}
})
<div id="app">
<button v-on:click="add">点我</button>
</div>
生命周期钩子
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
beforecreate : 完成实例初始化,初始化非响应式变量this指向创建的实例;可以在这加个loading事件;data computed watch methods上的方法和数据均不能访问
created实例创建完成完成数据(data props computed)的初始化 导入依赖项。可访问data computed watch methods上的方法和数据未挂载DOM,不能访问$el,$ref为空数组可在这结束loading,还做一些初始化,实现函数自执行,可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。若在此阶段进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中
berofeMount有了el,编译了template|/outerHTML能找到对应的template,并编译成render函数
mounted完成创建vm.$el,和双向绑定,完成挂载DOM 和渲染;可在mounted钩子对挂载的dom进行操作即有了DOM 且完成了双向绑定 可访问DOM节点,$ref可在这发起后端请求,拿回数据,配合路由钩子做一些事情;可对DOM 进行操作
beforeUpdate数据更新之前可在更新前访问现有的DOM,如手动移除添加的事件监听器;
updated :完成虚拟DOM的重新渲染和打补丁;组件DOM 已完成更新;可执行依赖的dom 操作注意:不要在此函数中操作数据,会陷入死循环的。
activated:在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了,如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发
deactivated for keep-alive 组件被移除时使用
beforeDestroy:在执行app.$destroy()之前可做一些删除提示,如:你确认删除XX吗?可用于销毁定时器,解绑全局时间 销毁插件对象
destroyed :
当前组件已被删除,销毁监听事件 组件 事件 子实例也被销毁
这时组件已经没有了,你无法操作里面的任何东西了。
钩子函数不要使用箭头函数的方式编写。比如
created: () => console.log(this.a)
或vm.$watch('a', newValue => this.myMethod())
。因为箭头函数并没有this
,this
会作为变量一直向上级词法作用域查找,直至找到为止,经常导致Uncaught TypeError: Cannot read property of undefined
或Uncaught TypeError: this.myMethod is not a function
之类的错误。钩子函数中的this就是vue的实例
//钩子函数
created() {
//this表示vue实例
this.msg = "hello vue. created.";
console.log(this);
}
这是es6的写法,如果报错,可以在idea->settings->language & frameworks ->javascript中修改language version为ECMAScript 6
5. 插值、v-text和v-html
目标:插值使用场景和要求;v-text和v-html的作用
小结:
插值可以使用在有需要显示vue实例数据的地方,可以在插值表达式中调用实例的数据属性和函数。
v-text和v-html的作用:可以将数据在模板中进行显示;区别:v-html会对内容中出现的html标签进行渲染,而v-text会将内容当做普遍文本输出到元素里面。
<span v-text="msg"></span><br>
<span v-html="msg"></span><br>
这类dom元素,只能由我们的数据去影响我们dom元素的展示(因为元素本身没有动态操作能力),所以叫单向绑定
<div id="app">
text: <span>{{msg}}</span><br>
v-text:<span v-text="msg"><h2>hello, vue</h2></span><br>
v-html:<span v-html="msg"></span><br>
v-text-h2:<h2 v-text="msg"></h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
msg: ""
}
});
</script>
v-text会输出文本格式的<h2>hello, vue</h2>
v-html会输出html格式的<h2>hello, vue</h2>
拓展:
迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
6. 指令-v-model使用
目标:使用v-model指令实现复选框的双向绑定
小结:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="checkbox" value="Java" v-model="language">Java<br>
<input type="checkbox" value="Python" v-model="language">Python<br>
<input type="checkbox" value="Swift" v-model="language">Swift<br>
<h2>
你选择了:{{language.join(",")}}
</h2>
<input type="checkbox" value="single" v-model="single">single<br>
<select v-model = "select">
<option value ="volvo">Volvo</option>
<option value ="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
<textarea v-model="textarea"></textarea>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
language:[],
single:false,
select:'opel',
textarea:'this is a textarea'
}
});
</script>
</body>
</html>
v-model限制在<input>、<select>、<textarea>中使用
多个 checkbox 对应一个model时,model的类型是一个数组,单个checkbox值是boolean类型,radio对应的值是input的value值input 和 textarea 默认对应的model是字符串select 单选对应字符串,多选对应也是数组
安装插件的方式:
https://www.cnblogs.com/wbl001/p/11063613.html
7. 指令-v-on使用
目标:了解v-on指令的语法实现按钮点击后的递增和递减
分析:
在没有使用vue之前;页面标签可以通过设置onXXX响应事件;在vue中可以通过v-on指令响应事件。
小结:
语法:
v-on:事件名="js片段或函数名"
简写语法:
@事件名="js片段或函数名"
v-on的使用:
<div id="app">
<button v-on:click="num++">增加</button>
<button @click="decrement">减少</button>
<h2>
num = {{num}}
</h2>
<hr>
事件冒泡测试:<br>
<div style="background-color: lightblue; width:100px;height:100px" @click="print('div被点击了')">
<button @click.stop="print('点击了button')">点我试试</button>
</div>
<br>阻止默认事件:<br>
<a href="http://www.itcast.cn" @click.prevent="print('点击了超链接')" >传智播客</a>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
num:1
},
methods: {
//递减
decrement(){
this.num--;
},
//打印
print(str){
console.log(str);
}
}
});
</script>
事件修饰符:语法v-on:xxxx.修饰符,常用的修饰符有:
.stop :阻止事件冒泡.prevent :阻止默认事件发生.capture :使用事件捕获模式,.capture在父级上加,先执行父级的函数,再执行子级的触发函数(一般用法).self :只有元素自身触发事件才执行。(冒泡或捕获的都不执行).once :只执行一次
可以通过@click.stop.prevent进行多个修饰
8. 指令v-for使用
目标:了解v-for指令语法实现对数组、对象的遍历
分析:
实现:可以在vue实例化的时候指定要遍历的数据,然后通过v-for指令在模板中遍历显示数据。一般情况下,要遍历的数据可以通过钩子函数created发送异步请求获取数据。
小结:
可以使用v-for遍历数组、对象:
<div id="app">
<ul>
<li v-for="(user, index) in users" :key="index">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
</ul>
<hr>
<ul>
<li v-for="(value, key, index) in person">
{{index}}--{{key}}--{{value}}
</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
users:[
{"name":"黑马","age":13,"gender":"男"},
{"name":"传智播客","age":13,"gender":"女"},
{"name":"酷丁鱼","age":4,"gender":"男"}
],
person:{"name":"传智汇","age":13,"gender":"男","address":"中国"}
}
});
</script>
如果遍历的时候需要使用到索引号,可以在循环遍历的位置,添加一个参数;该索引号是从0开始的。
遍历数组:
v-for="(item,index) in users"
v-for="item in users"
items:要迭代的数组item:迭代得到的数组元素别名index:迭代到的当前元素索引,从0开始。
遍历对象:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
1个参数时,得到的是对象的值2个参数时,第一个是值,第二个是键3个参数时,第三个是索引,从0开始
key:
你需要给Vue一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。也就是key是该项的唯一标识。
<ul>
<li v-for="(item,index) in items" :key="index"></li>
</ul>
这里绑定的key是数组的索引,应该是唯一的
拓展:
类似于 v-if
,你也可以利用带有 v-for
的 <template>
来循环渲染一段包含多个元素的内容。比如:
<ul>
<div v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</div>
</ul>
9. 指令-v-if和v-show使用
目标:说出v-if与v-show的区别;通过一个按钮的点击,实现遍历数组结果的显示存在与否并在遍历过程中使用v-if对数据进行判断处理;实现文本内容的隐藏
分析:
v-if:通过一个按钮的点击,实现遍历数组结果的显示存在与否并在遍历过程中使用v-if对数据进行判断处理
v-show:实现文本内容的隐藏
小结:
v-if在条件不满足的时候元素不会存在;v-show条件不满足的时候只是对元素进行隐藏。
v-if:
v-if="布尔表达式"
<h2 v-if="show">
Hello VueJS.
</h2>
data: {
show:true
}
当v-if和v-for出现在一起时,v-for优先级更高。也就是说,会先遍历,再判断条件。
可以使用 v-else 指令来表示 v-if 的“else 块”:
<li v-for="(user,index) in users" v-if="user.gender=='女'" style="backgroundcolor:
deeppink">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
<li v-else style="background-color: blue">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
v-else-if ,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
v-show:
带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性display 。
<h1 v-show="ok">Hello!</h1>
v-if vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
10. 指令-v-bind使用
目标:了解v-bind语法和作用;实现点击不同按钮切换不同的属性值;使用class属性中的特殊用法实现一个按钮切换背景色
分析:
<img src="" height=""/> 其中src和height的值如果不想写死,而是想获取vue实例中的数据属性值的话;那可以通过使用v-bind实现:
<img v-bind:src="vue实例中的数据属性名" :height="vue实例中的数据属性名"/>
小结:
可以使用v-bind:
<div id="app">
<button @click="color='red'">红色</button>
<button @click="color='blue'">蓝色</button>
<div :class="color">
点击按钮改变背景颜色
</div>
<hr>
<br>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
color:"red",
bool:true
}
});
</script>
v-bind的作用:可以对所有元素的属性设置vue实例的数据。
v-bind简写方式:直接使用冒号:
上面虽然实现了颜色切换,但是语法却比较啰嗦。Vue对class属性进行了特殊处理,可以接收数组或对象格式可以传给 :class 一个对象,以动态地切换 class:
<div :class="{ red: true,blue:false }"></div>
对象中,key是已经定义的class样式的名称,如本例中的:red 和blue对象中,value是一个布尔值,如果为true,则这个样式会生效,如果为false,则不生效。
<button @click="bool=!bool">点我改变下面色块的颜色</button>
<div :class="{red:bool, blue:!bool}">
点击按钮改变背景颜色
</div>
11. 计算属性的使用
目标:计算属性的应用场景,实现将一个日期时间值转换为yyyy-MM-dd格式字符串
分析:
一个日期的毫秒值要显示为格式化(yyyy-MM-dd)的日期字符串的话;可以使用computed计算属性里面的方法进行处理。
小结:
计算属性的应用:
<div id="app">
<h2>
你的生日是:
{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
</h2>
<hr>
<h2>
你的生日是:
{{birth}}
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
birthday:1429032123201
},
computed:{
birth(){
const date = new Date(this.birthday);
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDay();
}
}
});
</script>
computed计算属性的应用场景:可以应用在插值或者指令表示式复杂的时候。可以将一些属性数据经过方法处理之后返回。
12. watch基本和深度监控
目标:watch的使用场景;并使用其监听简单属性值及其对象中属性值的变化
分析:
在vue实例中数据属性;因为在页面中修改而产生了变化;可以通过watch监控获取其改变前后的值。
如果是修改的对象数据属性,可以开启深度监控获取修改后最新的对象数据。如:person.name
小结:
可以如下使用watch进行数据属性的监控:
<div id="app">
<input type="text" v-model="message">
<hr><br>
<input type="text" v-model="person.name"><br>
<input type="text" v-model="person.age"><button @click="person.age++">+</button>
<h2>
姓名为:{{person.name}};年龄为:{{person.age}}
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
message:"黑马",
person:{"name":"heima", "age":13}
},
watch:{
message(newValue, oldValue){
console.log("新值:" + newValue + ";旧值:" + oldValue);
},
person: {
//开启深度监控;监控对象中的属性值变化
deep: true,
//可以获取到最新的对象属性数据
handler(obj){
console.log("name = " + obj.name + "; age=" + obj.age);
}
}
}
});
</script>
watch使用场景:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。可以监控视图中数据的变化而做出响应,;如:下拉框列表中,当如果选择了对于的下拉框选项之后,要根据最新的值去加载一些其它数据的话。
很多情况下,我们需要对对象中的某个字段进行监控
'person.age':function f(newValue, oldValue) {
console.log("person.age变更" + newValue + ";旧值:" + oldValue)
}这样的写法没有用es6的语法
13. 组件使用
目标:了解组件的使用场景;定义点击则计数的组件并使用全局注册和局部注册方式
分析:
可以将通用或者公用的页面模块抽取成vue组件,在vue实例中引用。
小结:
在页面中可以如下实验组件:
<div id="app">
<!--使用组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script type="text/javascript">
//定义组件
const counter = {
template:"<button @click='num++'>你点击了{{num}}次</button>",
data(){
return {num:0}
}
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
counter: counter
}
});
</script>
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
组件使用场景:在项目需要重用某个模块(头部、尾部、新闻。。。)的时候,可以将模块抽取成组件,其它页面中注册组件并引用。
全局注册:在任何vue实例中都可以引用,如:一般网站的头部导航菜单
Vue.component("counter", counter);局部注册:可以在有需要的页面引入组件,如:商城网站首页页面中各种活动模块
var app = new Vue({
el:"#app",
components:{
counter: counter
}
});组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。data的定义方式比较特殊,必须是一个函数。
14. 父组件向子组件通信
目标:父组件与子组件通信的意义;实现父组件将简单字符串和对象更新到子组件
小结:
组件通信意义:父子组件之间数据的交换,能够及时更新组件内容。
父组件将简单字符串更新传递到子组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/vue-compo.js"></script>
</head>
<body>
<div id="app">
<!--使用组件-->
<introduce :title="msg" :title2="test"></introduce>
</div>
<script type="text/javascript">
//定义组件
const introduce = {
template:`<div><h2>{{title}}</h2>
<h3>{{title2}}</h3></div>
`,
//定义接收父组件的属性
props:["title","title2"]
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
Vue.component("introduce", introduce);
var app = new Vue({
el:"#app",
data:{
msg:"父组件的msg属性数据内容",
test:"这是第二个属性"
}
});
</script>
</body>
</html>
通过props来定义需要从父组件中接收的属性
父组件将数组更新传递到子组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--使用组件-->
<my-list :items="lessons" :items2="lessons2"></my-list>
</div>
<script type="text/javascript">
//定义组件
const myList = {
template:`
<div>
<h2>{{items2.id}}--{{items2.name}}</h2>
<ul>
<li v-for="item in items" :key="item.id">{{item.id}}--{{item.name}}</li>
</ul>
</div>
`,
//定义接收父组件的属性
props:{
items:{
//数据类型,如果是数组则是Array,如果是对象则是Object
type:Array,
//默认值
default(){
var data = [{"id":4, "name":"Java"},
{"id":5, "name":"Php"},
{ "id":6, "name":"前端"}]
return data
}
},
items2:{
type:Object,
default:[]
}
}
};
var app = new Vue({
el:"#app",
data:{
msg:"父组件的msg属性数据内容",
lessons:[
{"id":1, "name":"Java"},
{"id":2, "name":"Php"},
{"id":3, "name":"前端"}
],
lessons2:{"id":4, "name":"Java"}
},
components:{
myList
}
});
</script>
</body>
</html>
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
组件名如果有大写比如myList,需要在使用时写成my-list,大写改成小写,中间添加-
type:Array, 限定父组件传递来的必须是数组,否则报错;type的值可以是Array或者Object(传递对象的时候使用)
default:默认值,如果是对象则需要写成方法的方式返回默认值。如:
default(){
return {"xxx":"默认值"};
}
15. 子组件向父组件通信
目标:在子组件中点击对应按钮实现父组件中属性数据的改变
小结:
<div id="app">
<h2>num = {{num}}</h2>
<!--使用组件-->
<counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>
<script type="text/javascript">
//定义组件
const counter = {
template:`
<div>
<button @click='incrNum'>+</button>
<button @click='decrNum'>-</button>
</div>
`,
props:["snum"],
methods:{
//递增
incrNum(){
//调用到父组件中的方法
return this.$emit("plus");
},
decrNum(){
//调用到父组件中的方法
return this.$emit("reduce");
}
}
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
counter: counter
},
data:{
num:0
},
methods:{
numPlus(){
this.num++;
},
numReduce(){
this.num--;
}
}
});
</script>
子组件接收到父组件属性后,默认是不允许修改的。怎么办?既然只有父组件能修改,那么加和减的操作一定是放在父组件
首先在组件中定义方法,
//递增
incrNum(){
//调用到父组件中的方法
return this.$emit("plus");
},通过$emit调用了父组件的方法
在父组件中,生命自己的方法,这个名字可以随意定
numPlus(){
this.num++;
},通过v-on建立关联:
@plus="numPlus"@后面跟着的是在子组件中定义的父组件方法的名字,等于后面跟的是父组件中的方法名
拓展slot的用法:
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
它允许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 <navigation-link>
的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot>
元素有一个特殊的特性:name
。这个特性可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
完整示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--使用组件-->
<navigation-url url="http://www.baidu.com">
<h2>Your Profile</h2>
</navigation-url>
<navigation-url2 url="http://www.baidu.com" url2="http://www.jd.com">
<template v-slot:a1>
<h2>slot1</h2>
</template>
<template v-slot:a2>
<h3>slot2</h3>
</template>
</navigation-url2>
</div>
<script type="text/javascript">
//定义组件
const navigationUrl = {
template:`
<a :href="url" class="nav-link">
<slot></slot>
</a>
`,
props:["url"]
};
//定义组件
const navigationUrl2 = {
template:`
<div>
<a :href="url" class="nav-link">
<slot name="a1"></slot>
</a>
<a :href="url2" class="nav-link">
<slot name="a2"></slot>
</a>
</div>
`,
props:["url","url2"]
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
navigationUrl: navigationUrl,
navigationUrl2:navigationUrl2
},
data:{
num:0
}
});
</script>
</body>
</html>
16. axios概述
目标:axios的用途及了解常见方法
小结:
Vuejs 并没有直接处理ajax的组件,但可以使用axios或vue-resource组件实现对异步请求的操作。
axios的作用:发送异步请求获取数据。常见的方法:get、post;在发送的时候可以指定参数(地址、请求方式和请求头部信息);返回数据结构(data/status/statusText/headers/config)
axios可以使用的方法有:
axios(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
示例:
axios.get('/user/12345')
.then(function(response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用。
17. axios方法及get、post方法使用
目标:使用axios方法获取数据并在页面中将数据遍历显示;切换改为get/post方法实现数据加载
小结:
可以使用axios获取对应服务器数据;如果不是同一个服务的数据则可能会出现跨域请求;需要在响应的服务器上配置跨域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/axios.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(user, index) in users" :key="index">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
users:[]
},
created(){
//初始化加载数据
axios.post("data.json").then(res=>{
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err=>alert(err));
axios.get("http://localhost/user/8").then(res=>{
console.log(res.data);
}).catch(err=>alert(err));
/*
axios.get("data.json").then(res=>{
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err=>alert(err));
*/
/*
axios({
url:"data.json",
method:"get"
}).then(res=>{
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err=>alert(err));
*/
}
});
</script>
</body>
</html>
回调中通过this也能获取到vue对象
可以简化直接通过get post进行调用
跨域:在前端js中如果发送异步请求的话,请求的地址与当前服务器的ip或者端口号不同都是跨域请求,可以使用如下方式在服务器端进行配置:
拓展
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
所谓同源是指,域名,协议,端口均相同,不明白没关系,举个栗子:
http://www.123.com/index.html 调用http://www.123.com/server.PHP (非跨域)
http://www.123.com/index.html 调用http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 调用http://def.123.com/server.php(子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html调用http://www.123.com:8081/server.php(端口不同:8080/8081,跨域)
http://www.123.com/index.html 调用https://www.123.com/server.php(协议不同:http/https,跨域)
通过注解的方式允许跨域
非常简单,我们可以在Controller类或其方法上加@CrossOrigin注解,来使之支持跨域。
举例:
@CrossOrigin(origins = "*", maxAge=3600)
@RestController@RequestMapping("/User")
public class UserController {
}
其中origins为CrossOrigin的默认参数,即跨域来源,*即任何来源,也可以是其他域名。即可以以以下形式:
@CrossOrigin("http://test.com")
@CrossOrigin(origins="http://test.com",maxAge=3600)
该注解用于方法上,写法相同,处理时,SpringMVC会对类上标签和方法上标签进行合并。
通过配置文件的方式允许跨域
在web.xml中添加如下配置:
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用这个Filter即可让整个服务器全局允许跨域。
jsonp
jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议。
1.AJAX直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好.
2.不过我们在调用js文件的时候又不受跨域影响,比如引入jquery框架的,或者是调用相片的时候
3.如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.
4.而json又是一个轻量级的数据格式,还被js原生支持
5.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端,
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'onBack'
}).then((res) => {
console.log(res);
})
nginx进行代理
前端发布在nginx,域名和端口是nginx域名和端口
后端接口通过nginx去转发,访问后端接口也是访问nginx域名和端口
01-maven基础知识回顾
1.1 maven介绍
maven (gradle)是一个项目管理工具,主要作用是在项目开发阶段对Java项目进行依赖管理和项目构建。
依赖管理:就是对jar包的管理。通过导入maven坐标,就相当于将仓库中的jar包导入了当前项目中。
项目构建:通过maven的一个命令就可以完成项目从清理、编译、测试、报告、打包,部署整个过程。
1.2 maven的仓库类型
1.本地仓库
2.远程仓库
①maven中央仓库(地址:http://repo2.maven.org/maven2/)
②maven私服(公司局域网内的仓库,需要自己搭建)
③其他公共远程仓库(例如apache提供的远程仓库,地址:http://repo.maven.apache.org/maven2/)
1.3 maven常用命令
clean: 清理 清理项目的target目录
compile:编译 编译项目
test: 测试 编译项目后,再执行Junit测试方法
package:打包 编译项目后,再执行Junit测试方法,再把项目打包到target目录
install: 安装 编译项目后,再执行Junit测试方法,再把项目打包到target目录,再安装到本地仓库。
1.4 maven坐标书写规范
1.5 maven的依赖范围
| 依赖范围 | 对于编译classpath有效 | 对于测试classpath有效 | 对于运行时classpath有效 | 例子 | 介绍 |
|---|---|---|---|---|---|
| compile | Y | Y | Y | spring-core | compile 是默认值,如果没有指定 scope 值,该元素的默认值为 compile。被依赖项目需要参与到当前项目的编译,测试,打包,运行等阶段。打包的时候通常会包含被依赖项目。 |
| test | - | Y | - | Junit | 表示被依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。适用场景:例如,Junit 测试。 |
| provided | Y | Y | - | servlet-api | 被依赖项目理论上可以参与编译、测试、运行等阶段,相当于compile,但是再打包阶段做了exclude的动作。适用场景:例如, 如果我们在开发一个web 应用,在编译时我们需要依赖 servlet-api.jar,但是在运行时我们不需要该 jar 包,因为这个 jar 包已由应用服务器提供,此时我们需要使用 provided 进行范围修饰。 |
| runtime | - | Y | Y | JDBC驱动 | 表示被依赖项目无需参与项目的编译,但是会参与到项目的测试和运行。与compile相比,被依赖项目无需参与项目的编译。适用场景:例如,在编译的时候我们不需要 JDBC API 的 jar 包,而在运行的时候我们才需要 JDBC 驱动包。 |
| system | Y | Y | - | 本地的,maven仓库之外的类库 | system 元素与 provided 元素类似,但是被依赖项不会从 maven 仓库中查找,而是从本地系统中获取,systemPath 元素用于制定本地系统中 jar 文件的路径。 |
system写法:
<dependency>
<groupId>org.open</groupId>
<artifactId>open-core</artifactId>
<version>1.5</version>
<scope>system</scope>
<systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath>
</dependency>
02-什么是maven的依赖传递
在maven中,依赖是可以传递的,假设存在三个项目,分别是项目A,项目B以及项目C。假设C依赖B,B依赖A,那么我们可以根据maven项目依赖的特征不难推出项目C也依赖A。
通过上面的图可以看到,我们的web项目直接依赖了spring-webmvc,而spring-webmvc依赖了sping-aop、spring-beans等。最终的结果就是在我们的web项目中间接依赖了spring-aop、spring-beans等。
03-什么是依赖冲突
由于依赖传递现象的存在, spring-webmvc 依赖 spirng-beans-4.2.4,spring-aop 依赖 spring-beans-5.0.2,但是发现 spirng-beans-4.2.4 加入到了工程中,而我们希望 spring-beans-5.0.2 加入工程。这就造成了依赖冲突。
公司综合包(redis客户端)->redis客户端较低 2.0
A->REDIS(2.0)
部门综合包(redis客户端)->redis客户端较高 3.0
A->REDIS(3.0)
1.路径最短原则 3.0包
2.路径相同,第一声明者原则 2.0包
推荐插件:maven helper
上面可以清楚的看到依赖树和依赖冲突情况。
04-解决依赖冲突(使用第一声明者优先原则)
在 pom 文件中定义依赖,以先声明的依赖为准。其实就是根据坐标导入的顺序来确定最终使用哪个传递过来的依赖。
结论:通过上图可以看到,spring-aop和spring-webmvc都传递过来了spring-beans,但是因为spring-aop在前面,所以最终使用的spring-beans是由spring-aop传递过来的,而spring-webmvc传递过来的spring-beans则被忽略了。
注意:由于本例子中spring-beans所在层级相同,所以触发了第一声明者优先原则。
spring-webmvc 5.0.2 -> spring-beans 5.0.2
spring-aop 5.0.0 -> spring-beans 5.0.0
05-解决依赖冲突(使用路径近者优先原则)
在 pom 文件定义依赖,以路径近者为准。
还是上述情况,spring-aop 和 spring-webmvc 都会传递过来 spirng-beans,那如果直接把 spring-beans 的依赖直接写到 pom 文件中,那么项目就不会再使用其他依赖传递来的 spring-beans,因为自己直接在 pom 中定义 spring-beans要比其他依赖传递过来的路径要近。
总结:
1:如果依赖路径的长度不同,则“短路优先”:
A—>B—>C—>D—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
则A依赖于X(version 0.0.2)。
2:依赖路径长度相同情况下,则“先声明优先”:
A—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
则在项目A的<depencies></depencies>中,E、F哪个在先则A依赖哪条路径的X。
06-解决依赖冲突(排除依赖)
可以使用exclusions标签将传递过来的依赖排除出去。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
上面在webmvc中排除了spring-beans,所以spring-beans会由aop中的版本指定
07-解决依赖冲突(版本锁定)
采用直接锁定版本的方法确定依赖jar包的版本,版本锁定后则不考虑依赖的声明顺序或依赖的路径,以锁定的版本为准添加到工程中,此方法在企业开发中经常使用。
版本锁定的使用方式:
第一步:在dependencyManagement标签中锁定依赖的版本
第二步:在dependencies标签中声明需要导入的maven坐标
①在dependencyManagement标签中锁定依赖的版 本
②在dependencies标签中声明需要导入的maven坐标
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
相对于dependencyManagement,所有声明在dependencies里的依赖都会自动引入,并默认被所有的子项目继承。
spring-boot的parent dependencies中大量使用了dependencyManagement,这样我们的项目就可以不用指定starter的版本号了。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
总结 如何解决依赖冲突
1.使用maven提供的依赖调解原则
路径近者优先原则
第一声明者优先原则
2.排除依赖
3.锁定版本
08-基于maven构建SSM工程案例(数据库环境搭建)
需求描述
本案例基于maven构建 SSM(Spring+SpringMVC+Mybatis)工程,通过maven坐标进行依赖管理。最终实现根据 id 查询商品信息的功能。
1.数据库环境搭建
①创建数据库ssmtest
②创建商品表item
CREATE TABLE `item` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) default NULL,
`price` float default NULL,
`createtime` datetime default NULL,
`detail` varchar(255) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
09-基于maven构建SSM工程案例(maven项目构建步骤分析)
①创建maven web项目
②配置pom.xml文件
③实现spring+mybatis整合
创建POJO类
持久层DAO接口编写
Mapper映射文件编写
业务层Service编写
spring配置文件applicationContext-dao.xml编写
spring配置文件applicationContext-service.xml编写
④加入springmvc相关配置
表现层Controller编写
springmvc.xml文件编写
jsp页面编写
配置web.xml文件
10-基于maven构建SSM工程案例(创建项目并配置pom.xml)
1.创建maven web项目
这一步可以防止去中央仓库下载插件
2.增加java和resources文件夹,通过右键mark Directory as来更改文件夹类型。
java文件夹->Sources Root
resources文件夹->Resources Root
3.配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>ssmweb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>ssmweb Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.5.RELEASE</spring.version>
<springmvc.version>5.0.5.RELEASE</springmvc.version>
<mybatis.version>3.4.5</mybatis.version>
</properties>
<!--锁定jar版本-->
<dependencyManagement>
<dependencies>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- springMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Mybatis和mybatis与spring的整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- MySql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!-- springMVC核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- spring相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
本案例中由于要将spring、springmvc、mybatis进行整合,容易产生冲突的主要是spring的依赖
所以需要将spring的依赖放到DependencyManagement中进行版本锁定
mybatis相关依赖不会产生冲突,可以直接指定版本
11-基于maven构建SSM工程案例(实现spring和mybatis整合1)
1.创建POJO类
public class Item {
private Integer id;
private String name;
private Float price;
private Date createtime;
private String detail;
//省略setter、getter
}
持久层DAO接口编写
public interface ItemMapper {
public Item findById(int id);
}
Mapper映射文件编写
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.ssm.dao.ItemMapper">
<select id="findById" parameterType="int" resultType="item">
select * from item where id=#{id}</select>
</mapper>
注意在resources下生成包时,分隔符使用/而不要使用.。
持久层DAO接口名和Mapper映射文件名字需要一致
12-基于maven构建SSM工程案例(实现spring和mybatis整合2)
业务层Service编写
package com.itheima.ssm.service;
import com.itheima.ssm.pojo.Item;
public interface ItemService {
public Items findById(int id);
}
@Service
@Transactional
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
public Item findById(int id) {
return itemMapper.findById(id);
}
}
spring配置文件applicationContext-dao.xml编写
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/bean http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- url -->
<property name="url" value="jdbc:mysql://localhost:3306/ssmtest"/>
<!-- 用户名 -->
<property name="username" value="root"/>
<!-- 密码 -->
<property name="password" value="root"/></bean>
<!-- mapper配置 --> <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
<!--为指定包下的所有实体类创建别名-->
<property name="typeAliasesPackage" value="com.itheima.ssm.pojo"/></bean>
<!-- mapper扫描器 :用来产生代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.ssm.dao"></property>
</bean>
</beans>
spring配置文件applicationContext-service.xml编写
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置扫描器,扫描Service-->
<context:component-scan base-package="com.itheima.ssm.service"/>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事物注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
使用注解式驱动,不要忘记添加
<tx:annotation-driven transaction-manager="transactionManager"/>
13-基于maven构建SSM工程案例(测试Spring和mybatis整合)
test单元测试编写:
package com.itheima.ssm.test;
import com.itheima.ssm.pojo.Item;
import com.itheima.ssm.service.ItemService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext-dao.xml","classpath:applicationContext-service.xml"})
public class SpringMybatisTest {
@Autowired
private ItemService itemService;
@Test
public void test1(){
Item item = itemService.findById(1);
System.out.println(item);
}
}
拓展:
一种更好的实现方式:建立applicationContext-all.xml,包含了项目中所有的spring配置文件
<?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">
<import resource="applicationContext-dao.xml"/>
<import resource="applicationContext-service.xml"/>
</beans>
通过import语句进行引入
测试类中只需要引入applicationContext-all.xml文件即可
@ContextConfiguration(locations = {"classpath:applicationContext-all.xml"})
14-基于maven构建SSM工程案例(加入SpringMVC相关配置)
表现层Controller编写
@Controller
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("/showItem/{id}")
public String showItem(@PathVariable("id") int id, Model model){
Item item = itemService.findById(id);
model.addAttribute("item",item);
return "item";
}
}
springmvc.xml文件编写
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="com.itheima.ssm.controller"/>
<!-- 配置视图解析器的前缀和后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix“ value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
jsp页面编写
配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--指定Spring配置文件位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- <param-value>classpath*:applicationContext*.xml</param-value>-->
<param-value>classpath:applicationContext-all.xml</param-value>
</context-param>
<!--配置Spring框架启动时使用的监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
注意:初始代码中遗漏了:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
视图解析器中prefix路径有误:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp"/>
<property name="suffix" value=".jsp"/>
</bean>
15-基于maven构建SSM工程案例(部署到tomcat)
拓展:
在maven的plugin中添加以下内容
<!-- tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/SSM</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
可以使用tomcat7:run来进行运行。
16-分模块构建maven工程(分析)
在现实生活中,汽车厂家进行汽车生产时,由于整个生产过程非常复杂和繁琐,工作量非常大,所以车场都会将整个汽车的部件分开生产,最终再将生产好的部件进行组装,形成一台完整的汽车。
在企业项目开发过程中,由于项目规模庞大,业务复杂,参与的人员比较多,一般会通过合理的模块拆分将一个大型的项目拆分为N多个小模块,分别进行开发。而且拆分出的模块可以非常容易的被其他模块复用。
常见的拆分方式有两种:
第一种:按照业务模块进行拆分,每个模块拆分成一个maven工程,例如将一个项目分为用户模块、订单模块、购物车模块等,每个模块对应就是一个maven工程
第二种:按照层进行拆分,例如持久层、业务层、表现层等,每个层对应就是一个maven工程
不管是上面哪种拆分方式,通常都会提供一个父工程,将一些公共的代码和配置提取到父工程中进行统一管理和配置。
17-分模块构建maven工程(继承概念)
在Java语言中,类之间是可以继承的,通过继承,子类就可以引用父类中非private的属性和方法。同样,在maven工程之间也可以继承,子工程继承父工程后,就可以使用在父工程中引入的依赖。继承的目的是为了消除重复代码。
父工程:
<groupId>com.itheima</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
子工程:
<parent>
<artifactId>maven_parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
关于relativePath查找顺序:relativePath元素中的地址–本地仓库–远程仓库
<relativePath/>设定一个空值将始终从仓库中获取,不从本地路径获取
父工程一般会包含以下内容:
1.各个模块,使用module标签来标识,用于整体的动作,比如:打包。
<modules>
<module>maven_pojo</module>
<module>maven_service</module>
<module>maven_dao</module>
<module>maven_web</module>
</modules>
2.jar包版本的控制,使用dependencyManagement来统一进行管理。
<dependencyManagement>
<dependencies>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- springMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
3.插件的统一引入,由于插件不会被打入最终的war或者jar包中,所以可以在父工程中统一管理。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
18-分模块构建maven工程(聚合概念)
在maven工程的pom.xml文件中可以使用<modules>标签将其他maven工程聚合到一起,聚合的目的是为了进行统一操作。
例如拆分后的maven工程有多个,如果要进行打包,就需要针对每个工程分别执行打包命令,操作起来非常繁琐。这时就可以使用<modules>标签将这些工程统一聚合到maven工程中,需要打包的时候,只需要在此工程中执行一次打包命令,其下被聚合的工程就都会被打包了。
19-分模块构建maven工程(工程创建)
①父工程maven_parent构建
<properties>
<spring.version>5.0.5.RELEASE</spring.version>
<springmvc.version>5.0.5.RELEASE</springmvc.version>
<mybatis.version>3.4.5</mybatis.version>
</properties>
<!--锁定jar版本-->
<dependencyManagement>
<dependencies>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- springMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
②子工程maven_pojo构建
pom.xml
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
③3.1子工程maven_dao构建
3.2 配置maven_dao工程的pom.xml文件
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Mybatis和mybatis与spring的整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- MySql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!-- spring相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
20-分模块构建maven工程(完善工程代码)
创建DAO接口和Mapper映射文件
package com.itheima.ssm.dao;
import com.itheima.ssm.pojo.Item;
public interface ItemMapper {
public Item findById(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.ssm.dao.ItemMapper">
<select id="findById" parameterType="int" resultType="Item">
select * from item where id = #{id}
</select>
</mapper>
3.4 在resources目录下创建spring配置文件applicationContext-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置数据源信息,使用druid连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssmtest"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--配置spring整合mybatis框架的SQLSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--扫描pojo包,为实体类创建别名-->
<property name="typeAliasesPackage" value="com.itheima.ssm.pojo"/>
</bean>
<!--mapper扫描器,用于产生代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.ssm.dao"/>
</bean>
</bean
④子工程maven_service构建
第一步:创建maven_service工程
第二步:配置maven_service工程的pom.xml文件
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
第三步:创建Service接口和实现类
package com.itheima.ssm.service;
import com.itheima.ssm.pojo.Item;
public interface ItemService {
public Item findById(int id);
}
package com.itheima.ssm.service;
import com.itheima.ssm.dao.ItemMapper;
import com.itheima.ssm.pojo.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
public Item findById(int id) {
return itemMapper.findById(id);
}
}
第四步:创建spring配置文件applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置扫描器,扫描Service-->
<context:component-scan base-package="com.itheima.ssm.service"/>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事物注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
⑤子工程maven_web构建
第一步:创建maven_web工程,注意打包方式为war
第二步:配置maven_web工程的pom.xml文件
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<finalName>maven_web</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
第三步:创建Controller
package com.itheima.ssm.controller;
import com.itheima.ssm.pojo.Item;
import com.itheima.ssm.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("/showItem/{id}")
public String findById(@PathVariable("id") int id, Model model){
Item item = itemService.findById(id);
model.addAttribute("item",item);
return "item";
}
}
第四步:创建jsp页面
第五步:配置web.xml
<!--指定Spring配置文件位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext*.xml</param-value>
</context-param>
<!--配置Spring框架启动时使用的监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
第六步:创建springmvc配置文件springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置扫描器,扫描Controller-->
<context:component-scan base-package="com.itheima.ssm.controller"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
项目整体结构如下:
1)maven_parent为父工程,其余工程为子工程,都继承父工程maven_parent
2)maven_parent工程将其子工程都进行了聚合
3)子工程之间存在依赖关系:
maven_dao依赖 maven_pojo,
maven_service依赖maven_dao,
maven_web依赖maven_service
21-分模块构建maven工程(部署到tomcat)
将maven_web项目发布到tomcat即可。
maven_service和maven_dao会以jar包的形式被maven_web使用。
记得修改maven_dao中的dataSource相关配置,本案例中提供的源码是完整的。
是选择
war
还是war exploded
这里首先看一下他们两个的区别:war模式:将WEB工程以包的形式上传到服务器 ;war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器;
(1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布;
(2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
(3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面什么的东西才可以及时的显示出来。
参考阅读:
https://blog.csdn.net/z69183787/article/details/78030857
22-maven私服(安装nexus并启动服务)
私服说明
maven仓库分为本地仓库和远程仓库,而远程仓库又分为maven中央仓库、其他远程仓库和私服(私有服务器)。其中,中央仓库是由maven官方提供的,而私服就需要我们自己搭建了。
maven私服就是公司局域网内的maven远程仓库,每个员工的电脑上安装maven软件并且连接maven私服,程序员可以将自己开发的项目打成jar并发布到私服,其它项目组成员就可以从私服下载所依赖的jar。私服还充当一个代理服务器的角色,当私服上没有jar包时会从maven中央仓库自动下载。
nexus 是一个maven仓库管理器(其实就是一个软件),nexus可以充当maven私服,同时nexus还提供强大的仓库管理、构件搜索等功能。
搭建maven私服
①下载nexus
https://help.sonatype.com/repomanager2/download/download-archives---repository-manager-oss
②安装nexus
将下载的压缩包进行解压,进入bin目录
打开cmd窗口并进入上面bin目录下,执行nexus.bat install命令安装服务(注意需要以管理员身份运行cmd命令)
③启动nexus
经过前面命令已经完成nexus的安装,可以通过如下两种方式启动nexus服务:
在Windows系统服务中启动nexus
在命令行执行nexus.bat start命令启动nexus
④访问nexus
启动nexus服务后,访问http://localhost:8081/nexus
23-maven私服(nexus仓库类型)
点击右上角LogIn按钮,进行登录。使用默认用户名admin和密码admin123登录系统
登录成功后点击左侧菜单Repositories可以看到nexus内置的仓库列表(如下图)
nexus仓库类型
通过前面的仓库列表可以看到,nexus默认内置了很多仓库,这些仓库可以划分为4种类型,每种类型的仓库用于存放特定的jar包,具体说明如下:
①hosted,宿主仓库,部署自己的jar到这个类型的仓库,包括Releases和Snapshots两部分,Releases为公司内部发布版本仓库、 Snapshots为公司内部测试版本仓库
②proxy,代理仓库,用于代理远程的公共仓库,如maven中央仓库,用户连接私服,私服自动去中央仓库下载jar包或者插件
③group,仓库组,用来合并多个hosted/proxy仓库,通常我们配置自己的maven连接仓库组
④virtual(虚拟):兼容Maven1版本的jar或者插件
24-maven私服(nexus仓库和磁盘目录对应关系)
nexus仓库类型与安装目录对应关系
文件路径:/sonatype-work/nexus/storage
25-maven私服(将项目发布到maven私服)
maven私服是搭建在公司局域网内的maven仓库,公司内的所有开发团队都可以使用。例如技术研发团队开发了一个基础组件,就可以将这个基础组件打成jar包发布到私服,其他团队成员就可以从私服下载这个jar包到本地仓库并在项目中使用。
将项目发布到maven私服操作步骤如下:
配置maven的settings.xml文件
<server>
<id>releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
注意:一定要在idea工具中引入的maven的settings.xml文件中配置
配置项目的pom.xml文件
<distributionManagement>
<repository>
<id>releases</id>
<url>http://localhost:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id> <url>http://localhost:8081/nexus/content/repositories/snapshots/</url> </snapshotRepository>
</distributionManagement>
执行mvn deploy命令
拓展:
Snapshot版本代表不稳定、尚处于开发中的版本,快照版本。
Release版本则代表稳定的版本,发行版本。
什么时候用Snapshot版本?
依赖库中的jar正处于开发的阶段,会被经常被更新,这种情况下,如果使用Release,会频繁增加版本号,导致版本号的滥用。
在上传jar时,选择Snapshot和Release上传时,在版本号的构建上有所不同,如图:

可以很明显的看到,上传同样的版本号jar包时,Snapshot在版本号的后面会自动追加一串新的数字,就是所谓的日期标签,这样在不断更新jar包时,可以不用每次都要将版本号提高一个版本,可以有效的维护版本号。
稳定版使用Snapshot版本行不行?
不行,因为会造成不稳定。比如说,今天你依赖某个snapshot版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的snapshot库。你再次构建时,Maven会去远程repository下载snapshot的最新版本,你构建时用的库就是新的jar文件了,这时正确性就很难保证了。稳定版应该依赖Release版本的jar包,这样更新时需要更新版本号,版本号没变时,依赖的jar包也不会变。
26-maven私服(从私服下载jar到本地仓库)
前面我们已经完成了将本地项目打成jar包发布到maven私服,下面我们就需要从maven私服下载jar包到本地仓库。
具体操作步骤如下:
在maven的settings.xml文件中配置下载模板
<profile>
<id>dev</id>
<repositories>
<repository>
<id>nexus</id>
<!--仓库地址,即nexus仓库组的地址-->
<url>
http://localhost:8081/nexus/content/groups/public/</url>
<!--是否下载releases构件-->
<releases>
<enabled>true</enabled>
</releases>
<!--是否下载snapshots构件-->
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<!-- 插件仓库,maven的运行依赖插件,也需要从私服下载插件 -->
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>
http://localhost:8081/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
在maven的settings.xml文件中配置激活下载模板
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
附录:
将第三方jar安装到本地仓库和maven私服
在maven工程的pom.xml文件中配置某个jar包的坐标后,如果本地的maven仓库不存在这个jar包,maven工具会自动到配置的maven私服下载,如果私服中也不存在,maven私服就会从maven中央仓库进行下载。
但是并不是所有的jar包都可以从中央仓库下载到,比如常用的Oracle数据库驱动的jar包在中央仓库就不存在。此时需要到Oracle的官网下载驱动jar包,然后将此jar包通过maven命令安装到我们本地的maven仓库或者maven私服中,这样在maven项目中就可以使用maven坐标引用到此jar包了。
1 将第三方jar安装到本地仓库
①下载Oracle的jar包(略)
②mvn install命令进行安装
mvn install:install-file -Dfile=ojdbc14-10.2.0.4.0.jar -DgroupId=com.oracle -DartifactId=ojdbc14 –
Dversion=10.2.0.4.0 -Dpackaging=jar
③查看本地maven仓库,确认安装是否成功
2 将第三方jar安装到maven私服
①下载Oracle的jar包(略)
②在maven的settings.xml配置文件中配置第三方仓库的server信息
<server>
<id>thirdparty</id>
<username>admin</username>
<password>admin123</password>
</server>
③执行mvn deploy命令进行安装
mvn deploy:deploy-file -Dfile=ojdbc14-10.2.0.4.0.jar -DgroupId=com.oracle -DartifactId=ojdbc14 –
Dversion=10.2.0.4.0 -Dpackaging=jar –
Durl=http://localhost:8081/nexus/content/repositories/thirdparty/ -DrepositoryId=thirdparty
maven面试题:
Maven有哪些优点和缺点
优点如下:
简化了项目依赖管理:
易于上手,对于新手可能一个"mvn clean package"命令就可能满足他的工作
便于与持续集成工具(jenkins)整合
便于项目升级,无论是项目本身升级还是项目使用的依赖升级。
有助于多模块项目的开发,一个模块开发好后,发布到仓库,依赖该模块时可以直接从仓库更新,而不用自己去编译。
maven有很多插件,便于功能扩展,比如生产站点,自动发布版本等
缺点如下:
maven是一个庞大的构建系统,学习难度大
maven采用约定优于配置的策略(convention over configuration),虽然上手容易,但是一旦出了问题,难于调试。
当依赖很多时,m2eclipse 老是搞得Eclipse很卡。
中国的网络环境差,很多repository无法访问,比如google code, jboss 仓库无法访问等。
Maven坐标
一般maven使用[groupID,artifactId,version,packaging]来表示一个项目的某个版本,有时还会使用classifier来表示项目的附属构建,常见的附属构建有javadoc和sources包。
Maven常见的依赖范围有哪些?
compile:编译依赖,默认的依赖方式,在编译(编译项目和编译测试用例),运行测试用例,运行(项目实际运行)三个阶段都有效,典型地有spring-core等jar。
test:测试依赖,只在编译测试用例和运行测试用例有效,典型地有JUnit。
provided:对于编译和测试有效,不会打包进发布包中,典型的例子为servlet-api,一般的web工程运行时都使用容器的servlet-api。
runtime:只在运行测试用例和实际运行时有效,典型地是jdbc驱动jar包。
system: 不从maven仓库获取该jar,而是通过systemPath指定该jar的路径。
import: 用于一个dependencyManagement对另一个dependencyManagement的继承。
多模块如何聚合
配置一个打包类型为pom的聚合模块,然后在该pom中使用<module>元素声明要聚合的模块
对于一个多模块项目,如果管理项目依赖的版本
通过在父模块中声明dependencyManagement和pluginManagement, 然后让子模块通过<parent>元素指定父模块,这样子模块在定义依赖是就可以只定义groupId和artifactId,自动使用父模块的version,这样统一整个项目的依赖的版本。
一个项目的依赖来源于不同的组织,可能这些依赖还会依赖别的Jar包,如何保证这些传递依赖不会引起版本冲突。
使用<dependency>的<exclusion>元素将会引起冲突的元素排除。
常见的Maven私服的仓库类型。
(宿主仓库)hosted repository, (代理仓库)proxy repository, (仓库组)group repository
Docker
学习目标:
掌握Docker基础知识,能够理解Docker镜像与容器的概念
完成Docker安装与启动
掌握Docker镜像与容器相关命令
掌握Tomcat Nginx 等软件的常用应用的安装
掌握docker迁移与备份相关命令
能够运用Dockerfile编写创建容器的脚本
能够搭建与使用docker私有仓库
1 Docker简介
1.1 什么是虚拟化
在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资源的新虚拟部份是不受现有资源的架设方式,地域或物理组态所限制。一般所指的虚拟化资源包括计算能力和资料存储。
在实际的生产环境中,虚拟化技术主要用来解决高性能的物理硬件产能过剩和老的旧的硬件产能过低的重组重用,透明化底层物理硬件,从而最大化的利用物理硬件 对资源充分利用
虚拟化技术种类很多,例如:软件虚拟化、硬件虚拟化、内存虚拟化、网络虚拟化(vip)、桌面虚拟化、服务虚拟化、虚拟机等等。
1.2 什么是Docker
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。
Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。
Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。Docker 的基础是 Linux 容器(LXC)等技术。
在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。
为什么选择Docker?
(1)上手快。
用户只需要几分钟,就可以把自己的程序“Docker化”。Docker依赖于“写时复制”(copy-on-write)模型,使修改应用程序也非常迅速,可以说达到“随心所致,代码即改”的境界。
随后,就可以创建容器来运行应用程序了。大多数Docker容器只需要不到1秒中即可启动。由于去除了管理程序的开销,Docker容器拥有很高的性能,同时同一台宿主机中也可以运行更多的容器,使用户尽可能的充分利用系统资源。
(2)职责的逻辑分类
使用Docker,开发人员只需要关心容器中运行的应用程序,而运维人员只需要关心如何管理容器。Docker设计的目的就是要加强开发人员写代码的开发环境与应用程序要部署的生产环境一致性。从而降低那种“开发时一切正常,肯定是运维的问题(测试环境都是正常的,上线后出了问题就归结为肯定是运维的问题)”
(3)快速高效的开发生命周期
Docker的目标之一就是缩短代码从开发、测试到部署、上线运行的周期,让你的应用程序具备可移植性,易于构建,并易于协作。(通俗一点说,Docker就像一个盒子,里面可以装很多物件,如果需要这些物件的可以直接将该大盒子拿走,而不需要从该盒子中一件件的取。)
(4)鼓励使用面向服务的架构
Docker还鼓励面向服务的体系结构和微服务架构。Docker推荐单个容器只运行一个应用程序或进程,这样就形成了一个分布式的应用程序模型,在这种模型下,应用程序或者服务都可以表示为一系列内部互联的容器,从而使分布式部署应用程序,扩展或调试应用程序都变得非常简单,同时也提高了程序的内省性。(当然,可以在一个容器中运行多个应用程序)
1.3 容器与虚拟机比较
下面的图片比较了 Docker 和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。
与传统的虚拟机相比,Docker优势体现为启动速度快、占用体积小。
1.4 Docker 组件
1.4.1 Docker服务器与客户端
Docker是一个客户端-服务器(C/S)架构程序。Docker客户端只需要向Docker服务器或者守护进程发出请求,服务器或者守护进程将完成所有工作并返回结果。Docker提供了一个命令行工具Docker以及一整套RESTful API。你可以在同一台宿主机上运行Docker守护进程和客户端,也可以从本地的Docker客户端连接到运行在另一台宿主机上的远程Docker守护进程。
1.4.2 Docker镜像与容器
镜像是构建Docker的基石。用户基于镜像来运行自己的容器。镜像也是Docker生命周期中的“构建”部分。镜像是基于联合文件系统的一种层式结构,由一系列指令一步一步构建出来。例如:
添加一个文件;
执行一个命令;
打开一个窗口。
也可以将镜像当作容器的“源代码”。镜像体积很小,非常“便携”,易于分享、存储和更新。
Docker可以帮助你构建和部署容器,你只需要把自己的应用程序或者服务打包放进容器即可。容器是基于镜像启动起来的,容器中可以运行一个或多个进程。我们可以认为,镜像是Docker生命周期中的构建或者打包阶段,而容器则是启动或者执行阶段。 容器基于镜像启动,一旦容器启动完成后,我们就可以登录到容器中安装自己需要的软件或者服务。
所以Docker容器就是:
一个镜像格式;
一些列标准操作;
一个执行环境。
Docker借鉴了标准集装箱的概念。标准集装箱将货物运往世界各地,Docker将这个模型运用到自己的设计中,唯一不同的是:集装箱运输货物,而Docker运输软件。
和集装箱一样,Docker在执行上述操作时,并不关心容器中到底装了什么,它不管是web服务器,还是数据库,或者是应用程序服务器什么的。所有的容器都按照相同的方式将内容“装载”进去。
Docker也不关心你要把容器运到何方:我们可以在自己的笔记本中构建容器,上传到Registry,然后下载到一个物理的或者虚拟的服务器来测试,在把容器部署到具体的主机中。像标准集装箱一样,Docker容器方便替换,可以叠加,易于分发,并且尽量通用。
1.4.3 Registry(注册中心)
Docker用Registry来保存用户构建的镜像。Registry分为公共和私有两种。Docker公司运营公共的Registry叫做Docker Hub。用户可以在Docker Hub注册账号,分享并保存自己的镜像(说明:在Docker Hub下载镜像巨慢,可以自己构建私有的Registry)。
https://hub.docker.com/
2 Docker安装与启动
2.1 安装Docker
Docker官方建议在Ubuntu中安装,因为Docker是基于Ubuntu发布的,而且一般Docker出现的问题Ubuntu是最先更新或者打补丁的。在很多版本的CentOS中是不支持更新最新的一些补丁包的。
由于我们学习的环境都使用的是CentOS,因此这里我们将Docker安装到CentOS上。注意:这里建议安装在CentOS7.x以上的版本,在CentOS6.x的版本中,安装前需要安装其他很多的环境而且Docker很多补丁不支持更新。
请直接挂载课程配套的Centos7.x镜像
(1)yum 包更新到最新
sudo yum update(2)安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
sudo yum install -y yum-utils device-mapper-persistent-data lvm2(3)设置yum源为阿里云
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo(4)安装docker
sudo yum install docker-ce(5)安装后查看docker版本
docker -v2.2 设置ustc的镜像
ustc是老牌的linux镜像服务提供者了,还在遥远的ubuntu 5.04版本的时候就在用。ustc的docker镜像加速器速度很快。ustc docker mirror的优势之一就是不需要注册,是真正的公共服务。
https://lug.ustc.edu.cn/wiki/mirrors/help/docker
编辑该文件:
vi /etc/docker/daemon.json 在该文件中输入如下内容:
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}
2.3 Docker的启动与停止
systemctl命令是系统服务管理器指令
启动docker:
systemctl start docker停止docker:
systemctl stop docker
重启docker:
systemctl restart docker
查看docker状态:
systemctl status docker
开机启动:
systemctl enable docker
查看docker概要信息
docker info
查看docker帮助文档
docker --help
3 常用命令
3.1 镜像相关命令
3.1.1 查看镜像
docker images
REPOSITORY:镜像名称
TAG:镜像标签
IMAGE ID:镜像ID
CREATED:镜像的创建日期(不是获取该镜像的日期)
SIZE:镜像大小
这些镜像都是存储在Docker宿主机的/var/lib/docker目录下
3.1.2 搜索镜像
如果你需要从网络中查找需要的镜像,可以通过以下命令搜索
docker search 镜像名称
NAME:仓库名称
DESCRIPTION:镜像描述
STARS:用户评价,反应一个镜像的受欢迎程度
OFFICIAL:是否官方
AUTOMATED:自动构建,表示该镜像由Docker Hub自动构建流程创建的
3.1.3 拉取镜像
拉取镜像就是从中央仓库中下载镜像到本地
docker pull 镜像名称
例如,我要下载centos7镜像
docker pull centos:7
3.1.4 删除镜像
按镜像ID删除镜像
docker rmi 镜像ID
删除所有镜像
docker rmi `docker images -q`
3.2 容器相关命令
3.2.1 查看容器
查看正在运行的容器
docker ps
查看所有容器
docker ps –a
查看最后一次运行的容器
docker ps –l
查看停止的容器
docker ps -f status=exited
3.2.2 创建与启动容器
创建容器常用的参数说明:
创建容器命令:docker run
-i:表示运行容器
-t:表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。
--name :为创建的容器命名。
-v:表示目录映射关系(前者是宿主机目录,后者是映射到宿主机上的目录),可以使用多个-v做多个目录或文件映射。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。
-d:在run后面加上-d参数,则会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器,如果只加-i -t两个参数,创建后就会自动进去容器)。
-p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个-p做多个端口映射
(1)交互式方式创建容器
docker run -it --name=容器名称 镜像名称:标签 /bin/bash
这时我们通过ps命令查看,发现可以看到启动的容器,状态为启动状态
退出当前容器
exit
(2)守护式方式创建容器:
docker run -di --name=容器名称 镜像名称:标签
登录守护式容器方式:
docker exec -it 容器名称 (或者容器ID) /bin/bash
3.2.3 停止与启动容器
停止容器:
docker stop 容器名称(或者容器ID)
启动容器:
docker start 容器名称(或者容器ID)
3.2.4 文件拷贝
如果我们需要将文件拷贝到容器内可以使用cp命令
docker cp 需要拷贝的文件或目录 容器名称:容器目录
也可以将文件从容器内拷贝出来
docker cp 容器名称:容器目录 需要拷贝的文件或目录
3.2.5 目录挂载
我们可以在创建容器的时候,将宿主机的目录与容器内的目录进行映射,这样我们就可以通过修改宿主机某个目录的文件从而去影响容器。创建容器 添加-v参数 后边为 宿主机目录:容器目录,例如:
docker run -di -v /usr/local/myhtml:/usr/local/myhtml --name=mycentos3 centos:7
如果你共享的是多级的目录,可能会出现权限不足的提示。
这是因为CentOS7中的安全模块selinux把权限禁掉了,我们需要添加参数 --privileged=true 来解决挂载的目录没有权限的问题
3.2.6 查看容器IP地址
我们可以通过以下命令查看容器运行的各种数据
docker inspect 容器名称(容器ID)
也可以直接执行下面的命令直接输出IP地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称(容器ID)
3.2.7 删除容器
删除指定的容器:
docker rm 容器名称(容器ID)
4 应用部署
4.1 MySQL部署
(1)拉取mysql镜像
docker pull centos/mysql-57-centos7
(2)创建容器
docker run -di --name=tensquare_mysql -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql
-p 代表端口映射,格式为 宿主机映射端口:容器运行端口
-e 代表添加环境变量 MYSQL_ROOT_PASSWORD 是root用户的登陆密码
(3)远程登录mysql
连接宿主机的IP ,指定端口为33306
4.2 tomcat部署
(1)拉取镜像
docker pull tomcat:7-jre7
(2)创建容器
创建容器 -p表示地址映射
docker run -di --name=mytomcat -p 9000:8080
-v /usr/local/webapps:/usr/local/tomcat/webapps tomcat:7-jre7
虚拟机可以访问http://
curl http://localhost:9000/helloworld
4.3 Nginx部署
(1)拉取镜像
docker pull nginx
(2)创建Nginx容器
docker run -di --name=mynginx -p 80:80 nginx
4.4 Redis部署
(1)拉取镜像
docker pull redis
(2)创建容器
docker run -di --name=myredis -p 6379:6379 redis
12. docker-compose应用
目标:编写模版文件同时启动docker容器
分析:
docker-compose可以实现一次启动多个容器;通过配置docker-compose模板文件(docker-compose.yml),在这个配置文件中去配置各个容器及其相关的依赖。
编写模板文件;
创建并启动docker-compose项目(模板文件所在的目录,docker-compose.yml)
测试
小结:
docker-compose可以配置一个模板文件实现一次可以启动多个容器,运行时候可以执行一个up则会创建并启动模板文件中的各个服务。
模板文件是yml格式的;编写的时候需要注意其格式。
5 迁移与备份
5.1 容器保存为镜像
我们可以通过以下命令将容器保存为镜像
docker commit mynginx mynginx_i
5.2 镜像备份
我们可以通过以下命令将镜像保存为tar 文件
docker save -o mynginx.tar mynginx_i
5.3 镜像恢复与迁移
首先我们先删除掉mynginx_img镜像 然后执行此命令进行恢复
docker load -i mynginx.tar
-i 输入的文件
执行后再次查看镜像,可以看到镜像已经恢复
6 Dockerfile
6.1 什么是Dockerfile
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。
1、对于开发人员:可以为开发团队提供一个完全一致的开发环境;2、对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了;3、对于运维人员:在部署时,可以实现应用的无缝移植。
6.2 常用命令
| 命令 | 作用 |
|---|---|
| FROM image_name:tag | 定义了使用哪个基础镜像启动构建流程 |
| MAINTAINER user_name | 声明镜像的创建者 |
| ENV key value | 设置环境变量 (可以写多条) |
| RUN command | 是Dockerfile的核心部分(可以写多条) |
| ADD source_dir/file dest_dir/file | 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压 |
| COPY source_dir/file dest_dir/file | 和ADD相似,但是如果有压缩文件并不能解压 |
| WORKDIR path_dir | 设置工作目录 |
6.3 使用脚本创建镜像
步骤:
(1)创建目录
mkdir –p /usr/local/dockerjdk8
(2)下载jdk-8u171-linux-x64.tar.gz并上传到服务器(虚拟机)中的/usr/local/dockerjdk8目录
(3)创建文件Dockerfile vi Dockerfile
#依赖镜像名称和ID
FROM centos:7
#指定镜像创建者信息
MAINTAINER ITCAST
#切换工作目录
WORKDIR /usr
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把java添加到容器中
ADD jdk-8u202-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_202
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
(4)执行命令构建镜像
docker build -t='jdk1.8' .
注意后边的空格和点,不要省略
(5)查看镜像是否建立完成
docker images
7 Docker私有仓库
7.1 私有仓库搭建与配置
(1)拉取私有仓库镜像(此步省略)
docker pull registry
(2)启动私有仓库容器
docker run -di --name=registry -p 5000:5000 registry
(3)打开浏览器 输入地址http://192.168.184.141:5000/v2/_catalog看到{"repositories":[]}
表示私有仓库搭建成功并且内容为空
(4)修改daemon.json
vi /etc/docker/daemon.json
添加以下内容,保存退出。
{"insecure-registries":["192.168.184.141:5000"]}
此步用于让 docker信任私有仓库地址
(5)重启docker 服务
systemctl restart docker
7.2 镜像上传至私有仓库
(1)标记此镜像为私有仓库的镜像
docker tag jdk1.8 192.168.174.130:5000/jdk1.8
(2)再次启动私服容器
docker start registry
(3)上传标记的镜像
docker push 192.168.174.130:5000/jdk1.8
8.3其他服务器拉取私有仓库镜像
# 打开配置文件
vi /usr/lib/systemd/system/docker.server
# 在打开的上述文件中添加如下内容,注意修改下面内容的IP地址
--add-registry=-192.168.174.130:5000 --insecure-registry=192.168.174.130:5000 \
# 修改完后需要重新加载docker配置文件并重启docker
systemctl daemon-reload
systemctl restart docker




