陈建华的博客
专注web开发
Spring Cloud 入门以及案例代码
2019-03-07 10:53:40   阅读179次

spring cloud 入门系列一:初识spring cloud

最近看到微服务很火,也是未来的趋势,

所以就去学习下,在dubbo和spring cloud之间我选择了从spring cloud,主要有如下几种原因:

  1. dubbo主要专注于微服务中的一个环节--服务治理,像服务注册和发现这种还需要zookeeper第三方的中间;但是spring cloud提供了微服务的一站式解决方案,该有的功能都有了。

  2. spring cloud 社区似乎更加活跃,未来可能成为这个行业的标准。

  3. 自己所在公司的云部门就是用的spring boot/cloud,有先入为主的赶脚吧。

 

好,接下来我们来认识下spring cloud.


一、什么是spring cloud?

它的中文官网这样说:

微服务架构集大成者,云计算最佳业务实践。

百度百科这样说的:

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,
如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,
通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。


二、基础知识

从spring cloud的释义里面能看出,它需要用到spring boot,因此学习spring cloud需要spring boot的基础。以下讲解详细的知识模块


spring boot 系列之一:spring boot 入门


最近在学习spring boot,感觉确实很好用,开发环境搭建和部署确实省去了很多不必须要的重复劳动。

接下来就让我们一起来复习下。

一、什么是spring boot ? spring boot是干嘛的?

  Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

二、spring boot 入门实例

  接下来将通过一个Hello 实例来看下怎么使用spring boot 搭建一个应用。

1.创建maven项目

1345071-20180411152317826-320181086.png


22.png

33.png

创建的目录结构如下:

44.png

2.配置pom文件

<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>spring-boot</groupId>
    <artifactId>study</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 指定parent项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
    </parent>
    <properties>
        <!-- spring boot默认的jdk 版本为1.6,我们在这里改为1.8 -->
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <!-- 引入spring-boot-starter-web 依赖 -->
            <!-- 由于在spring boot 顶层POM文件中定义了相关dependencyManagement,因此这里就不需要配置 <version></version>, 
                相关说明可以参考 https://blog.csdn.net/liutengteng130/article/details/46991829 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>


3.创建controller

package com.study.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 这里@RestController = @ Controller + @ ResponseBody,
 * 
 * 会将方法的返回结果直接放入http 返回报文的正文部分,直接显示到页面
 *
 */
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello() {
        return "hello ,spring boot";
    }
}

4.创建app启动类

package com.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {    public static void main(String[] args) {
       SpringApplication.run(App.class, args);
   }
}


5.测试

1)上述步骤完成之后,还有一个错误需要解决,根据其提示进行修复即可

55.png

2)最终的目录结构如下:


66.png

3.运行App的main 方法:Run As-->Java Application

77.png

4)通过浏览器访问,正常返回controller中的配置内容

88.png

三、总结

从上述例子我们看到spring boot默认替我们做了一些操作

  1. 嵌入的Tomcat,无需部署WAR文件,默认端口号为8080

  2. 简化Maven配置,自动为我们引入依赖

  3. 自动配置Spring,省去了我们配置spring xml文件的麻烦

  4. 默认我们的项目命名空间为"/"

  5. 还有一个比较重要但是例子中未能显示体现出来的是:spring boot 默认自动扫描 配置了@SpringBootApplication注解的类所在的包及其子包,并且这个类不能放在默认包下,否则会报错

** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package.  并且不能正常启动。


spring boot 系列之二:spring boot 如何修改默认端口号和contextpath

上一篇文件我们通过一个实例进行了spring boot 入门,我们发现tomcat端口号和上下文(context path)都是默认的,

如果我们对于这两个值有特殊需要的话,需要自己制定的时候怎么办呢?

一、问题解决:

1.在src/main/resources目录下新建文件application.properties,并进行配置,来重写spring boot的默认配置

#指定端口号
server.port=8090
#指定context path
server.context-path=/test

2.目录结构如下:

99.png

3.启动服务,我们发现context和端口号都变了

00.png

4.正常访问:

11.png

二、知识点引申

其实上面application.properties一共有四个目录可以放置:


  1. 外置,在相对于应用程序运行目录的/congfig子目录里

  2. 外置,在应用程序运行的目录里

  3. 内置,在config包内

  4. 内置,在classpath根目录(即我们上述实例中的方式)

