springboot入门

前言

本文的主要内容:

事务处理
Docker安装及常用命令
接入Redis缓存及配置Session
整合MongoDB
配置开发与生产环境
部署项目到Docker上
事务处理

关于事务,可以简单理解为,当执行多条数据操作时,能确保每条操作能同时执行成功,否则有一条失败就会回滚前面所有执行成功的操作,保证一致性。下面我们来做一个简单的例子。

@Service
public class MyUserServices {
@Resource
private MyUserMapper userMapper;
@Transactional(rollbackFor = IllegalArgumentException.class, noRollbackFor = IllegalStateException.class)
public MyUser insertUser(MyUser user) {
userMapper.insertUser(user);
if (userMapper.selectUserByName2(user.getUserName()).size() >= 2) {
throw new IllegalArgumentException(“userName " +user.getUserName() + " is all through exist”);
}
if (user.getPassword().equals(“123456”)) {
throw new IllegalStateException(“can’t insert password 123456”);
}
return user;
}
}
可以看到我们给 inserUser方法添加了 Transactional注解,里面包含了 rollbackFor和 noRollbackFor两个属性,通过字面上我们可以看出,一个是回滚一个是不回滚,value值都是接收异常class。可以这样理解,当方法中抛出 rollbackFor定义的异常则会执行事务回滚,当方法中抛出 noRollbackFor定义的异常则不会执行事务回滚。后面有两个判断,一个是通过判断如果插入了两个相同的用户名,则抛出异常进行回滚,实际上开发中不会这样去做,这里是为了学习事务处理来做的,另一个是通过判断密码为123456抛出异常但不进行回滚。

Docker安装及常用命令

Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似iPhone的app),更重要的是容器性能开销极低。

安装完之后,运行下面命令,验证是否安装成功

docker version
 或者
docker info
常用的docker命令如下:

 搜索image
docker search [imageNmae]
 拉取 image
docker pull [imageName]
 列出本机所有 image
docker image ls
docker images
 查看 image 信息
docker images [imageName]
 强制删除 image
docker rmi -f [imageId]
 后台运行容器
docker run -d [imageName]
 查看正在运行的容器
docker ps
 杀掉容器
docker kill [containerId]
#在运行的容器中执行命令
docker exec -it [containerId] [cmd]
#强制删除容器
docker rm -f [containerId]

接入Redis缓存及配置Session

通过Docker添加Redis, 两行命令完成, pull命令时间可能会比较久,需要耐心等待。

#拉取redis镜像文件
docker pull redis
运行redis容器
-p 6379:6379: 将容器的6379端口映射到主机的6379端口
-v $PWD/data:/data: 将主机中当前目录下的data挂载到容器的/data
-d redis redis-server --appendonly yes: 在容器中后台执行redis-server启动命令,并打开redis持久化配置
docker run -p 6379:6379 -v $PWD/data:/data -d redis redis-server --appendonly yes
接下来添加Redis依赖

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置application.yml, 添加spring cache和redis配置, 密码默认为空:

spring:
 cache:
 type: redis
 cache-names: soaic
 redis:
 database: 0
 host: 192.168.0.184
 port: 6379
 password:
 jedis:
 pool:
 #连接池支持的最大连接数 
 max-active: 1000
 #连接池中连接用完时,新的请求等待时间,毫秒
 max-wait: -1ms
 #连接池中最多可空闲maxIdle个连接
 max-idle: 400
 min-idle: 0
 timeout: 1000ms
MyUserServices核心代码:

@Service
public class MyUserServices {
 @Resource
 private MyUserMapper userMapper;
 @CachePut(value = "user", key = "#user.id") //如果方法参数为对象,并且不指定key,需要重写toString方法
 @Transactional(rollbackFor = IllegalArgumentException.class, noRollbackFor = IllegalStateException.class)
 public MyUser insertUser(MyUser user) {
 userMapper.insertUser(user);
 System.out.println("添加缓存key为"+user.getId());
 if (userMapper.selectUserByName2(user.getUserName()).size() >= 2) {
 throw new IllegalArgumentException("userName " +user.getUserName() + " is all through exist");
 }
 if (user.getPassword().equals("123456")) {
 throw new IllegalStateException("can't insert password 123456");
 }
 return user;
 }
 @Cacheable(value = "user", key="id", condition="#id>0") //不指定key,默认以方法参数为key
 public MyUser selectUser(Integer id) {
 MyUser user = userMapper.selectUser(id);
 System.out.println("添加缓存key为" + id);
 return user;
 }
 @CacheEvict(value = "user")
 public Integer removeUser(Integer id) {
 Integer result = userMapper.deleteUser(id);
 System.out.println("删除缓存key为" + id);
 return result;
 }
}

