依赖注入(DI)是 Spring 最核心的技术点
- Maven
Maven 是一个项目管理和构建自动化工具
Maven 使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构:
这里的 ${basedir} 代表的是 Java 工程的根路径,在我们这里就是工程的根目录啦。一个 Maven 项目在默认情况下会产生 JAR (Java 的一种压缩格式)文件,另外 ,编译后 的 classes 会放在 ${basedir}/target/classes 下面, JAR 文件会放在 ${basedir}/target 下面
- Maven命令
1、mvn clean compile
编译命令,Maven 会自动扫描 src/main/java 下的代码并完成编译工作,执行完,会在根目录下生成 target/classes 目录(存放所有的 class)
2、mvn clean package
编译并打包命令,这个命令是 compile 和 package 的集合,也就是说会先执行 compile 命令,然后在执行 jar 打包命令,这个的结果会把所有的 java 文件和资源打包成一个jar,jar 是 java 的一个压缩格式,方便我们灵活的运用多个代码
3、mvn clean install
执行安装命令,这个命令是 compile 和 package、install 的集合,也就是说会先执行 compile 命令,然后在执行 jar 打包命令,然后执行 install 命令安装到本地的 Maven 仓库目录里,这个目录是${user_home}/.m2
这个 ${user_home}
指的就是你的电脑登录用户名的个人目录
4、mvn compile exec:java -Dexec.mainClass=${main}
这个命令的意思是在 compile 执行完后,执行运行 Java 的命令,具体执行哪个 Java 类是由 -Dexec.mainClass=${main} 参数指定的,比如hello我们想执行 com.hello.Test类,那么这个完整的命令就是
mvn compile exec:java -Dexec.mainClass=com.hello.Test
- Maven配置
pom.xml(Project Object Model)
一般我们会把别人写的代码库称为三方库,自己、团队写的称为二方库;
阿里云镜像服务器 https://maven.aliyun.com/mvn/search
- Annotation (注解)
1、Target
java.lang.annotation.Target
自身也是一个注解,它只有一个数组属性,用于设定该注解的目标范围,比如说可以作用于类或者方法等。因为是数组,所以可以同时设定多个范围
2、Retention
3、Documented
4、@interface
5、Annotation 属性
- Spring Bean
IoC(Inversion of Control,控制反转) 容器是 Spring 框架最最核心的组件,没有 IoC 容器就没有 Spring 框架。
房屋中介 IoC
1. 找中介 ----> 1. 找IoC容器
2. 中介介绍房子 ----> 2. 找IoC容器
3. 租房、入住 ----> 3. 使用对象
Spring 主要有两种配置元数据的方式,一种是基于 XML、一种是基于 Annotation 方案的,目前主流的方案是基于 Annotation 的,Annotation 类型的 IoC 容器对应的类是
org.springframework.context.annotation.AnnotationConfigApplicationContext
如果要启动 IoC 容器,可以运行下面的代码
ApplicationContext context =
new AnnotationConfigApplicationContext("fm.douban");
- 自动注入(Autowired)
前提当前类是SpringBean
- Spring Rosource
在 Spring 当中定义了一个 org.springframework.core.io.Resource 类来封装文件,这个类的优势在于可以支持普通的 File 也可以支持 classpath 文件。
//创建接口
public interface FileService {
String getContent(String name);
}
//实现类
import fm.douban.service.FileService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
@Service
public class FileServiceImpl implements FileService {
@Autowired
private ResourceLoader loader;
@Override
public String getContent(String name) {
try {
InputStream in = loader.getResource(name).getInputStream();
return IOUtils.toString(in,"utf-8");
} catch (IOException e) {
return null;
}
}
}
//调用
FileService fileService = context.getBean(FileService.class);
//读取resources目录下文件
String content = fileService.getContent("classpath:data/urls.txt");
System.out.println(content);
//读取工程目录下文件
String content2 = fileService.getContent("file:mywork/readme.md");
System.out.println(content2);
//抓取网站
String content2 = fileService.getContent("https://www.zhihu.com/question/34786516/answer/822686390");
System.out.println(content2);
- SpringBean的生命周期(Lifecycle)
通过添加注释@PostConstruct声明方法init,就可以使该方法在 Spring Bean 启动后会自动执行
- SpringMVC
- SpringController
基本上所有的网页加载都是这样的一个过程。在 Spring Boot 方案里,一个网页请求到了服务器后,首先我们进入的是 Java Web 服务器,然后进入到 Spring Boot 应用,最后匹配到某一个 Spring Controller (这其实也是一个 Spring Bean),然后路由到具体某一个 Bean 的方法,执行完后返回结果,输出到客户端来。
- RequestMapping注解
完成路由配置
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloControl {
@RequestMapping("/hello")
public String say(){
return "html/hello.html";
}
}
- Get Request
获取 Http URL 参数: 用@RequestParam注解
public String index( @RequestParam("id") String id)
public String index(@RequestParam("id") String id, @RequestParam("pageNum") int pageNum)
用@ResponseBody
注解Spring MVC 会自动的把对象转化成 JSON 字符串输出到网页了,这也是 Spring MVC 比较强大和方便的地方,一般我们会把这种输出JSON数据的方法称为 API
- Thymeleaf
Thymeleaf 是一个模板框架,它可以支持多种格式的内容动态渲染非常强大,它天然和 HTML 是相融合的,Thymeleaf 模板文件也是以 html 作为文件格式的
Spring MVC 把页面数据层封装的非常完善,只需要我们在方法参数里引入一个Model对象,就可以通过这个 Model 对象传递数据到页面中了。
@Controller
public class SongListControl {
@Autowired
private SongListService songListService;
@RequestMapping("/songlist")
public String index(@RequestParam("id")String id,Model model){
SongList songList = songListService.get(id);
//传递歌单对象到模板当中
//第一个 songList 是模板中使用的变量名
// 第二个 songList 是当前的对象实例
model.addAttribute("songList",songList);//添加到模板上下文
return "songList";
}
}
- Thymeleaf变量
<span th:text="${msg}">Hello</span>
这段代码的执行结果就是用 msg 变量值替换了 span 标签内的 Hello 字符串,比如说 msg 变量值是你好,那么代码渲染的结果是
<span>你好</span>
- Thymeleaf循环
th:each 代表的就是循环语句
<ul th:each="song : ${songs}">
<li th:text="${song.name}">歌曲名称</li>
</ul>
<ul th:each="song,it: ${songs}">
<li>
<span th:text="${it.count}"></span>
<span th:text="${song.name}"></span>
</li>
</ul>
- Thymeleaf表达式
Thymeleaf 表达式主要用于两种场景:
1、字符串处理
- 字符串拼接:
<span th:text="'00:00/'+${totalTime}"></span>
- 字符串拼接优化:
<span th:text="|00:00/${totalTime}|"></span>
2、数据转化
- Thymeleaf 默认集成了大量的工具类可以方便的进行数据转化,一般我们使用最多的是
dates
工具类的运用和变量不同,变量使用的是${变量名}
,工具类使用的是#{工具类}
。
<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>
dates 和 temporals 支持的方法是一样的,只是支持的类型不同,dates 支持的是 Date 类,temporals 支持的是 LocalDate 和 LocalDateTime
- 除了日期方法,
#strings
也是我们使用比较多的,支持字符串的数据处理,比如
- 内联表达式
尽管我们使用th:text
也比较方便,但是有些时候可能我们还是更喜欢直接把变量写在 HTML 中,比如这种写法
<span>Hello [[${msg}]]</span>//结果中有Hello
上面的[[变量]]这种格式就是内联表达式,支持我们直接在 HTML 中调用变量
[[]] 是用来替代 th:text 的,不是替代所有的th:标签哦
- Thymeleaf条件语句
1、以th:开头的属性,这次我们使用的是 th:if,if 表达式的值是 ture 的情况下就会执行渲染<span th:if="${user.sex == 'male'}">男</span>
2、还可以使用 th:unless 代表的是否定条件,这个语句和 if 是相反的,表达式的值是 false 情况才会执行渲染
3、th:if 条件判断除了判断 boolean 值外,Thymeleaf 还认为如下表达式为 true:
- #strings.contains
检查字符串变量是否包含片段
${#strings.contains(name,'abc')}
- strings 逻辑判断
-
Spring Validation
Spring 对于数据验证支持的也非常好,我们可以借助 Spring Validation 来处理表单数据的验证 -
Validation 注解
大多数情况下,我们建议使用 NotEmpty 替代 NotNull、NotBlank
举例:
package com.bookstore.model;
import javax.validation.constraints.*;
public class User {
@NotEmpty(message = "名称不能为 null")
private String name;
@Min(value = 18, message = "你的年龄必须大于等于18岁")
@Max(value = 150, message = "你的年龄必须小于等于150岁")
private int age;
@NotEmpty(message = "邮箱必须输入")
@Email(message = "邮箱不正确")
private String email;
// standard setters and getters
}
- SpringBoot
报错
Field subjectService in fm.douban.app.control.SongListControl required a bean of type 'fm.douban.service.SubjectService' that could not be found.
@SpringBootApplication(scanBasePackages={
"fm.douban.app", "fm.douban.service"})
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
- Spring Boot Logger的应用
配置中可设置日志级别,如
logging.level.fm.douban.app=info
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
@RestController
public class SongListControl {
private static final Logger LOG = LoggerFactory.getLogger(SongListControl.class);
@PostConstruct
public void init(){
LOG.info("SongListControl 启动啦");
}
}
- 配置文件
application.properties
通过@Value
注解读取配置内容,例如:
song.name=God is a girl
import org.springframework.beans.factory.annotation.Value;
public class SongListControl {
@Value("${song.name}")
private String songName;
}
只需要使用 @Value 注解即可,注意写法,花括号中的配置项名称,与配置文件中保持一致即可。
- Spring Session
- 读Cookie
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@RequestMapping("/songlist")
public Map index(HttpServletRequest request) {
Map returnData = new HashMap();
returnData.put("result", "this is song list");
returnData.put("author", songAuthor);
Cookie[] cookies = request.getCookies();
returnData.put("cookies", cookies);
return returnData;
}
- 写Cookie
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@RequestMapping("/songlist")
public Map index(HttpServletResponse response) {
Map returnData = new HashMap();
returnData.put("result", "this is song list");
returnData.put("name", songName);
Cookie cookie = new Cookie("sessionId","CookieTestInfo");
// 设置的是 cookie 的域名,就是会在哪个域名下生成 cookie 值
cookie.setDomain("baidu.com");
// 是 cookie 的路径,一般就是写到 / ,不会写其他路径的
cookie.setPath("/");
// 设置cookie 的最大存活时间,-1 代表随浏览器的有效期,也就是浏览器关闭掉,这个 cookie 就失效了。
cookie.setMaxAge(-1);
// 设置是否只能服务器修改,浏览器端不能修改,安全有保障
cookie.setHttpOnly(false);
response.addCookie(cookie);
returnData.put("message", "add cookie successfule");
return returnData;
}
-
Session
Cookie放在客户端,Session放在服务器端,相比之下信息会更安全
-
读操作
登录信息类
import java.io.Serializable;
public class UserLoginInfo implements Serializable {
private String userId;
private String userName;
}
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@RequestMapping("/songlist")
public Map index(HttpServletRequest request, HttpServletResponse response) {
Map returnData = new HashMap();
returnData.put("result", "this is song list");
// 取得 HttpSession 对象
HttpSession session = request.getSession();
// 读取登录信息
UserLoginInfo userLoginInfo = (UserLoginInfo)session.getAttribute("userLoginInfo");
if (userLoginInfo == null) {
// 未登录
returnData.put("loginInfo", "not login");
} else {
// 已登录
returnData.put("loginInfo", "already login");
}
return returnData;
}
- 写操作
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@RequestMapping("/loginmock")
public Map loginMock(HttpServletRequest request, HttpServletResponse response) {
Map returnData = new HashMap();
// 假设对比用户名和密码成功
// 仅演示的登录信息对象
UserLoginInfo userLoginInfo = new UserLoginInfo();
userLoginInfo.setUserId("12334445576788");
userLoginInfo.setUserName("ZhangSan");
// 取得 HttpSession 对象
HttpSession session = request.getSession();
// 写入登录信息
session.setAttribute("userLoginInfo", userLoginInfo);
returnData.put("message", "login successfule");
return returnData;
}
- SpringRequest 拦截器
Spring提供的一种统一处理相同逻辑的机制
实现拦截器有三个步骤:
1、创建拦截器
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class InterceptorDemo implements HandlerInterceptor {
// Controller方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 只有返回true才会继续向下执行,返回false取消当前请求
return true;
}
//Controller方法执行之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
// 整个请求完成后(包括Thymeleaf渲染完毕)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2、实现 WebMvcConfigurer
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebAppConfigurerDemo implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 多个拦截器组成一个拦截器链
// 仅演示,设置所有 url 都拦截
registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**");
}
}
通常拦截器,会放在一个包(例如interceptor)里。而用于管理拦截器的配置类,会放在另一个包(例如config)里。
- 页面跳转
- MongoDB数据库
对数据库的操作一定要放在 @Service 类中,而不是放在 @Controller 类中;且 @Controller 类可以调用 @Service 类的方法,反之则不行。这是 SpringMVC 的经典架构设计理念
- 增
import org.springframework.data.mongodb.core.MongoTemplate;
@Autowired
private MongoTemplate mongoTemplate;
public void test() {
Song song = new Song();
song.setSubjectId("s001");
song.setLyrics("...");
song.setName("成都");
mongoTemplate.insert(song);
}
- 查
mongoTemplate.findById(songId, Song.class)
- 改
// 修改 id=1 的数据
Query query = new Query(Criteria.where("id").is("1"));
// 把歌名修改为 “new name”
Update updateData = new Update();
updateData.set("name", "new name");
// 执行修改,修改返回结果的是一个对象
UpdateResult result = mongoTemplate.updateFirst(query, updateData, Song.class);
// 修改的记录数大于 0 ,表示修改成功
System.out.println("修改的数据记录数量:" + result.getModifiedCount());
- 删
Song song = new Song();
song.setId(songId);
// 执行删除
DeleteResult result = mongoTemplate.remove(song);
// 删除的记录数大于 0 ,表示删除成功
System.out.println("删除的数据记录数量:" + result.getDeletedCount());
- 条件查找
// 条件对象构建查询对象
Query query = new Query(criteria);
List<Song> songs = mongoTemplate.find(query, Song.class);
return songs;