其中1和2适用于生产环境,打包后由于发布包不能修改,配置文件放在发布包之外,可以很方便的配置。

位置如下:

22.png

3和4适用于开发环境,位置如下:

33.png


如果同时在四个地方都有配置文件,配置文件的优先级是 1 > 2 > 3 >4。

spring boot 系列之三:spring boot 整合JdbcTemplate

前面两篇文章我们讲了两件事情:

  1. 通过一个简单实例进行spring boot 入门

  2. 修改spring boot 默认的服务端口号和默认context path

这篇文章我们来看下怎么通过JdbcTemplate进行数据的持久化。

废话不多说,直接上干货。

一、代码实现

1.修改pom文件,引入相关依赖

<!-- 引入jdbc 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 引入 mysql 数据库连接依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

2.配置数据库信息,在application.properties中添加如下内容:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root


3.创建实体类并创建数据库

1)实体类

package com.study.entity;
public class User {
    private Integer id;
    private String userName;
    private String password;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}


2)数据库

44.png

4.实现dao层

@Repository
public class UserDao {
    @Autowired
    JdbcTemplate jdbcTemplate;
    public void save(User user) {
        String sql = "insert into t_user(user_name, password) values(?,?)";
        jdbcTemplate.update(sql, user.getUserName(), user.getPassword());
    }
}

5.实现service层

1)接口

public interface UserService {
    public void save(User user);
}

2)实现类

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserDao userDao;    
    public void save(User user){
        userDao.save(user);
    }
    
}

6.实现controller层

@RestControllerpublic class UserController {
   @Autowired
   UserService service;

   @RequestMapping("/saveUser")    public String saveUser(User user) {

       service.save(user);        return "save user successful";
   }
}

7.测试

1)页面正确返回信息

55.png

2)数据库正确保存

66.png


二、总结

由此我们发现,spring boot 只是简化了xml的配置麻烦,并没有减少我们java代码的编写量。

spring boot 不是spring 功能的增强,而是提供了一种快速使用spring 的方式:开箱即用,没有代码生成,也无需XML配置。


spring boot 系列之四:spring boot 整合JPA

上一篇我们讲了spring boot 整合JdbcTemplate来进行数据的持久化,

这篇我们来说下怎么通过spring boot 整合JPA来实现数据的持久化。

一、代码实现 

1.修改pom,引入依赖

<!-- 引入jpa 依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2.修改application.properties,配置相关信息

#修改tomcat默认端口号
server.port=8090
#修改context path
server.context-path=/test
#配置数据源信息
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
#配置jpa
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true


3.创建实体类

package com.study.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="t_user")
public class User {
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
    private String userName;
    private String password;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

4.创建repository接口并继承CrudRepository

package com.study.repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.study.entity.User;
/**
 * 注意:
 * 1.这里这里是interface,不是class
 * 
 * 2.CrudRepository里面的泛型,第一个是实体类,第二个是主键的类型
 * 
 * 3.由于crudRepository 里面已经有一些接口了,如deleteAll,findOne等, 我们直接调用即可
 * 
 * 4.当然,我们也可以根据自己的情况来实现自己的接口,如下面的getUser()方法,jpql语句和hql语句差不多
 * 
 * */
public interface UserRepository extends CrudRepository<User, Integer> {
    /**
     * 我们这里只需要写接口,不需要写实现,spring boot会帮忙自动实现
     * 
     * */
    
    @Query("from User where id =:id ")
    public User getUser(@Param("id") Integer id);
}

5.创建service

1)接口

package com.study.service;
import com.study.entity.User;
public interface UserService {
    public User getUser(Integer id);
}

2)实现

package com.study.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.study.entity.User;
import com.study.repository.UserRepository;
import com.study.service.UserService;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserRepository repository;
    
    @Override
    public User getUser(Integer id) {
        //有两种方式:
        //1.调用crudRepository的接口
//        return repository.findOne(id);
        //2.调用我们自己写的接口
        return repository.getUser(id);
    }
    
}

6.创建controller

package com.study.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.study.entity.User;
import com.study.service.UserService;
@RestController
public class UserController {
    @Autowired
    UserService service;
    
    @RequestMapping("/getUser/{id}")
    public User getUser(@PathVariable("id") Integer id){
        
        return service.getUser(id);
    }
}

7.测试,页面以json格式显示数据库值

77.png

二、知识点引申

关于Repository知识点,可以去看下下面这篇文章