注解 @CachePut()表示当有缓存刷新缓存,没有则添加缓存,有三个属性, value为缓存的名称为application.yml配置的cache-names; key为缓存的key, 如果不指定key,默认以方法参数为key,如果方法参数为对象,需要重写toString方法; condition为缓存的条件,条件为true时才进行缓存。注解 @Cacheable()表示当有缓存则取缓存,没有则添加缓存,三个属性和注解@CachePut()属性一致。注解 @CacheEvict()表示当有缓存则清除缓存,除了前面说的三个属性外,还多出两个属性, allEntries为true清除所有元素;beforeInvocation为true时表示在调用该方法之前清除缓存中的指定元素,默认是方法成功执行之后触发,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。

SpringBoot 入门
接着添加RedisCacheConfig配置key生成规则、配置RedisTemplate和缓存序列化以json格式存储

@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
 /**
 * 配置redis key
 */
 @Bean
 @Override
 public KeyGenerator keyGenerator() {
 return (target, method, params) -> {
 StringBuilder sb = new StringBuilder();
 sb.append(target.getClass().getName());
 for (Object obj : params) {
 sb.append(":");
 sb.append(obj.toString());
 }
 return sb.toString();
 };
 }
 /**
 * RedisTemplate 序列化
 */
 @Bean
 public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
 Jackson2JsonRedisSerializer redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
 ObjectMapper objectMapper = new ObjectMapper();
 objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
 redisSerializer.setObjectMapper(objectMapper);
 RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
 redisTemplate.setConnectionFactory(redisConnectionFactory);
 redisTemplate.setValueSerializer(redisSerializer);
 redisTemplate.setKeySerializer(new StringRedisSerializer());
 redisTemplate.setHashKeySerializer(new StringRedisSerializer());
 redisTemplate.afterPropertiesSet();
 return redisTemplate;
 }
 /**
 * redis 缓存序列化
 */
 @Bean
 public CacheManager cacheManager(RedisConnectionFactory connectionFactory){
 Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
 ObjectMapper objectMapper = new ObjectMapper();
 objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
 redisSerializer.setObjectMapper(objectMapper);
 RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));
 return RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();
 }
}

最后把 MyUser实现 Serializable, 否则会报序列化错误

public class MyUser implements Serializable {

}
测试。先通过Controller(这里没有写出代码,可以到github上查看)调用MyUserServices中方法,然后进入docker运行的容器查看

查看正在运行的容器ID
docker ps
#进入redis容器中
docker exec -it [containerId] redis-cli
进入之后输入 info 可以查看redis信息
info
查看所有key
keys *
查看某个key的值
get key
接下来就是配置Session了。为什么要用redis配置session?有这么几点原因:1、方便管理session 2、托管到redis共享Session方便使用集群, 即一台服务器坏了,切换到另一台服务器,无需再次登录还能正常访问

首先添加依赖

<dependency>
 <groupId>org.springframework.session</groupId>
 <artifactId>spring-session-data-redis</artifactId>
</dependency>

要注意一下的是,如果 spring-boot-starter-parent版本是2.1.5.RELEASE时,添加依赖后运行会报下面这个错误,解决办法可以把版本降低,改成2.1.4.RELEASE即可。

java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration.taskScheduler
配置application.yml, 设置session存储方式

spring:
 session:
 store-type: redis
添加RedisSessionConfig配置,启用Session, 并设置失效时间

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)//原 Spring Boot 的 server.session.timeout 属性不再生效。
public class RedisSessionConfig {
}

接下来配置Session拦截器以及Login接口中登录成功设置session,上篇文章中介绍的还比较详细,这里就简单的贴一下核心代码:

SessionInterceptor核心代码

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 System.out.println("SessionInterceptor preHandle");
 HttpSession session = request.getSession(false);
 if (session != null && session.getAttribute("user") != null) {
 return true;
 } else {
 PrintWriter printWriter = response.getWriter();
 printWriter.write("{code: 501, message:\"not login!\"}");
 return false;
 }
}
MyUserController核心代码

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseResult<MyUser> login(HttpServletRequest request, String userName, String password) {
 ResponseResult<MyUser> responseResult;
 try {
 List<MyUser> myUser = myUserServices.login(userName, password);
 if (myUser != null && myUser.size() > 0) {
 request.getSession(true).setAttribute("user", myUser.get(0));
 responseResult = new ResponseResult<>(200, "login success", myUser.get(0));
 } else {
 responseResult = new ResponseResult<>(501, "login failure: invalid userName or password", null);
 }
 } catch (Exception e) {
 e.printStackTrace();
 responseResult = new ResponseResult<>(501, "login failure: " + e.getMessage(), null);
 }
 return responseResult;
}

