什么是微服务?
“火车模型”
微服务优缺点
Spring IoC介绍
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User
{
private String name;
private Integer id;
}
package com.heyexi.helloworld.config;
import com.heyexi.helloworld.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfigBean
{
@Bean //代表注入一个Bean 用这个注解代码着Spring将接管这个Bean
public User getUser()
{
return new User("何夜息",1); //返回我们的User对象
}
}
@SpringBootTest
class HelloworldApplicationTests
{
@Autowired //自动装配注解从IOC取出相应类型的注入对象
private User u;
@Test
void contextLoads()
{
System.out.println(u); //输出
}
}
2020-05-14 19:30:34.104 INFO 19140 --- [ main] c.h.h.HelloworldApplicationTests : Starting HelloworldApplicationTests on 何夜息 with PID 19140 (started by Administrator in E:\source\java\SpringBoot\helloworld)
2020-05-14 19:30:34.105 INFO 19140 --- [ main] c.h.h.HelloworldApplicationTests : No active profile set, falling back to default profiles: default
2020-05-14 19:30:36.495 INFO 19140 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-14 19:30:36.624 INFO 19140 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
2020-05-14 19:30:36.929 INFO 19140 --- [ main] c.h.h.HelloworldApplicationTests : Started HelloworldApplicationTests in 3.575 seconds (JVM running for 6.88)
User(name=何夜息, id=1)
@Configuration
public class ConfigBean
{
@Bean
public User getUser()
{
return new User("何夜息",1);
}
@Bean
public String uname()
{
return "我是何夜息"; //这个我们直接通过@AutoWire获取
}
@Bean
public String zhangsan() //这个我们通过方法名获取,因为默认Bean的名称时方法名
{
return "我是张三";
}
@Bean(name = "lisi")
public String uname3()
{
return "我是李四"; //这个我们通过bean的别名获取
}
}
@SpringBootTest
class HelloworldApplicationTests
{
@Autowired
private User u;
@Autowired
private String uname;//这里好像只能用uname,因为有多个返回值为String的Bean
@Autowired
@Qualifier("zhangsan") //通过方法名获取
private String zs;
@Autowired
@Qualifier("lisi") //通过bean的name属性获取
private String ls;
@Test
void contextLoads()
{
System.out.println(uname);
System.out.println(zs);
System.out.println(ls);
System.out.println(u);
}
}
2020-05-14 20:01:42.862 INFO 18996 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
2020-05-14 20:01:43.201 INFO 18996 --- [ main] c.h.h.HelloworldApplicationTests : Started HelloworldApplicationTests in 4.006 seconds (JVM running for 6.573)
我是何夜息
我是张三
我是李四
User(name=何夜息, id=1)
2020-05-14 20:01:43.857
Spring Boot 核心功能
Spring Boot的优缺点
1)优点
2)缺点
pom.xml中引入依赖
注解详情:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //使用date注解后不需在下面定义set和get方法,可以直接调用get和set还有toString方法
@AllArgsConstructor //提供所有参数的构造器
@NoArgsConstructor //提供无参数的构造器吧
public class User
{
private int id;
private String name;
private int age;
private String qq;
private String address;
}
package com.com.heyexi.controller;
import com.heyexi.entity.*;
public class Test
{
public static void main(String[] args)
{
User zhangsan = new User();
zhangsan.setAddress("云南昆明");
zhangsan.setAge(18);
System.out.println(zhangsan.toString());
User lisi = new User(1,"李四",18,"18548787","北京");
System.out.println(lisi.toString());
}
}
@Configuration 注解相当于一个spring的xml文件配置,通常和@Bean注解一起使用,@Bean注解是代表一个JavaBean。
package com.heyexi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.heyexi.entity.*;
@Configuration //相当于是一个xml配置文件
public class JavaConfigA
{
@Bean
public User getUser()
{
return new User(1,"李四",22,"14874","杭州");
}
}
SpringBoot的核心:自动装配
注意:包必须建在主程序入口同级目录,不然无法识别
package com.heyexi.helloworld.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Hello
{
//http://localhost:8080/hello 这个是接口
@RequestMapping("/hello")
public String hello()
{
return "hello,world";
}
}
在应用配置中可以修改端口号
启动器:这些启动器会自动引用需要的配置,比如spring-boot-starter-web就是负责启动web环境,自动启动Tomcat,达到了自动装配的功能。
org.springframework.boot
spring-boot-starter-web
SpringBoot使用一个全局的配置文件,配置文件名也是固定的:
application.properties 或者是application.yaml
yaml语法:
key: 值 ----->>>> 冒号后面必须跟一个空格
# 普通
name: 何夜息
# 对象
Student:
name: zhangsan
age: 18
# 行内写法
Teacher: {name: kingsley,age: 20}
# 数组
books: [家,春,秋,飘,挪威的森林]
首页的设定
templates是模板目录,这里的文件不能直接被访问,只能通过控制器来访问。
模板引擎
模板引擎:模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。
简单来说,就是模板文件,也就是PHP中的html文件,里面有些数据绑定的地方,我们使用{$xxx}来标记,这就是前端页面。而需要的数据则来自后台,然后通过模板引擎,把这个模板文件和数据重新组合,得到最终的html文档显示给用户,这就是模板引擎的功能。
用途:
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。
遇到的小问题,浏览器调用“test”时没有跳到模板文件test.html中
@RestController
public class TestController
{
@RequestMapping("/test")
public String test()
{
return "test";
}
}
原因是@RestController返回的是json对象,而不是跳到模板文件。需要改为
@Controller
public class TestController
{
@RequestMapping("/test")
public String IsTest()
{
return "test";
}
}
为什么能跳到test.html文件中呢?不是和这个类的名字有关,和tp5一样,和return后面的字符串有关,会吧"test"字符串自动通过thymeleaf渲染成"test.html",这个文件会去"templates"目录中去找。
要使用thymeleaf模板引擎,需要在pom.xml中导入thymeleaf的依赖。
org.springframework.boot
spring-boot-starter-thymeleaf
控制器中:
@Controller
public class TestController
{
@RequestMapping("/test")
public String IsTest(Model model)
{
model.addAttribute("name","何夜息");
return "test";
}
}
模板文件中:
LinkedList
@RequestMapping("/hd_login")
public String hd_login(@RequestParam("uname") String name, @RequestParam("pwd") String pwd, Model model)
{
if(!name.isEmpty() && !pwd.isEmpty())
{
model.addAttribute("name",name);
model.addAttribute("pwd",pwd);
}else {
model.addAttribute("name","空空如也");
model.addAttribute("pwd","nothing");
}
return "index";
}
/* 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);
/* 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);
/***静态资源处理**/
void addResourceHandlers(ResourceHandlerRegistry registry);
/* 默认静态资源处理器 */
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
/*** 这里配置视图解析器**/
void configureViewResolvers(ViewResolverRegistry registry);
/* 配置内容裁决的一些选项*/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer
{
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/nice").setViewName("/login");
}
}
public class LoginInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
return false;
}
}
public class LoginInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
//获取用户session
Object user = request.getSession().getAttribute("user");
if(user==null)
{
request.setAttribute("msg","登录失败!");
request.getRequestDispatcher("{/index.html}").forward(request,response);
return false;
}else {
return true;
}
}
}
如上:request.getRequestDispatcher("{/index.html}").forward(request,response);是代表跳转页面,getRequestDispatcher是代表要跳转的网址,forward()是代表继续转发或者说是携带request,response中的数据给转发后的网址继续处理。
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer
{
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/nice").setViewName("/login");
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/test").excludePathPatterns("/login");
}
//这其中呢,addPathPatterns是代表要拦截的页面,也就是说,拦截器没有放行之前
//这个页面是无法进行访问,只有放行后才能访问。
//excludePathPatterns("/login");方法代表除了...,也就是这个页面可以访问。
}
function login() {
if(check_regist()) {
var uname = document.getElementById("user_name").value;
var pwd = document.getElementById("pwd").value;
$.ajax({
url:'/adduser',
type:'post',
data:{name:uname,pwd:pwd},
success:function (res) {
},
error:function (res) {
alert("获取数据失败!");
}
});
}
}
然后在控制器里我写了如下:
@RestController
public class AddUserController
{
@Autowired
private Users user;
@Autowired
private UserDao userDao;
@PostMapping("/adduser")
@ResponseBody
public Object adduser(@RequestParam("name") String name,@RequestParam("pwd") String pwd)
{
//先查询是否存在用户
user = userDao.findUser(name);
if(user != null)
{
System.out.println(user.getU_name());
System.out.println("用户存在");
}else {
System.out.println("用户不存在");
}
return user;
}
遇到问题:
今天遇到一个SQL问题:org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
问题出在这句代码上:
Map
这是查询数据记录的,后来百度了才知道,jdbcTemplate的queryForMap方法是查询数据中一条记录的,而且这条记录必须存在!不然就报错了,又是一个坑!
Incorrect result size: expected 1, actual 0 -》 不准确的结果大小,预期是1,结果是0,代表了返回为空,这个必须返回是1,如果有多条记录机会变成 Incorrect result size: expected 1, actual x,其中x就是代表返回的结果数。
解决办法就是老老实实用如下代码
try
{
SqlRowSet res = jdbcTemplate.queryForRowSet(sql);
while (res.next())
{
user.setU_id(res.getInt("u_id"));
user.setU_name(res.getString("u_name"));
user.setU_gender(res.getInt("u_gender"));
user.setU_wxid(res.getString("u_wxid"));
user.setU_pwd(res.getString("u_pwd"));
user.setU_rdate(String.valueOf((res.getString("u_rdate"))));
return user;
}
return null;
}catch (Exception e)
{
e.printStackTrace();
return null;
}
又遇坑:
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/").setViewName("/index");
}
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/").setViewName("/view/index");
}
注意:SpringBoot中控制器返回页面,页面必须还有thymeleaf的引用,不然普通网页的话,控制器不能跳转到改页面。
又是记录bug的一天:在Windows下控制器的跳转页面可以写成return "/view/**",但是发布到linux服务器却出现了问题,所以应该写成"return "view/**",也就是吧前面的/去掉,可以是路径的问题吧。还有就是前端问题,有一个标签是textarea文本域标签,如果想显示placeholder内容,这个标签之间不能有内容,因为它会把里面的空格也当做textarea的内容。