https://segmentfault.com/a/1190000012346333

spring boot 系列之五:spring boot 通过devtools进行热部署

前面已经分享过四篇随笔:

  1. spring boot 系列之一:spring boot 入门

  2. spring boot 系列之二:spring boot 如何修改默认端口号和contextpath

  3. spring boot 系列之三:spring boot 整合JdbcTemplate

  4. spring boot 系列之四:spring boot 整合JPA

在上述代码操作的过程中肯定也发现了一个问题:哪怕是一个个小小的修改,都必须要重新启动服务才能使修改生效。


那能不能通过配置实现spring boot的热部署呢?答案是肯定的。


接下来我们来一起看下怎么通过devtools实现spring boot的热部署。很简单,只需要简单几步:


1.引入依赖

<!-- 引入devtools 依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<!-- optional=true,依赖不会传递-->
	<optional>true</optional>
</dependency>

2.对于eclipse,还需要加上maven-plugin插件

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<fork>true</fork>
			</configuration>
		</plugin>
	</plugins>
</build>

3.Project --> Build Automatically 要勾上

88.png


spring boot 系列之六:深入理解spring boot的自动配置


我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子:

  • Spring的JdbcTemplate是不是在Classpath里面?如果是,并且DataSource也存在,就自动配置一个JdbcTemplate的Bean

  • Thymeleaf是不是在Classpath里面?如果是,则自动配置Thymeleaf的模板解析器、视图解析器、模板引擎

那个这个是怎么实现的呢?原因就在于它利用了Spring的条件化配置,条件化配置允许配置存在于应用中,但是在满足某些特定条件前会忽略这些配置。

要实现条件化配置我们要用到@Conditional条件化注解。

本篇随便讲从如下三个方面进行展开:

  1. @Conditional小例子,来说明条件化配置的实现方式

  2. spring boot 的条件化配置详解

  3. spring boot 自动配置源码分析

  4. 自己动手实现spring boot starter pom

一、@Conditional小例子

我们知道在windows下显示列表的命令是dir,而在linux系统下显示列表的命令是ls,基于条件配置,我们可以实现在不同的操作系统下返回不同的值。

1.判断条件定义

1)windows下的判定条件

/**
 * 实现spring 的Condition接口,并且重写matches()方法,如果操作系统是windows就返回true
 *
 */
public class WindowsCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}

2)linux下的判定条件

/**
 * 实现spring 的Condition接口,并且重写matches()方法,如果操作系统是linux就返回true
 *
 */
public class LinuxCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
    
}

2.不同系统下的Bean的类

1)接口

public interface ListService {
    public String showListLine();
}

2)windows下的Bean类

public class WindowsListService implements ListService{
    @Override
    public String showListLine() {
        return "dir";
    }
}

3)linux下的Bean的类

public class LinuxListService implements ListService{
    @Override
    public String showListLine() {
        return "ls";
    }
}

3.配置类

@Configuration
public class ConditionConfig {
    /**
     * 通过@Conditional 注解,符合windows条件就返回WindowsListService实例
     * 
     */
    @Bean
    @Conditional(WindowsCondition.class)
    public ListService windonwsListService() {
        return new WindowsListService();
    }
    /**
     * 通过@Conditional 注解,符合linux条件就返回LinuxListService实例
     * 
     */
    @Bean
    @Conditional(LinuxCondition.class)
    public ListService linuxListService() {
        return new LinuxListService();
    }
}


4.测试类

public class ConditionTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
        ListService listService = context.getBean(ListService.class);
        System.out.println(context.getEnvironment().getProperty("os.name") + " 系统下的列表命令为: " + listService.showListLine());
    }
}

5.运行测试类

由于我的是windows7 系统,因此结果是

Windows 7 系统下的列表命令为: dir

如果你的是linux系统,则结果就会是

Linux 系统下的列表命令为: ls


二、spring boot 的条件化配置

在spring boot项目中会存在一个名为spring-boot-autoconfigure的jar包


99.png

条件化配置就是在这个jar里面实现的,它用到了如下的条件化注解,这些注解都是以@ConditionalOn开头的,他们都是应用了@Conditional的组合注解:

1011.png



接下来我们看个源码的列子:

以JdbcTemplateAutoConfiguration为例,它里面有这段代码:

@Bean
@Primary
@ConditionalOnMissingBean(JdbcOperations.class)
public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(this.dataSource);
}