最后测试,当第一时间访问其它接口会报501错误,而当访问login登录成功后,则会保存session,后面其它接口访问都可以正常。这里没有用多台服务器测试,只测试了下,当服务器停止再启动后,访问接口都不用再重新登录,session存储在redis中,不会因为服务器断开而消失,这就区别于不托管redis,session是存储在内存中,每次停止服务器再启动都要重新登录。

整合MongoDB

通过Docker添加MongoDB, 同样两行命令完成。

docker pull mongo
-p 27017:27017: 将容器的27017 端口映射到主机的27017 端口
-v $PWD/db:/data/db: 将主机中当前目录下的db挂载到容器的/data/db,作为mongo数据存储目录
docker run -p 27017:27017 -v $PWD/db:/data/db -d mongo
添加MongoDB依赖

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
配置application.yml, 这里用Docker添加的所以没有账号密码,当有账号密码时,url格式为:mongodb://user:pwd@ip:port/soaic?maxPoolSize=256; 当有多台数据库时,url格式为:mongodb://user:pwd@ip1:port1,ip2:port2/database?maxPoolSize=512

spring:
 data:
 mongodb:
 uri: mongodb://localhost:27017/soaic?maxPoolSize=256

修改MyUser对象, 在类作用域上添加 @Document注解定义为一个文档,,也可以理解为是一张表,注解 @Id定义属性为ID, 注解 @Field定义为存储表中的字段名

@Document("myUser")
public class MyUser implements Serializable {
 @Id
 private String id;
 private String userName;
 private String password;
 @Field("roles") //在文档中的名称为roles, 以数组形式存储
 private Collection<Role> roles = new LinkedHashSet<>();
}
添加 Role 对象

public class Role {
 private String roleName;
 public String getRoleName() {
 return roleName;
 }
 public void setRoleName(String roleName) {
 this.roleName = roleName;
 }
}

在SpringBoot中对mongoDB操作数据有多种方式,如:通过继承 MongoRepository对象调用定义好的方法, 或者遵从定义方法名的规则,或者通过注解 @Query实现查询, ?0为占位符取方法参数的第一个,依次类推

public interface MyUserRepository extends MongoRepository<MyUser, String> {
 List<MyUser> findByUserName(String name);
 @Query("{'userName': ?0}")
 List<MyUser> withQueryUserName(String name);
}

通过 MongoTemplate来实现, 下面代码简单的实现了增删改查CRUD

@Service 
public class MyUserDaoImpl implements MyUserDAO{
 @Autowired
 MongoTemplate mongoTemplate;
 @Override
 public List<MyUser> findByUserName(String userName) {
 Query query = new Query(Criteria.where("userName").is(userName));
 return mongoTemplate.find(query, MyUser.class);
 }
 @Override
 public MyUser insertUser(MyUser myUser) {
 return mongoTemplate.insert(myUser);
 }
 @Override
 public boolean deleteUser(String id) {
 Query query = new Query(Criteria.where("id").is(id));
 DeleteResult result = mongoTemplate.remove(query, MyUser.class);
 return result.getDeletedCount() > 0;
 }
 @Override
 public boolean updateUser(MyUser myUser) {
 Criteria criteria= Criteria.where("id").is(myUser.getId());
 Update update = new Update();
 if (myUser.getUserName() != null)
 update.set("userName", myUser.getUserName());
 if (myUser.getPassword() != null)
 update.set("password", myUser.getPassword());
 UpdateResult result = mongoTemplate.updateFirst(new Query(criteria), update, MyUser.class);
 return result.getModifiedCount() > 0;
 }
}

通过单元测试对上面的代码进行测试, 在test/java/com.soaic.hellospringboot中创建MongoDBTests, 添加注解 @SpringBootTest和 @RunWith(SpringRunner.class), 测试的方法添加 @Test注解,然后分别点击方法左边的绿色小图标 Run Test 即可

