SpringBoot的基本使用
SpringBoot四大神器
1. 自动配置
- 针对很多Spring应用程序和常见的应用功能,SpringBoot能自动提供依赖
2. 起步依赖
- 高速SpringBoot需要什么功能,他就引入需要的依赖库
3. Actuator
- 能够深入运行中的SpringBoot应该用程序
4. 命令行界面
- springboot的可选特性,主要针对Groovy语言使用
SpringBoot的创建方式
- 通过IDEA或者Eclipse的spring Initializer创建(需要连网)
- 访问http://start.spring.io/创建spring boot的骨架,然后导入Eclipse或者Idea
- 手动创建Maven项目,导入以下pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
SpringBoot运行方式
- 运行DemoApplication的main方法
- 打jar包,运行java -jar xxxx.jar即可
- 通过spring-boot-plugin的方式
SpringBoot参数传递
- 通过ApplicationArguments对象封装了jvm传递的参数
- applicationArguments.getNonOptionArgs() // 获取jvm参数
application.properties配置文件
-
加载优先级(由高到底)
- 当前项目/config子目录
- 当前项目
- classpath:/config目录
- classpath目录
-
默认读取application.properties(最好不要改),修改名字需要在启动时带上–spring.config.name=文件名
-
server.port=8082 修改tomcat启动端口为8082
SpringApplication简介
- 修改banner,把banner.txt放在resources目录下即可
spring.main.banner-mode=off
# 不显示banner- 启动springboot方式1
public static void main(String[] args) {
// SpringApplication.run(DemoApplication.class, args);
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.run(args);
}
- 启动方式2(链式编程)
public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class).run(args);
}
SpringBoot注解
@ConfigurationProperties参数绑定
- Bean的注入方式
- 自定义类注入方式
@Data @Component @ConfigurationProperties(prefix = "db") public class MyDruid { private String username; private String password; private String url; private String driverClassName; }
- 第三方类注入方式
@Bean @ConfigurationProperties("db") public MyDruid MyDruid(){ return new MyDruid(); }
- 属性注入的方式
- 在application.properties中配置
db.username=123 db.password=password db.url=jdbc:mysql://localhost:3306/druid db.driver_class_name=com.mysql.jdbc.Driver
- 通过java命令行参数注入
- java -jar xxxxx.jar --username=123 password=456 url=abcd driver_class_name
- 属性注入大写可以换成下划线或者横线
SpringBoot的热部署
- 导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!--option为true表示该依赖不传递到下一个依赖当前项目的项目-->
- 原理
- SpringBoot重启是reload重启,通过监控classpath的变化,如果,classpath变化,即触发重启。SpringBobasiot通过两个classpath来完成reload,一个basic classloader中加载不变的类,一个restart classloader中加载classpath中的类,充气垫是后,restart classloader中的类丢弃并重新加载;
- 排除资源
spring.devtools.restart.exclude=static/**,templates/**
spring.devtools.restart.enabled=false
// 禁用自动重启spring.devtools.restart.triggerFile=trigger.file
使用triggerfile的重启策略,注意这个文件不要放到default_excludes目录下面
Spring常用工具包
spring-boot-starter
核心工具包,提供了自动配置的支持,日志和yaml配置支持spring-boot-starter-activemq
针对快速集成ActiveMQ的工具包spring-boot-starter-aop
提供了快速集成AOP和AspectJ的工具包spring-boot-starter-deta-redis
提供了快速集成Redis和Jedis的工具包spring-boot-starter-fremarker
提供了快速集成Freemarker的工具包spring-boot-start-mail
提供了快速集成右键发送的工具包spring-boot-starter-web
提供了对web开发的工具包,包括基于SpringMVC的Restful应用开发,内置的tomcat服务器等spring-boot-starter-actuator
提供了对生产环境中应用监控的工具包spring-boot-starter-logging
提供了对日志的工具包,默认使用Logback
SpringBoot集成mysql,druid dataSource
- 导入pom依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
- 手动装配dataSource
@Bean @ConfigurationProperties("db") public DataSource dataSource(){ return new DruidDataSource(); }
db.username=root db.password=123456 db.url=jdbc:mysql:///springboot db.driver_class_name=com.mysql.jdbc.Driver
- 自动装配
spring.datasource.druid.username=root spring.datasource.druid.password=123456 spring.datasource.druid.url=jdbc:mysql:///springboot spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.initial-size=5
SpringBoot集成Mybaits
- 导入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybais-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
-
配置(可以不要mybatis-cfg.xml文件)
mybatis.type-aliases-package=com.wyq.test.domain mybatis.mapper-locations=classpath:mapper/*.xml mybatis.config-location=classpath:conf/mybatis-cfg.xml logging.level.com.wyq.test.mapper=debug # sql打印
- Mapper文件放在resources目录中
- Mapper文件放在java文件中需要在pom中添加如下配置,不然不会编译到classpath目录中(添加到build标签中)
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources>
-
在启动类添加扫描Mapper接口注解或者在Mapper接口上添加Mapper注解
- @MapperScan(“com.wyq.test.mapper”)
SpringBoot集成事务
- 基于注解的事务管理(推荐)
- 在主配置类贴上@EnableTransactionManagement
- 在需要事务的Service上贴上@Transactional注解,表示该类下面的所有方法都需要事务
- 在不需要事务的方法上贴上 @Transactional(readOnly = true)
- 基于xml配置(略)
SpringBoot的Web开发
静态资源的加载问题
- 静态资源加载目录
- 默认可以是classpath:/META-INF/resources/,classpath:/resources/,classpath:static/,classpath:/public/
- 指定静态资源加载目录
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:static/,classpath:/public/,classpath:/mydir
SpringBoot集成Freemarker
- Freemarker页面以ftl为后缀
- SpringBoot对Jsp的支持不好,不建议使用
- 导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
- 在低版本中freemarker是不开启session的,也就是说前端页面无法获取session中的值
- 可以通过开启
spring.freemarker.expose-session-attributes=true
,在高版本中无论是true还是false都支持 - SpringBoot对freemarker的配置
spring.freemarker.enabled=true
是否开启freemarker支持spring.freemarkerallow-request-override
是否允许request中的属性覆盖model中同名属性,默认是falsespring.freemarker.allow-session-override
是否允许session中的属性覆盖model中同名属性,默认是falsespring.freemarke.cacher
是否支持模板缓存,默认是falsespring.freemarker.charset=utf-8
模板编码spring.freemarker.content-type=text/html
模板contenttypespring.freemarker.expose-request-attributes
是否开启request属性expose,默认是falsespring.freemarker.expose-session-attributes
是否开启session属性expose,默认是falsespring.freemarker.expose-sprong-macro-helpers
是否开启spring的freemarker宏支持,默认为falsespring.freemarker.prefer-file-system-access
默认是true,支持实时检查模板修改spring.freemarker.prefix
加载模板时候的前缀spring.freemarker.setting.*
直接配置freemarker参数spring.freemarker。suffix
模板文件后缀spring.freemarker.template-loader-path=classpath:/temppates/
模板加载地址
统一异常处理
- 统一异常处理类(SpringMVC本来就有的,并不是SpringBoot独有的)
@ControllerAdvice public class ErrorControllerAdvice { @ExceptionHandler(Exception.class) public void hadlerError(Exception e, HandlerMethod handlerMethod){ System.out.println("统一异常处理"); System.out.println(e.getMessage()); System.out.println("出错的类:" + handlerMethod.getBean().getClass()); System.out.println("出错的方法:" + handlerMethod.getMethod()); } }
- 统一异常处理页面
- SpringBoot默认情况下,把所有的错误都重新定位到/error这个处理路径上,有BasicErrorController类完成处理
- SpringBoot提供了默认的替换错误页面的路径
src/resources/public/error/404.html src/resources/public/error/401.html src/resources/public/error/5xx.html
SpingBoot集成Servlet,Filter,Listener
- 注解方式创建Bean
- 在启动类添加servlet的注解扫描
@ServletComponentScan
(启动类的扫描默认扫描当前包及其子包)
- 在启动类添加servlet的注解扫描
-
手动创建Bean的方式
@Bean public ServletRegistrationBean testServlet(){ ServletRegistrationBean bean = new ServletRegistrationBean(); bean.setServlet(new TestServlet()); bean.addUrlMappings("/testServlet"); return bean; } 同理:过滤器用:FilterRegistrationBean 监听器用: ServletListenerRegistrationBean<TestListener>
SpringBoot集成文件上传
- 与SpringMVC一样
- 不同的是需要先给一个存储文件的绝对路径,因为没有webapp目录,只能通过配置文件注入进来
@Value("${file.path}") private String path; @RequestMapping("/upload") @ResponseBody public String upload(MultipartFile file) throws IOException{ String fileName = UUID.randomUUID() + file.getOriginalFilename(); FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(new File(path + fileName))); return "上传成功"; }
SpringBoot注册拦截器
- 创建自定义拦截器类
public class MyInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor preHandle 拦截器"); return true; } }
- 改写启动类(让它继承WebMvcConfigurerAdapter)
public class TestApplication extends WebMvcConfigurerAdapter { @Override // 注册拦截器的方法 public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor()).addPathPatterns("/*"); } @Bean // 创建拦截器的Bean public MyInterceptor myInterceptor(){ return new MyInterceptor(); } public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
SpringBoot集成Redis
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
- 在application.properties中配置连接信息
# Redis
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=1
spring.redis.timeout=0
- 创建相关Bean(Redis的template)
@Bean
public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
CacheManager cacheManager = new RedisCacheManager(redisTemplate);
return cacheManager;
/*RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
// 多个缓存的名称,目前只定义了一个
rcm.setCacheNames(Arrays.asList("thisredis"));
//设置缓存默认过期时间(秒)
rcm.setDefaultExpiration(600);
return rcm;*/
}
// 以下两种redisTemplate自由根据场景选择
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}
-
操作Redis,redis与数据库配合实现增删改查
- 用代码的方式
package com.wyq.test.service.impl; import com.wyq.test.mapper.UserMapper; import com.wyq.test.pojo.User; import com.wyq.test.service.UserService; import com.wyq.test.util.RedisKeyPrefix; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Resource private RedisTemplate<String, User> redisTemplate; @Override public List<User> selectAllUser() { List<User> users = userMapper.selectAllUser(); return users; } @Override public int addUser(User user) { return userMapper.addUser(user); } @Override public int deleteById(long id) { // 缓存存在,删除缓存 String key = "user_" + id; boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { redisTemplate.delete(key); System.out.println("删除用户时候,从缓存中删除用户 >> " + id); } return userMapper.deleteById(id); } @Override public int update(User user) { long userId = user.getId(); // 缓存存在,删除缓存 String key = "user_" + userId; boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { redisTemplate.delete(key); System.out.println("更新用户时候,从缓存中删除用户 >> " + userId); } return userMapper.update(user); } @Override public User selectOne(long id) { String key = RedisKeyPrefix.USER + id; // 缓存存在 boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { User user = redisTemplate.opsForValue().get(key); System.out.println("从缓存中查"); return user; } // 从数据库取,并存回缓存 User user = userMapper.findOne(id); System.out.println("从数据库查"); // 放入缓存,并设置缓存时间 redisTemplate.opsForValue().set(key, user, 600, TimeUnit.SECONDS); return user; } }
- 用注解的方式
// 从redis中删除该用户并把数据库中的一起删除 @CacheEvict(value="thisredis", key="'user:'+#id",condition="#id!=1") public int deleteById(long id) { System.out.println("从数据库删除user,并删除缓存中数据"); return userMapper.deleteById(id); } // 从缓存中删除该用户,下次再查询时重新从数据库查,然后再更新到redis @CacheEvict(value="thisredis", key="'user:'+#id") public int update(User user) { return userMapper.update(user); } // 第一次从数据库查,保存到redis中,以后从redis中查 @Cacheable(value="thisredis", key="'user:'+#id") public User selectOne(long id) { User user = userMapper.findOne(id); System.out.println("缓存中没有,从数据库查"); return user; }
-
在启动类贴上@EnableCaching // 开启springboot中的缓存
SpringBoot整合RabbitMQ
- 导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
- 配置虚拟主机,连接等信息
# rabbitmq spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=wyq spring.rabbitmq.password=123456 spring.rabbitmq.publisher-confirms=true spring.rabbitmq.virtual-host=/
- 创建队列的Bean
@Configuration public class SenderConf { @Bean public Queue queue() { return new Queue("queue"); } }
- 创建生产者与消费者
-
Direct 直接模式(默认的模式)
@Autowired private AmqpTemplate template; @Override // 生产user public void addUser(User user) { System.out.println("把user保存到rabbitMQ中"); template.convertAndSend("queue",user); } @Override // 监听并消费user @RabbitListener(queues="queue") public void getUserSaveInDB(User user) { System.out.println("从rabbitmq中取出数据并保存到数据库:" + user); userMapper.addUser(user); }
-
Fanout Exchange 广播模式
- 创建多个队列
@Bean(name="Amessage") public Queue AMessage() { return new Queue("fanout.A"); } @Bean(name="Bmessage") public Queue BMessage() { return new Queue("fanout.B"); } @Bean(name="Cmessage") public Queue CMessage() { return new Queue("fanout.C"); }
- 创建1个FanoutExchange交换机
@Bean FanoutExchange fanoutExchange() { return new FanoutExchange("fanoutExchange");//配置广播路由器 }
- 将多个队列与这个交换机绑定
@Bean Binding bindingExchangeA(@Qualifier("Amessage") Queue AMessage,FanoutExchange fanoutExchange) { return BindingBuilder.bind(AMessage).to(fanoutExchange); } @Bean Binding bindingExchangeB(@Qualifier("Bmessage") Queue BMessage, FanoutExchange fanoutExchange) { return BindingBuilder.bind(BMessage).to(fanoutExchange); } @Bean Binding bindingExchangeC(@Qualifier("Cmessage") Queue CMessage, FanoutExchange fanoutExchange) { return BindingBuilder.bind(CMessage).to(fanoutExchange); }
- 创建三个监听来监听消费这三个队列
@RabbitListener(queues="fanout.A") public void processA(String str1) { System.out.println("ReceiveA:"+str1); } @RabbitListener(queues="fanout.B") public void processB(String str) { System.out.println("ReceiveB:"+str); } @RabbitListener(queues="fanout.C") public void processC(String str) { System.out.println("ReceiveC:"+str); }
- 创建生产者并测试
@Test public void testRabbitFanout() { template.convertAndSend("fanoutExchange", "", "发送的消息"); }
- 创建多个队列
-
Topic转发模式
- 创建队列
@Bean(name="topicQueue1") public Queue topicQueue1() { return new Queue("topic.queue1"); } @Bean(name="topicQueue2") public Queue topicQueue2() { return new Queue("topic.queue2"); }
- 创建交换机
@Bean public TopicExchange exchange() { return new TopicExchange("exchange"); }
- 绑定
@Bean Binding bindingExchangeMessage(@Qualifier("topicQueue1") Queue topicQueue1, TopicExchange exchange) { return BindingBuilder.bind(topicQueue1).to(exchange).with("topic.queue1"); } @Bean Binding bindingExchangeMessages(@Qualifier("topicQueue2") Queue topicQueue2, TopicExchange exchange) { return BindingBuilder.bind(topicQueue2).to(exchange).with("topic.#");//*表示一个词,#表示零个或多个词 }
- 创建监听
@RabbitListener(queues="topic.queue1") //监听器监听指定的Queue public void process1(String str) { System.out.println("queue1:"+str); } @RabbitListener(queues="topic.queue2") //监听器监听指定的Queue public void process2(String str) { System.out.println("queue2:"+str); }
- 创建生产者并测试
@Test public void testRabbitTopic() { template.convertAndSend("exchange", "topic.queue1", "hello rabbitmq topic"); }
- 创建队列
-