一、Spring
1.Spring概念
Spring是分层得javaSE/EE应用full-stack轻量级开源框架,以ioC和AOP为内核。提供了展现层SpringMVC和持久层Spring JDBCTEmplete以及业务层事务管理等众多得企业级应用技术,还能整合开源世界众多得第三方框架和类库,逐渐成为使用最多得JavaEE企业应用开源框架
2.Spring的优势
(1)方便解耦,简化开发:通过Spring提供的IOC容器,可以将对象之间的依赖关系交由Spring进行控制,避免编码所造成的过度耦合,用户也不必为单调模式类,属性解析等这些底层的需求编写代码,可以更专注与上层的应用
(2)AOP编程的支持:通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现
(3)声明式事务的支持:可以将我们从单调烦闷的事务管理中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量
(4)方便程序的测试:可以用容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
(5)方便集成各种优秀框架:Spring对各种优秀框架的支持
(6)降低JavaEE API的使用难度:Spring对JavaEE API进行了薄薄的封层装,使得这些API的使用难度大卫降低
(7)Java源码是经典学习范例:Spring的源代码设计精妙,结构清晰,匠心独用,他的源代码是java技术的最佳时间案例。
3.Spring程序的开发步骤
(1)导入Spring开发的基本包坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
</dependencies>
(2)编写Dao接口和实现类
(3)创建Spring核心配置文件
(4)在Spring配置文件中配置UserDaoImpl
(5)使用Spring的API获得Bean实例
4.Spring中bean标签中的scope属性
bean标签scope属性:
作用:用于指定bean的作用范围
取值:常用的是单例和多例
singleton 单例(默认)
prototype 多例
request 作用于web应用的请求范围
session 作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,他就是session
5.Bean实例化的三种方式
(1)无参构造方法实例化(最常用)
(2)工厂静态方法实例化
(3)工厂实例方法实例化
6.Bean的依赖注入分析
目前UserService实例和UserDao实例都存在与Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在程序中结合使用。因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是User Service,所以可以在Spring容器中,将UserDao设置到UserService内部。
这样就涉及到了依赖注入的概念。所谓依赖注入就是Spring框架核心IOC的具体实现,在编写程序时,通过控制反转,把对象的创建交给Spring,但是代码中不可能出现没有依赖的情况,IOC解耦只是降低他们的依赖关系,但不会消除。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
Bean的依赖注入方式有两种。一、通过set方法,二、通过构造方法
(1)Set方法
第一步,先在UserServiceImpl类里面创建set方法,如下
public class UserServiceImpl implements UserService {
private Demo1 demo1;
public void setDemo1(Demo1 demo1){
this.demo1=demo1;
}
@Override
public void save() {
System.out.println("你好呀,刘金隆");
demo1.sou();
}
}
第二部,在applicationContext.xml里面配置
<bean id="userService" class="Service.Impl.UserServiceImpl" scope="prototype" >
<property name="demo1" ref="demo1"></property>
</bean>
第三步,进行测试
public class UserServiceTest {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
7.Bean的依赖注入的数据类型
依赖注入除了对象的引用可以注入,普通的数据类型,集合等都可以在容器中进行注入。注入数据的三种数据类型为普通数据类型,引用数据类型,集合数据类型
8.引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置到其他配置文件中,而在Spring主配置文件通过import标签进行加载。如
<import resource ="applicationContext-xxx.xml"/>
9.Spring配置文件知识要点
10.ApplicationContext的实现类
11,数据源(连接池)的作用
(1)数据源(连接池)是提高程序性能出现的
(2)事先实例化数据源,初始化部分连接资源
(3)使用连接资源从数据源中获取
(4)使用完毕后将连接资源归还给数据yuan
12.数据源的开发步骤
(1)导入数据源的坐标和数据库驱动坐标
(2)创建数据源对象
(3)设置数据源的基本连接对象
(4)使用数据源获取连接资源和归还连接资源
13.Spring配置数据源
14,Spring的原始注解
Spring原始注解主要是替代的配置
注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法
注意:
(1).使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
需要在application Context.xml中编写如下代码
<!-- 注解的组件扫描-->
<context:component-scan base-package="Dao"/>
<context:component-scan base-package="Service" />
其中base-package里面的值为需要扫描的包名,在这里我扫描了Dao和Service两个包
(2).在web层的@Component注解可以用@Controller代替
在Service层的@Component注解可以用@Service注解代替
在Dao层的@Component注解可以用@Repository注解代替
(3).@Autowired注解是按照数据类型从Spring容器中进行匹配
@Qualifier注解是按照id值从容器中进行匹配的,但是此处的Qualifier必须结合@Autowired注解使用,如
@Autowired//
@Qualifier("userDao")
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Resource注解相当于@Autowired+@Qualifier结合使用,使用它有属性
如
//@Autowired//
//@Qualifier("userDao")
@Resource(name = "userDao")
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
相当于上面的代码
(4)、@value注解,相当于定义一个常数,为其赋值,如
@Value("${jdbc.driver}")
private String driver;
就是定义了一个字符串为Driver,然后通过@Value注解,用${}表达式获取jdbc.driver的值
(5).@PostConstruct注解和PreDestroy注解
@PostConstruct
public void init(){
System.out.println("Service对象的初始化方法");
}
@PreDestroy
public void destory(){
System.out.println("Service对象的销毁方法");
}
ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.cou();
app.close();
如上述代码,在测试过程中用ApplicationContext实例化的对象不能进行关闭资源的操作,所以需要升个级,用ClassPathXmlApplication来实例化对象。
15、Spring新注解
使用上述的注解不能全部替代xml文件,还需要使用注解替代的配置如下
- 非自定义的Bean的配置
- 加载properties文件的配置context:property-placeholder
- 组件扫描的配置context:component-scan
- 引入其他文件
所以就有了Spring新注解
@Configuration 作用:指定当前类是一个配置类,等同于applicationContext.xml
@ComponentScan 作用:用于通过注解指定spring在创建容器时要扫描的包
属性:value,basePackages(两个属性别名互相引用,所以作用相同)指定要扫描的包
使用注解@ComponentScan(basePackages = {“com.study”})作用等同于
在xml中配置了<context:component-scan base-package=“com.study”></context:component-scan>
@Bean 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:name:用于指定bean的id,当不写时,默认值时当前方法的名称
细节:当使用注解配置方法时,如果方法有参数,spring会去容器中查找有没有可用的bean对象
查找的方式和Autowired相同,根据类型匹配,有一个注入成功,没有注入失败,有多个会找bean的id和该参数名相同的进行匹配
如果有多个,可以在参数前使用@Qualifier("")注解指定容器中的bean(单独使用的情况)
@Bean
public JdbcTemplate createJdbcTemplate(@Qualifier(“ds1”) DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Scope 作用:声明此bean对象是多例的
@Import 作用:导入其他配置类
参数:value:用于指定其他配置类的字节码
当我们使用@Import后,当前类为主配置类,导入的为子配置类
@PropertySource 作用:用于指定properties文件的位置
属性:value:指定文件的名称和路径
关键字:classpath:表示类路径下
注意:当使用新注解之后,通过新注解@Configuration 创建了一个配置类,那么如何使用这个配置类 ,如下
public static void main(String[] args) {
// ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService bean = app.getBean(UserService.class);
bean.cou();
}
在这里,new一个AnnotationConfigApplicationContext,里面传入新建的配置类的地址为SpringCongiguration.class
16、Spring集成Junit
在原始Junit测试Spring的问题中,每个测试都有
// ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService bean = app.getBean(UserService.class);
两行代码,这两行代码的作用是获取容器,如果不写,直接会提示空指针异常,所以不能轻易删除。
为了解决上述问题,让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉他,然后将要进行测试的Bean直接在测试类中进行注入。这就是Spring继承Junit
(1)Spring集成Junit步骤
- 导入Spring集成Junit的坐标
- 使用@Runwith注解替换原来的运行期
- 使用@ContextConfiguration指定配置文件或配置类
- 使用@Autowire注入需要测试的对象
- 创建测试方法进行测试
例子如下
第一步,导入Spring集成Junit的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.6</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
//使用@Runwith注解替换原来的运行期
@ContextConfiguration("classpath:applicationContext.xml")
//或者@ContextConfiguration(classes=xxxx.class)
//使用@ContextConfiguration指定配置文件或配置类
public class SprngJunitTest {
@Autowired
//使用@Autowire注入需要测试的对象
private UserService userService;
@Test
//创建测试方法进行测试
public void test1(){
userService.cou();
}
}
17、Spring与Web环境的集成
(1)ApplicationContext应用上下文获取方式
应用上下文对象是通过newClasspathXmlApplicationContext(Spring配置文件)方式获取的,但是每次从容器中过的Bean时都要编写new ClasspathXmlApplicationContext(Spring配置文件),这样的弊端时配置文件加载多次,应用上下文对象创建多次。如
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfigurtion.class);
UserService u = app.getBean(UserService.class);
u.cou();
}
在web项目中,可以使用ServletContext监听Web应用的启动,我们可以在web应用启动时,就加载Spring的配置文件,创建应用上下文对象Application,再将其存储到最大的域selrvletContext中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。步骤如下;
(1)定义一个类去实现ServletContextListener类。ServletContextListener类它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。会重写两个方法。
public class ContextLoaderListener implements ServletContextListener {
@Override
//上下文初始化方法
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext=servletContextEvent.getServletContext();
//读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//将这个Spring应用上下文对象存储到ServletContext域中
ApplicationContext app=new AnnotationConfigApplicationContext(contextConfigLocation);
servletContext.setAttribute("app",app);
System.out.println("Spring 容器创建完毕");
}
@Override
//上下文销毁的方法
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
在上述代码中通过全局初始化参数来优化代码
<!-- 全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.xml</param-value>
</context-param>
(2)需要在web.xml文件中去配置监听器。
在上一步中创建了一个类实现了ServletContextListener方法。实现了ServletContextListener接口的作用就是当项目一经启动,就会激活实现了此接口的类方法,可以进行相关的初始化操作。ServletContextListener接口实现了
public void contextInitialized(ServletContextEvent event)与
public void contextDestroyed(ServletContextEvent event)
两个方法,
意味着项目一经启动,会进入contextInitialized方法中,进行Spring的相关配置。并且contextInitialized方法有ServletContext参数,可以在web.xml中配置参数,用来ServletContext读取相关Spring配置文件, 一般 Dao, Service 的 Spring 配置都会在 listener 里加载。
项目退出时激活contextDestroyed方法。
<!-- 配置监听器-->
<listener>
<listener-class>com.example.Listener.ContextLoaderListener</listener-class>
</listener>
(3)在web层应用
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfigurtion.class);
ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
UserService u = app.getBean(UserService.class);
u.cou();
}
(2)Spring提供获取应用上下文的工具
上面(1)中的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
我们只需要做两件事
-ContextLoaderListener监听器(导入Spring-web坐标)。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE </version>
</dependency>
- 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext。
ServletContext servletContext = this.getServletContext();
WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService user = app.getBean(UserService.class);
user.cou();
二、SpringMVC
1.SpringMVC概述
SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中。
它就是一个Spring内置的MVC框架。它解决WEB开发中的常见问题(参数接收,文件上传,表单验证,国际化等等),而且使用简单,与Spring无缝衔接,支持RESTful风格的url请求。采用了松散耦合可插拔的组件结构,比其他MVC框架更具拓展性和灵活性。
他(MVC:Model-View-Controller)的作用:是解决页面代码和后台代码的分离。
SpringMVC的原理:在没有使用SpringMVC之前我们都是使用Servlet在做Web开发。但是使用Servlet开发在接收请求参数,数据共享,页面跳转等操作相对比较复杂。servlet是java进行web开发的标准,既然springMVC是对servlet的封装,那么很显然SpringMVC底层就是Servlet,SpringMVC就是对Servlet进行深层次的封装。
2.SpringMVC的开发步骤
需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。
- 导入SpringMVC的相关坐标,在pom.xml文件中添加spring-webmvc依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version> </dependency>
webmvc依赖包含beans、context、core、expression、commons-logging、aop、web、webmvc,换言之导入一个webmvc依赖,就间接导入了启动mvc框架的所有依赖。
–web:spring对web项目的支持。
–webmvc:SpringMVC核心包。 - 配置SpringMVC核心控制器DispathcerServlet
<!-- 配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 创建Controller类和视图页面
//创建的Controller类
public class UserController {
public String save(){
System.out.println("Controller save running");
return "success.jsp";
}
}
<%-- 创建视图 --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
succesee
</body>
</html>
- 使用注解配置Controller类中业务方法的映射地址。在这一步中,使用注解@Controller和@RequestMapping来配置Controller类中的业务方法,但是这个东西对于Spring来说是不知道的,
@Controller
public class UserController {
@RequestMapping("/quick")
public String save(){
System.out.println("Controller save running");
return "success.jsp";
}
}
- 配置SpringMVC核心配置文件spring-mvc.xml。由于在上一步中,配置完的Controller类中配置好的业务方法,对于Spring时不知道的,所以需要配置一个Spring文件来进行组件扫描。
<?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">
<!-- Controller的组件扫描-->
<context:component-scan base-package="com.example.controller"/>
</beans>
在这一步中由于需要context命名空间,需要补一个context命名空间。如上代码中的xmlns:context="http://www.springframework.org/schema/context" 和
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
但是,新建的这个Spring文件没有被加载。需要告诉SpringMVC它在哪。需要在配置Servlet时告诉它这个Spring文件在哪,需要在第二步配置SpringMVC核心控制器DispathcerServlet中加入里面的这段代码
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
- 客户端发起请求测试
3、SpringMVC执行流程和原理
–SpringMVC执行流程:
01、用户发送出请求被前端控制器DispatcherServlet拦截进行处理。
02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
03、HandlerMapping找到具体的处理器(查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
04、DispatcherServlet调用HandlerAdapter(处理器适配器)。
05、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
06、Controller执行完成返回ModelAndView对象。
07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
09、ViewReslover解析ModelAndView后返回具体View(视图)给DispatcherServlet。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应View给用户。
–涉及组件分析:
1、前端控制器DispatcherServlet(不需要程序员开发)由框架提供,在web.xml中配置。
作用:接收请求,响应结果,相当于转发器,中央处理器。
2、处理器映射器HandlerMapping(不需要程序员开发)由框架提供。
作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。
3、处理器适配器HandlerAdapter(不需要程序员开发)由框架提供。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler中的方法。
4、处理器Handler(也称之为Controller,需要程序员开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。
5、视图解析器ViewResolver(不需要程序员开发)由框架提供。
作用:进行视图解析,把逻辑视图解析成真正的物理视图。
SpringMVC框架支持多种View视图技术,包括:jstlView、freemarkerView、ThymeleafView等。
6、视图View(需要工程师开发)
作用:把数据展现给用户的页面
View是一个接口,实现类支持不同的View技术(jsp、freemarker、pdf等)
4、SpringMVC组件解析
(1)SpringMVC注解解析
@RequestMapping(请求映射)
作用:用于建立请求URL和处理请求方法之间的对应关系
位置:
类上,请求URl的第一级访问目录,与方法上的二级访问目录拼接访问,此处不写的话,就相当于应用的根目录。
方法上,请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一级目录一起组成访问虚拟路径。
属性:
- value:用于指定的请求的URL,它和path属性的作用时一样的
- method:用于指定请求的方式
- params:用于指定限制请求参数的条件,它支持简单的表达式,要求请求参数的key和value必须和配置的一摸一样。
5、SpringMVC的数据响应
-
页面跳转
(1) 直接返回字符串:此种凡是会将返回的字符串与视图解析器的前后缀拼接后跳转。
(2) 通过ModelAndView对象返回
-
回写数据
直接返回字符串:①通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void。
@RequestMapping("/quick3")
public void save3(HttpServletResponse response) throws IOException {
response.getWriter().println("hello world");
}
②将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在HTTP响应体中返回。
@RequestMapping("/quick4")
@ResponseBody//告知SpringMVC框架不进行视图跳转,直接进行数据响应
public String save4() throws IOException {
return "hello world";
}
返回对象或者集合:①通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在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>
实现代码如下;
@RequestMapping("/quick5")
@ResponseBody//告知SpringMVC框架不进行视图跳转,直接进行数据响应
//期望SpringMVC自动将User转换成json格式的字符串
public User save5() throws IOException {
User user=new User();
user.setUsername("shangsan");
user.setAge(15);
return user;
}
②在方法中添加@ResponseBody就可以返回json格式的字符串,但是这样的配置比较麻烦,配置的代码比较多(如上①),因此,我们可以使用mvc的注解驱动代替上述配置。
<!-- mvc的注解驱动-->
<mvc:annotation-driven/>
在SpringMVC的各个组件中,处理器映射器,处理器适配器,视图解析器称为SpringMVC的三大组件。使用自动加载RequestMapping(处理映射器)和RequestMappingHanderAdapter(处理适配器),可在Spring-xml.xml配置文件中使用mvc:annotation-driver替代注解处理器和适配器的配置。同时使用mvc:annotation-driven默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
6、SpringMVC获得请求数据响应(从前端页面获取数据到后台)
1、客户端请求参数的格式是:name=value&name=value…
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数;
- 基本数据类型
- POJO类型参数
- 数组类型参数
- 集合类型参数
1.1获得基本类型参数
controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
1.2获得POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
public class UserController {
@RequestMapping("/quick7")
@ResponseBody
public void save7(User user) throws IOException {
System.out.println(user);
}
1.3获得数组类型数据
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
@RequestMapping("/quick8")
@ResponseBody
public void save8(String[] str) throws IOException {
System.out.println(Arrays.asList(str));
}
1.4获得集合类型参数
获得集合参数时,要将集合参数包装到一个POJO中才可以。
当使用ajax提交时,可以指定controllerType为json形式,那么方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
<!-- 开放资源的访问-->
<mvc:resources mapping="/js/**" location="/js/"/>
mapping代表映射地址,location代表是哪个目录下的资源
<mvc:default-servlet-handler/>
上面这句代码代表的意思是访问找对应的匹配地址,如果找不到就交由原始的容器,让他内部的机制去找,和上述开放资源访问的代码起一个作用。
1.5请求数据乱码问题
当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤。在xml.web中配置
<!-- 配置全局过滤的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>
1.6参数绑定@ResquestParam
当请求的参数名称与Controller的业务参数不一样时就需要通过@ResquestParam注解显示的绑定。
注解@ResquestParam参数
- value:与请求参数的名称
- required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错。
- defaultValue:当没有指定请求参数时,则使用指定的默认值赋值。
1.7获得Restful风格的参数
Restful是一种软件架构风格,设计风格,而不是标准,只是提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用“url+请求风格”表示以此请求目的的,HTTP协议里面四个表示操作方式的动词如下:
- GET:用于获取资源
- POST:用于新建资源
- PUT;用于更新资源
- DELEFT:用于删除资源
//http://localhost:8080/demo_war_exploded/quick9/sahngsan
@RequestMapping("/quick9/{username}")//大括号起占位符的作用
@ResponseBody
public void save9(@PathVariable(value = "username") String username) throws IOException {
System.out.println(Arrays.asList(username));
}
1.8自定义类型转换器
- SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
- 但是不是所有的数据类型都提供了转换器,例如:日期类型的数据就需要自定义转换器
自定义转换器的开发步骤
①、自定义转换器实现Converter接口
public class DateConverter implements Converter<String,Date> {
@Override
public Date convert(String dateStr) {
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
Date d=null;
try {
d = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return d;
}
}
②、在配置文件中声明转换器
<!-- 声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.example.converter.DateConverter"></bean>
</list>
</property>
</bean>
在这里设置bean id="conversionService"是为了第③步
③、在 <annotation-driver>
中引用转换器
<!-- mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
1.9获得Servlet相关的API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下
- HttpServletRequest
- HttpServletResponse
- HttpSession
@RequestMapping("/quick11")
@ResponseBody
public void save11(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
运行结果
org.apache.catalina.connector.RequestFacade@29976dcf
org.apache.catalina.connector.ResponseFacade@44fc01fc
org.apache.catalina.session.StandardSessionFacade@5a4cb713
1.10、获得请求头
①、@RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.个体Header(name)。@RequestHeader注解的属性如下
- value:请求头的名称
- required:是否必须携带此请求头
@RequestMapping("/quick12")
@ResponseBody
public void save12(@RequestHeader(value = "User-Agent" ,required = false)String user_agent) throws IOException {
System.out.println(user_agent);
}
②、@CookieValue
使用@CookieValue可以获得指定Cookie的值。@CookieValue的注解的属性如下;
- value:指定cookie的名称
- required:是否必须携带此Cookie
1.11文件上传
①文件上传的三要素
- 表单项type=“file”
- 表单的提交方式是post
- 表单的enctype属性是多部份表单形式,及enctype=“multipart/from-data”
②文件上传原理
- 当form表单修改为多部份表单时,request.getParameter()将失效
- enctype="application/x-www-from-urlencoded"时,form表单的正文内容格式是:key=value&key=value
- 当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部份形式。
③、单文件上传步骤
- 导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
- 配置文件上传解析器
<!-- 配置文件上传解析器-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="commonsMultipartResolver">
<!-- 配置文件的编码类型-->
<property name="defaultEncoding" value="UTF_8"/>
<property name="maxUploadSize" value="500000"/>
</bean>
- 编写文件上传代码
@RequestMapping("/quick13")
@ResponseBody
public void save13(String username, MultipartFile upLoadFile) throws IOException {
System.out.println(username);
System.out.println(upLoadFile);
}
7、Spring JdbcTemplate基本使用
JabcTemplate概述
他是spring框架中的一个对象,是对原始繁琐的Jdbc API对象的简单封装,spring框架为我们提供了很多的操作模板类。例如:操作关系型数据库的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
1、Java程序使用JDBC接口访问关系型数据库时的步骤
- 创建全局DataSource实例,表示数据库连接池
- 在需要读写数据库的方法内部,按如下步骤
①、从全局DataSource中获取connection实例
②、通过connection创建PreparedStatement实例
③、执行sql语句,如果是查询,通过ResultSet获取结果集。如果是修改,则获得int结果。
正确编写JDBC代码的关键是使用try … finally释放资源,涉及到事务的代码需要正确提交或回滚事务。
在Spring使用JDBC,首先我们通过IoC容器创建并管理一个DataSource实例,然后,Spring提供了一个JdbcTemplate,可以方便地让我们操作JDBC,因此,通常情况下,我们会实例化一个JdbcTemplate。顾名思义,这个类主要使用了Template模式。
编写示例代码或者测试代码时,我们强烈推荐使用HSQLDB这个数据库,它是一个用Java编写的关系数据库,可以以内存模式或者文件模式运行,本身只有一个jar包,非常适合演示代码或者测试代码。
2、JdbcTemplate开发步骤
- 导入spring-jdbc和spring-tx坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- 创建数据库表和实体
- 创建JdbcTemplate对象
//创建数据源对象
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate=new JdbcTemplate();
//设置数据源,知道数据库在哪
jdbcTemplate.setDataSource(dataSource);
- 执行数据库操作
//执行操作
int tom = jdbcTemplate.update("insert into account value(?,?)", "tom", 5000);
System.out.println(tom);
3.Spring创建模板对象使用JdbcTemplate
①、在application.xml中配置数据源对象和jdbc模板
<!-- 数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
②、在测试类中使用配置
@Test
//测试Spring产生jdbcTemplate对象
public void test2(){
ApplicationContext app=new ClassPathXmlApplicationContext("application.xml");
JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
int tom = jdbcTemplate.update("insert into account value(?,?)", "jam", 5000);
System.out.println(tom);
}
上述的代码可以进行简化
①、在项目resources中新建一个Properties读取配置文件。以键值对的形式存储开发过程中获得连接的四个参数(驱动、URL、用户名、密码)。其好处是方便后期维护,如果需要更换数据库,只需要修改配置文件即可
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456
②、在Spring配置文件中导入jdbc.properties文件
<!-- 加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties.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"></property>
</bean>
总结,通过Properties读取配置文件对原先复杂的文件进行简化,使得开发过程更加分明,方便后期的更改和维护。
4、JdbcTemplate的知识总结
- 导入spring-jdbc和spring-tx坐标
- 创建数据库表和实体
- 创建JdbcTemplate对象
①JdbcTemplate jdbcTemplate=new JdbcTemplate();
②jdbcTemplate.setDataSource(dataSource);
- 执行数据库操作
①、更新操作:jdbcTemplate.update(sql,params);
②、查询操作:jdbcTemplate.query(sql,Mapper,params);
jdbc.queryForObject(sql,Mapper,params);
重点:查询操作总结
【query方法】返回结果是list,且list中元素必须是自定义bean;不能是list。
【queryForObject】查询出一条记录并封装到一个对象中。可以返回的是String、Integer、Double或者自定义bean。但是如果查询的记录为0条或者大于1条,对不起抛出异常。
【queryForList】这个方法返回一个list,这个方法比较特殊,可以返回List ,还可以返回list<Map<String,Objec>>。
【queryForMap】查询一行数据封装到Map中。如果查询多行记录抛出异常。
//1.查询一行数据并返回int型结果
jdbcTemplate.queryForInt("select count(*) from test");
//2. 查询一行数据并将该行数据转换为Map返回
jdbcTemplate.queryForMap("select * from test where name='name5'");
//3.查询一行任何类型的数据,最后一个参数指定返回结果类型
jdbcTemplate.queryForObject("select count(*) from test", Integer.class);
//4.查询一批数据,默认将每行数据转换为Map
jdbcTemplate.queryForList("select * from test");
//5.只查询一列数据列表,列类型是String类型,列名字是name
jdbcTemplate.queryForList("
select name from test where name=?", new Object[]{"name5"}, String.class);
//6.查询一批数据,返回为SqlRowSet,类似于ResultSet,但不再绑定到连接上
SqlRowSet rs = jdbcTemplate.queryForRowSet("select * from test");
8、Spring练习
在这个环节中,由于我是在B站跟着视频学习,没有up主的资源,无法跟着练习,所以在这环节中总结我学到的东西。
1、Spring环境搭建步骤
①、创建工程(Project&Module)
②、导入静态页面(jsp页面)
③、导入需要坐标(pom.xml文件)
④、创建包结构(controller,service,dao,domain,utils)
⑤、导入数据库脚本
⑥、创建POJO类
⑦、创建配置文件(applicationContext.xml,spring-mvc.xml,jdbc.properties,log4j.properties)
学到的知识:
一、pom.xml文件
在课程的讲解中,UP主说相当于jar包,以下为我查找的资料
1.、什么是POM
Project Object Model,项目对象模型。通过xml可扩展标记语言(EXtensible Markup Language)格式保存的pom.xml文件。作用类似ant的build.xml文件,功能更强大。该文件用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。
1、基本设置
1.1、头文件
字段 | 说明 |
---|---|
xmlns | 命名空间,类似包名,因为xml的标签可自定义,需要命名空间来 |
xmlns:xsi | xml遵循的标签规范 |
xsi:schemaLocation | 用来定义xmlschema的地址,也就是xml书写时需要遵循的语法 |
如:
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">
1.2、maven的基本信息
字段 | 说明 |
---|---|
modelVersion | 声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。 |
groupId | 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成,如com.winner.trade,maven会将该项目打成的jar包放本地路径:/com/winner/trade |
artifactId | 本项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 |
version | 本项目目前所处的版本号 |
packaging | 打包类型,可取值:pom , jar , maven-plugin , ejb , war , ear , rar , par等等 |
name | 项目的名称, Maven产生的文档用,可省略 |
url | 项目主页的URL, Maven产生的文档用 ,可省略 |
二、包结构(controller,dao,service)
1、三层架构:三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层(又称为持久层)、业务逻辑层(又或称为领域层)、表示层。
表示层(UI层):
表示层也称为界面层,位于最外层(最上层),离用户最近。用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。
业务逻辑层(BLL层):
负责关键业务的处理和数据的传递。复杂的逻辑判断和涉及到数据库的数据验证都需要在此做出处理。主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。
数据访问层(DAL层):
主要负责对数据库的直接访问,为业务逻辑层提供数据,根据传入的值来操作数据库,增、删、改、查。
三、
创建的springContext.xml文件和spring-mvc.xml文件想要集成到web环境当中,需要配置web.xml。
1.配置spring监听器:监听器的作用是监听一些事件的发生从而进行一些操作。当Web容器启动后,Spring的监听器会启动监听,监听是否创建ServletContext的对象,如果发生了创建ServletContext对象这个事件(当web容器启动后一定会生成一个ServletContext对象,所以监听事件一定会发生),ContextLoaderListener类会实例化并且执行初始化方法,将spring的配置文件中配置的bean注册到Spring容器中,
<!-- 配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
监听器配置好了之后,加载配置文件创建Spring容器,需要给他初始化参数。
<!-- 全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
则完整的代码为
<!-- 全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<!-- 配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2、SpringMVC的前端控制器(是一个负责处理所有请求的Servlet.)
2.1、SpringMVC的实验原理
每个医生(控制器方法)中声明填写可以诊治的病(可以处理的请求). (前端控制器)前台在每日上班(每次项目启动)时, 去医生办公区(控制器所在目录)的每个科室(控制器)收集(加载)各个医生(控制器方法)可以诊治的病(处理的请求), 汇总并整理成文档(方法与URL映射Mapping). 当患者(客户端)来就医(发送请求)时, 由前台接待(前端控制器处理所有请求), 前台根据患者的病情(访问的请求URL)从整理的文档(Mapping)中找到可以诊治该病的医生(控制器方法), 并交由(分发)相应的医生进行诊治(执行业务逻辑).
2.2、SpringMVC前端控制器配置
<!-- 配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3、ModelAndView详解
①、当控制器处理完请求时,通常会将包含视图名称或视图对象以及一些模型属性的ModelAndView对象返回到DispatcherServlet。因此,经常需要在控制器中构造ModelAndView对象。ModelAndView类提供了几个重载的构造器和一些方便的方法,让你可以根据自己的喜好来构造ModelAndView对象。这些构造器和方法以类似的方式支持视图名称和视图对象。当你只有一个模型属性要返回时,可以在构造器中指定该属性来构造ModelAndView对象
②、ModelAndView如其名所示,代表MVC Web程序中Model和View对象,只不过他是一次性返回这两个对象的holder,Model和View仍是分离的概念。
- Model代表模型,模型就是数据,就是dao,bean。
- View代表视图,视图就是网页,就是jsp。
- Controller代表控制器, 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。
9、SpringMVC拦截器
一、拦截器(interceptor)的作用
SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。比如通过拦截器来进行用户权限验证,或者用来判断用户是否已经登录。Spring MVC拦截器是可插拔式的设计,需要某一功能拦截器,只需在配置文件中应用该拦截器即可;如果不需要这个功能拦截器,只需在配置文件中取消应用该拦截器。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用,拦截器也是AOP思想的具体实现。
二、拦截器快速入门
自定义拦截器的步骤
①、创建拦截器实现HandIerIntercrptor接口。需要重写以下代码中的三种方法。
public class MyInterceptor implements HandlerInterceptor {
@Override
//在目标方法执行之前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;//返回ture代表放行,返回false代表不放行,程序在这会中断停止
}
@Override
//在目标方法执行之后视图返回之前
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
//在流程都执行完毕后执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
②、配置拦截器。在下面代码中<mvc:mapping path="/**"/> <!--表示对哪些资源进行拦截操作-->
中表示要对哪些资源进行拦截操作,path的值为/**表示对所有的资源进行拦截操作。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!--表示对哪些资源进行拦截操作-->
<bean class="com.example.interceptor.MyInterceptor"/>
<!-- 配置哪些资源排除拦截操作 -->
<mvc:exclude-mapping path="user/login"/>
</mvc:interceptor>
</mvc:interceptors>
③、测试拦截器拦截效果、
三、拦截器方法说明
方法名 | 说明 |
---|---|
preHandle() | 方法在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当他返回为false时,表示请求结果,后续的Interceptor和Controller都不会再执行,当返回值为true时就会继续调用下一个interCeptorde 的preHandle方法 |
postHandle() | 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值是true时才能被调用,且他会在DispatcherServlet进行视图渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。 |
afterCompletion() | 该方法在整个请求结束之后,也就是在DispatchServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用。 |
10、SpringMVC异常处理
系统中的异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范现代码开发,测试等手段减少运行时异常的发生。
系统的Dao,Service,Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,
一、异常处理的两种方式
①、使用Spring MVC提供的简单异常处理SimpleMappingExceptionResolver ,SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相对应异常与视图的映射配置。
<!-- 配置简单映射异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="errer"></property> <!--默认错误视图,vlaue值为跳转页面-->
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException " value="errer1"/>
<entry key="MyException" value="errer2"/>
</map>
</property>
</bean>
②、实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器
二、自定义异常处理步骤
- 创建异常处理类实现HandlerExceptionResolver
- 配置异常处理器
- 编写异常界面
11、AOP
AOP(Aspect Oriented Programming)的缩写,意思是面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加某种特定功能的一种技术,AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务,例如,审计和事务管理进行内聚性的开发,应用对象只实现他们应该做的——完成业务逻辑——仅此而已,他们并不负责其他系统级关注点,例如日志和事务支持。
AOP是OOP(面向对象编程)的延续。利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率
一、AOP的作用以及优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少代码重复,提高开发效率,并且方便维护
二、AOP的底层实现
实际上,AOP的底层实现是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强。
三、AOP的动态代理技术
①、JAVA动态代理
代理模式:为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托的了(真实情况)预处理信息,过滤信息,传递信息给委托类,代理类不实现具体服务,而是利用委托类来完成服务,并将执行结果封装处理。其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
静态代理:创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
动态代理:利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个handler类来实现InvocationHandler接口。
②、常用的动态代理技术
- JDK代理,基于接口的动态代理技术
- cglib代理:基于父类的动态代理技术
JDK代理实现:不需要导额外的JAR包
接口:
// 表示功能的,厂家,商家都要完成的功能
public interface UsbSell {
//定义方法 参数 amount:表示一次购买的数量,暂时不用
//返回值表示一个u盘的价格。
float sell(int amount);
//可以多个其它的方法
//void print();
}
目标类
//目标类: 金士顿厂家, 不接受用户的单独购买。
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int amount) {
System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
//一个128G的u盘是 85元。
//后期根据amount ,可以实现不同的价格,例如10000个,单击是80, 50000个75
return 85.0f;
}
}
代理类:
//taobao是一个商家,代理金士顿u盘的销售。
public class TaoBao implements UsbSell {
//声明 商家代理的厂家具体是谁
private UsbKingFactory factory = new UsbKingFactory();
@Override
//实现销售u盘功能
public float sell(int amount) {
//向厂家发送订单,告诉厂家,我买了u盘,厂家发货
float price = factory.sell(amount); //厂家的价格。
//商家 需要加价, 也就是代理要增加价格。
price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
//在目标类的方法调用后,你做的其它功能,都是增强的意思。
System.out.println("淘宝商家,给你返一个优惠券,或者红包");
//增加的价格
return price;
}
}
测试:
public class ShopMain {
public static void main(String[] args) {
//创建代理的商家taobao对象
TaoBao taoBao = new TaoBao();
//通过代理类,实现购买u盘,增加了优惠券,红包等等
float price = taoBao.sell(1);
System.out.println("通过淘宝的商家,购买u盘单价:"+price);
}
}
cglib的动态代理
四、AOP相关概念
Spring的AOP实现底层就是对上面的动态代理的代码进行封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
AOP的相关术语:
- Target(目标对象):代理的目标对象
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
- Advice(通知/增强):所谓通知增强是指拦截到Joinpoint之后所要作的事情就是通知。
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而Aspect采用编译期织入和类装载织入。
五、AOP开发
①、需要编写的内容
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
②、AOP技术实现的内容
Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
③、AOP底层使用哪种代理方式
在Spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
12、基于XML的AOP开发
一、快速入门
- 导入AOP相关坐标
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面类的对象创建权交给Spring
- 在applicationContext.xml中配置织入关系
- 测试代码
<!--AOP配置目标对象-->
<bean id="target" class="com.example.aop.Target"></bean>
<!--AOP配置切面对象-->
<bean id="myAspect" class="com.example.aop.MyAspect"></bean>
<!-- 配置织入,告诉spring框架,哪些方法需要哪些增强-->
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知 pointcut:表示切点表达式的写法-->
<aop:before method="before" pointcut="execution(public void com.example.Proxy.cglib.Taget.save())"/>
</aop:aspect>
</aop:config>
二、XML配置AOP详解
①、切点表达式的写法
表达式语法:
execution(【修饰符】 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号*代表任意
- 包名和类名之间一个点,代表当前包下的类,两个点表示当前包及其子包下的类
- 参数列表可以使用两个点…表示任意个数,任意类型的参数列表
②、通知的类型
通用的配置语法
<aop:通知类型 method=“切面类中的方法” pointcut=“切点表达式”></aop:通知类型>
③、切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式。
<!-- 配置抽取切点表达式-->
<aop:pointcut id="myAspect" expression="execution(public void com.example.Proxy.cglib.Taget.save())"/>
13、基于注解的AOP开发
一、快速入门
基于注解的aop开发步骤
- 创建目标接口和目标类(内部有切点)
//目标类
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save runing...");
}
}
//接口
public interface TargetInterface {
public void save();
}
- 创建切面类(内部有调用方法)
//切面类
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
public void afterReturning(){
System.out.println("后置增强");
}
public void afterThrowing(){
System.out.println("异常抛出增强");
}
public void after(){
System.out.println("最终增强");
}
}
- 将目标类和切面类的对象创建权交给Spring
//接口
@Component("target")
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save runing...");
}
}
@Component("myaspect")
//切面类
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
public void afterReturning(){
System.out.println("后置增强");
}
public void afterThrowing(){
System.out.println("异常抛出增强");
}
public void after(){
System.out.println("最终增强");
}
}
- 在切面类中使用注解配置织入关系
@Component("myaspect")
@Aspect//标注当前MyAspect类是一个切面类
//切面类
public class MyAspect {
//配置前置增强
@Before("execution(* com.example.ann.*.*(..))")
public void before(){
System.out.println("前置增强");
}
public void afterReturning(){
System.out.println("后置增强");
}
public void afterThrowing(){
System.out.println("异常抛出增强");
}
public void after(){
System.out.println("最终增强");
}
}
- 在配置文件中开启组件扫描和AOP的自动代理
<!-- 开启组件扫描-->
<context:component-scan base-package="com.example.ann"/>
<!-- 配置AOP的自动代理-->
<aop:aspectj-autoproxy/>
- 测试
二、注解通知的类型
通知的配置语法:@通知注解(“切点表达式”)
- 前置通知(@Before):用于配置前置通知,指定增强的方法在切入点方法之前执行
- 后置通知(@AfterReturning):用于后置通知,指定增强的方法在切入点方法之后执行
- 环绕通知(@Around):用于配置环绕通知,指定增强的方法在切入点方法之前和之后执行
- 异常抛出通知(@AfterThrowing):用于异常抛出通知,指定增强的方法在出现异常时执行
- 最终通知(@After):用于配置最终通知,无论增强方法执行是否有异常都会执行
①、切点表达式的抽取
同xml配置aop一样,我们可以将切点表达式抽取,抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在增强注解中进行引用,具体如下
@Component("myAspect")
@Aspect
public class MyAspect{
@Before("MyAspect.myPoint()")
public void before(){
System.out.println("前置代码增强");
}
@Pointcut("execution(* com.example.ann.*.*")
public void myPoint(){}
}
14、Spring的事务控制
事务:事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
一、编程式事务控制相关对象
1.1、PlatformTransactionManager(平台事务管理器)接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法
方法 | 说明 |
---|---|
TransactionStatus getTransaction(TransactionDefination defination) | 说明 |
void commit(TransactionStatus status) | 提交事务 |
void rollback(TransactionStatus status) | 回滚事务 |
注意:PlatformTransactionManager是接口类,不同的Dao层技术则有不同的实现类,类如:Dao层技术是jdbc或mybatis时,org.springframework.jdbc.datasource.DataSoureTransactionManger 。Dao层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManger。
1.2、TransactionDefinition
TransactionDefinition是事务的定义信息对象,里面有如下方法
方法 | 说明 |
---|---|
int getIsolationLevel() | 获得事务的隔离级别 |
int getPropogationBehavior() | 获得事务的传播行为 |
int getTimeout() | 获得超时时间 |
boolean isReadOnly() | 是否只读 |
①、设置隔离级别,可以解决事务并发产生的问题,如脏读,不可重复读和续读,有五个隔离级别
②、事务传播行为
事务传播行为(propagation behavior)指的就是当一个事务被另一个事务方法调用时,这个事务应该如何进行。
如:
public void methodA(){
methodB();
//doSomething
}
@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,一般的选择(默认值)
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
- MANDATORY:使用当前事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
- NOT_SUPPORIED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行REQUIRED类似的操作
- 超时时间:默认值是-1,没有超时限制,如果有,以秒为单位进行设置
- 是否只读:建议查询时间设置为只读
1.3、TransactionStatus(事务状态)
TransactionStatus接口提供的是事务集体的运行状态,方法介绍如下:
方法 | 说明 |
---|---|
boolean hasSavepoint() | 是否存储回滚点 |
boolean isCompleted() | 事务是否完成 |
boolean isNewTransaction() | 是否是新事务 |
boolean isRoolbackOnly() | 事务是否回滚 |
二、基于XML的声明式事务控制
什么是声明式事务控制:
Spring的声明式事务顾名思义就是采用声明的方式来处理事务,这里说的声明就是在配置文件中声明,用在Spring配置文件中声明式的处理方式来代替代码式的处理事务。
声明式事务处理的作用
- 事务管理不侵入开发的组件,具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可。
- 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便
注意:Spring声明式事务控制底层就是AOP
基于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="save" isolation="REPEATABLE_READ" propagation="NESTED"></tx:method>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务的AOP织入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.example.Dao.Service.*(..))"></aop:advisor>
</aop:config>
其中<tx:method>
代表切点方法的事务参数的配置,例如:<tx:method name="save" isolation="REPEATABLE_READ" propagation="NESTED"></tx:method>
- name:切点党法名称
- isolation:事务的隔离级别
- propogation:事务的传播行为
- timeout:超出时间
- read-only:是否只读
三、基于注解的声明式事务控制
①、使用注解配置声明式事务控制解析
- 使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如隔离级别,传播行为等
- 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置
- 使用在方法上,不同的方法可以采用不同的事务参数配置
- XML配置文件要开启事务的注解驱动
<tx:annotation-driven/>