只有在不存在JdbcOperations(如果查看JdbcTemplate的源码,你会发现JdbcTemplate类实现了JdbcOperations接口)实例的时候,才会初始化一个JdbcTemplate 的Bean。

基于以上内容,我们就可以阅读自动配置相关的源码了。

 

三、spring boot 自动配置源码分析

spring boot项目的启动类用的注解--@SpringBootApplication是一个组合注解,其中@EnableAutoConfiguration是自动配置相关的。

1022.png

而这个@EnableAutoConfiguration注解里面有个@Import注解导入了EnableAutoConfigurationImportSelector用来实现具体的功能

1033.png

(注:由于我本地的spring boot版本不是最新的,这里的EnableAutoConfigurationImportSelector已经不建议使用了,新版本可能已经换成了其他类,但是不影响我们看代码)

 这个类继承了AutoConfigurationImportSelector

1044.png

进入父类,里面有个方法selectImports()调用了方法getCandidateConfigurations(),进而调用了SpringFactoriesLoader.loadFactoryNames()方法

1055.png

1066.png

在SpringFactoriesLoader.loadFactoryNames()方法里面,我们看到会查询META-INF/spring.factories这个配置文件

1077.png


SpringFactoriesLoader.loadFactoryNames方法会扫描具有META-INF/spring.factories文件的jar包,而我们的spring-boot-autoconfigure.jar里面就有一个这样的文件,此文件中声明了具体有哪些自动配置:

1088.png


我们上面提到的JdbcTemplateAutoConfiguration自动配置类就在里面。

 四、编写自己的spring boot starter pom

接下来,我们就来写一个简单的spring boot starter pom。

步骤如下:

1.新建starter maven项目spring-boot-starter-hello

2.修改pom文件

<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.sam</groupId>
	<artifactId>spring-boot-starter-hello</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<dependencies>
		<!-- 这里需要引入spring boot的自动配置作为依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>


	</dependencies>
</project>

3.属性配置

/**
 * @ConfigurationProperties
 * 自动匹配application.properties文件中hello.msg的值,然后赋值给类属性msg,这里的msg默认值为“spring boot”
 *
 */
@ConfigurationProperties(prefix="hello")
public class HelloServiceProperties {
    private static final String MSG = "spring boot";
    
    private String msg = MSG;
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    
    
}

4.判定依据类

/**
 * 后面的代码会依据此类是否存在,来决定是否生产对应的Bean
 *
 */
public class HelloService {
    private String msg;
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public String sayHello() {
        return "hello " + msg;
    }
}

5.自动配置类

@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", matchIfMissing = true, value = "enabled")
public class HelloServiceAutoConfiguration {
    @Autowired
    HelloServiceProperties helloServiceProperties;
    @Bean
    @ConditionalOnMissingBean(HelloService.class)
    public HelloService helloService() {
        HelloService service = new HelloService();
        service.setMsg(helloServiceProperties.getMsg());
        return service;
    }
}

根据HelloServiceProperties提供的参数,并通过@ConditionalOnClass(HelloService.class)判定HelloService这个类在Classpath中是否存在,存在并且还没有对应的Bean,就生成对应的helloService Bean


6.注册配置,需要到META-INF/spring.factories文件中注册改自动配置类:在src/main/source目录下新建改文件,然后进行配置。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sam.spring_boot_starter_hello.HelloServiceAutoConfiguration

7.对该工程进行mvn clean install,将jar推送到本地maven仓库,供后续使用。

使用starter ,使用我们这个starter 需要新建一个或使用既存的一个spring boot工程(这里我用的是既存的),然后

1)修改pom,引入上述的依赖

<dependency>
	<groupId>com.sam</groupId>
	<artifactId>spring-boot-starter-hello</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

2)实现controller

@RestController
public class HelloController {
  //代码中没有配置这个helloService Bean,但是自动配置能够帮忙实例化,因此可以直接注入
    @Autowired
    HelloService helloService;
    
    @RequestMapping(value="/helloService")
    public String sayHello() {
        return helloService.sayHello();
    }
}

3)页面访问/helloService接口

1099.png

4)在application.properties里面配置hello.msg=sam,然后再次访问/helloService接口

2011.png



-----------------------------------------------------
转载请注明来源此处
原地址:#

-----网友评论----
暂无评论
-----发表评论----
微网聚博客乐园 ©2014 blog.mn886.net 鲁ICP备14012923号   网站导航