@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDBTests {
 @Autowired
 private MyUserRepository myUserRepository;
 @Autowired
 private MyUserDaoImpl myUserDaoImpl;
 @Test
 public void testSave() {
 MyUser myUser = new MyUser();
 myUser.setUserName("Soaic");
 myUser.setPassword("123456");
 Collection<Role> roles = new LinkedHashSet<>();
 Role role = new Role();
 role.setRoleName("管理员");
 roles.add(role);
 Role role1 = new Role();
 role1.setRoleName("程序员");
 roles.add(role1);
 myUser.setRoles(roles);
 //myUserRepository.save(myUser);
 myUserDaoImpl.insertUser(myUser);
 }
 @Test
 public void testFind() {
 List<MyUser> myUserList = myUserDaoImpl.findByUserName("Soaic");
 System.out.println(JSON.toJSONString(myUserList));
 }
 @Test
 public void testUpdate() {
 List<MyUser> myUserList = myUserRepository.withQueryUserName("Soaic");
 for (MyUser myUser: myUserList) {
 myUser.setPassword("1234567");
 myUserDaoImpl.updateUser(myUser);
 }
 }
 @Test
 public void testDel() {
 List<MyUser> myUserList = myUserRepository.findByUserName("Soaic");
 for (MyUser myUser: myUserList) {
 myUserDaoImpl.deleteUser(myUser.getId());
 }
 }
}

查询mongoDB数据库数据有无变化,可以通过如下命令连接mongoDB查询

连接mongodb

docker run -it mongo mongo --host 172.17.0.1

查看所有数据库

show dbs

切换数据库

use soaic

查看数据库状态

db.stats()

查询数据库下所有表

show collections

查询某个表的数据

db.collection.find()
配置开发与生产环境

添加 application-dev.yml开发环境配置和 application-prod.yml生产环境配置, 如果我们想使用生产环境,因为程序会默认加载 application.yml, 所以只需要在里面配置 spring.profiles.active为 prod即可,配置为 dev则为开发环境。

spring:
profiles:
active: prod
部署项目到Docker上

部署到Docker上需要先把项目打包成jar包,可以通过idea中的 Maven Projects 找到Lifecycle下的clean 和 package 依次双击执行(也可以执行命令 mvn cleanpackage),最后就可以在target文件夹下找到 hellospringboot-0.0.1-SNAPSHOT.jar

如果不部署到Docker上,可以直接运行下面命令启动项目

java -jar hellospringboot-0.0.1-SNAPSHOT.jar
如果部署到docker上,我们需要创建一个Dockerfile文件在项目的根目录,里面内容如下:

FROM java:8
MAINTAINER Soaic
ADD target/hellospringboot-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8443
EXPOSE 8088
ENTRYPOINT [“java”, “-jar”, “/app.jar”]
第一行:基于镜像为Java, 标签版本为8 第二行:作者Soaic 第三行:将hellospringboot-0.0.1-SNAPSHOT.jar添加到镜像中,并重命名为app.jar 第四行和第五行:运行镜像的容器,监听8443和8088端口 第六行:启动时运行 java -jar app.jar

接下来编译镜像,在项目根目录下执行下面命令,其中hellospringboot为镜像名称,最后一个".",用来指明Dockerfile路径,表示在当前路路径下,编译第一次需要下载java8,后面编译就不需要下载了

docker build -t hellospringboot .
编译完成后,可以通过下面命令查看及运行项目

查看是否有一个image为hellospringboot

docker images

后台运行项目,并映射两个端口号8443和8088,–name为修改运行容器名称可加可不加默认为image名称

docker run -d --name hellospringboot -p 8443:8443 -p 8088:8088 hellospringboot
最后可以通过 http://localhost:8088https://localhost:8443 这个两个地址访问了。

彩蛋时间:免费分享Java技术资料,需要的可以私信我
在这里插入图片描述
SpringBoot 入门
来源:知乎

作者:白天不懂夜的黑

原文:https://zhuanlan.zhihu.com/p/73717030

Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它提供了一种简化的方法来配置和部署应用程序,使开发人员能够更快地开发和运行应用程序。 Spring Boot Actuator是Spring Boot的一个组件,它可以帮助我们监控和管理Spring Boot应用程序,包括健康检查、审计、统计和HTTP追踪等功能。要使用Spring Boot Actuator,只需引入相应的起步依赖,并在应用程序的入口点类上添加@SpringBootApplication注解即可。在该类中,使用@SpringBootApplication注解相当于同时添加了@Configuration、@EnableAutoConfiguration和@ComponentScan注解,它标识了当前应用程序是一个Spring Boot应用程序。要启动Spring Boot应用程序,只需在主启动类中编写main函数,通过调用SpringApplication.run(Application.class, args)方法来启动应用程序。在开发过程中,<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SpringBoot入门](https://blog.youkuaiyun.com/weixin_45905210/article/details/121712027)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [spring boot 入门](https://blog.youkuaiyun.com/zhshx19900318/article/details/129476812)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值