Spring入门
POM依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
Bean定义文件/resource/spring-context.xml:
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDAO" class="com.qf.dao.UserDAOImpl" scope="singleton" lazy-init="false" init-method="init" destroy-method="destroy"/>
<bean id="userService" class="com.qf.service.UserServiceImpl" autowire="byName">
<property name="userDAO" ref="userDAO"/>
</bean>
</beans>
编写对应的java类;
开始使用:
//获取context
ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
//获取bean
UserService userService = (UserService) context.getBean("userService");
spring-ioc-xml方式
Bean实例化三种方式
无参构造方法实例化(id,class)
工厂静态方法实例化(id,class,factory-method)
工厂实例方法实例化(id,factory-bean,factory-method)
Bean的作用范围
scope属性:singleton(默认值)和prototype
singleton:单例模式,默认饿汉模式
prototype:非单例模式,默认懒汉模式
Bean的生命周期方法
init-method
destroy-method
依赖注入
1、set方法注入:
<bean id="userDAO" class="com.qf.dao.UserDAOImpl"/>
<bean id="userService" class="com.qf.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
UserServiceImpl类中含有字段userDAO,和对应setter方法。
2、构造方法注入
<bean id="student" class="com.qf.entity.Student">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="小明"/>
</bean>
构造方法入参需要按顺序写,name并不是入参名,它可以随便写。
3、自动注入
<!--no方式,默认值,即不使用自动注入-->
<!--byName方式,需要UserServiceImpl类中有个字段叫userDAO-->
<bean id="userDAO" class="com.qf.dao.UserDAOImpl"/>
<bean id="userService" class="com.qf.service.UserServiceImpl" autowire="byName"/>
<!--byType方式,spring容器会找到符合类型的bean赋给字段,如果找到多个bean则报错-->
<bean id="aaa" class="com.qf.dao.UserDAOImpl"/>
<bean id="userService" class="com.qf.service.UserServiceImpl" autowire="byType"/>
<!--constructor方式,构造方法的byType方式,会自动注入构造入参,必须要能找到唯一一个符合的bean-->
<bean id="aaa" class="com.qf.dao.UserDAOImpl"/>
<bean id="userService" class="com.qf.service.UserServiceImpl" autowire="constructor"/>
注入数据的类型
1、普通数据类型
2、引用数据类型
3、集合数据类型
<bean id="user" class="com.qf.entity.User">
<!--int-->
<property name="id" value="1"/>
<!--String-->
<property name="name" value="张灿"/>
<!--Date,要按格式写-->
<property name="bornDate" value="1993/10/1 1:2:3"/>
<!--String[]-->
<property name="hobbys">
<array>
<value>football</value>
<value>basketball</value>
</array>
</property>
<!--List-->
<property name="names">
<list>
<value>tom</value>
<value>jack</value>
</list>
</property>
<!--Set-->
<property name="phones">
<set>
<value>11122223333</value>
<value>11122223333</value>
</set>
</property>
<!--Map-->
<property name="countries">
<map>
<entry key="zh" value="china"/>
<entry key="en" value="english"/>
</map>
</property>
<!--Properties-->
<property name="files">
<props>
<prop key="url">jdbc:mysql:xxx</prop>
<prop key="username">root</prop>
</props>
</property>
<!--引用数据-->
<property name="address" ref="addr"/>
</bean>
<!--被引用的bean-->
<bean id="addr" class="com.qf.entity.Address">
<property name="id" value="1"/>
<property name="city" value="bj"/>
</bean>
引入其他配置文件
<import resource="other.xml"/>
spring-ioc-annotation方式
spring原始注解
@Component,使用在类上用于实例化Bean
@Controller,同@Component,表明用于web层
@Service,同@Component,表明用于service层
@Repository,同@Component,表明用于dao层
@Autowired,使用在字段上用于根据类型依赖注入
@Qualifier,结合@Autowired一起使用用于根据名称进行注入
@Resource,相当于@Autowired+@Qualifier
@Value,注入普通属性
@Scope,标注Bean的作用范围
@PostConstructor,标注Bean的初始化方法
@PreDestroy,标注Bean的销毁方法
spring新注解
@Configuration,用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解
@ComponentScan,用于指定spring在初始化容器时要扫描的包
@Bean,使用在方法上,标注将该方法的返回值存储到spring容器中
@PropertySource,用于加载properties文件中的配置
@Import,用于导入其他配置类
示例代码
编写配置类:
@Configuration
@ComponentScan("com.qf")
@PropertySource("classpath:jdbc.properties")
@Import(SubConfiguration.class)
public class SpringConfiguration {
@Bean("userDAO")
@Scope("singleton")
public UserDAO getUserDAO() {
return new UserDAO() {
@Override
public void deleteUser(int id) {
System.out.println("xxx");
}
};
}
}
其他Bean类:
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("${jdbc.driver}")
private String name;
@Autowired
@Qualifier("userDAO")
private UserDAO userDAO;
...
}
使用:
// 获取context
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// 获取bean
UserDAO userDAO = (UserDAO) context.getBean("userDAO");
spring集成junit
为什么要集成?
传统的junit测试方法中,需要手动创建spring容器和获取bean,如下:
ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
UserService userService = (UserService) context.getBean("userService");
spring集成junit步骤
(1)导入spring集成junit的依赖
(2)使用@Runwith注解替换原来的运行时
(3)使用@ContextConfiguration指定配置文件或配置类
(4)使用@Autowired注入需要测试的对象
(5)创建测试方法进行测试
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.9</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:spring-anno-ctx.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.deleteUser(1);
}
}
JavaWeb入门
简单示例
(1)新建java ee工程
(2)自动引入依赖:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
(3)创建一个Servlet:
package com.example.tomcattest;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
private String message;
public void init() {
message = "Hello World! servlet中文12";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
(4)上面servlet使用了注解方式,如果不用注解,则需要在webapp/WEB-INFO/web.xml
中指定映射关系:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.example.tomcattest.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello-servlet</url-pattern>
</servlet-mapping>
</web-app>
web容器监听器
每次在servlet方法中创建spring context不仅繁琐,而且增加内存消耗。
我们可以在web容器启动时,先创建好spring context,后面使用时直接获取。
(1)创建一个ServletContextListener,并添加注解:
@WebListener
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
sce.getServletContext().setAttribute("ctx", context);
System.out.println("ContextLoaderListener set ctx success.");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
(2)在servlet中获取:
ServletContext servletContext = request.getServletContext();
ApplicationContext context = (ApplicationContext) servletContext.getAttribute("ctx");
UserService userService = (UserService) context.getBean("userService");
SpringMVC
SpringMVC的开发步骤
1、导入SpringMVC包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.9</version>
</dependency>
2、配置Servlet(共有行为,web.xml文件)
<!--配置SpringMVC前端控制器-->
<servlet>
<servlet-name>DispatherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3、编写Controller(特有行为)
4、将Controller使用注解配置到Spring容器中
@Controller()
public class UserController {
@RequestMapping("/quick")
public String save() {
System.out.println("UserController.save...");
return "success.jsp";
}
}
5、创建SpringMVC核心配置文件(spring-mvc.xml文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/beans/spring-context.xsd">
<!--Controller的组件扫描-->
<context:component-scan base-package="com.example.tomcattest.controller"/>
</beans>
6、执行访问测试
SpringMVC的执行流程
1、用户请求至DispatcherServlet前端控制器
。
2、DispatcherServlet
调用HandlerMapping处理器映射器
。
3、HandlerMapping
找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
。
4、DispatcherServlet
调用HandlerAdapter处理器适配器
。
5、HandlerAdapter
经过适配调用具体的处理器(Controller
,也叫后端控制器)。
6、Controller
执行完成返回ModelAndView
。
7、HandlerAdapter
把ModelAndView
返回给DispatcherServlet
。
8、DispatcherServlet
把ModelAndView
传给ViewResolver视图解析器
。
9、ViewResolver
解析后返回具体View
。
10、DispatcherServlet
根据View
进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet
响应用户。
SpringMVC的XML配置解析
1、视图解析器
SpringMVC有默认组件配置,在org/springframework/web/servlet/DispatcherServlet.properties
文件中。
spring-web.xml
举例:
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
SpringMVC的数据响应
1.1 数据响应方式
1、页面跳转(直接返回字符串、通过ModelAndView对象返回)
2、回写数据(直接返回字符串、返回对象或集合)
1.2 页面跳转
1、返回字符串形式:此种方式会将返回的字符串与视图解析器的前后缀拼接后进行跳转。
在resource/spring-mvc.xml
中配置内部资源视图解析器:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
编写Controller:
@RequestMapping("/save")
public String save() {
return "success";
}
表示渲染/webapp/jsp/success.jsp
页面。
还可以返回带有前缀的字符串(不受前后缀影响):
转发:forward:save
表示交由save()方法处理。forward:/jsp/success.jsp
表示直接渲染该jsp页面。
重定向:redirect:save表示
交由save()方法处理且浏览器地址变为ip:port/save
。redirect:/jsp/success.jsp
表示直接渲染该jsp页面。
2、返回ModelAndView对象
基本用法:
@RequestMapping("model")
public ModelAndView model() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username", "张灿");//jsp中可以使用${username}
modelAndView.setViewName("success");
return modelAndView;
}
ModelAndView也可以注入:
@RequestMapping("model")
public ModelAndView model(ModelAndView modelAndView) {
modelAndView.addObject("username", "张灿");
modelAndView.setViewName("success");
return modelAndView;
}
可以只注入Model来设置数据,返回视图文件名:
@RequestMapping("model2")
public String model2(Model model) {
model.addAttribute("username", "model2");
return "success";
}
也可以注入传统的HttpServletRequest:
@RequestMapping("model3")
public String model3(HttpServletRequest request) {
request.setAttribute("username", "model3");
return "success";
}
1.3 回写数据
1、直接返回字符串
使用jave web的方式:
@RequestMapping("data")
public void data(HttpServletResponse response) throws IOException {
response.getWriter().print("data");
}
使用SpringMVC的方式:
@RequestMapping("data2")
@ResponseBody //告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回
public String data2() {
return "data2";
}
返回json字符串:
(1)pom.xml添加依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.15.0</version>
</dependency>
(2)编写User类(注意要有get方法)
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
(3)编写Controller类的方法
@RequestMapping("data3")
@ResponseBody
public String data3() throws JsonProcessingException {
User user = new User("zc", 30);
//使用jackson将对象转换成json格式字符串
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
}
2、返回对象或集合(需要导入上面的jackson)
先修改/resources/spring-mvc.xml
文件:
<!--配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
再编写Controller类的方法:
@RequestMapping("data4")
@ResponseBody
//期望SpringMVC自动将User转换成json格式字符串
public User data4() {
return new User("data4", 30);
}
上面配置spring-mvc.xml
比较麻烦,我们可以使用mvc的注解驱动代替上述配置:
(记得要引入mvc的命名空间)
(把上面的 配置处理器映射器 注释掉)
<!--mvc的注解驱动-->
<mvc:annotation-driven/>
原理:在SpringMVC的各个组件中,处理器映射器
、处理器适配器
、视图解析器
称为三大组件。
使用<mvc:annotation-driven/>
会自动加载RequestMappingHandlerMapping(处理器映射器)
和RequestMappingHandlerAdapter(处理器适配器)
,可在spring-mvc.xml
配置文件中替代注解处理器和适配器的配置。
同时默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
SpringMVC的请求
1.1 获得请求参数
1、请求参数类型
基本类型参数
POJO类型参数
数组类型参数
集合类型参数
1.2 获得基本类型参数
@RequestMapping("request")
@ResponseBody
public String request(String name, int age) {
return "name = " + name + ", age = " + age;
}
1.3 获得POJO类型参数
private static class Dog {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@RequestMapping("request2")
@ResponseBody
public String request2(Dog dog) {
return dog.toString();
}
//发送请求:/api/request2?name=abc&age=11
1.4 获得数组类型参数
@RequestMapping("request3")
@ResponseBody
public String request3(String[] strs) {
return Arrays.toString(strs);
}
//发送请求:/api/request3?strs=a&strs=b
1.5 获得集合类型参数——封装POJO方式
准备一个jsp页面来发送表单:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Form</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/request4" method="post">
<input type="text" name="dogList[0].name"><br/>
<input type="text" name="dogList[0].age"><br/>
<input type="text" name="dogList[1].name"><br/>
<input type="text" name="dogList[1].age"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
处理请求:
private static class VO {
private List<Dog> dogList;
public List<Dog> getDogList() {
return dogList;
}
public void setDogList(List<Dog> dogList) {
this.dogList = dogList;
}
@Override
public String toString() {
return "VO{" +
"dogList=" + dogList +
'}';
}
}
@RequestMapping("request4")
@ResponseBody
public String request4(VO vo) {
return vo.toString();
}
1.6 获得集合类型参数——直接接收数据
处理请求:
@RequestMapping("request5")
@ResponseBody
//要给入参加上@RequestBody注解
public String request5(@RequestBody List<Dog> dogList) {
return dogList.toString();
}
发送请求:要表示json类型
POST http://localhost:8080/tomcat_test_war_exploded/request5
Content-Type: application/json;charset=utf-8
[
{"name":"a","age":1},
{"name":"b","age":2}
]
1.7 静态资源访问的开启
静态资源是路径形式的,RequestMapping映射不出来,可以这样解决:
<!--方法1:指定静态资源不走RequestMapping映射-->
<mvc:resources mapping="/static/**" location="/static/"/>
<!--方法2:如果RequestMapping映射失败,交由原始容器处理-->
<mvc:default-servlet-handler/>
1.8 配置全局乱码过滤器
在web.xml,修改读取输入参数时的编码:
<!--配置全局过滤的filter-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在spring-mvc.xml,修改输出时的编码:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
1.9 参数绑定注解@requestParam
@RequestMapping("request6")
@ResponseBody
public String request6(@RequestParam(value = "name", required = false, defaultValue = "def") String username) {
return username;
}
1.10 获得RESTful风格的参数
@RequestMapping(value = "request7/{name}", method = RequestMethod.GET)
@ResponseBody
public String request7(@PathVariable(value = "name", required = false) String username) {
return "用户" + username;
}
1.11 自定义类型转换器
自定义转换器类实现Converter接口:
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
在spring-mvc.xml中声明转换器:
<!--声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.example.tomcattest.converter.DateConverter"/>
</set>
</property>
</bean>
spring-mvc.xml在<annotation-driven>
中引用转换器:
<mvc:annotation-driven conversion-service="conversionService"/>
1.12 获得Servlet相关API
直接注入就行:
@RequestMapping("request9")
@ResponseBody
public void request9(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
1.13 获得请求头
(1)使用@RequestHeader
@RequestMapping("request10")
@ResponseBody
public String request10(@RequestHeader(value = "User-Agent", required = false) String userAgent) {
return userAgent;
}
(2)特殊的头@CookieValue
@RequestMapping("request11")
@ResponseBody
public String request11(@CookieValue("JSESSIONID") String jsessionID) {
return jsessionID;
}
1.2 文件上传
1.1 客户端表单实现
(1)导入fileupload和io坐标(实际使用不写也没事)
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
(2)sping-mvc配置文件上传解析器
bean id是固定的,不能随便写,不然会不生效。
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="500000000"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
(3)编写处理代码
@RequestMapping("upload")
@ResponseBody
public String upload(String name, MultipartFile uploadFile) {
return name + ", " + uploadFile.getOriginalFilename();
}
发送请求:
POST /tomcat_test_war_exploded/upload HTTP/1.1
Host: localhost:8080
Content-Length: 294
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="uploadFile"; filename="/C:/Users/张灿/Pictures/高性能.png"
Content-Type: image/png
(data)
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"
张灿
------WebKitFormBoundary7MA4YWxkTrZu0gW--
如果上传多个同名的文件,则在服务端用MultipartFile[]接收。
SpringMVC的拦截器
1.1 拦截器的作用
对处理器进行预处理和后处理。
1.2 拦截器快速入门
①创建拦截器类实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor {
//在目标方法执行之前,执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String s = String.format("MyInterceptor1.preHandle: request_path={%s}", request.getPathInfo());
System.out.println(s);
return HandlerInterceptor.super.preHandle(request, response, handler);
}
//在目标方法执行之后、视图对象返回之前,执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String s = String.format("MyInterceptor1.postHandle: response_status={%d}", response.getStatus());
System.out.println(s);
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
//在流程都执行完毕后,执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1.afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
②配置拦截器
spring-mvc.xml:
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.example.spring_test.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
③测试
访问http://localhost:8080/spring_test_war_exploded/test/test1
查看打印:
MyInterceptor1.preHandle: request_path={/test/test1}
TestController.test1
MyInterceptor1.postHandle: response_status={200}
MyInterceptor1.afterCompletion
login拦截器示例
拦截器类:
public class PrivilegeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (user == null) {
// response.sendError(401, "未登录");
request.getRequestDispatcher("/login/unlogined").forward(request, response);
return false;
}
return true;
}
}
spring-mvc配置拦截器:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<mvc:exclude-mapping path="/login/**"/>
<bean class="com.example.spring_test.interceptor.PrivilegeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
login请求的操作:
@Controller
@RequestMapping("login")
public class LoginController {
@Autowired
private UserService userService;
@PostMapping
@ResponseBody
public String login(@RequestBody User user, HttpSession session) {
User user1 = userService.login(user.getUsername(), user.getPassword());
if (user1 != null) {
session.setAttribute("user", user1);
return "登录成功";
}
return "登录失败";
}
@RequestMapping("unlogined")
@ResponseBody
public String unLogined() {
return "未登录";
}
}
SpringMVC的异常处理
1.1 异常处理的两种方式
(1)使用框架提供的简单异常处理器SimpleMappingExceptionResolver
(2)使用自定义异常处理器,实现HandlerExceptionResolver
1.2 简单异常处理器
在spring-mvc.xml配置:
<!--配置异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/>
<property name="exceptionMappings">
<map>
<entry key="com.example.spring_test.exception.MyException1" value="error1"/>
</map>
</property>
</bean>
1.3 自定义异常处理器
(1)编写异常处理类:
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
if (ex instanceof MyException1) {
modelAndView.addObject("info", "自定义异常");
}
modelAndView.setViewName("error");
return modelAndView;
}
}
(2)配置spring-mvc.xml:
<bean class="com.example.spring_test.exception.MyExceptionResolver"/>
tomcat控制台中文乱码问题
tomcat启动控制台和system.out.println输出中文乱码问题 - 癫狂编程 - 博客园 (cnblogs.com)
Spring JdbcTemplate
1、开发步骤
导入spring-jdbc和spring-tx
创建数据库表和实体
创建JdbcTemplate对象
执行数据库操作
2、入门代码
(1)导入依赖:
mysql-connector-java、c3p0、spring-jdbc
(2)在mysql中创建数据表
(3)代码
//创建数据源对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/sql_test");
dataSource.setUser("root");
dataSource.setPassword("toor");
//创建JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//执行更新操作
int rows =jdbcTemplate.update("insert into account value(?,?)", "zc1", "1.1");
System.out.println("更新行数:" + rows);
3、Spring产生JdbcTemplate
就用spring的ioc实现,这里以xml方式方式举例。
(1)编写spring配置文件/resource/spring-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
(2)上面引用的属性文件/resource/jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sql_test
jdbc.username=root
jdbc.password=toor
(3)使用:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);
int rows =jdbcTemplate.update("insert into account value(?,?)", "zc2", "1.2");
System.out.println("更新行数:" + rows);
4、常用操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class JdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testUpdate() {
jdbcTemplate.update("update account set money=? where name=?", 10, "zc1");
}
@Test
public void testDelete() {
jdbcTemplate.update("delete from account where name=?", "zc1");
}
@Test
public void testQueryAll() {
List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
System.out.println(accountList);
}
@Test
public void testQueryOne() {
Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<>(Account.class), "zc2");
System.out.println(account);
}
@Test
public void testQueryCount() {
Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
}
}
Spring AOP
AOP,面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
实现原理:基于jdk的动态代理、基于cglib的动态代理。
基于jdk的动态代理
public class ProxyDemo {
public static void main(String[] args) {
//目标对象
Target target = new Target();
//增强对象
Advice advice = new Advice();
//handler
InvocationHandler handler = new InvocationHandler() {
//调用代理对象的任何方法,都会走进这里
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();
Object ret = method.invoke(target, args);
advice.after();
return ret;
}
};
//代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
TargetInterface.class.getClassLoader(),//目标对象类加载器
new Class<?>[]{TargetInterface.class},//目标对象实现的接口
handler
);
proxy.save();
}
}
基于cglib的动态代理
public class ProxyDemo {
public static void main(String[] args) {
//目标对象
Target target = new Target();
//增强对象
Advice advice = new Advice();
//基于cglib生成代理对象
//1、创建增强器
Enhancer enhancer = new Enhancer();
//2、设置父类
enhancer.setSuperclass(Target.class);
//3、设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
advice.before();
Object ret = method.invoke(target, args);
advice.after();
return ret;
}
});
//4、创建代理对象
Target proxy = (Target) enhancer.create();
proxy.save();
}
}
AOP相关概念
Target(目标对象):代理的目标对象
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Joinpoint(连接点):那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):要对哪些Joinpoint进行拦截
Advice(通知/增强):拦截到Joinpoint之后要做的事情
Aspect(切面):切入点和通知的结合
Waeving(织入):把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类加载织入
基于xml的AOP开发
入门代码
①导入AOP相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
②创建目标接口和目标类(内部有切点)
public interface TargetInterface {
void save();
}
public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("Target.save");
}
}
③创建切面类(内部有增强方法)
public class MyAspect {
public void before() {
System.out.println("前置增强...");
}
}
④将目标类和切面类的对象创建权交给spring
<!--目标对象-->
<bean id="target" class="com.qf.proxy.aop.Target"/>
<!--切面对象-->
<bean id="myAspect" class="com.qf.proxy.aop.MyAspect"/>
⑤在spring-context.xml中配置织入关系
<!--配置织入:哪些方法需要进行哪些增强-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切点+通知-->
<aop:before method="before" pointcut="execution(public void com.qf.proxy.aop.Target.save())"/>
</aop:aspect>
</aop:config>
⑥测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1() {
target.save();
}
}
切点表达式的写法
execute([修饰符] 返回值 包名.类名.方法名(参数))
举例:
execute(public void com.zc.Target.method(String,int)) //具体某个方法
execute(void com.zc.Target.*(..)) //Target类下的所有返回值为void的方法
execute(* com.zc.*.*(..)) //com.zc包下的所有类的方法
execute(* com.zc..*.*(..)) //com.zc包及其子包下的所有类的方法
execute(* *..*.*(..)) //所有方法
通知的种类
<aop:before>:前置通知
<aop:after-returning>:后置通知
<aop:around>:环绕通知,即前置+后置都执行
<aop:after-throwing>:异常抛出通知,即出现异常时执行
<aop:after>:最终通知,即无论是否有异常都会执行
其中,around的增强方法比较特殊,需要这样定义:
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强...");
Object ret = pjp.proceed();
System.out.println("环绕后增强...");
return ret;
}
切点表达式的抽取
前面的切点表达式很多是重复的,我们可以把切点表达式抽取出来,然后引用它:
<!--配置织入:哪些方法需要进行哪些增强-->
<aop:config>
<!--抽取切点表达式-->
<aop:pointcut id="aopPointcut" expression="execution(* com.qf.proxy.aop.Target.*(..))"/>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切点+通知-->
<aop:before method="before" pointcut-ref="aopPointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="aopPointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="aopPointcut"/>
<aop:after method="after" pointcut-ref="aopPointcut"/>
</aop:aspect>
</aop:config>
基于注解的AOP开发
入门代码
①创建目标接口和目标类(内部有切点)
public interface TargetInterface {
void save();
}
@Component("target1")
public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("Target.save");
}
}
②创建切面类(内部有增强方法)
@Component("myAspect")
@Aspect
public class MyAspect {
@Before("execution(* com.qf.proxy.anno.Target.*(..))")
public void before() {
System.out.println("前置增强...");
}
//引用切点表达式
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("后置增强...");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强...");
Object ret = pjp.proceed();
System.out.println("环绕后增强...");
return ret;
}
@AfterThrowing("MyAspect.pointcut()")
public void afterThrowing() {
System.out.println("抛出异常后增强...");
}
@After("pointcut()")
public void after() {
System.out.println("最终增强...");
}
//切点表达式的抽取
@Pointcut("execution(* com.qf.proxy.anno.Target.*(..))")
private void pointcut() {
}
}
③将目标类和切面类的对象创建权交给spring
④在切面类中使用注解配置织入关系
见步骤2
⑤在配置文件中开启组件扫描和AOP自动代理
@Configuration
@ComponentScan("com.qf.proxy.anno")
@EnableAspectJAutoProxy
public class MyAppConfiguration {}
⑥测试
声明式事务控制
编程式事务控制相关对象
PlatformTransactionManager
接口是spring的事务管理器。
TransactionDefinition
是事务的定义信息对象。
TransactionStatus
是事务具体的运行状态。
基于xml的声明式事务控制
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通知:事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务的aop织入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zc.service.impl.*.*(..))"/>
</aop:config>
基于注解的声明式事务控制
平台事务管理器配置(xml方式)
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
事务通知的配置(注解配置),写在业务类中
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED)
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan, money);
int a = 1/0;
accountDao.in(inMan, money);
}
}
事务注解驱动的配置(xml方式)
<tx:annotation-driven transaction-manager="transactionManager"/>