为什么要使用 Spring Boot
因为 Spring,SpringMVC 需要使用的大量的配置文件 (xml文件)
还需要配置各种对象,把使用的对象放入到 spring 容器中才能使用对象
需要了解其他框架配置规则。
SpringBoot 就相当于 不需要配置文件的 Spring+SpringMVC。 常用的框架和第三方库都已经配置好了。
拿来就可以使用了。
SpringBoot开发效率高,使用方便多了
使用 java 类作为 xml 配置文件的替代, 是配置 spring 容器的纯 java 的方式。
在这个 java 类中可以创建 java 对象,把对象放入 spring 容器中(注入到容器)。
对于此JavaConfig相关的示例创建的是普通的Java项目,并非SpringBoot项目!
@Configuration@Bean@Bean中的 name 属性,则从容器中获取该对象需要用其方法名@Bean中的 name 属性,则从容器中获取该对象需要用配置的 name 名称需要使用到两个注解:
@Configuration:放在一个类的上面,表示这个类是作为配置文件使用的。@Bean:放在配置类中的方法上,声明对象,把对象注入到容器中。配置类示例:
/**
* Configuration:表示当前类是作为配置文件使用的。 就是用来配置容器的
* 位置:在类的上面
*
* SpringConfig这个类就相当于beans.xml
*/
@Configuration
public class SpringConfig {
/**
* 创建方法,方法的返回值是对象。 在方法的上面加入@Bean
* 方法的返回值对象就注入到容器中。
*
* @Bean: 把对象注入到spring容器中。 作用相当于<bean>
*
* 位置:方法的上面
*
* 说明:@Bean,不指定对象的名称,默认是方法名是 id
*
*/
@Bean
public Student createStudent(){
Student s1 = new Student();
s1.setName("张三");
s1.setAge(26);
s1.setSex("男");
return s1;
}
/***
* 指定对象在容器中的名称(指定<bean>的id属性)
* @Bean的name属性,指定对象的名称(id)
*/
@Bean(name = "lisiStudent")
public Student makeStudent(){
Student s2 = new Student();
s2.setName("李四");
s2.setAge(22);
s2.setSex("男");
return s2;
}
}
测试:
public class MyTest {
@Test
public void test01() {
// 使用配置文件方式声明的bean,需要用 ClassPathXmlApplicationContext,参数传配置文件路径
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("myStudent");
System.out.println(student);
}
@Test
public void test02() {
// 使用配置类方式将对象注入到容器,需要使用 AnnotationConfigApplicationContext,参数传配置类的class
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
// 配置类中方法的Bean设置name属性,则通过name属性名获取对象;否则默认可通过其方法名获取
Student s = (Student) ctx.getBean("lisiStudent");
// Student s = (Student) ctx.getBean("makeStudent");
// Student s = (Student) ctx.getBean("createStudent");
System.out.println(s);
}
}
小结:
@Configuration;需要在配置类中的方法上使用@Bean声明对象,将对象注入到容器中。@Bean中的 name 属性,则获取该对象时需要使用方法名获取;若配置了@Bean中的 name 属性,则通过配置的 name 名称获取该对象。ClassPathXmlApplicationContext接口实现类,不再通过配置文件来注入到容器对象中;而是使用AnnotationConfigApplicationContext接口实现类,通过配置类的 class 来将对象注入到容器。@ImportResource<import resources="其他配置文件"/>@Configuration
@ImportResource(value = "classpath:applicationContext.xml")
public class SpringConfig {
}
@PropertySource@ComponentScan:使用在配置类上,扫描指定包中注解,来创建对象以及给属性赋值
示例:
--配置类--
@Configuration
@ImportResource(value = "classpath:applicationContext.xml") // 导入xml配置文件
@PropertySource(value = "classpath:config.properties") // 读取属性配置文件
@ComponentScan(basePackages = "com.luis.vo") // 声明组件扫描器,扫描指定包中的注解
public class SpringConfig {
}
--config.properties--
tiger.name=东北虎
tiger.age=3
--vo数据类--
@Component("tiger")
public class Tiger {
@Value("${tiger.name}")
private String name;
@Value("${tiger.age}")
private Integer age;
}
--测试--
@Test
public void test04() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
Tiger tiger = (Tiger) ctx.getBean("tiger");
System.out.println(tiger);
}
SpringBoot 是 Spring 中的一个成员,可简化 Spring,SpringMVC 的使用。
其核心还是 IOC 容器。
Create stand-alone Spring applications
创建 spring 应用
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
有内嵌的 tomcat,jetty,Undertow
Provide opinionated 'starter' dependencies to simplify your build configuration
提供了 starter 起步依赖,简化应用的配置。
比如使用 MyBatis 框架,需要在 Spring 项目中,配置 MyBatis 的对象 SqlSessionFactory ,Dao 的代理对象
如今在 SpringBoot 项目中,在 pom.xml 里面,只需加入一个 mybatis-spring-boot-starter 依赖即可
Automatically configure Spring and 3rd party libraries whenever possible
尽可能去配置 spring 和第三方库,也叫做自动配置(就是把 spring 中的,第三方库中的对象都创建好,放到容器中,开发人员可以直接使用)
Provide production-ready features such as metrics, health checks, and externalized configuration
提供了健康检查,统计,外部化配置
Absolutely no code generation and no requirement for XML configuration
不用生成代码,不用使用xml,做配置
创建 SpringBoot 项目有三种方式:
使用 Spring 提供的初始化器,就是使用向导创建 SpringBoot 应用(使用 https://start.spring.io 地址)【联网】
使用 Spring 提供的初始化器,就是使用向导创建 SpringBoot 应用(使用国内的 https://start.springboot.io 地址)【联网】
还可以选择使用阿里云的地址进行创建:https://start.aliyun.com
先直接创建 maven 项目,然后补充相关依赖,补全目录结构即可【不需联网】
创建简单 SpringBoot web 项目示例:(以下选择向导方式创建)
新建 SpringBoot 模块,选择 Spring Initializr向导创建
配置项目相关信息,依赖选择 Spring Web 进行创建
整理项目结构以及 pom 文件
在主启动类所在的包下创建 controller 控制器类
@Controller
public class HelloSpringBoot {
@RequestMapping("/hello")
@ResponseBody
public String helloSpringBoot() {
return "欢迎使用SpringBoot";
}
}
查看是否配置有 Spring Boot 启动服务,若没有则进行配置
运行主启动类的 main 方法或 IDEA 主面板运行项目
浏览器输入地址访问 http://localhost:8080/hello(默认8080端口,无上下文)
@SpringBootApplication其自动配置在主类之上,每个 SpringBoot 项目都会有一个主类
@SpringBootApplication
public class SpringbootTest03Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootTest03Application.class, args);
}
}
@SpringBootApplication
是一个复合注解,包含以下三个注解(以下注解的功能其全具备)
@SpringBootConfiguration -----> 该注解标注的类可当做配置类使用
@EnableAutoConfiguration -----> 启用自动配置,将常用框架以及第三方对象创建好
@ComponentScan -----> 可扫描到该注解标注的类的包及其子包下其他注解
详解:
1.@SpringBootConfiguration
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
说明:使用了@SpringBootConfiguration注解标注的类,可以作为配置文件使用,
可以使用@Bean声明对象,注入到容器,
故主类可作为一个配置类使用,在其类中可在方法上使用@Bean声明对象
2.@EnableAutoConfiguration
启用自动配置,把java对象配置好,注入到spring容器中;例如可以把mybatis的对象创建好,放入到容器中。
3.@ComponentScan
扫描器,找到注解,根据注解的功能创建对象,给属性赋值等。
默认扫描的包: @ComponentScan所标注的类所在的包和子包。
避免中文乱码,需要在 IDEA 中设置 encoding 为 UTF-8
配置文件名: application
扩展名有:properties( k=v) 以及 yml ( k: v)
可使用 application.properties,application.yml 两种文件格式
创建 SpringBoot 项目时默认创建的是 application.properties 配置文件格式
以下为配置示例:
例1:application.properties 中设置端口和上下文
# 设置端口
server.port=8888
# 设置访问应用的上下文路径
server.servlet.context-path=/myWeb
例2:application.yml 中设置端口和上下文(推荐使用)
这种格式的配置文件,结构比较清晰
注意:value 值前有一个空格
server:
port: 8083
servlet:
context-path: /myboot
注意:如果同时存在两种格式的配置文件,即 application.properties 和 application.yml 两个配置文件同时存在,则默认使用 application.properties 这种格式中的配置!
有开发环境,测试环境,上线的环境。
每种环境有不同的配置信息,例如端口,上下文访问路径,数据库 url,用户名,密码等,所以需要多种环境配置。
使用多环境配置文件,可以方便的切换不同的配置。
多环境配置方式:
创建多个配置文件,文件命名规则:application-环境名称.properties(yml)【一般环境名使用 dev/test/online 这几种】
创建开发环境的配置文件:application-dev.yml
#项目开发使用的环境
server:
port: 8080
servlet:
context-path: /mydev
创建测试环境的配置文件:application-test.yml
#项目测试使用的环境
server:
port: 8081
servlet:
context-path: /mytest
创建上线环境的配置文件:application-online.yml
#项目上线使用的环境
server:
port: 8082
servlet:
context-path: /myonline
在主配置文件 application.yml 中激活要使用的环境【激活的配置信息是根据多环境文件的命名来确定】
主配置文件中激活要使用的环境:application.yml
#激活使用的环境
spring:
profiles:
active: online
我们可以在主配置文件中使用 key-value 键值对的格式进行数据的配置;
因为主配置文件已经由 Spring 自动管理,所以可以直接在自己写的控制器类的属性上通过 @Value("${server.port}")读取配置文件中数据,并对属性进行赋值。
@Controller
public class MyController {
@Value("${server.port}")
private Integer port;
@Value("${server.servlet.context-path}")
private String contextPath;
@Value("${project.name}")
private String projectName;
@Value("${project.dev}")
private String projectDev;
@RequestMapping("/data")
@ResponseBody
public String queryData() {
return port + "========> port " + contextPath + "========> contextPath "
+ projectName + "========> projectName " + projectDev + "========> projectDev";
}
}
@ConfigurationPropertiesprefix 属性:即配置文件中的某些 key 开头的内容使用示例:@ConfigurationProperties(prefix = "school")
使用解释:类上添加@ConfigurationProperties注解后,其将从属性配置文件中找以配置的 prefix 属性值,即school开头的配置,将所有 school后面的配置参数与该类中的所有属性相比较,如果匹配成功,则将配置文件中配置的对应的数据赋值给该类中同名属性,完成数据映射。
使用示例:
在配置文件 application.properties 中配置自定义数据
#配置端口号
server.port=8888
#配置上下文访问路径
server.servlet.context-path=/myboot
#自定义数据
school.name=哈师大
school.addr=东北
school.level=2
创建数据对象
@Component
@ConfigurationProperties(prefix = "school")
public class SchoolInfo {
private String name;
private String addr;
private Integer level;
// getter,setter,toString...
}
创建控制器类
@Controller
public class MyController {
@Resource
private SchoolInfo info;
@RequestMapping("/info")
@ResponseBody
public String querySchoolInfo() {
return "info = " + info;
}
}
运行,访问测试
访问:http://localhost:8888/myboot/info
结果:info = SchoolInfo{name='哈师大', addr='东北', level=2}
SpringBoot 默认不支持 JSP,需要进行相关配置才能使用 JSP。
JSP 技术慢慢被淘汰,有更好的技术替代它。
使用 SpringBoot 框架时不推荐使用 JSP,后面将使用 Thymeleaf 模板技术完全替代 JSP!
使用步骤:
创建 SpringBoot 项目,整理项目结构和 pom 文件
添加一个处理 JSP 的依赖,负责编译 JSP 文件(如果需要使用 servlet jsp jstl 功能则需要另外加对应依赖)
<!--处理JSP的依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
在 main 目录下创建一个名为 webapp 的目录(名称固定);在项目工程结构中设置该目录为 web 资源目录(进入项目结构设置后,点击 Web 选项,从 Web Resource Directories 下添加新创建的 webapp 目录为 web 资源目录即可)
在 webapp 目录下新建 index.jsp 用来显示 controller 中的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>jsp</title>
</head>
<body>
SpringBoot中使用JSP显示数据:${data}
</body>
</html>
创建 controller 控制器类,指定数据和视图
@Controller
public class JspController {
@RequestMapping("/myjsp")
public String doJsp(Model model) {
// 将数据放到request中(与request.setAttribute作用相同)
model.addAttribute("data", "SpringBoot使用JSP");
return "index";
}
}
配置文件中配置视图解析器,端口号以及上下文等信息
#配置端口号
server.port=7070
#配置上下文访问路径
server.servlet.context-path=/myboot
#配置视图解析器
# / = src/main/webapp
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
在 pom.xml 中指定 jsp 文件编译后的存放目录【在 pom 的 build 标签下进行指定】
修改 pom 文件后,记得重新加载下,确保 pom 文件资源完全刷新!
<!-- 指定jsp编译后的存放目录 -->
<resources>
<resource>
<!-- jsp原来的目录 -->
<directory>src/main/webapp</directory>
<!-- 指定编译后的存放目录 -->
<targetPath>META-INF/resources</targetPath>
<!-- 指定治理后的目录和文件 -->
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
直接运行并启动主程序,输入地址访问测试
http://localhost:7070/myboot/myjsp
两点需要注意下:
原因:因为 SpringBoot 使用的是内嵌的 Tomcat
使用场景:不想启动整个项目,只想测试某个功能,就可直接通过run方法获取容器对象,通过容器对象进行相关测试。
如果想通过代码,从容器中获取对象:
通过SpringApplication.run(Application.class, args); 其返回值即可获取容器对象。
SpringApplication.run(SpringbootUseJspApplication.class, args)返回的就是一个 ApplicationContext 容器对象!
解析:SpringApplication.run(SpringbootUseJspApplication.class, args);
点进其run方法:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
查看ConfigurableApplicationContext类:
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {...}
总结:通过run方法获取的是容器对象ApplicationContext的子类,即也是一个容器对象。
容器使用示例:
创建 SpringBoot 项目时选择 Spring Web 依赖,整理项目目录和 pom 文件
创建 service 接口及其实现类,定义测试方法
public interface UserService {
void sayHello(String name);
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
在主启动类中直接获取并使用容器对象进行测试
@SpringBootApplication
public class SpringbootUseContainerApplication {
public static void main(String[] args) {
// 直接通过run方法获取容器对象
ApplicationContext ctx = SpringApplication.run(SpringbootUseContainerApplication.class, args);
// 从容器中获取指定对象
UserService userService = (UserService) ctx.getBean("userService");
// 调用对象的方法进行测试
userService.sayHello("luis");
}
}
介绍:这两个接口都有一个 run 方法。
执行时机:容器对象创建好后自动会执行接口中的 run() 方法
使用场景:需要在容器启动后执行一些内容,比如读取配置文件,数据库连接之类的。
使用方式:实现接口,重写 run 方法,方法中写需要在容器启动后待执行的内容。
@FunctionalInterface
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
使用示例:(以下以实现 CommandLineRunner接口为例)
创建 SpringBoot 项目,不用选择依赖,整理项目目录和 pom 文件
新建 service 接口以及实现类,定义测试方法
public interface UserService {
void sayHello(String name);
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void sayHello(String name) {
System.out.println("你好," + name);
}
}
让启动类实现 CommandLineRunner接口,重写 run 方法进行测试
@SpringBootApplication
public class SpringbootRunApplication implements CommandLineRunner {
@Resource
private UserService userService;
public static void main(String[] args) {
System.out.println("main start");
// 创建容器对象
SpringApplication.run(SpringbootRunApplication.class, args);
System.out.println("main end");
}
@Override
public void run(String... args) throws Exception {
System.out.println("容器对象创建后执行此run方法");
// 此方法在容器对象创建后自动执行
// 可在此调用容器对象中的方法
userService.sayHello("jack");
}
}
运行主程序,进行测试
运行结果:
main start
容器对象创建后执行此run方法
你好,jack
main end
讲三个内容:拦截器、Servlet、Filter
拦截器是 SpringMVC 中的一种对象,能拦截对 Controller 的请求。
在框架中有系统实现好的拦截器, 也可以自定义拦截器,实现对请求的预先处理。
创建类实现 SpringMVC 框架的 HandlerInterceptor 接口,重写对应的方法
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
在 SpringMVC 的配置文件中,声明拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:path="url" />
<bean class="拦截器类全限定名称"/>
</mvc:interceptor>
</mvc:interceptors>
示例:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录和 pom 文件
创建拦截器类,实现 HandlerInterceptor接口,重写相关方法
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行LoginInterceptor拦截器类的preHandle方法");
return true;
}
}
创建自定义配置类实现 WebMvcConfigurer接口,重写addInterceptors接口方法,在此方法中进行拦截器的相关配置(注入拦截器对象,指定拦截地址等)
@Configuration // 成为配置类=====
public class MyAppConfig implements WebMvcConfigurer {
// 重写addInterceptors方法,添加拦截器对象,注入到容器中
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 创建拦截器对象
HandlerInterceptor loginInterceptor = new LoginInterceptor();
// 指定拦截的请求URI地址
String[] path = {"/user/**"};
// 指定不拦截的URI地址
String[] excludePath = {"/user/login"};
// 添加拦截器对象,指定拦截以及不拦截的URI地址
registry.addInterceptor(loginInterceptor)
.addPathPatterns(path)
.excludePathPatterns(excludePath);
}
}
创建控制器类,进行资源访问拦截测试
@Controller
@RequestMapping("/user")
public class BootController {
@RequestMapping("/account")
@ResponseBody
public String userAccount() {
return "访问/user/account地址";
}
@RequestMapping("/login")
@ResponseBody
public String userLogin() {
return "访问/user/login地址";
}
}
运行主程序,进行访问测试
分别访问下列两个地址:
http://localhost:8080/user/account
http://localhost:8080/user/login
访问的测试结果:
访问http://localhost:8080/user/account时,后台有拦截器拦截信息输出【此请求走拦截器,被拦截器拦截】
访问http://localhost:8080/user/login时,后台无拦截器拦截信息输出【此请求不走拦截器,不会被拦截器拦截】
使用步骤:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录和 pom 文件
创建 servlet 类,继承 HttpServlet,重写相关方法
public class MyServlet extends HttpServlet {
// 直接重写service方法,则doGet和doPost请求都直接走此方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置字符编码
response.setContentType("text/html;charset=UTF-8");
// 使用应答对象HttpServletResponse,输出应答结果
PrintWriter out = response.getWriter();
out.print("SpringBoot中使用Servlet");
out.flush();
out.close();
}
}
创建自定义配置类,在类中定义指定返回值类型的方法,注册 Servlet 对象
@Configuration
public class WebApplicationConfig {
// ====定义指定返回值类型的方法,注册servlet对象====
@Bean
public ServletRegistrationBean servletRegistrationBean() {
// 两种方式注册servlet:
// 方式一:使用ServletRegistrationBean对象的有参构造
// 下列对象构造方法参数:第一个是需要注册的servlet对象,第二个是对应servlet的访问地址
// ServletRegistrationBean bean = new ServletRegistrationBean(new MyServlet(), "/login1", "/login2");
// 方式二:使用ServletRegistrationBean对象的无参构造
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyServlet());
bean.addUrlMappings("/login3", "/login4");
return bean;
}
}
运行主程序,输入地址访问测试
http://localhost:8080/login1
http://localhost:8080/login2
http://localhost:8080/login3
http://localhost:8080/login4
Filter 是 Servlet 规范中的过滤器,可以处理请求,对请求的参数,属性进行调整。
常常在过滤器中处理字符编码。
SpringBoot 中使用过滤器 Filter:
使用步骤:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录和 pom 文件,刷新 pom
创建自定义的过滤器类,实现 javax.servlet.Filter 接口,重写 doFilter 方法(注意实现的是 servlet 中的 Filter 接口)
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行自定义过滤器类MyFilter中的doFilter方法");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
创建自定义配置类,在类中定义指定返回值类型的方法,注册过滤器对象
@Configuration
public class WebApplicationConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
// 注册自定义过滤器类,并指定过滤的地址
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new MyFilter());
bean.addUrlPatterns("/user/*");
return bean;
}
}
创建控制器类,进行资源访问过滤测试
@Controller
public class CustomFilterController {
@RequestMapping("/user/login")
@ResponseBody
public String userLogin() {
return "访问/user/login";
}
@RequestMapping("/query")
@ResponseBody
public String query() {
return "访问/query";
}
}
运行主启动类,输入访问地址测试
http://localhost:8080/query
http://localhost:8080/user/login
一般,都是使用字符集过滤器 CharacterEncodingFilter解决 post 请求中文乱码的问题。
CharacterEncodingFilter : 解决 post 请求中文乱码的问题。
在 SpringMVC 框架, 需要在 web.xml 注册过滤器并配置其相关属性。
在SpringBoot 中使用字符集过滤器解决 post 请求乱码有两种方式:
使用示例:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录和 pom 文件,刷新 pom
创建自定义 servlet,处理请求
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html"); // 此处并没有设置字符编码方式
PrintWriter out = response.getWriter();
out.write("没有设置编码方式,默认使用ISO-8859-1,会出现中文乱码");
out.flush();
out.close();
}
}
创建自定义配置类,注册 servlet,设置访问路径
@Configuration
public class WebSystemConfig {
// 注册servlet,设置访问路径
@Bean
public ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyServlet());
bean.addUrlMappings("/myservlet");
return bean;
}
}
启动主程序,输入访问地址进行测试
http://localhost:8080/myservlet
结果:出现中文乱码
?????????????ISO-8859-1????????
不仅需要在配置类中注册字符集过滤器类,设置字符编码,还需在配置文件中关闭默认启用的过滤器。
CharacterEncodingFilter字符集过滤器类,设置字符编码@Configuration
public class WebSystemConfig {
// 注册servlet,设置访问路径
@Bean
public ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyServlet());
bean.addUrlMappings("/myservlet");
return bean;
}
// ==================================================================
// 注册CharacterEncodingFilter,并设置字符编码方式
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean reg = new FilterRegistrationBean();
CharacterEncodingFilter filter = new CharacterEncodingFilter();
// 设置字符编码方式
filter.setEncoding("UTF-8");
// 指定request,response都使用encoding的值
filter.setForceEncoding(true);
reg.setFilter(filter); // 注册字符集过滤器对象CharacterEncodingFilter
reg.addUrlPatterns("/*"); // 设置过滤地址
return reg;
}
// ==================================================================
}
修改 application.properties 文件,让自定义的过滤器起作用
#SpringBoot中默认已经配置了CharacterEncodingFilter。 编码默认ISO-8859-1
#设置enabled=false 作用是关闭系统中配置好的过滤器, 使用自定义的CharacterEncodingFilter
server.servlet.encoding.enabled=false
只需要通过几行配置,即可完成字符编码的设置。
#让系统的CharacterEncdoingFilter生效(默认为true)
server.servlet.encoding.enabled=true
#指定使用的编码方式
server.servlet.encoding.charset=UTF-8
#强制request,response都使用charset属性的值
server.servlet.encoding.force=true
ORM 是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)
使用 MyBatis 框架操作 MySQL。
使用 MyBatis 框架操作数据,使用 SpringBoot 框架集成 MyBatis。
以下是集成步骤示例,创建 dao 接口的代理对象有好几种方式可供选择,以下方式并不完美,注意后续自己修改!
前期准备
数据库名:springdb
数据库表:student
字段:id (int auto_increment) name(varchar) age(int)
步骤:
创建 SpringBoot 项目,添加 Spring Web、MyBatis、MySQL 依赖,整理项目目录和 pom 文件,刷新 pom
创建实体类 model
public class Student {
private Integer id;
private String name;
private Integer age;
// getter,setter,toString...
}
写 dao 接口和 mapper 文件
此处 dao 接口上使用了
@Mapper注解,告诉MyBatis这是 dao 接口,创建此接口的代理对象
@Mapper // 告诉MyBatis这是dao接口,创建此接口的代理对象
public interface StudentDao {
Student selectById(@Param("stuId") Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luis.dao.StudentDao">
<select id="selectById" resultType="com.luis.model.Student">
select id,name,age from student where id=#{stuId}
</select>
</mapper>
创建 service 接口及其实现类
// 接口
public interface StudentService {
Student queryStudentById(Integer id);
}
// 实现类
@Service
public class StudentServiceImpl implements StudentService {
@Resource
private StudentDao studentDao;
@Override
public Student queryStudentById(Integer id) {
return studentDao.selectById(id);
}
}
创建控制器类
@Controller
@RequestMapping("/student")
public class StudentController {
@Resource
private StudentService studentService;
@RequestMapping("/queryById")
@ResponseBody
public String queryStudentById(Integer id) {
Student student = studentService.queryStudentById(id);
return student.toString();
}
}
在 pom 文件的 build 标签下添加 mapper 文件的 resources 插件
<!-- resources插件,将mapper文件复制到classes目录下 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
在主配置文件中配置端口,上下文访问路径,数据库连接等信息
server.port=8888
server.servlet.context-path=/orm
#配置数据库连接信息 注意mysql新旧版本配置不同
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=luis
运行启动类,输入访问地址测试【注意:填的id必须数据库中有,否则会出现空指针异常】
http://localhost:8888/orm/student/queryById?id=29
在主配置文件中配置 mybatis 日志:(可选不同的日志打印,如选 log4j 这种的,则另需添加相关依赖)
# 日志配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@Mapper(不建议)示例:
@Mapper // 告诉MyBatis这是dao接口,创建此接口的代理对象
public interface StudentDao {
Student selectById(@Param("stuId") Integer id);
}
@MapperScanbasePackages属性:通常配置 dao 接口所在包名@Mapper注解示例:
@SpringBootApplication
@MapperScan(basePackages = "com.luis.dao") // 扫描指定包,找到dao接口和mapper文件,mybatis自动创建接口的代理对象
public class SpringbootMapperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMapperApplication.class, args);
}
}
之前我们的 dao 目录下既有 Java 文件,也有 xml/mapper 文件;如果希望 Java 目录下只有 Java 文件,将 xml/mapper 文件放到 resources 下分开管理,那么只使用之前的
@MapperScan注解是扫描不到 resources 下的 Mapper 文件的,此时我们就需要在配置文件中指定 Mapper 文件的位置。
示例:
操作:在 resources 下创建子目录(自定义的),例如 mapper,用来存放 mapper 映射文件,将之前 dao 下的 mapper 文件全剪切到 mapper 目录下。
@MapperScan注解指定 dao 所在的包,扫描 dao 接口:@SpringBootApplication
@MapperScan(basePackages = "com.luis.dao") // 扫描指定包,找到dao接口和mapper文件,mybatis自动创建接口的代理对象
public class SpringbootMapperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMapperApplication.class, args);
}
}
#指定mapper文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
<!-- resources插件,将所有资源文件复制到classes目录下【配置在pom.xml的build标签下】 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
注意:如果项目编译后 .xml/properties/yml 文件没有出现在 classes 目录下,则需要在 pom 中添加 resources 资源插件。
回顾 Spring 框架中的事务:
管理事务的对象:事务管理器(接口,接口有很多的实现类)
例如:使用 Jdbc 或 mybatis 访问数据库,使用的事务管理器:DataSourceTransactionManager
声明式事务:使用 xml 配置文件或者使用注解说明事务控制的内容
控制事务:隔离级别,传播行为,超时时间
事务处理方式:
SpringBoot 中使用事务:上面的两种方式都可以。
使用步骤:(以下以注解方式演示事务的使用:)
在业务方法的上面加入 @Transactional ,加注解后,方法就有事务功能
明确的在主启动类的上面,加入@EnableTransactionManager,启用事务管理器(可不用添加此注解)
/**
* @Transactional 表示方法有事务支持,抛出运行时异常,则进行回滚
* 使用MySQL默认的传播行为、隔离级别、超时时间
*/
@Transactional // 可通过属性配置传播行为、隔离级别、超时时间
@Override
public int addUser(User user) {
System.out.println("业务方法addUser开始执行");
int rows = userMapper.insert(user);
System.out.println("业务方法addUser结束执行");
// 模拟发生运行时异常
// int num = 10 / 0;
return rows;
}
SpringBoot 中通过注解使用事务,一句话说明:直接在需要开启事务的业务方法上添加 @Transactional 注解即可!
示例:(有mybatis自动代码生成器的使用示例,可自动生成 model 实体类, dao 接口以及 mapper 映射文件)
准备数据库表:
数据库名:springdb 表名:user
字段:id(int auto_increment)name(varchar)age(int)
创建 SpringBoot 项目,添加 Spring Web、MyBatis、MySQL 依赖,整理项目目录和 pom 文件
在 pom 的 plugins 标签下添加 mybatis 自动代码生成插件
<!--mybatis自动代码生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!--配置文件的位置:在项目的根目录下,和src平级-->
<configurationFile>GeneratorMapper.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
在 pom 的 build 标签下添加资源插件,处理资源文件,刷新 pom
<!-- 处理资源文件 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**.*</include>
</includes>
</resource>
</resources>
在项目根目录下创建 GeneratorMapper.xml文件,其与 src 平级;将下列内容拷贝到文件中;依照注释修改配置,修改完后,点开右侧 maven 快捷窗口,使用当前项目下 plugins 下 mybatis-genetate 下的 mybatis 自动代码生成插件进行生成。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 指定连接数据库的JDBC驱动包所在位置,指定到你本机的完整路径 -->
<classPathEntry location="D:\Java\tools\mysql-connector-java-8.0.28.jar"/>
<!-- 配置table表信息内容体,targetRuntime指定采用MyBatis3的版本 -->
<context id="tables" targetRuntime="MyBatis3">
<!-- 抑制生成注释,由于生成的注释都是英文的,可以不让它生成 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"
userId="root"
password="luis">
<!--MySQL 不支持 schema 或者 catalog 所以需要添加这个-->
<!--设置原因参考:https://blog.csdn.net/qq_40233736/article/details/83314596-->
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!-- 生成model类,targetPackage指定model类的包名, targetProject指定生成的model放在eclipse的哪个工程下面-->
<javaModelGenerator targetPackage="com.luis.model"
targetProject="D:\1a-Projects\springboot-projects\springboot-transactional\src\main\java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="false" />
</javaModelGenerator>
<!-- 生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名, targetProject指定生成的mapper.xml放在eclipse的哪个工程下面 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成MyBatis的Mapper接口类文件,targetPackage指定Mapper接口类的包名, targetProject指定生成的Mapper接口放在eclipse的哪个工程下面 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.luis.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 数据库表名及对应的Java模型类名 -->
<table tableName="user" domainObjectName="User"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="b_bid_info" domainObjectName="BidInfo"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="b_income_record" domainObjectName="IncomeRecord"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="b_recharge_record" domainObjectName="RechargeRecord"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="u_user" domainObjectName="User"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="u_finance_account" domainObjectName="FinanceAccount"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
</context>
</generatorConfiguration>
主配置文件配置
#配置端口
server.port=9090
#context-path
server.servlet.context-path=/mytrans
#数据库连接配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=luis
#配置mapper文件路径
mybatis.mapper-locations=classpath:mapper/*.xml
#开启日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
使用 mybatis 生成器生成的 model、dao、mapper 文件
// model
public class User {
private Integer id;
private String name;
private Integer age;
// get,set
}
// dao
public interface UserMapper {
int deleteByPrimaryKey(Integer id);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luis.dao.UserMapper">
<resultMap id="BaseResultMap" type="com.luis.model.User">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="age" jdbcType="INTEGER" property="age" />
</resultMap>
<sql id="Base_Column_List">
id, name, age
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from user
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.luis.model.User">
insert into user (id, name, age
)
values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}
)
</insert>
<insert id="insertSelective" parameterType="com.luis.model.User">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
name,
</if>
<if test="age != null">
age,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.luis.model.User">
update user
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.luis.model.User">
update user
set name = #{name,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
创建 service 接口及其实现类,在实现类的业务方法上添加 @Transactional注解,开启事务支持
public interface UserService {
int addUser(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
/**
* @Transactional 表示方法有事务支持,抛出运行时异常,则进行回滚
* 使用MySQL默认的传播行为、隔离级别、超时时间
*/
@Transactional
@Override
public int addUser(User user) {
System.out.println("业务方法addUser开始执行");
int rows = userMapper.insert(user);
System.out.println("业务方法addUser结束执行");
// 模拟发生运行时异常
int num = 10 / 0; // 测试时通过注释以及放开观察事务控制
return rows;
}
}
创建控制器类
@Controller
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/addStudent")
@ResponseBody
public String addStudent(String name, Integer age) {
User user = new User();
user.setName(name);
user.setAge(age);
int rows = userService.addUser(user);
return "添加结果:" + rows;
}
}
主启动类上添加@EnableTransactionManagement注解,启用事务管理器,与业务方法上的@Transactional配套使用;继续在主启动类上添加@MapperScan注解,指定dao所在的包,扫描dao包下dao接口和mapper文件。
@SpringBootApplication
@EnableTransactionManagement // 启用事务管理器,与业务方法上的@Transactional一起使用
@MapperScan(basePackages = "com.luis.dao") // 指定dao所在的包,扫描dao包下dao接口和mapper文件
public class SpringbootTransactionalApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTransactionalApplication.class, args);
}
}
运行主启动类,浏览器输入访问地址;配合业务方法中设置的运行时异常(分别注释和放开),观察访问结果以及数据库表中数据变化进行测试
http://localhost:9090/mytrans/addUser?name=luis&age=5
测试结果:发生运行时异常,进行了事务回滚,数据操作撤销,但主键会自增;无运行时异常,数据添加成功
结论:该业务方法支持了事务
- 接口: API(Application Programming Interface,应用程序接口)是一些预先定义的接口(如函数、HTTP接口),或指软件系统不同组成部分衔接的约定。 用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。
此接口不是指 Java 中接口 Interface,而是一种应用程序接口,指可访问 servlet,controller 的 url,调用其他程序的函数!
前后端分离的思想:前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据。前后端之间完全通过public API约定。
前后端分离(解耦)的核心思想是:前端Html页面通过Ajax调用后端的RestFul API并使用Json数据进行交互。
注: 【在互联网架构中,web服务器:一般指像nginx,apache这类的服务器,他们一般只能解析静态资源。应用服务器:一般指像tomcat,jetty,resin这类的服务器可以解析动态资源也可以解析静态资源,但解析静态资源的能力没有web服务器好。】
一般只有Web服务器才能被外网访问,应用服务器只能内网访问。
————————————————
版权声明:本文为CSDN博主「山河远阔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37539378/article/details/79956760前后端分离并非仅仅只是一种开发模式,而是一种架构模式(前后端分离架构)。
千万不要以为只有在撸代码的时候把前端和后端分开就是前后端分离了,需要区分前后端项目!
前端项目与后端项目是两个项目,放在两个不同的服务器,需要独立部署,两个不同的工程,两个不同的代码库,不同的开发人员。
前后端工程师需要约定交互接口,实现并行开发,开发结束后需要进行独立部署,前端通过ajax来调用http请求调用后端的restful api。
前端只需要关注页面的样式与动态数据的解析&渲染,而后端专注于具体业务逻辑。
————————————————下面原文很有阅读价值!
版权声明:本文为CSDN博主「山河远阔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37539378/article/details/79956760
REST (英文:Representational State Transfer ,简称 REST;中文: 表现层状态转移 )
一种互联网软件架构设计的风格,但它并不是标准,它只是提出了一组客户端和服务器交互时的架构理念和设计原则,基于这种理念和原则设计的接口可以更简洁,更有层次。
任何的技术都可以实现这种理念,如果一个架构符合 REST 原则,就称它为 RESTFul 架构。
比如我们要访问一个 http 接口:http://localhost:8080/boot/order?id=1021&status=1
采用 RESTFul 风格则 http 地址为:http://localhost:8080/boot/order/1021/1
表现层状态转移::
表现层就是视图层,显示资源的,通过视图页面,jsp 等等显示操作资源的结果。
状态: 资源变化
转移:资源是可以变化的。资源能创建,new 状态,资源创建后可以查询资源,能看到资源的内容,这个资源内容,可以被修改,修改后,资源和之前的不一样。
REST中的要素:
用 REST 表示资源和对资源的操作。在互联网中,表示一个资源或者一个操作。
资源是使用 url 表示的,在互联网中,使用的图片,视频,文本,网页等等都是资源。
资源用名词表示
操作方式:
使用 http 中的动作(请求方式)表示对资源的不同操作(CURD)
需要的分页,排序等参数,依然放在 url 的后面,例如
一句话说明 REST: 使用 url 表示资源 ,使用 http 动作操作资源。
方式:使用 http 中不同动作(请求方式)表示对资源的不同操作(CURD)
示例:
处理单个资源:用其单数方式
http://localhost:8080/myboot/student/1001
http://localhost:8080/myboot/student/1001/1
处理多个资源:使用复数形式
http://localhost:8080/myboot/students/1001/1002
http://localhost:8080/myboot/student
在post请求中传递数据
<form action="http://localhost:8080/myboot/student" method="post">
姓名:<input type="text" name="name" />
年龄:<input type="text" name="age" />
</form>
<form action="http://localhost:8080/myboot/student/1" method="post">
姓名:<input type="text" name="name" />
年龄:<input type="text" name="age" />
<input type="hidden" name="_method" value="PUT" />
</form>
<a href="http://localhost:8080/myboot/student/1">删除1的数据</a>
需要的分页,排序等参数,依然放在 url 的后面,例如
http://localhost:8080/myboot/students?page=1&pageSize=20
SpringBoot 开发 RESTful 主要是通过以下几个注解实现:
@PathVariable :从 url 中获取简单类型数据【最重要的一个注解】
@GetMapping:支持的 get 请求方式,等同于@RequestMapping(method=RequestMethod.GET)
@PostMapping :支持 post 请求方式 ,等同于@RequestMapping(method=RequestMethod.POST)
@PutMapping:支持 put 请求方式,等同于@RequestMapping(method=RequestMethod.PUT)
@DeleteMapping:支持 delete 请求方式,等同于@RequestMapping( method=RequestMethod.DELETE)
@RestController:一个复合注解,是@Controller和 @ResponseBody的组合。
在类的上面使用@RestController,表示当前类的所有方法都加入了@ResponseBody
使用示例:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理 pom 文件,刷新 pom
创建控制器类,使用相关注解
/**
* @RestController:复合注解,包含@Controller和@ResponseBody
* 控制器类上加上此注解,则类中所有方法都是返回对象数据
*/
@RestController
public class MyRestController {
/**
* @GetMapping:接收get请求
* @PathVariable(路径变量):获取url中数据
* 使用位置:控制器方法的形参前
* 注解的属性value:路径变量名
* 注意:当@PathVariable的路径变量名value与形参名相同时,属性value值可以省略
* 查询资源:使用GET请求方式
* http://localhost:9999/myboot/student/1002
*/
@GetMapping("/student/{stuId}") // 接收get请求
public String queryStudent(@PathVariable("stuId") Integer studentId) {
return "查询的学生id:" + studentId;
}
}
主配置文件中配置端口和上下文路径
server.port=9999
server.servlet.context-path=/myboot
运行主启动类,浏览器输入访问地址进行测试
http://localhost:9999/myboot/student/1002
基于以上,进行其他请求测试:
控制器类中添加 post 请求处理方法:
/**
* 创建资源:使用POST请求方式
* http://localhost:9999/myboot/student/luis/22
* 注意:当@PathVariable的路径变量名value与形参名相同时,属性value值可以省略
*/
@PostMapping("/student/{name}/{age}") // 接收post请求
public String addStudent(@PathVariable String name,
@PathVariable Integer age) {
return "创建student信息:" + name + " " + age;
}
在 resources/static 下创建 addStudent.html 页面发送 post 请求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>post请求创建资源</title>
</head>
<body>
<p>创建学生信息</p>
<form action="student/luis/22" method="post">
<input type="submit" value="提交post创建学生信息">
</form>
</body>
</html>
运行主启动类,先访问页面,在页面上发送 post 请求:
http://localhost:9999/myboot/addStudent.html
可以发现,开发中模拟各种 post 请求,都需要自己创建表单页面,十分不便!
可以使用 Postman 工具,可模拟发送各种请求,使用王妈妈提供的汉化版本,免安装,免登陆!
控制器类中分别创建更新资源和删除资源的处理方法,启动项目,在 Postman 上进行访问测试:
/**
* 修改资源:使用PUT请求方式,浏览器使用POST处理PUT请求
* http://localhost:9999/myboot/student/luis/24
* 注意:当@PathVariable的路径变量名value与形参名相同时,属性value值可以省略
*/
@PutMapping("/student/{name}/{age}") // 修改资源使用@PutMapping
public String modifyStudent(@PathVariable String name,
@PathVariable Integer age) {
return "修改信息:" + name + " " + age;
}
/**
* 删除资源:使用DELETE请求方式,浏览器使用POST处理DELETE请求
* http://localhost:9999/myboot/student/3
* 注意:当@PathVariable的路径变量名value与形参名相同时,属性value值可以省略
*/
@DeleteMapping("/student/{id}")
public String removeStudentById(@PathVariable Integer id) {
return "删除信息:id = " + id;
}
浏览器是不支持 PUT 和 DELETE 请求的,只支持 GET 和 POST;即在表单中只支持 get 和 post!
在 SpringMVC 中有一个过滤器,支持 post 请求转为 put 和 delete
过滤器:org.springframework.web.filter.HiddenHttpMethodFilter
作用:把请求中的 post 请求转为 put 或 delete
使用说明:在表单中想发送 put 或 delete 请求,需配置请求方式为 post,然后在表单的隐藏域标签中指定name=_method,value=put/delete;在 SpringBoot 的配置文件中启用 SpringMVC 的过滤器后,会自动将 post 请求转为指定的 put 或 delete。
实现步骤:
application.properties(yml) 开启使用 HiddenHttpMethodFilter 过滤器
#启用过滤器,支持put和delete请求
spring.mvc.hiddenmethod.filter.enabled=true
在请求页面中,包含 _method 参数,其值是 put 或 delete,发起这个请求需使用 post 方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试表单提交方式</title>
</head>
<body>
<form action="student/test" method="post">
<!-- 在隐藏域标签中指定将post转化为何种请求方式 -->
<input type="hidden" name="_method" value="put"/>
<input type="submit" value="表单提交put请求"/>
</form>
<form action="student/test" method="post">
<!-- 在隐藏域标签中指定将post转化为何种请求方式 -->
<input type="hidden" name="_method" value="delete"/>
<input type="submit" value="表单提交delete请求"/>
</form>
</body>
</html>
附控制器测试方法:
@RestController
public class MyRestController {
@PutMapping("/student/test")
public String testPut() {
return "put请求成功";
}
@DeleteMapping("/student/test")
public String testDelete() {
return "delete请求成功";
}
}
使用 REST 注意,必须保证请求地址 URL 加上请求方式唯一,不然会出现冲突。
如下面两个请求处理方法,在请求时就会出现冲突,无法确定用哪个方法处理请求!
@GetMapping("/student/{id}")
public String queryId(@PathVariable Integer id) {
return "查询的学生id:" + id;
}
@GetMapping("/student/{age}")
public String queryAge(@PathVariable Integer age) {
return "查询的学生age:" + age;
}
Redis:一个NoSQL数据库,常用作缓存使用(cache)
Redis 的五种数据类型:string、hash、set、zset、list
Redis 是一个中间件,是一个独立的服务器。
Java 中著名的客户端:Jedis,lettuce,Redisson
SpringBoot 中有 RedisTemplate 和 StringRedisTemplate,都可以处理和 redis 的数据交互。
PS:其实使用的 RedisTemplate,其底层还是调用的 lettuce 客户端方法来处理数据。
临时性使用 redis ,可直接在 Windows 上使用解压后的安装包,启动服务端和客户端即可!(网盘有压缩包,解压即可使用)
Redis-x64-3.2.100.rar 解压缩到一个非中文的目录
redis-server.exe:服务端,启动后,不要关闭
redis-cli.exe:客户端,访问 redis 中的数据
redisclient-win32.x86_64.2.0.jar:Redis 的一个图形界面客户端
执行方式:在这个文件所在的目录,执行 java -jar redisclient-win32.x86_64.2.0.jar 即可
步骤:
创建 SpringBoot 项目,选择 Spring Web 和 Redis 依赖,整理 pom 并刷新
application.properties/yml 中配置连接 redis 的相关参数
server.port=7777
server.servlet.context-path=/myredis
#配置redis:host ip password
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=123
创建控制器类,创建请求处理方法,获取操作 redis 数据的对象,处理数据交互
@RestController
public class RedisController {
/**
* 注入RedisTemplate(处理和redis的数据交互)
* 其默认使用的jdk序列化,可读性差(查看数据库中数据有类似乱码问题)
* 三种泛型情况:
* RedisTemplate<String,String>
* RedisTemplate<Object,Object>
* RedisTemplate
*/
@Resource
private RedisTemplate redisTemplate;
/**
* 注入StringRedisTemplate(处理和redis的数据交互)
* 其默认使用的String序列化,可读性好(数据库中数据正常显示)
*/
@Resource
private StringRedisTemplate stringRedisTemplate;
@PostMapping("/redis/addstring")
public String addToRedis(String key, String value) { // 使用传统风格
// 添加数据到redis(使用RedisTemplate对象)
ValueOperations valueOperations = redisTemplate.opsForValue(); // 获取redis中操作string类型数据的对象
valueOperations.set(key, value);
return "成功添加数据到redis";
}
@GetMapping("/redis/getstring")
public String getKFromRedis(String key) { // 使用传统风格
// 从redis获取数据(使用RedisTemplate对象)
ValueOperations valueOperations = redisTemplate.opsForValue(); // 获取redis中操作string类型数据的对象
Object o = valueOperations.get(key);
return key + " = " + key + ", value = " + o;
}
@PostMapping("/redis/{k}/{v}")
public String addStringKV(@PathVariable String k,
@PathVariable String v) { // 使用RESTful风格
// 添加数据到redis(使用StringRedisTemplate对象)
ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set(k, v);
return "成功添加数据到redis";
}
@GetMapping("/redis/{k}")
public String getK(@PathVariable String k) { // 使用RESTful风格
// 从redis获取数据(使用StringRedisTemplate对象)
ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
String v = stringStringValueOperations.get(k);
return k + " = " + k + ", v = " + v;
}
}
运行主启动类,使用 Postman 进行请求测试
StringRedisTemplate:使用的是 String 的序列化方式,把 k,v 都是作为 String 处理,可读性好。
但是有局限性,只能存 String 类型,且只能是 String 的序列化方式,无法修改为其他的序列化方式。
RedisTemplate:默认使用的是 JDK 的序列化方式,把 k,v 经过了 JDK 序列化存到 redis;
经过了 JDK 序列化的 k,v 在数据库中不能直接识别。
其默认使用的 jdk 序列化,但可以修改为其他的序列化方式。
其使用比 StringRedisTemplate 更加广泛,可修改为其他的序列化方式,不仅仅只能存字符串。
序列化和反序列化:
序列化:把对象转化为可传输的字节序列的过程称为序列化。
反序列化:把字节序列还原为对象的过程称为反序列化。
为什么需要序列化?
什么情况下需要序列化?
本质上存储和网络传输都需要经过把一个对象状态保存成一种跨平台识别的字节格式,然后其他的平台才可以通过字节信息解析还原对象信息。
序列化的方式:
序列化只是一种拆装和组装对象的规则,这种规则有多种多样,比如现在常见的序列化方式有:
JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protofbuff 等
例如:
java 的序列化:把 java 对象转为 byte[] 二进制数据
json 的序列化:就是将对象转换为 JSON 字符串格式
json 的反序列化:就是将JSON 字符串格式转换为对象
Student( name=zs, age=20) ---- { "name":"zs", "age":20 }
@PostMapping("/redis/addData")
public String addData(String k, String v) {
// 设置key,value均使用String序列化方式(在进行存取值之前设置)
// 注意,key 和 value 的序列化方式可分别设置
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
// 获取redis中操作string类型数据的对象
ValueOperations valueOperations = redisTemplate.opsForValue();
// 向redis中添加数据
valueOperations.set(k, v);
return "设置RedisTemplate序列化方式,并向redis中添加数据";
}
设置 json 序列化,存储时把对象转为 json 格式字符串进行存储;获取时将 json 字符串格式转为对象。
如果是 String 序列化方式,则只能存储字符串类型,无法存储对象。
示例:
准备 vo 数据对象 Student(需要进行序列化)
public class Student implements Serializable {
private static final long serialVersionUID = 684647176563341892L; // 添加后就不要再改动!
private Integer id;
private String name;
private Integer age;
// getter,setter,toString
}
写处理请求的处理器方法
@PostMapping("/redis/addjson")
public String addJson() {
// 准备对象数据
Student student = new Student();
student.setId(111);
student.setName("luis");
student.setAge(22);
// 设置key的序列化方式为String(默认的是jdk方式)
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式为json(默认的是jdk方式)
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
// 获取redis中操作string类型数据的对象
ValueOperations valueOperations = redisTemplate.opsForValue();
// 向redis中存数据
// 会依据value设置的json序列化方式自动将Student对象转换为json格式字符串
valueOperations.set("mystudent", student);
return "key-string序列化,value-json序列化";
}
@GetMapping("/redis/getjson")
public String getJson() {
// 设置key的序列化方式为String(默认的是jdk方式)
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式为json(默认的是jdk方式)
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
// 获取redis中操作string类型数据的对象
ValueOperations valueOperations = redisTemplate.opsForValue();
// 从redis中获取数据
// 会依据value设置的json序列化方式自动将json格式字符串转换为Student对象,反序列化回来
Object o = valueOperations.get("mystudent");
return "json反序列化:" + o;
}
使用 Postman 发请求测试
开发中使用 SpringBoot 集成 Dubbo 进行分布式开发!
公共工程负责提供实体 bean 以及业务接口;
服务提供者工程负责实现公共工程中的业务接口,提供并暴露服务,供消费者调用;
服务消费者工程负责调用服务提供者提供的服务。
需要三个工程:接口工程【java】、服务提供者工程【可web】、服务消费者工程【web】
接口工程【java】:负责实体 bean 以及业务接口(提供者和消费者均依赖于它)
服务提供者工程【可web】:负责实现接口工程中的服务接口,提供服务,暴露服务,供消费者调用
服务消费者工程【web】:负责调用提供者提供的服务
示例:
创建一个 maven 项目,无需选择模板,名为 interface-api(公共的接口工程)
创建 vo 数据类 Student,进行序列化
public class Student implements Serializable {
private static final long serialVersionUID = -3498691273686241331L;
private Integer id;
private String name;
private Integer age;
// getter,setter,toString
}
定义服务接口(业务接口)
public interface StudentService {
Student queryStudent(Integer id);
}
创建 SpringBoot 项目,无需添加其他依赖,项目命名 springboot-service-provider(服务提供者工程)
在其 pom 文件中加入公共工程依赖、dubbo 起步依赖和 Zookeeper 依赖,整理 pom,刷新
<!--公共项目(接口工程)的gav-->
<dependency>
<groupId>com.luis</groupId>
<artifactId>interface-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--dubbo起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.8</version>
<type>pom</type>
<exclusions>
<!-- 排除log4j依赖,解决多次引入 -->
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
注意:服务提供者中如果要使用到redis,并涉及json序列化操作,在不添加web起步依赖下,必须加下列jackson依赖!
<!--单独添加下面四个Jackson的依赖(或者直接将web起步依赖加上)-->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jdk8 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.13.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-parameter-names -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.13.2</version>
</dependency>
实现接口工程中的业务方法,使用 dubbo 中的注解暴露服务
// @Component 可不用加此注解创建对象!加@DubboService后,该注解可自行创建该类对象。
//@DubboService(interfaceClass = StudentService.class, version = "1.0", timeout = 5000)//暴露服务
@DubboService(version = "1.0", timeout = 5000) // 暴露服务(interfaceClass属性可不加)
public class StudentServiceImpl implements StudentService {
@Override
public Student queryStudent(Integer id) {
Student student = new Student();
if (1001 == id) {
student.setId(1001);
student.setName("1001-luis");
student.setAge(22);
} else {
student.setId(1002);
student.setName("1002-jack");
student.setAge(24);
}
return student;
}
}
配置 application.properties/yml 文件
#配置服务名称
spring.application.name=studentservice-provider
#配置扫描的包,扫描@DubboService
dubbo.scan.base-packages=com.luis.service
#配置dubbo协议(使用zookeeper注册中心就不用配置)
#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20881
#配置zookeeper注册中心
dubbo.registry.address=zookeeper://localhost:2181
在主启动类上添加注解启用dubbo
@SpringBootApplication
@EnableDubbo // 启用dubbo,该注解包含@EnableDubboConfig和@DubboComponentScan
public class SpringbootServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootServiceProviderApplication.class, args);
}
}
创建 SpringBoot 项目,添加 Spring Web 依赖,项目命令为 springboot-consumer(服务消费者工程)
在其 pom 文件中加入公共工程依赖、dubbo 起步依赖和 Zookeeper 依赖,整理 pom,刷新。
其需要的依赖和服务消费者工程所需相同!
<!--公共项目(接口工程)的gav-->
<dependency>
<groupId>com.luis</groupId>
<artifactId>interface-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--dubbo起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.8</version>
<type>pom</type>
<exclusions>
<!-- 排除log4j依赖 -->
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
创建控制器类,写请求处理方法,进行远程接口调用
@RestController
public class DubboController {
// 引用远程服务,将创建好的代理对象,注入给studentService
//@DubboReference(interfaceClass = StudentService.class, version = "1.0")
@DubboReference(version = "1.0") // 如果不指定interfaceClass,则默认采用引用类型的class
private StudentService studentService;
@GetMapping("/query")
public String queryStudent() {
// 调用远程接口,获取对象
Student student = studentService.queryStudent(1001);
return "调用远程接口获取的对象:" + student;
}
}
在主启动类上添加启用dubbo的注解
@SpringBootApplication
@EnableDubbo // 启用Dubbo
public class SpringbootConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootConsumerApplication.class, args);
}
}
配置 application.properties/yml
#指定服务名称
spring.application.name=consumer-application
#指定注册中心
dubbo.registry.address=zookeeper://localhost:2181
启动 zookeeper 注册中心(双击 zookeeper/bin 目录下的 zkServer.cmd)
运行服务提供者模块的主启动类
运行服务消费者模块的主启动类
根据服务消费者提供的访问接口进行服务访问测试
http://localhost:8080/query
示例:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录
此处直接列举整个 pom,依据注释的内容进行添加,然后刷新 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.luis</groupId>
<artifactId>springboot-war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!--指定打包类型-->
<packaging>war</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--处理jsp的依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--servlet,jsp,jstl需要用再加-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!--打包后的文件名称-->
<finalName>myboot</finalName>
<!--resources插件-->
<resources>
<!--将jsp编译到指定的目录-->
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<!--将src/main/java下的文件包含到classes下-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<!--将src/main/resources下的文件包含到classes下-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在 main 目录下,与 java 和 resources 目录平级,创建名为 webapp 的目录;在项目工程结构中指定该目录为 web 资源目录
在 webapp 目录下创建 index.jsp 页面,用来显示 controller 中的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index.jsp</title>
</head>
<body>
index.jsp,显示controller中的数据:${data}
</body>
</html>
创建 controller 控制器类,处理请求
@Controller
public class JspController {
@RequestMapping("/main")
public String main(Model model) {
model.addAttribute("data", "SpringBoot打包为war文件");
return "index";
}
}
配置 application.properties/yml
server.port=9001
server.servlet.context-path=/myjsp
#指定视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
让主启动类继承 SpringBootServletInitializer并重写 configure 方法,修改返回内容。
/**
* 让主启动类继承 SpringBootServletInitializer 并重写重写 configure 方法才可以使用外部 Tomcat!
* SpringBootServletInitializer 就是原有 web.xml 的替代。
*/
@SpringBootApplication
public class SpringbootWarApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringbootWarApplication.class, args);
}
// 重写此configure方法,修改返回内容!
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootWarApplication.class);
}
}
运行主启动类,浏览器输入访问地址,测试
http://localhost:9001/myjsp/main
测试正常后,使用 maven 的 lifecycle 中 package 打包命令进行打包(右侧 maven 窗口中);【建议先clean再package】
在 target 目录下即可看到打包完成的 war 包。
将打包好的 war 包放到 Tomcat 的 webapps 目录下,启动 Tomcat;
若是本地 Tomcat,则通过http://localhost:8080/myboot/main可访问该项目。
重点小结:
SpringBootServletInitializer并重写 configure 方法,修改返回内容。示例:
创建 SpringBoot 项目,选择 Spring Web 依赖,整理项目目录
此处直接列举整个 pom,依据注释的内容进行添加,然后刷新 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.luis</groupId>
<artifactId>springboot-jar</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--处理jsp的依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--servlet,jsp,jstl需要用再加-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!--打包后的文件名称-->
<finalName>myboot</finalName>
<!--resources插件-->
<resources>
<!--将jsp编译到指定的目录-->
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<!--将src/main/java下的mapper文件包含到classes下-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!--将src/main/resources下的文件包含到classes下-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 打包jar,有jsp文件时,必须指定maven-plugin插件版本为 1.4.2.RELEASE -->
<version>1.4.2.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
在 main 目录下,与 java 和 resources 目录平级,创建名为 webapp 的目录;在项目工程结构中指定该目录为 web 资源目录
在 webapp 目录下创建 main.jsp 页面,用来显示 controller 中的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
main.jsp,显示数据:${data}
</body>
</html>
创建控制器类,处理请求
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView hello() {
ModelAndView mv = new ModelAndView();
mv.addObject("data", "SpringBoot打包为jar文件");
mv.setViewName("main");
return mv;
}
}
配置 application.properties/yml
server.port=9002
server.servlet.context-path=/myboot
#视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
运行主启动类,浏览器输入地址访问测试
http://localhost:9002/myboot/hello
测试正常后,使用 maven 的 lifecycle 中 package 打包命令进行打包(右侧 maven 窗口中);【建议先clean再package】
在 target 目录下即可看到打包完成的 jar 包。
在 jar 包所在目录,开启 cmd 窗口,输入 java -jar myboot.jar,回车,即可独立运行项目;
(可以将java -jar myboot.jar这段命令写进一个文件,改后缀为 .bat,放在 jar 包同级下,双击即可运行项目)
后台信息打印可看到端口和项目访问上下文路径;
此时使用的是内嵌的 tomcat,输入http://localhost:9002/myboot/hello即可访问。
重点小结:
Thymeleaf 的官方网站:http://www.thymeleaf.org
Thymeleaf 官方手册:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
添加了 Thymeleaf 依赖后,在创建的 html 标签中使用其相关语法,若想使用 IDEA 的语法提示,还需进行以下设置:
在默认创建的 html 的 <html lang="en">中补充xmlns:th="http://www.thymeleaf.org",如下:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
创建 SpringBoot 项目,选择 Spring Web 和 Thymeleaf 依赖,整理 pom,并刷新
写控制器类,处理请求,转发页面到指定的 Thymeleaf 页面,显示数据
@Controller
public class MyController {
@GetMapping("/hello")
public String helloThymeleaf(HttpServletRequest request, Model model) {
// 将数据放到request作用域
request.setAttribute("data1", "欢迎使用Thymeleaf模板引擎");
// 将数据放到request作用域
model.addAttribute("data2", "我是后台的数据");
// 指定转发的逻辑视图(使用Thymeleaf模板引擎,其通常使用的html)
// 模板引擎默认配置了resources/tmeplates下的以.html结尾文件的视图解析
return "hello";
}
}
在 resources/templates 下,创建 hello.html 页面显示数据(可在 html 标签中使用 Thymeleaf)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- xmlns:th="http://www.thymeleaf.org" 是手动加的,加上后idea使用Thymeleaf会有相关提示 -->
<head>
<meta charset="UTF-8">
<title>hello.html</title>
</head>
<body>
<h3>Thymeleaf使用示例</h3>
<!-- 后台有数据填充,则替换p标签的value内容;没有,则不替换 -->
<p th:text="${data1}">想显示数据</p>
<p th:text="${data2}">静态展示数据</p>
</body>
</html>
配置 application.properties/yml
#关闭模板缓存,开发阶段配置,让修改立即生效(项目上线后需修改为false,效率更高)
spring.thymeleaf.cache=false
# 去掉html5的语法验证(thymeleaf对html的标签约束非常严格,所有的标签必须有开有闭,比如<br></br>或者<br/>是可以的,但是<br>会报错,
# 配置spring.thymeleaf.mode=LEGACYHTML5 目的就是为了解决这个问题,可以使页面松校验。)
spring.thymeleaf.mode=LEGACYHTML5
#以下是一些可能用到的配置,需要改变时配置即可,一般不需要配置,使用默认即可
#-------------------------------------------------
#编码格式(默认是UTF-8)
spring.thymeleaf.encoding=UTF-8
#模板的类型(默认是HTML)
spring.thymeleaf.mode=HTML
#模板的前缀(默认是classpath:/templates/,其下html文件不用配置视图解析器,直接可用逻辑名称)
spring.thymeleaf.prefix=classpath:/templates/
#模板的后缀(默认是.html)
spring.thymeleaf.suffix=.html
#-------------------------------------------------
运行主启动类,浏览器输入地址访问测试;
然后不通过地址,直接在 idea 中点击右上角浏览器标识,打开 hello.html,查看效果
http://localhost:8080/hello
结果:后台没有数据传过来时,显示的是页面原有数据;后台有数据传过来,则覆盖原有数据进行显示!
使用 Thymeleaf 可实现前后端同时有效开发,互不影响;前端可用自己的示例数据,后端只需进行数据替换!
语法:${key}
作用:从 request 作用域中获取 key 对应的文本数据;
可使用request.setAttribute()或model.addAttribute()向 request 中存数据
使用:在页面的 html 标签中,使用th:text="${key}"表达式
示例:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>expression1</title>
</head>
<body>
<p3>标准变量表达式:${key}</p3>
<div>
<p>获取相关数据:</p>
<p th:text="${msg}">msg</p>
<p th:text="${myuser.id}">id</p>
<p th:text="${myuser.name}">name</p>
<p th:text="${myuser.sex}">sex</p>
<p th:text="${myuser.age}">age</p>
</div>
</body>
</html>
语法: *{key}
作用:获取这个 key 对应的数据;*{key}需要和th:object这个属性一起使用,简化对象属性值的获取。
示例:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>expression2</title>
</head>
<body>
<p3>选择变量表达式:*{key}</p3>
<p>获取对象相关数据:</p>
<div th:object="${myuser}">
<p th:text="*{id}">id</p>
<p th:text="*{name}">name</p>
<p th:text="*{sex}">sex</p>
<p th:text="*{age}">age</p>
</div>
<p>使用*{}完整表示对象的属性值,此用法其实和${key}作用相同,了解即可</p>
<p th:text="*{myuser.name}">name</p>
</body>
</html>
语法:@{url}
作用:表示链接,可使用在如下等情境中
<script src="...">,<link href="...">,<a href="..">,<form action="...">,<img src="...">
示例:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>链接表达式</title>
</head>
<body>
<p3>链接绝对路径</p3><br/>
<a th:href="@{http://www.baidu.com}">链接到百度</a><br/>
<p3>链接的是相对路径</p3><br/>
<a th:href="@{/tpl/queryAccount}">相对地址,没有参数</a><br/>
<p3>链接的相对地址,使用字符串链接传递参数</p3><br/>
<a th:href="@{'/tpl/queryAccount?id=' + ${userId}}">获取model中的数据添加到链接地址中(不推荐此种拼接方式)</a><br/>
<p3>推荐使用的传参方式:</p3><br/>
<a th:href="@{/tpl/queryAccount(id=${userId})}">传递一个参数(参数值可自己设置,也可从作用域中获取)</a><br/>
<a th:href="@{/tpl/queryUser(name='李四',age=20)}">传递多个参数(参数值可自己设置)</a><br/>
<a th:href="@{/tpl/queryUser(name=${name},age=${age})}">传递多个参数(参数值可自己设置,也可从作用域中获取)</a><br/>
</body>
</html>
大部分属性和 html 的一样,只不过前面加了一个 th 前缀;加了 th 前缀,表示经过了模板引擎处理;经过了模板引擎处理的属性,就可以使用其相关表达式,可获取动态变量。
th:action、th:method、th:href、th:src、th:text、th:style、th:name、th:value等
基本属性示例:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>模板属性</title>
<!-- 引入jQuery -->
<script th:src="@{/js/jquery-3.4.1.js}" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
$("#btn").click(function(){
alert("按钮单击绑定======");
})
})
function btnClick() {
alert("按钮单击回调");
}
</script>
</head>
<body>
<p3>使用模板属性可获取动态变量</p3>
<form th:action="@{/login}" th:method="${methodAttr}">
<input th:type="text" th:name="${paramname}" th:value="${paramvalue}" />
<input type="button" value="按钮" id="btn" th:onclick="btnClick()" />
</form>
<p th:style="${textcolor}">获取动态变量,改变字体颜色</p>
<p th:style="'color:skyblue'">此处经过模板处理,若仍手动设置,需要使用字符串</p>
<p style="color: red">原版的改变字体颜色</p>
</body>
</html>
th:eachth:each循环List/Arrayeach 循环集合 List 和循环数组 Array 语法相同!
语法:在一个 html 标签中,如下列方式使用 th:each(以下以 div 为例,实际多循环表格标签)
<div th:each="集合循环成员,循环状态变量:${key}">
<p th:text="${集合循环成员}"></p>
</div>
集合循环成员,循环状态变量:两个名称都是自定义的。
“循环状态变量”这个名称可不定义,默认名称是"集合循环成员Stat"
如果不定义“循环状态变量”,则直接可这样写:(但要使用其时,直接使用"集合循环成员Stat"这个默认的名称即可)
<div th:each="集合循环成员:${key}">
<p th:text="${集合循环成员}"></p>
</div>
具体语法说明:
th:each="user,iterStat:${userlist}"中的${userList}是后台传过来的集合
◼ user
定义变量,去接收遍历${userList}集合中的一个数据
◼ iterStat
${userList} 循环体的信息
◼ 其中 user 及 iterStat 自己可以随便取名
◼ interStat 是循环体的信息,通过该变量可以获取如下信息
index: 当前迭代对象的 index(从0开始计算)
count: 当前迭代对象的个数(从1开始计算)这两个用的较多
size: 被迭代对象的大小
current: 当前迭代变量
even/odd: 布尔值,当前循环是否是偶数/奇数(从0开始计算)
first: 布尔值,当前循环是否是第一个
last: 布尔值,当前循环是否是最后一个
注意:循环体信息 interStat 也可以不定义,则默认采用迭代变量加上 Stat 后缀,即 userStat
示例:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>eachList.html</title>
</head>
<body>
<p3>each循环List</p3>
<table border="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>sex</th>
<th>age</th>
<th>userStat</th>
</tr>
</thead>
<tbody>
<tr th:each="user,userStat:${myusers}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.sex}"></td>
<td th:text="${user.age}"></td>
<td th:text="${userStat}"></td>
</tr>
</tbody>
</table>
<br/>
<table border="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>sex</th>
<th>age</th>
</tr>
</thead>
<tbody>
<!-- 可不写循环体信息userStat,默认会自动加上去 -->
<tr th:each="user:${myusers}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.sex}"></td>
<td th:text="${user.age}"></td>
</tr>
</tbody>
</table>
</body>
</html>
th:each循环Map语法:在一个 html 标签中,如下列方式使用 th:each(以下以 div 为例,实际多循环表格标签)
<div th:each="集合循环成员,循环的状态变量:${key}">
<p th:text="${集合循环成员.key}" ></p>
<p th:text="${集合循环成员.value}"></p>
</div>
集合循环成员,循环的状态变量:两个名称都是自定义的。
“循环的状态变量”这个名称可以不定义,默认是"集合循环成员Stat"
key:map集合中的key
value:map集合key对应的value值
示例循环一个稍复杂点的数据类型如:List<Map<String,Student>>
第一次循环,取出每一个 Map;
第二次循环,取出每一个 Map 中的 key 和 value。
<div th:each="lm:${listMap}">
<div th:each="map:${lm}">
<p th:text="${map.key}"></p>
<p th:text="${map.value}"></p>
</div>
</div>
th:if和th:unlessth:if:判断语句,当条件为 true,显示 html 标签体内容,反之不显示;没有 else 语句
示例:
<div th:if="10 > 0">条件为true才显示此内容</div>
<p th:if="${sex=='m'}">性别是男才显示</p>
<p th:if="${isLogin}">isLogin为true才显示</p>
<p th:if="${age>18}">年龄大于18才显示</p>
<!--""空字符是true-->
<p th:if="${str}">当str为空串表示true,会显示</p>
<!--null是false-->
<p th:if="${str}">当str为null表示false,不会显示</p>
th:unless:判断语句,当条件为 false,显示 html 标签体内容,反之不显示;没有 else 语句
示例:
<div th:unless="10 < 0">条件为false才显示此内容</div>
动态变量控制的示例此处省略,和if的使用相同,就是显示条件相反!
th:swith和th:caseth:switch 和 Java 中的 switch 是一样的;
进行条件匹配,按上下顺序匹配,一旦匹配成功则结束;否则使用默认的;无论如何,最终只有一个匹配上!
<div th:switch="要比对的值">
<p th:case="值1">
结果1
</p>
<p th:case="值2">
结果2
</p>
<p th:case="*">
默认结果
</p>
以上的case只有一个语句执行
</div>
示例:
<div th:switch="${sex}">
<p th:case="m">男</p>
<p th:case="f">女</p>
<p th:case="*">性别未知</p>
</div>
th:inline作用:不依赖于 html 标签,直接通过内联表达式[[${key}]]来获取动态数据
th:inline有三个取值类型 (text, javascript 和 none),通常使用 text 和 javascript 这两个
语法:th:inline="text/javascript"
内联表达式:[[${key}]]
作用:在 html 标签外,获取动态数据。
语法:th:line="text",不指定则默认使用内联 text!
特点:可显示的指定使用内联 text,不指定则默认使用内联 text!
示例:
<!-- 可显示的指定使用内联text -->
<div th:inline="text">
<p>性别:[[${sex}]],年龄[[${age}]]</p>
</div>
<!-- 没有指定内联类型,默认使用内联text -->
<div>
<p>性别:[[${sex}]],年龄[[${age}]]</p>
</div>
作用:可以在 js 中,获取模版中的数据
语法:th:inline="javascript",必须指定!
示例:
<button onclick="fun()">单击按钮</button>
<script type="text/javascript" th:inline="javascript">
var name = [[${myuser.name}]];
var id = [[${myuser.id}]];
function fun() {
alert("click 用户是"+name+",他的 id 是"+id);
}
</script>
<div style="margin-left: 400px">
<h3>文本字面量: 使用单引号括起来的字符串</h3>
<p th:text="'我是'+${name}+',我所在的城市是'+${city}">数据显示</p>
<h3>数字字面量</h3>
<p th:if="${20>5}"> 20大于 5</p>
<h3>boolean字面量</h3>
<p th:if="${isLogin == true}">用户已经登录系统</p>
<h3>null字面量</h3>
<p th:if="${myuser != null}">有myuser数据</p>
</div>
在模板文件的表达式中连接字符串有两种语法:
<p th:text="'我是'+${name}+',我所在的城市是'+${city}">数据显示</p>
<p th:text="|我是${name},我所在城市是${city}|">
显示数据
</p>
使用示例:
<div style="margin-left: 400px">
<h3>使用运算符</h3>
<p th:text="${age > 10}">年龄大于 10 </p>
<p th:text="${20 + 30}">显示运算结果</p>
<p th:if="${myuser == null}">myuser是null</p>
<p th:if="${myuser eq null}">myuser是null</p>
<p th:if="${myuser ne null}">myuser不是null</p>
<p th:text="${isLogin == true ? '用户已经登录' : '用户需要登录'}"></p>
<p th:text="${isLogin == true ? (age > 10 ? '年龄大于10' : '年龄小于10') : '用户需要登录'}"></p>
</div>
三元运算符:
表达式 ? true的结果 : false的结果
三元运算符可以嵌套
常用的内置对象有以下几个:
#request:表示 HttpServletRequest 对象
#session:表示 HttpSession 对象
session:表示一个 Map 对象,是 #session的简单表示方式,用来便捷获取 session 中指定 key 的值。
#session.getAttribute("loginname") 等同 session.loginname
使用示例:
<div style="margin-left: 350px">
<h3>内置对象 #request,#session,session 的使用</h3>
<p>获取作用域中的数据</p>
<p th:text="${#request.getAttribute('requestData')}"></p>
<p th:text="${#session.getAttribute('sessionData')}"></p>
<p th:text="${session.loginname}"></p>
<br/>
<br/>
<h3>使用内置对象的方法</h3>
getRequestURL=<span th:text="${#request.getRequestURL()}"></span><br/>
getRequestURI=<span th:text="${#request.getRequestURI()}"></span><br/>
getQueryString=<span th:text="${#request.getQueryString()}"></span><br/>
getContextPath=<span th:text="${#request.getContextPath()}"></span><br/>
getServerName=<span th:text="${#request.getServerName()}"></span><br/>
getServerPort=<span th:text="${#request.getServerPort()}"></span><br/>
</div>
内置工具类:Thymeleaf 自己的一些类,提供对日期、数字、字符串、list集合等的一些处理方法。
下面是一些常用的内置工具类对象:
#dates:处理日器的工具类
#numbers:处理数字的工具类
#strings:处理字符串的工具类
#lists:处理 list 集合的工具类
使用示例:
<div style="margin-left: 350px">
<h3>日期类对象 #dates</h3>
<p th:text="${#dates.format(mydate )}"></p>
<p th:text="${#dates.format(mydate,'yyyy-MM-dd')}"></p>
<p th:text="${#dates.format(mydate,'yyyy-MM-dd HH:mm:ss')}"></p>
<p th:text="${#dates.year(mydate)}"></p>
<p th:text="${#dates.month(mydate)}"></p>
<p th:text="${#dates.monthName(mydate)}"></p>
<p th:text="${#dates.createNow()}"></p>
<br/>
<h3>内置工具类 #numbers,操作数字的</h3>
<p th:text="${#numbers.formatCurrency(mynum)}"></p>
<p th:text="${#numbers.formatDecimal(mynum,5,2)}"></p>
<br/>
<h3>内置工具类 #strings, 操作字符串</h3>
<p th:text="${#strings.toUpperCase(mystr)}"></p>
<p th:text="${#strings.indexOf(mystr,'power')}"></p>
<p th:text="${#strings.substring(mystr,2,5)}"></p>
<p th:text="${#strings.substring(mystr,2)}"></p>
<p th:text="${#strings.concat(mystr,'---java开发的黄埔军校---')}"></p>
<p th:text="${#strings.length(mystr)}"></p>
<p th:text="${#strings.length('hello')}"></p>
<p th:unless="${#strings.isEmpty(mystr)}"> mystring 不是 空字符串 </p>
<br/>
<h3>内置工具类 #lists, 操作list集合</h3>
<p th:text="${#lists.size(mylist)}"></p>
<p th:if="${#lists.contains(mylist,'a')}">有成员a</p>
<p th:if="!${#lists.isEmpty(mylist)}"> list 集合有多个成员</p>
<br/>
<h3>处理null</h3>
<!-- 对象前加问号,会自动查询其是否为null,不是null则继续获取;是null则不获取。 -->
<p th:text="${zoo?.dog?.name}"></p>
</div>
模板定义语法:th:fragment="模板自定义名称"
示例:(在 head.html 中创建下列模板)
<div th:fragment="top">
<p>百度地址</p>
<p>www.baidu.com</p>
</div>
有两种使用的语法格式:
~{文件名称 :: 自定义的模板名}文件名称 :: 自定义的模板名PS:文件名称指的自定义的模板所在的 html 文件名,不含后缀
对于模板,有两种常用的使用方式:包含模板(th:include),插入模板(th:insert)
解释:是在原有标签中,将自定义的模板添加进来,不会失去原有标签,但原有标签下的内容将失去。
语法
th:insert="~{文件名称 :: 自定义的模板名}"th:insert="文件名称 :: 自定义的模板名"示例:(在 test.html 中,将 head.html 中名为 top 的模板插入到指定标签中)
使用格式1:
<div th:insert="~{head :: top}">
此div标签下内容将会丢失,但div标签仍然存在!
</div>
使用格式2:
<div th:insert="head :: top">
此div标签下内容将会丢失,但div标签仍然存在!
</div>
解释:用自定义的模板替换原有标签,原有标签将不存在
语法
th:include="~{文件名称 :: 自定义的模板名}"th:include="文件名称 :: 自定义的模板名"示例:(在 test.html 中,将 head.html 中名为 top 的模板包含(替换)到指定的标签)
使用格式1:
<div th:include="~{head :: top}">
整个div标签将被完全替换为自定义的模板!
</div>
使用格式2:
<div th:include="head :: top">
整个div标签将被完全替换为自定义的模板!
</div>
如果要将整个 html 的内容作为模板插入(insert)或包含(include)到指定的标签中,可使用下列几种方式:
示例:将整个 head.html 作为模板,插入到指定标签处
方式一:
<div th:insert="head :: html">
将整个 head.html 作为模板,插入到此标签处!
</div>
方式二:
<div th:insert="head">
将整个 head.html 作为模板,插入到此标签处!
</div>
示例:将整个 head.html 作为模板,包含(替换)到指定标签处
方式一:
<div th:include="head :: html">
将整个 head.html 作为模板,包含(替换)到此标签处!
</div>
方式二:
<div th:include="head">
将整个 head.html 作为模板,包含(替换)到此标签处!
</div>
说明:如果要使用的自定义模板或要使用的整个 html 模板与当前页面不在同一目录下,则在指定使用模板的文件名时需要使用相对路径即可!
例如:在 test.html 某标签中要将同级 common 目录下 left.html 作为整个模板来使用:
方式一:插入
<div th:insert="common/left :: html"></div>
方式二:插入
<div th:insert="common/left"></div>
方式一:包含
<div th:include="common/left :: html"></div>
方式二:包含
<div th:include="common/left"></div>
以上是使用其他目录中的整个 html 作为模板示例;使用其他目录中指定 html 页面中的某个自定义模板用法和其相同。
Spring + SpringMVC + SpringBoot
创建对象的:
@Controller:放在类的上面,创建控制器对象,注入到容器中
@RestController:放在类的上面,创建控制器对象,注入到容器中。
作用:复合注解,含@Controller,@ResponseBody,使用这个注解类的,里面的控制器方法的返回值都是数据。
@Service:放在业务层的实现类上面,创建service对象,注入到容器
@Repository:放在dao层的实现类上面,创建dao对象,放入到容器。没有使用这个注解,是因为现在使用MyBatis框架,dao对象 是MyBatis通过代理生成的。不需要使用@Repository,所以没有使用。
@Component:放在类的上面,创建此类的对象,放入到容器中。
赋值的:
@Value:简单类型的赋值,例如在属性的上面使用@Value("李四") private String name
还可以使用@Value获取配置文件者的数据(properties或yml)。
@Value("${server.port}") private Integer port
@Autowired:引用类型赋值自动注入的,支持byName, byType. 默认是byType 。 放在属性的上面,也可以放在构造 方法的上面。 推荐是放在构造方法的上面
@Qualifer:给引用类型赋值,使用byName方式。
@Autowird, @Qualifer都是Spring框架提供的。
@Resource:来自jdk中的注解,javax.annotation。 实现引用类型的自动注入, 支持byName, byType.
默认是byName, 如果byName失败,再使用byType注入;在属性上面使用。
其他:
@Configuration:放在类的上面,表示这是个配置类,相当于xml配置文件
@Bean:放在方法的上面,把方法的返回值对象,注入到spring容器中。
@ImportResource:加载其他的xml配置文件,把文件中的对象注入到spring容器中。
@PropertySource:读取其他的properties属性配置文件
@ComponentScan:扫描器,指定包名,扫描注解的
@ResponseBody:放在方法的上面,表示方法的返回值是数据,不是视图
@RequestBody:把请求体中的数据,读取出来,转为java对象使用。
@ControllerAdvice:控制器增强,放在类的上面,表示此类提供了方法,可以对controller增强功能。
@ExceptionHandler:处理异常的,放在方法的上面
@Transactional:处理事务的, 放在service实现类的public方法上面,表示此方法有事务
SpringBoot中使用的注解:
@SpringBootApplication:放在启动类上面,包含了@SpringBootConfiguration
@EnableAutoConfiguration,@ComponentScan
MyBatis相关的注解:
@Mapper:放在类的上面,让MyBatis找到接口,创建他的代理对象
@MapperScan:放在主类的上面,指定扫描的包,把这个包中的所有接口都创建代理对象,将对象注入到容器中
@Param:放在dao接口的方法的形参前面,作为命名参数使用的。
Dubbo注解:
@DubboService:在提供者端使用的,暴露服务的,放在接口的实现类上面
@DubboReference:在消费者端使用的,引用远程服务,放在属性上面使用。
@EnableDubbo:放在主类上面,表示当前引用启用Dubbo功能。
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk=Var(yt)Cov(yt,yt−k)其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它