1、spring的点点滴滴
1.1、Spring IOC
IOC容器:就是具有依赖注入功能的容器。Spring中BeanFactory是IOC容器的实际代表者。
1.2、Spring AOP
AOP:面向切面编程,动态代理
OOP:面向对象编程
OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。
比如,我们可以为动物这个集合,同时引入跑、走、叫、吃,这些对象,oop很容易做到。
当我们想为动物、植物、房屋等不同集合、没有关联的集合,同时引入高度记录、耐高温强度等这样的需求oop很难做到。aop可以很方便的实现。
我们常常用AOP做日志功能、安全性、异常处理等
便于减少系统的重复代码,降低模块间的耦合度,利于维护
spring aop有五种通知类型:
- 前置通知:在目标方法前运行(@Before)
- 后置通知:在目标方法结束后 ,不管有没有异常(@After)
- 返回通知:在目标方法正常返回值后运行(@AfterReturning)
- 异常通知:在目标方法出现异常后运行(@AfterThrowing)
- 环绕通知:动态代理,需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)
1.3、BeanDefinition
在加载每个bean之前,BeanDefinition先会为每个bean贴上一些标签(就像这个bean的说明书一样)。贴上例如类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等这些信息。
就像水果在加工厂流水线上,BeanDefinition就是一个筛选、并贴说明书的机器。
BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如 ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。
BeanDefinition的执行结果最终会以 map(bean名字,bean说明书)的形式存储。
这个map的具体名字叫 beanDefinitionMap()
1.4、注解@RequestParam如何使用加与不加的区别
加上注解@RequestParam表示该参数在 url中必须要有,否则报错
平时我们在使用的时候不加注解也可以传递参数(并且不加注解,url中没有参数也不会报错)
注解@RequestParam还有几个参数可以配置
required 表示是否必须,默认为 true,必须。
defaultValue 可设置请求参数的默认值。
value 为接收url的参数名(相当于key值)。
当然这样的限制是可以选择的,@RequestParam里添加required=false来关闭必须参数的限制,这样就和第一种不带注解的效果一样了
1.5、Spring中Model、ModelMap及ModelAndView之间的区别
springmvc中,直接在controller方法形参上定义默认类型的对象,可以使用这些对象。
- HttpServletRequest对象
- HttpServletResponse对象
- HttpSession对象
- Model/ModelMap对象
1、Model是一个接口,包含addAttribute方法。
2、ModelMap类实现了Map接口。
3、ExtendedModelMap继承了ModelMap类。
Model通过addAttribute方法向页面传递数据
Model可以接收各种类型的数据,如果接收的数据是List,那么这个时候Model实际上是ModelMap。
ModelAndView通过addObject方法向页面传递数据
Model与ModelAndView的区别
第一点:Model只是用来传输数据的,并不会进行业务的寻址。ModelAndView 却是可以进行业务寻址的;所以Model的返回值是url地址,而ModelAndView的返回值是ModelAndView对象;
第二点:Model是每一次请求可以自动创建,但是ModelAndView 是需要我们自己去new的。所以使用Model时Controller的参数是Model。
1.6、@ModelAttribute模型属性
@ModelAttribute:用来
被 @ModelAttribute 注释的方法会在Controller每个方法执行之前都执行,
因此对于一个Controller中包含多个URL的时候,要谨慎使用。
它可以使用在
- 应用在方法上
- 应用在方法的参数上
- 应用在方法上,并且方法也使用了@RequestMapping
应用在无返回值的方法上
如上例子中:
请求/modelattribute/method?abc=aaa,会先执行myModel方法,再接着执行method方法
请求 /modelattribute/method时,Model会被带到页面上
把myModel和method合二为一
应用在有返回值的方法上
@ModelAttribute
public String myModel(@RequestParam(required = false) String abc) {
return abc;
}
@ModelAttribute
public Student myModel(@RequestParam(required = false) String abc) {
Student student = new Student(abc);
return student;
}
@ModelAttribute
public int myModel(@RequestParam(required = false) int number) {
return number;
}
返回值对象会被默认放到隐含的Model中,在Model中的key为返回值首字母小写,value为返回的值。
上面3种情况等同于:
model.addAttribute("string", abc);
model.addAttribute("student", student);
model.addAttribute("int", number);
@ModelAttribute还可以添加value属性
@ModelAttribute(value = "num")
public int myModel(@RequestParam(required = false) int number) {
return number;
}
//这样就相当于 model.addAttribute("num", number);
使用@ModelAttribute注解的参数
@Controller
@RequestMapping(value = "/modelattribute")
public class ModelAttributeParamController {
@ModelAttribute(value = "attributeName")
public String myModel(@RequestParam(required = false) String abc) {
return abc;
}
@ModelAttribute
public void myModel3(Model model) {
model.addAttribute("name", "zong");
model.addAttribute("age", 20);
}
@RequestMapping(value = "/param")
public String param(@ModelAttribute("attributeName") String str,
@ModelAttribute("name") String str2,
@ModelAttribute("age") int str3) {
return "param";
}
}
从代码中可以看出,使用@ModelAttribute注解的参数,意思是从前面的Model中提取对应名称的属性。
应用在方法上,并且方法上也使用了@RequestMapping
@Controller
@RequestMapping(value = "/modelattribute")
public class ModelAttributeController {
@RequestMapping(value = "/test")
@ModelAttribute("name")
public String test(@RequestParam(required = false) String name) {
return name;
}
}
这种情况下,返回值String(或者其他对象)就不再是视图了,而是放入到Model中的值,此时对应的页面就是@RequestMapping的值test。
如果类上有@RequestMapping,则视图路径还要加上类的@RequestMapping的值,本例中视图路径为modelattribute/test.jsp。
1.7、@RequestBody和@RequestParam区别
@RequestBody这个一般处理的是在ajax请求中声明contentType: "application/json; charset=utf-8"时候。也就是json数据或者xml(我没用过这个,用的是json)
@RequestParam这个一般就是在ajax里面没有声明contentType的时候,为默认的。。。urlencode格式时,用这个。
2、spring boot
2.1、上传附件
需要的依赖web、commons-fileupload、thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在resources/templates下 新建u.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data" >
<input type="file" value="请选择附件..." title="上传附件" /><br>
<input type="submit" value="提交">
</form>
</body>
</html>
新建控制类TestController.java
如下是保存在G盘根目录下
package com.haha.myboot05.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.Date;
@Controller
public class TestController {
@RequestMapping("/u")
public String uploadFile(){
return "u";
}
@RequestMapping(value="/upload",method = RequestMethod.POST)
public String upload( MultipartFile file) throws IOException {
//用来检测程序运行时间
long startTime=System.currentTimeMillis();
System.out.println("正上传文件:"+file.getOriginalFilename());
try {
//获取输出流
OutputStream os = new FileOutputStream("G:\\"+new Date().getTime()+file.getOriginalFilename());
//获取输入流 CommonsMultipartFile 中可以直接得到文件的流
InputStream is=file.getInputStream();
int temp;
//一个一个字节的读取并写入
while((temp=is.read())!=(-1))
{
os.write(temp);
}
os.flush();
os.close();
is.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long endTime=System.currentTimeMillis();
System.out.println("方法一的运行时间:"+String.valueOf(endTime-startTime)+"ms");
return "u";
}
}
然后运行,访问http://localhost:8080/u就可以了
2.2、下载
在Controller里面
@RequestMapping("/download")
public ResponseEntity downloadImg() throws Exception{
FileSystemResource file = new FileSystemResource("src/main/resources/1602249098889.png");
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition","attachment; filename=123.png");
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(file.getInputStream()));
}
}
2.3、spring boot + redis
spring boot在1.x.x的版本时默认使用的jedis客户端,
现在是2.x.x版本默认使用的lettuce客户端。
区分一下这两:
- Jedis :直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
- Lettuce:是基于Netty的,连接实例可以在多个线程间并发访问,因为StatefulRedisConnection是线程安全的,所以一个连接实例可以满足多线程并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。lettuce主要利用netty实现与redis的同步和异步通信。
2.3.1、redis安装
redis官方文档:http://redis.cn/documentation.html
windows下安装redis
下载地址:https://github.com/tporadowski/redis/releases/download/v5.0.9/Redis-x64-5.0.9.zip
备用(下载zip包):https://github.com/tporadowski/redis/releases
下载完解压出来的目录下执行启动命令
redis-server.exe redis.windows.conf
redis的可视化管理工具: Redis Desktop Manager
安装包:https://pan.baidu.com/s/1Jvr9MbgFn4UJh4M1AMo3gA
提取码:3i9b
除此之外还可以将redis加入windows系统,开机自启
安装redis服务
redis-server --service-install redis.windows.conf
检查安装是否成功,搜索redis服务
点击设置为开机自启
2.3.2、SpringBoot+ Redis 存数据
系统:windows
开发工具:idea
项目管理工具:maven
配置文件:yaml
这里使用的是 springboot 2.X.X ,Lettuce客户端
新建spring’项目(只勾选一个web的依赖就行)
再手动加入redis的依赖
<!--redis依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置完就可以写一个测试类试试
package com.haha.bootandredis;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
@SpringBootTest
class BootandredisApplicationTests {
@Autowired
StringRedisTemplate stringRedisTemplate; //操作key-value都是字符串,最常用
@Test
public void test01(){
//字符串操作
stringRedisTemplate.opsForValue().append("msg","coder");
//列表操作
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
}
}
如果引入了mysql的依赖必须加MySQL的配置内容,否则会报错
java.lang.IllegalStateException: Failed to load ApplicationContext
启动,运行
然后查看redis,多了两个我们新插入的内容
对于Redis的五大常用数据类型都提供了方法
String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
stringRedisTemplate.opsForValue();[String(字符串)]
stringRedisTemplate.opsForList();[List(列表)]
stringRedisTemplate.opsForSet();[Set(集合)]
stringRedisTemplate.opsForHash();[Hash(散列)]
stringRedisTemplate.opsForZSet();[ZSet(有序集合)]
2.3.3、springboot + mybatis +redis
插入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
配置mybatis和redis
注意redis端口、mysql端口、库名、账号密码
#redis配置
#Redis服务器地址
spring:
redis:
host: 127.0.0.1
#Redis服务器连接端口
port: 6379
#Redis数据库索引(默认为0)
database: 0
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useSSL=true&serverTimezone=GMT
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
在test库里新建表user
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8;
INSERT INTO user VALUES (1,"lili","20200909",1,"aaaaa");
INSERT INTO user VALUES (2,"yaya","20200910",2,"bbbbb");
新建一个实体类user
import java.io.Serializable;
import java.util.Date;
public class user implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "user{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
新建UserMapper类
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(int id);
}
自定义序列化类MyRedisConfig,将存储在Redis的对象序列化为json格式,不会产生乱码
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
@Configuration
@EnableAutoConfiguration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, User> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, User> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<User> ser = new Jackson2JsonRedisSerializer<>(User.class);
template.setDefaultSerializer(ser);
return template;
}
}
工具类RedisUtil
里面写了三个功能 判断是否存在key、从redis中获取值、向redis插入值
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
//工具类中使用Autowired注解需要加上Compoent
@Component
public class RedisUtil {
@Autowired
RedisTemplate redisTemplate; //key-value是对象的
//判断是否存在key
public boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
//从redis中获取值
public Object get(String key){
return redisTemplate.opsForValue().get(key);
}
//向redis插入值
public boolean set(final String key,Object value){
boolean result = false;
try{
redisTemplate.opsForValue().set(key,value);
result = true;
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
service接口:IUserService
@Service
public interface IUserService {
//根据id查用户信息
User findById(int id);
}
UserService实现类:UserServiceImpl
@Service
public class UserServiceImpl implements IUserService {
User user;
@Autowired
UserMapper userMapper;
@Autowired
private RedisUtil redisUtil;
@Override
public User findById(int id) {
String key = "user"+id;
if(redisUtil.hasKey(key)) {
user = (User)redisUtil.get(key);
System.out.println("查询的是缓存");
}else{
user = userMapper.findById(id);
System.out.println("查询的是数据库");
System.out.println(redisUtil.set(key,user) ? "插入成功" : "插入失败");
}
return user;
}
}
控制层UserController
@RestController
public class UserController {
@Autowired
IUserService userService;
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Integer id){
User user = userService.findById(id);
return user;
}
}
运行程序(redis记得处于开启状态)
访问 http://localhost:8080/user/1
查看redis
思路整理
第一步:创建项目,加入相关依赖web、mybatis、mysql、redis
第二步:配置文件,填mysql、redis的配置信息,创建数据库、表
第三步:写数据表对应的实体类、及mapper(sql语句)
第四步:创建一个redis的配置类(实现json转化)、工具类(判断缓存存在、插入、获取)
第五步:写一个服务接口及实现类
第六步:控制类
3、gradle项目管理工具
和maven差不多,是项目管理工具
gradle不像maven那样使用xml配置,它使用Groovy
3.1、用idea创建一个gradle项目
gradle下载地址:https://services.gradle.org/distributions/
下载完成后可以将bin文件夹配置到windows的系统变量path中
idea里面集成了gradle,所以可以不用下载。
用idea新建一个gradle项目
创建完成需要稍等一会才能创建完成(第一次创建是比较慢的)
gradle的目录结构和maven基本一样
jar依赖放在build.gradle里的dependencies里
3.2、build.gradle
gradle中行尾加不加分号都一样
repositories:指定仓库位置,mavenCentral()表示使用中央仓库
如下图表示先从本地仓库找依赖,没有再从中央仓库找
repositories {
mavenLocal()
mavenCentral()
}
怎么找到依赖呢
进入maven官网,找到我们需要的依赖
3.3、修改本地仓库位置
快速修改:在系统变量里添加GRADLE_USER_HOME,值为仓库位置
我们先找到它默认的本地项目地址
该目录下caches\modules-2\files-2.1就是仓库依赖位置,我们可以将整个caches都删了。
然后区系统变量里面配置一个GRADLE_USER_HOME,位置可以为maven的本地仓库位置
配置完重启idea,再打开settings里的gradle的仓库位置就变了
3.4、打包
jar包的位置
3.5、创建web项目
创建spring mvc项目:
其他的和创建web项目都一样,多下面这几个依赖
然后添加一下tomcat
4、java与jdk版本问题
Java10以前:
Java X=Java SE X=JDK1.X
例如java 8 = java SE 8 = jdk1.8。
Java10以后:
JDK对应名称为:JDK10、JDK11、JDK12、JDK13、JDK14
到目前Java 8 (也就是jdk1.8)依然是开发者最常用的版本。(2020年9月19日)
jdk :Java Development Kit (java开发工具集)
比较特殊的是这个工具集版本1.2 ~ 1.4 ,被称为 Java SDK (软件开发包, Software Development Kit )
JRE:Java Runtime Environment(Java运行环境),它包含虚拟机,但不包含编译器
JRE并不是开发者想要的环境, 而是专门为不需要编译器的用户而提供。
java SE:Java Standard Edition(java标准版本)
java EE:Java Enterprise Edition(java企业版)
Java ME:java Micro Edition(java微环境)