Spring学习记录01
JavaEE概述
第一阶段:Java2.0 版本提出Java划分为3大平台;
JavaSE:标准版 (桌面应用:eclipse ,Navicat,IDEA)
JavaME:微型版 (移动端应用)
JavaEE:企业版 (开发大型企业应用)
大型企业应用需求
1、高并发(能够支持大量的用户同时在线 几万-几百万);
2、大数据量(单表的数据可能都达到 几千万);
3、支持分布式事务;
4、支持集群,支持负载均衡;
注意:单台的tomcat 在单位时间1s内,能支持0-500个并发请求;
需要的技术
轻量级框架 + POJOS;
POJOS
就是普通的Java类,就是一个普通的JavaBean;
所谓的普通Java类就是指,一个Java类身上没有强制性的要求必须要继承什么类或必须实现什么接口的方法;
POJOS的作用:传输数据以及处理业务;
轻量级的框架
轻量级框架是相对于重量级框架来定义的,重量级框架(EJB)我们不需要学习;
轻量级框架特点:
1、它把业务 和 其他框架任务进行分离,业务就交给POJOS来处理;组件的管理,AOP切面的实现,数据库的访问,这些任务就让轻量级框架来处理;
2、它对代码的侵入式不高(甚至有的框架在代码中都看不到代码);
3、它提倡“面向对象编程”;
4、它不依赖与某些外部的特殊的服务器,直接使用main()或者Junit这种测试框架,即可完成对功能的测试;
轻量级框架的分类:
表现层:struts1/2 ,springmvc
业务层:(面向对象编程,面向过程编程);
持久层:hibernate,mybatis,spring-data-jpa(orm)
注意:spring框架来做全体把控;
重量级框架(了解)
重量级框架的特点:
它既可以处理业务,又可以传输数据,还可以提供集群支持,也可以提供分布式事务;
但是:
1、它提倡“面向过程编程”;
2、它过于依赖某些API ,类如 Servlet 继承HTTPServlet;
3、过于依赖服务器(对于开发、测试、部署都非常麻烦);
4、它对于代码的侵入式很高;
所以才有了轻量级框架;
Spring
spring:整合框架,管理各层的组件(java类),维护组件间的关系,AOP(事务,日志);
spring衍生的框架:springioc ,springMVC,springboot,springdata,springcloud;
准备工作1
–配置maven环境
作用:
自动下载jar包,并管理jar包;
管理jar包之间的依赖关系;
可以帮我们测试,上线,部署,打包(java是jar,web是war包);
maven:它是项目管理工具
maven 是apache开源平台上的一个免费开源项目;
1、下载:http://maven.apache.org/download.cgi
2、安装:首先,在D:\Program Files 目录下,解压apache-maven-3.6.3-bin.zip;
记录bin目录以上的位置:D:\software\apache-maven-3.6.3-bin\apache-maven-3.6.3
3、配置:
在系统变量中,添加M2_HOME
M2_HOME=D:\Program Files\apache-maven-3.6.0-bin\apache-maven-3.6.0
找到path变量,在path中添加 (win10 系统中,不用添加;)
%M2_HOME%\bin
保存环境变量
4、测试是否配置OK:
打开DOC命令创建,输入:mvn -v,若出现版本信息,则表示安装正确。
5、maven原理
6、配置本地仓库
打开安装目录 conf 下的settings.xml文件
修改:
然后再D盘中,创建一个目录repo。
7、替换国外的maven源为国内的
把国外的源,替换成国内阿里加速源!
在settings.xml 中 mirrors 节点中,添加如下内容:
<mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror>
准备工作2
–替换idea。
跟着安装文档走就行。
JavaBean编程规范
JavaBean 是领域对象 来自于 对客户需求的分析,这个对象既具有行为也具有属性。
要求:
1、所有的JavaBean 都必须实现序列化接口;
2、Java 中的所有属性 都必须是 私有的,并按需提供getter和setter方法;
3、如果需要定义有参构造器,需要先提供无参构造器;
4、toString可提供也可以不提供;
Java程序设计原则
开发程序,都需要满足“高内聚,低耦合”;
如何才能做到?
答:此时就需要从设计层面上,满足:7大设计原则,23个设计模式;
七大原则
1、单一原则:一个类只做一件事;
2、开闭原则:新需求来了,需要对新功能的扩展是开放的,而对旧功能的修改是关闭的。
3、接口隔离原则:接口最小化原则,设计接口时,接口中的方法要尽量少;
4、依赖倒置原则:层次之间相互依赖,应该是上层依赖下层的抽象,而不依赖下层的具体实现,下层应该对上层无知(不知道上层的任何代码);
5、里氏替换原则:父类出现的任何地方,子类都可以替代;(意思是子类不要去重写父类已经实现过的方法);
6、迪米特法则:最少知道原则,定义类的时候,只和方法的参数、方法的返回、成员变量等相关类进行交互。
7、组合,聚合原则:遇到重复代码的问题。
以前解决的方法:以前是通过继承,但是继承会破坏封装,以及增大了继承的冗余度;
现在的方法:解决了以前方法的问题;
Maven项目的目录结构
Maven项目的约定
src/main/java 核心代码;
src/main/resources 配置文件所存放的位置;
src/test/java 测试代码;
src/test/resources 测试的配置文件所存放的位置;
pom.xml:project object model 项目对象模型,这篇配置文件,主要用来描述,项目是如何组成的。
pom.xml
每个项目都是由3部分构成:
<!--项目的坐标--><groupId>org.example</groupId> //叫做组织机构<artifactId>springIoc</artifactId> //是你的项目名称<version>1.0-SNAPSHOT</version> //项目当前的版本
项目全局配置信息:
<!--全局配置信息--><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties>
定义依赖关系:
<!--指定依赖关系--><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- <scope>test</scope> 表示该jar包 仅在测试阶段有效--><!-- <scope>compile</scope> 表示该jar包 仅在编译阶段有效--><!-- <scope>runtime</scope> 表示该jar包 在运行期间有效--></dependencies>
定义插件关系(几乎不用修改):
<build><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-jar-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin><!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --><plugin><artifactId>maven-site-plugin</artifactId><version>3.7.1</version></plugin><plugin><artifactId>maven-project-info-reports-plugin</artifactId><version>3.0.0</version></plugin></plugins></pluginManagement></build>
Spring框架
定义
spring框架是03年由Rod Johnson创建,该框架的主要作用:让我们的应用程序满足“高内聚,低耦合”,并始终遵循面向接口编程的思想,来做到松散耦合,主要解决的业务逻辑层和其他各层的松耦合问题。
Spring核心框架
Spring框架的核心:IOC(容器),AOP(面向切面编程);
Spring 框架的特点
1、方便程序解耦,简化开发(高内聚低耦合);
–它的底层采用的是:工厂+反射+配置文件 来实现。
(1)它可以帮助程序员去创建组件的实例;
(2)它可以程序员去管理组件之间的依赖关系;
2、AOP编程的支持
–Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行等功能。
3、声明式事务的支持
–只需要通过配置就可以完成对事务的管理,而无需手动编程。
4、方便程序的测试
–Spring对Junit4(单元测试)支持,可以通过注解方便的测试Spring程序。
5、方便集成各种优秀的框架
–Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持。
6、降低JavaEE API 的使用难度。
–Spring对JavaEE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API 应用难度大大降低。
Spring体系结构—7大功能模块(重点)
–Spring 框架是一个分层架构,由7个定义良好的模块组成。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean 的方式,如图所示:
1、核心容器(Spring Core)
–核心容器提供Spring框架的基本功能。核心容器的主要组件是 BeanFactory, 它是工厂模式的实现。BeanFactory使用强制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
2、Spring 上下文(Spring Context)
–Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、检验和调度功能。
3、Spring AOP
–通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了Spring框架中。所以,可以会让你容易地使Spring 框架管理的任何对象支持 AOP。Spring AOP模块基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,不用依赖 EJB组件,就可以将声明性事务管理集成到应用程序中。、
4、Spring Dao
–JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误信息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向JDBC 的异常遵从通用的DAO异常层次结构。
5、Spring ORM
–Spring 框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、Hibernate和 iBatis SQL Map。所有这些都遵从Spring的通用事务和DAO 异常层次结构。
6、Spring Web 模块
–Web 上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring 框架支持Jakarta Struts 的集成。Web还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
7、Spring MVC 框架
–MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成高度可配置的,MVC容纳了大量的视图技术,其中包括了JSP、Velocity、Tiles、iText 和 POI。
Spring容器的核心类
1、 BeanFactory接口;
2、 ApplicationContext接口;
——ApplicationContext接口 继承了 BeanFactory 接口,它们两个的实现类都可以成为Spring容器。
——Spring容器实际上就是一个超大型的工厂,它的底层:工厂+反射。
——BeanFactory 提供了容器的所有功能,但还是需要ApplicationContext,原因是:
BeanFactory 在产生实例的时候,是调用 getBean()方式时,才产生实例;
而ApplicationContext 在创建容器实例的时候,就开始初始化创建所有组件的实例。
——我们一般用的更多是ApplicationContext来作为容器(因为效率高)。
——ApplicationContext 除了实现了BeanFactory的所有功能之外,还扩展了很多其他动能:支持i18n(国际化) 支持任务调度,支持邮件服务,支持WebSocket等。
ApplicationContext接口
实现类:(1)ClassPathXmlApplicationContext、(2)FileSystemXmlApplicationContext、(3)AnnotationConfigApplicationContext(注解)。
区别:
——ClassPathXmlApplicationContext 使用相对路径加载applicationContext.xml配置文件;
——FileSystemXmlApplicationContext使用绝对路径加载applicationContext.xml配置文件;
——AnnotationConfigApplicationContext 提供注解支持。
——注意:但是这三者在管理组件、和维护组件的方式上,都是一样的,没有什么区别。
在代码中如何启动容器
//开启一个Spring容器ApplicationContext context = new AnnotationConfigApplicationContext("classpath:applicationContext.xml");
Spring框架开发步骤
文字流程
1、创建一个Maven项目;
2、在pom.xml 导入spring的依赖包;
<!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency>
3、定义需要的JavaBean;
4、针对业务,定义三层架构的各层接口;
5、针对接口进行编写实现类,并完成面向接口编程。
6、板鞋applicationContext.xml 配置文件,在配置文件中,使用标记来申明需要被容器管理的组件。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="com.woniuxy.springIoc.service.impl.UserServiceImpl"></bean><bean id="userController" class="com.woniuxy.springIoc.controller.UserController"><property name="userService" ref="userService"></property></bean>
</beans>
注意:id 是组件在容器中的唯一标识,class 是组件在容器的类的全路径。
7、测试。
图文流程
创建好Maven项目之后,导入依赖关系。
<!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
编写JavaBean
package com.woniuxy.bean;
import java.io.Serializable;
public class StudentBean implements Serializable {private Long id;private String stuName;private Integer age;/*** 0 女* 1 男* -1 未知*/private Integer gender;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}@Overridepublic String toString() {return "StudentBean{" +"id=" + id +", stuName='" + stuName + '\'' +", age=" + age +", gender=" + gender +'}';}
}
编写各层接口
(1)表现层没有接口;
(2)业务层有接口: IStudentService;
(3)持久层有接口: StudentMapper;
(4)并在接口中,提供对应的抽象方法;
业务层(IStudentService):
package com.woniuxy.service;
import com.woniuxy.bean.StudentBean;
import java.util.List;
public interface IStudentService {/*** 新增学生* @param studentBean*/void saveStudentBean(StudentBean studentBean);/*** 修改学生* @param studentBean*/void updateStudentBean(StudentBean studentBean);/*** 删除学生* @param studentBean*/void deleteStudentBean(StudentBean studentBean);/*** 根据ID查询学生* @param id* @return*/StudentBean getOne(Long id);/*** 根据学生名称,模糊查询所有的学生信息* @param studentName* @return*/List<StudentBean> findAllByStuName(String studentName);
持久层(StudentMapper.java):
package com.woniuxy.mapper;
import com.woniuxy.bean.StudentBean;
import java.util.List;
public interface StudentMapper {/*** 新增学生* @param studentBean*/void saveStudentBean(StudentBean studentBean);/*** 修改学生* @param studentBean*/void updateStudentBean(StudentBean studentBean);/*** 删除学生* @param studentBean*/void deleteStudentBean(StudentBean studentBean);/*** 根据ID查询学生* @param id* @return*/StudentBean getOne(Long id);/*** 根据学生名称,模糊查询所有的学生信息* @param studentName* @return*/List<StudentBean> findAllByStuName(String studentName);
}
持久层(StudentMapper.java):
package com.woniuxy.mapper;
import com.woniuxy.bean.StudentBean;
import java.util.List;
public interface StudentMapper {/*** 新增学生* @param studentBean*/void saveStudentBean(StudentBean studentBean);/*** 修改学生* @param studentBean*/void updateStudentBean(StudentBean studentBean);/*** 删除学生* @param studentBean*/void deleteStudentBean(StudentBean studentBean);/*** 根据ID查询学生* @param id* @return*/StudentBean getOne(Long id);/*** 根据学生名称,模糊查询所有的学生信息* @param studentName* @return*/List<StudentBean> findAllByStuName(String studentName);
}
分层实现各层接口:
–针对StudentMapper 无非就编写StudentMapper.xml
针对于IStudentService 无非编写一个它的实现类StudentServiceImpl。
——StudentServiceImpl.java
package com.woniuxy.service.impl;
import com.woniuxy.bean.StudentBean;
import com.woniuxy.service.IStudentService;
import java.util.List;
public class StudentServiceImpl implements IStudentService {@Overridepublic void saveStudentBean(StudentBean studentBean) {}@Overridepublic void updateStudentBean(StudentBean studentBean) {}@Overridepublic void deleteStudentBean(StudentBean studentBean) {}@Overridepublic StudentBean getOne(Long id) {return null;}@Overridepublic List<StudentBean> findAllByStuName(String studentName) {return null;}
}
——将实现类交给Spring容器
——将实现类交由Spring容器进行管理
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--将实现类,交给Spring容器来进行管理,并定义它的标识为:studentServiceImpl--><bean id="studentServiceImpl"class="com.woniuxy.service.impl.StudentServiceImpl"></bean>
</beans>
——测试
package com.woniuxy.service.impl;
import com.woniuxy.service.IStudentService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.junit.Assert.*;
public class StudentServiceImplTest {/*** 创建容器对象*/private ApplicationContext context;private IStudentService studentService;@Beforepublic void setUp() throws Exception {context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");studentService = (IStudentService) context.getBean("studentServiceImpl");System.out.println(context.getBean("studentServiceImpl") == context.getBean("studentServiceImpl") );}@Testpublic void saveStudentBean() {System.out.println(studentService);}@Testpublic void updateStudentBean() {}@Testpublic void deleteStudentBean() {}@Testpublic void getOne() {}@Testpublic void findAllByStuName() {}
}
SpringIOC
——Spring 框架可以帮助我们管理组件,减少程序员的任务量。
IOC Inversion Of Control(控制反转)(面试题)
——如果没有Spring 容器,程序在使用某些组件的实例的时候,需要自己创建,也就是创建组件实例的控制权是一种 “主动关系” ,但是 有了 Spring 容器之后,现在就是由 容器来创建, 创建好之后,再传递给我们。程序现在创建对象的权利,由之前的主动变成了被动:被动接受。
控制反转的好处
1、降低程序之间的耦合度,由容器帮我们松散了组件之间的耦合关系。
2、可以让我们真正做到:面向抽象编程。(依赖倒置原则)
注意:正式靠这种反转机制,Spring容器才可以做到,“松散程序之间的耦合”;
DI Dependency Injection(依赖注入)(面试题)
DI 和IOC 实际上是同一个东西,没有区别,只是在各自的描述不一样。
依赖注入(面试题)
——在没有Spring 容器的时候,程序在完成某项业务时,需要直接依赖某一个实例。在有了Spring 容器之后,依赖关系 由直接依赖实例,变成依赖容器,让容器去创建实例,并将实例重新注入到程序的过程。
IOC 和 DI 的区别(面试题)
——实际上没有区别,只是 关注点不一样,IOC 关注的是创建对象的控制权,(由主动创建变成了被动接受),DI关注的是依赖关系(由直接依赖对象,变成了依赖 Spring 容器)。
容器将实例传递给程序的两种方式
1、设值注入
——实际上就是指,让你提供setter()方法,由Spring 容器调用该方法,将容器中的实例注入到程序中来。
package com.woniuxy.controller;
import com.woniuxy.bean.StudentBean;
import com.woniuxy.service.IStudentService;
import com.woniuxy.service.impl.StudentServiceImpl;
public class StudentController {private IStudentService studentService;public void setStudentService(IStudentService studentService) {this.studentService = studentService;}/*** 保存学生* @param studentBean*/public void saveStudentBean(StudentBean studentBean){studentService.saveStudentBean(studentBean);}
}
具体操作方式:
(1)给属性提供setter();
(2)在spring容器的配置文件中,使用property标记完成组件之间的装配。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--将实现类,交给Spring容器来进行管理,并定义它的标识为:studentServiceImpl--><bean id="studentServiceImpl"class="com.woniuxy.service.impl.StudentServiceImpl"></bean><bean id="studentController" class="com.woniuxy.controller.StudentController"><!--通过property 调用setter() 完成对组件的装配--><property name="studentService" ref="studentServiceImpl"></property></bean>
</beans>
2、构造注入
——需要程序提供构造器,Spring 容器通过调用构造器的方式,给属性赋值。
具体代码:
(1)在程序中创建一个有参构造器;
package com.woniuxy.controller;
import com.woniuxy.bean.StudentBean;
import com.woniuxy.service.IStudentService;
public class StudentController {private IStudentService studentService;public StudentController(IStudentService studentService) {this.studentService = studentService;}/*** 保存学生* @param studentBean*/public void saveStudentBean(StudentBean studentBean){studentService.saveStudentBean(studentBean);}
}
(2)在Spring 容器的配置文件中,使用constructor-arg 完成构造装配。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--将实现类,交给Spring容器来进行管理,并定义它的标识为:studentServiceImpl--><bean id="studentServiceImpl"class="com.woniuxy.service.impl.StudentServiceImpl"></bean><bean id="studentController" class="com.woniuxy.controller.StudentController"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
</beans>
3、构造注入和设值注入的区别(面试题)
(1)注入的原理不一样,一个是通过构造器装配,一个是通过setter来装配。
(2)设值注入的注入顺序是:Spring容器先把对象创建出来,然后调用setter()注入内容;而构造注入是产生对象时,就注入内容。
注意:它们两种没有谁好谁坏,只是使用的场景不一样:
–1:设值注入容易被java程序员接受,
–2:如果对注入的顺序有严格要求,就比较适合构造注入;
–3:如果对创建对象的顺序由要求的话,也比较适合构造注入。
*单例模式:
单例模式:它是23种设计模式的一种,它通常的使用场景:当程序中需要某一个类只具备唯一实例的时候,就可以使用这种模式。
(1)懒汉模式:
package com.woniuxy.bean;
public class Calendar {private static Calendar calendar;/*** 私有构造器*/private Calendar(){}/*** 懒汉模式* @return*/public static Calendar getInstance(){if(calendar != null){return calendar;}else{synchronized (Calendar.class){if(calender == null){calendar = new Calendar();}}}return calendar;}
}
(2)饿汉模式
package com.woniuxy.bean;
public class Calendar {private static Calendar calendar = new Calendar();/*** 私有构造器*/private Calendar(){}/*** 饿汉模式* @return*/public static Calendar getInstance(){return calendar;}
}
饿汉模式:是在类加载的时候,实例本身就已经产生了!
懒汉模式:是在调用类的方法的方式,才产生实例
饿汉模式,天生就是线程安全的!!
*克隆模式(原型模式)
–克隆模式:以某一个东西为原型,批量产生 新的对象的方式
–克隆模式,除了产生新的对象,还会复制对象的数据到新的对象身上;
–克隆模式的适用场景: 当一个对象拥有大量的属性,并且属性都有值;现在需要大量的 相同类型的对象
–具体实现:需要实现 一个Cloneable的接口
package com.woniuxy.bean;
import java.io.Serializable;
public class StudentBean implements Serializable,Cloneable {private Long id;private String stuName;private Integer age;/*** 0 女* 1 男* -1 未知*/private Integer gender;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "StudentBean{" +"id=" + id +", stuName='" + stuName + '\'' +", age=" + age +", gender=" + gender +'}';}
}
package com.woniuxy.bean;
import org.junit.Test;
public class StudentBeanTest {@Testpublic void testClone(){StudentBean studentBean = new StudentBean();studentBean.setStuName("黑崎一护");studentBean.setAge(18);studentBean.setGender(1);//克隆try {StudentBean studentBean1 = (StudentBean) studentBean.clone();studentBean1.setStuName("鸣人");System.out.println(studentBean);System.out.println(studentBean1);} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}
(1)浅克隆
package com.woniuxy.bean;
import java.io.Serializable;
public class StudentBean implements Serializable,Cloneable {private Long id;private String stuName;private Integer age;/*** 0 女* 1 男* -1 未知*/private Integer gender;private GirlFriendBean girlFriendBean;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public GirlFriendBean getGirlFriendBean() {return girlFriendBean;}public void setGirlFriendBean(GirlFriendBean girlFriendBean) {this.girlFriendBean = girlFriendBean;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "StudentBean{" +"id=" + id +", stuName='" + stuName + '\'' +", age=" + age +", gender=" + gender +", girlFriendBean=" + girlFriendBean +'}';}
}
package com.woniuxy.bean;
import java.io.Serializable;
public class GirlFriendBean implements Serializable {private Long id;private String girlName;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getGirlName() {return girlName;}public void setGirlName(String girlName) {this.girlName = girlName;}
}
package com.woniuxy.bean;
import org.junit.Test;
public class StudentBeanTest {@Testpublic void testClone(){StudentBean studentBean = new StudentBean();studentBean.setStuName("黑崎一护");studentBean.setAge(18);studentBean.setGender(1);GirlFriendBean girlFriendBean = new GirlFriendBean();girlFriendBean.setGirlName("井上");studentBean.setGirlFriendBean(girlFriendBean);//克隆try {StudentBean studentBean1 = (StudentBean) studentBean.clone();studentBean1.setStuName("鸣人");System.out.println(studentBean);System.out.println(studentBean1);} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}
(2)深克隆
深克隆除了克隆对象本身,也会克隆对象的关联对象
package com.woniuxy.bean;
import java.io.Serializable;
public class GirlFriendBean implements Serializable ,Cloneable{private Long id;private String girlName;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getGirlName() {return girlName;}public void setGirlName(String girlName) {this.girlName = girlName;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}
package com.woniuxy.bean;
import java.io.Serializable;
public class StudentBean implements Serializable,Cloneable {private Long id;private String stuName;private Integer age;/*** 0 女* 1 男* -1 未知*/private Integer gender;private GirlFriendBean girlFriendBean;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public GirlFriendBean getGirlFriendBean() {return girlFriendBean;}public void setGirlFriendBean(GirlFriendBean girlFriendBean) {this.girlFriendBean = girlFriendBean;}@Overridepublic Object clone() throws CloneNotSupportedException {StudentBean studentBean = (StudentBean) super.clone();GirlFriendBean girlFriendBean = (GirlFriendBean) this.girlFriendBean.clone();studentBean.setGirlFriendBean(girlFriendBean);return studentBean;}@Overridepublic String toString() {return "StudentBean{" +"id=" + id +", stuName='" + stuName + '\'' +", age=" + age +", gender=" + gender +", girlFriendBean=" + girlFriendBean +'}';}
}
package com.woniuxy.bean;
import org.junit.Test;
public class StudentBeanTest {@Testpublic void testClone(){StudentBean studentBean = new StudentBean();studentBean.setStuName("黑崎一护");studentBean.setAge(18);studentBean.setGender(1);GirlFriendBean girlFriendBean = new GirlFriendBean();girlFriendBean.setGirlName("井上");studentBean.setGirlFriendBean(girlFriendBean);//克隆try {StudentBean studentBean1 = (StudentBean) studentBean.clone();studentBean1.setStuName("鸣人");System.out.println(studentBean);System.out.println(studentBean1);} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}
Spring 容器创建的实例的生命周期(作用域)
1、singleton: 单实例 (默认) Spring容器默认 产生组件时,组件在Spring 容器中 就只有一个唯一的实例。组件实例的整个生命周期,都是由Spring容器来负责,容器在创建时就产生组件的实例,容器在销毁时,组件的实例跟着销毁
2、prototype: 原型模式(克隆模式)
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="prototype"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
组件的实例,由Spring容器来创建,但是创建好之后组件的使用,以及何时销毁,Spring容器是不负责,由程序员自己负责(JVM垃圾回收);
3、request 组件的实例,根HttpRequest 有关,它能在做到在同一HttpRequest范围内,组件的实例只有一个!不同的HttpRequest 拥有不同的组件实例。
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="request"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
4、session 组件的实例,根HttpSession 有关,它能在做到在同一HttpSession范围内,组件的实例只有一个!不同的HttpSession 拥有不同的组件实例。
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="session"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
5、application 组件的实例,根ServletContext 有关,它能在做到在同一ServletContext范围内,组件的实例只有一个!ServletContext 的生命周期,通常和WEB容器有关
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="application"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
6、websocket 组件的实例,根websocket创建的连接有关,它能做到在同一连接范围内,组件的实例只有一个!
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="websocket"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
上述的6种作用域,最重要的:singleton prototype 其他的仅做了解!
自动装配
通过程序员自已定义 组件之间的关系,我们叫:手动装配
<bean id="studentServiceImpl"class="com.woniuxy.service.impl.StudentServiceImpl"></bean><bean id="studentController" class="com.woniuxy.controller.StudentController"><constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg></bean>
但是上述的方式,非常的麻烦,Spring容器给我们提供了一种:自动装配模式。
byName byType contructor default
ByName
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="byName"></bean>
按照属性名称装配,Spring容器在产生组件实例时,如果发现该组件需要装配其他组件,并且装配方式定义byName,Spring容器将会查找组件的所有属性的名字,回到Spring容器问询是否存在,如果存在则正常装配,如果不存在,Spring容器也不会抛出异常,但是在代码执行时,程序会抛出NullPointerException
byName 需要给属性定义一个setter()
ByType
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="byType"></bean>
按照属性类型装配,Spring容器在产生组件实例时,如果发现该组件需要装配其他组件,并且装配方式定义byType,Spring容器将会查询组件的所有属性的类型,回到Spring容器问询是否有 这种类型的实例存在,如果只找到1个,正常装配,如果找到多个,将抛出UnsatisfiedDependencyException;如果1个都没找到,Spring容器也不会抛出异常,但是在代码执行时,程序会抛出NullPointerException
byName 需要给属性定义一个setter()
constructor (构造装配)
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="constructor"></bean>
构造装配:调用构造器来完成组件的装配,在装配时,构造参数按照ByType的模式来进行!
一定要提供构造器!
no 或者 default
就是手动装配。
注解
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--将实现类,交给Spring容器来进行管理,并定义它的标识为:studentServiceImpl--><bean id="studentService"class="com.woniuxy.service.impl.StudentServiceImpl"></bean><bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="constructor"></bean>
</beans>
通过上述的配置文件方式,声明组件,声明组件的之间的关系,如果组件少,还好说,如果组件超过1000,还好说吗?
约定优于配置
约定:你我说好的事情 程序中什么可以定义约定? 接口 注解
配置既需要维护Java代码,又需要维护配置代码,而注解,只需要维护Java代码!
注解的作用
1、注解具有注释的作用,解释代码
2、和第3方达成一定的约定 :
@Override 和 编译器达成预定,说这是一个重写的方法
@Deprecated 和 编译器达成预定,说这是一个过时的方法
3、注解中的代码,可以在程序中再次使用
Spring框架的常用注解
@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {String value() default "";
}
该组件:通常用于普通Java类身上,表示这是一个需要被Spring容器进行管理的组件
@Component
public class StudentServiceImpl implements IStudentService{}
===
<bean id="studentService"
class="com.woniuxy.service.impl.StudentServiceImpl"></bean>
@Controller
用于描述 表现层的类,说这个类需要被Spring容器进行管理!
@Service
用于描述 业务层的类,说这个类需要被Spring容器进行管理!
@Repository
用于描述 持久层的类,说这个类需要被Spring容器进行管理!
开启Spring容器的自动扫描功能
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启容器的自动扫描功能--><context:component-scan base-package="com.woniuxy"></context:component-scan>
</beans>
base-package 基准包: 以此包为基础,扫描该包以及所有子包 Java类是否使用@Component @Service @Controller @Repository 这种Java类就需要被纳入到Spring容器中
默认会将类的类名,首字母小写作为类的实例,在容器中的ID
可以通过下面的方法来查看:
context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
想取名字,就自己写
@Controller("sc")
装配组件的2种方式
@Autowired
自动装配,由Spring框架提供,它装配规则是:先byType 再byName
@Resource
自动装配,由Java提供,它装配规则是: 先byName 再byType
@Resource(name="studentServiceImpl")
课堂练习(1)
说:现在有2张表 user_info goods_info 都需要实现CRUD
现在需要:定义各自的JavaBean, 按照三层架构思维,定义各层的接口,各层的实现
并将各层的实现 使用注解的方式,交由Spring容器进行管理 持久池暂时可以不写
在业务层的方法中,输出传递的参数内容!
并写出测试代码!
步骤:
1、先定义UserBean(id,userName,age) GoodsBean(id,goodsName,price)
2、定义业务接口 IUserService (CRUD) IGoodsService (CRUD)
3、定义业务实现 UserServiceImpl GoodsServiceImpl
4、定义表现层代码 UserController GoodsController
5、表现层调用业务业务,完成业务逻辑
6、在Spring的配置文件中,开启组件的自动扫描
7、业务实现类上添加@Service
8、在表现层代码上,添加@Controller
9、在表现层 的属性上,添加@Resource 或者 @Autowired
10、分别编写测试类,进行代码测试
Spring兼容Junit框架
第一步
在pom.xml 文件中,导入Spring兼容Junit框架的兼容包
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target><!-- 定义一个全局变量 --><spring.version>5.2.9.RELEASE</spring.version></properties><!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version><scope>test</scope></dependency>
第二步,修改测试类
package com.woniuxy.controller;
import com.woniuxy.bean.StudentBean;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Arrays;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value={"classpath:applicationContext.xml"})
public class StudentControllerTest {@Resourceprivate StudentController controller;@Testpublic void saveStudentBean() {StudentBean studentBean = new StudentBean();studentBean.setStuName("黑崎一护");controller.saveStudentBean(studentBean);}
}
@RunWith(SpringJUnit4ClassRunner.class) //在JUnit单元测试,启动一个Spring容器
@ContextConfiguration(value={“classpath:applicationContext.xml”}) //将applicationContext.xml 交给上述的Spring容器
@Scope 作用域注解
@Scope(value="prototype")
@Controller
public class StudentController {……
}
该注解的作用是:修改组件的作用域范围,默认是:singleton
扩展2个,不常用的注解
@PostConstruct 组件实例化时,需要执行的初始化代码
@PreDestroy 组件实例在销毁时,需要执行的收尾代码
这2个方法,写到任何的 由Spring管理的组件里面,都可以:
/*** 组件实例化时,需要执行的初始化代码* 替代的是 实例初始化块 { }*/@PostConstructpublic void init(){System.out.println("妈妈,我准备好了!!!!");}/*** 组件实例在销毁时,需要执行的收尾代码*/@PreDestroypublic void destroy(){System.out.println("妈妈,我出门了!!!!");}
代理模式
概念
使用场景:如果想隐藏自己,让其他类帮我们去做任务的情况下,我们就可以使用它;
现实生活:明星+经纪人,让别人送情书。
分类
静态代理
看的到代理类的代理模式;
举例:
1.丁力非常喜欢冯程程,但是丁力的背景太差,是一个挑担夫。
2.丁力自己写情书,让许文强给冯程程,冯程程把信看完非常的高兴,但以为是许文强写的。
3.于是冯程程通过许文强 ,再次和丁力通信。
分析:
1.冯程程 是 丁力追求的目标。
2.许文强 是 丁力的代理。
3.许文强 具有与 丁力 相同的能力。
package com.woniuxy.proxy.statics;
public interface IProxy {void sendLetter();
}
package com.woniuxy.proxy.statics;
/*** 真实对象*/
public class RealObject implements IProxy{private GrilBean grilBean;public RealObject() {}public RealObject(GrilBean grilBean) {this.grilBean = grilBean;}/*** 给女孩送信*/public void sendLetter(){System.out.println("给[" + grilBean.getGirlName() + "]" + "送信!!!");}public void getLetter(){System.out.println("亲爱的["+grilBean.getGirlName()+"],我喜欢你!!!");}
}
package com.woniuxy.proxy.statics;
/*** 代理对象*/
public class ProxyObject implements IProxy{private GrilBean grilBean;private RealObject realObject;public ProxyObject(GrilBean grilBean) {this.grilBean = grilBean;//代理对象,肯定认识 真实对象realObject = new RealObject(grilBean);}/*** 代理对象的送信行为*/@Overridepublic void sendLetter() {realObject.getLetter();//送信之前System.out.println("将信的署名修改为代理对象的!!!!");//送信realObject.sendLetter();//送信之后}
}
package com.woniuxy.proxy.statics;
import java.io.Serializable;
/*** 目标对象*/
public class GrilBean implements Serializable {private String girlName;public GrilBean() {}public GrilBean(String girlName) {this.girlName = girlName;}public String getGirlName() {return girlName;}public void setGirlName(String girlName) {this.girlName = girlName;}
}
package com.woniuxy.proxy;
import com.woniuxy.proxy.statics.GrilBean;
import com.woniuxy.proxy.statics.IProxy;
import com.woniuxy.proxy.statics.ProxyObject;
public class Main {public static void main(String[] args) {// write your code hereGrilBean grilBean = new GrilBean("冯程程");//将女孩交给代理对象IProxy proxy = new ProxyObject(grilBean);//代理对象给它送信proxy.sendLetter();}
}
动态代理
看不到代理类的代理模式
1、JDK代理
定义:JDK代理,是属于接口代理模式,真是类身上一定要有接口,代理类和真实类之间是兄弟关系。但它不合适,没有接口真实类。
package com.woniuxy.proxy.auto.jdk;
public interface IUserService {void saveUserBean();void updateUserBean();
}
package com.woniuxy.proxy.auto.jdk;
public class UserServiceImpl implements IUserService{@Overridepublic void saveUserBean() {System.out.println("我在做保存!!!!");}@Overridepublic void updateUserBean() {System.out.println("我在做更新!!!!");}
}
package com.woniuxy.proxy.auto.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyFactory {private Object target;public void setTarget(Object target) {this.target = target;}public Object getProxyIntance(){//返回一个目标对象的,代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),//接口方法,在调用时,如何实现new InvocationHandler() {/**** @param proxy 代理对象* @param method 正在执行的方法* @param args 方法的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//调用代理对象执行目标方法return method.invoke(target,args);}});}
}
package com.woniuxy.proxy.auto.jdk;
public class MainEnter {public static void main(String[] args) {JdkProxyFactory proxyFactory = new JdkProxyFactory();//真实对象IUserService userService = new UserServiceImpl();proxyFactory.setTarget(userService);//产生一个代理对象IUserService proxyInstance = (IUserService)proxyFactory.getProxyIntance();//通过代理对象,调用代理方法proxyInstance.saveUserBean();proxyInstance.updateUserBean();//返回对象的类的父类System.out.println(userService.getClass().getSuperclass());System.out.println(proxyInstance.getClass().getSuperclass());}
}
2、Cglib代理
cglib代码,它不再是接口那种兄弟代理方法,它采用的是 子类代理
cglib 的功能,在spring-core.jar包中有完整的实现,所以需要引入:
<!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency>
package com.woniuxy.springaop.bean;
public class UserBean {
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
public class UserServiceImpl {/*** 新增用户* @param userBean*/public void saveUserBean(UserBean userBean){System.out.println("saveUserBean()" + userBean);}/*** 修改用户* @param userBean*/void updateUserBean(UserBean userBean) {System.out.println("updateUserBean()" + userBean);}
}
package com.woniuxy.springaop.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyFactory implements MethodInterceptor {/*** 目标对象*/private Object target;public void setTarget(Object target) {this.target = target;}public Object getProxyInstance(){//1. 工具类Enhancer en = new Enhancer();//2. 设置父类en.setSuperclass(target.getClass());//3. 设置回调函数en.setCallback(this);//4. 创建子类(代理对象)return en.create();}/**** @param o 代理对象* @param method 目标方法* @param objects 目标参数* @param methodProxy 代理方法* @return* @throws Throwable*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//目标方法执行之前(操作事务,记录日志……)//代理方法正式执行Object obj = methodProxy.invoke(target,objects);//放行,给该类授权,授权可以访问其他类的非 public元素method.setAccessible(true);//目标方法执行之后return obj;}
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
import com.woniuxy.springaop.proxy.cglib.CglibProxyFactory;
import org.junit.Test;
import static org.junit.Assert.*;
public class UserServiceImplTest {@Testpublic void saveUserBean() {UserServiceImpl userService = new UserServiceImpl();CglibProxyFactory proxyFactory = new CglibProxyFactory();proxyFactory.setTarget(userService);//产生代理实例UserServiceImpl proxyInstance = (UserServiceImpl)proxyFactory.getProxyInstance();//调用代理实例的方法UserBean userBean = new UserBean();proxyInstance.saveUserBean(userBean);}@Testpublic void updateUserBean() {}
}
需要注意的是: 真实类由于采用的子类代理,所以不能使用final关键字来修饰类 或者 方法
JDK代理和Cglib代理的区别(面试题)
1、两种代理的原理不一样,JDK代理属于接口代理,代理类和真实类之间的关系是,朋友关系;而Cglib代理是属于子类代理,代理类和真实类之间的关系是:父子关系(真实类是父,代理类是子)。
2、Cglib 是通过 ASM 这种字节码处理框架ASM来转换字节码并生成新的类,而JDK 是通过 JVM 底层在内存中 根据接口,动态产生一个代理实例出来。
代理模式的作用
1、隐藏真实对象本身;
2、给真实对象方法,执行之前,或执行之后可以 增强一些其他方法。
SpringAOP
Aspect Oriented Programing with Spring (面向切面编程);
我们之前的所写代码,一般都是表现层 调用业务层 业务层调用持久层,代码的执行顺序是从上至下依次执行,也就说:我们之前一般都是纵向关注代码。而AOP是横向关注代码。
概念图
作用
AOP最大作用:采用核心代码模式,将核心业务与非核心业务进行分离关注核心业务,我们采用纵向关注,而 非核心 业务,我们采用横向关注!
核心业务:我们采用纵向关注,而非核心业务,我们采用横向关注!!
交叉业务:不同的功能模块中,都拥有的业务(几乎都是非核心功能)。
常见概念:
1、切面 又叫方面 每个切面实际上就是我们的非核心功能,一般一个非核心功能一个切面;
2、切入点 切面根据条件,能够适配的功能点,都切面的切入点。这个切入点可以是一个方法,也可以是方法的结束,甚至可以是方法的异常;
3、连接点 切面正式进入到某个功能点上的切入点时,所在的点就是连接点;
4、通知,也叫增强 定义在切面上,给目标方法执行之前,或执行之后,需要新增的的方法,也就是提供在切面上的功能(前置通知,后置通知,后置返回通知,后置异常通知,环绕通知);
5、目标对象 就是指的是 被切面需要增强的对象;
6、代理对象 就是AOP切面,动态给目标对象产生出来的 代理实例,这个实例往往采用JDK代理,以及CGLIB代理产生出来的(目标对象有接口的,采用JDK代理,目标对象没有接口的,采用BGLIB代理);
7、Weaving织入 AOP切面应用到代理对象的过程,就叫织入;
Spring提供切面的2种方式
切入点表达式的格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)
modifiers-pattern 访问修饰符 可以省略。
ret - type - pattern 返回类型 不能省略。
declaring - type - pattern 类的类路径 可以省略。
name - pattern 方法的名称 不能省略。
param - pattern 参数列表 不能省略。
throws- pattern ? 异常表达式 可以省略。
//拦截com.woniuxy.service包下所有类的所有方法
execution(*com.woniuxy.service.*.*(..))
//拦截所有public方法
execution(public * *(..))
//save开头的方法
execution(* save*(..))
//拦截指定类的指定方法, 拦截时候一定要定位到方法
execution(public com.woniuxy.g_pointcut.OrderDao.save(..))
//拦截指定类的所有方法
execution(* com.woniuxy.g_pointcut.UserDao.*(..))
//拦截指定包,以及其子包下所有类的所有方法
execution(* com..*.*(..))
//多个表达式
// ||和or表示两种满足其一即可,取两个表达式的并集
execution(* com.woniuxy.g_pointcut.UserDao.save()) || execution(* com.woniuxy.g_pointcut.OrderDao.save())
execution(* com.woniuxy.g_pointcut.UserDao.save()) or execution(* com.woniuxy.g_pointcut.OrderDao.save())
// &&和and 表示两种都同时满足才行,取交集
//下面2个且关系的,没有意义
execution(* com.woniuxy.g_pointcut.UserDao.save()) && execution(* com.woniuxy.g_pointcut.OrderDao.save())
execution(* com.woniuxy.g_pointcut.UserDao.save()) and execution(* com.woniuxy.g_pointcut.OrderDao.save())
//取非值 !和not表示不在该范围内的作为切点
!execution(* com.woniuxy.g_pointcut.OrderDao.save())
not execution(* com.woniuxy.g_pointcut.OrderDao.save())
方式一
Spring自带的Schema-based 基于配置文件(了解)
1、导入需要的Jar包
<dependencies><!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version><scope>runtime</scope></dependency><!--spring兼容Junit4--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.9.RELEASE</version><scope>runtime</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>runtime</scope></dependency></dependencies>
2、创建Bean文件
package com.woniuxy.springaop.bean;
public class GoodsBean {
}package com.woniuxy.springaop.bean;
public class UserBean {
}
3、创建业务层接口
package com.woniuxy.springaop.service;
import com.woniuxy.springaop.bean.UserBean;
public interface IUserService {/*** 新增用户* @param userBean*/void saveUserBean(UserBean userBean);/*** 修改用户* @param userBean*/void updateUserBean(UserBean userBean);
}package com.woniuxy.springaop.service;
import com.woniuxy.springaop.bean.GoodsBean;
public interface IGoodsService {void saveGoodsBean(GoodsBean goodsBean);
}
4、业务层接口实现类
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.GoodsBean;
import com.woniuxy.springaop.service.IGoodsService;
import org.springframework.stereotype.Service;
@Service
public class GoodsServiceImpl implements IGoodsService {@Overridepublic void saveGoodsBean(GoodsBean goodsBean) {System.out.println("saveGoodsBean()" + goodsBean);}
}package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
import com.woniuxy.springaop.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {/*** 新增用户* @param userBean*/public void saveUserBean(UserBean userBean){System.out.println("saveUserBean()" + userBean);}/*** 修改用户* @param userBean*/public void updateUserBean(UserBean userBean) {System.out.println("updateUserBean()" + userBean);}
}
5、配置文件的内容
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启容器的自动扫描--><context:component-scan base-package="com.woniuxy"></context:component-scan><!--配置一个切面--><aop:config><!--execution(public * com.woniuxy.springaop.service.impl.UserServiceImpl.*(..) throws NullPointerException)切入点的条件:方法必须是public 修饰的,返回类型无所谓,必须是com.woniuxy.springaop.service.impl.UserServiceImpl下的方法必须要抛出throws NullPointerException--><aop:pointcut id="poincutOne" expression="execution(* com.woniuxy.springaop.service.impl.*ServiceImpl.*(..))"/><aop:advisor advice-ref="beforAdvice" pointcut-ref="poincutOne"></aop:advisor><aop:advisor advice-ref="afterRuturningAvice" pointcut-ref="poincutOne"></aop:advisor></aop:config>
</beans>
6、测试
7、总结:Spring自带的切面技术,1、需要实现1套接口,2、编写配置文件
非常的麻烦。
方式二
Spring兼容AspectsJ
需要使用第三方的切面技术:AspectsJ;
以事务控制为例;
推荐的原因:
1、不需要继承或实现任何接口;
2、不过于依赖配置,它提倡使用注解。
步骤
1、先引入第三方Jar文件
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version>
</dependency>
2、开启Spring的切面代理模式
<!--开启切面的动态代理支持:jdk 以及 cglib--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3、切面上定义通知(前置,后置,后置返回,后置异常),其他代码不变
package com.woniuxy.springaop.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/*** 事务切面*/
@Component //将切面交给Spring容器
@Aspect //将该类申明为一个 切面类
public class TransactionAspect {/*** 定义切入点*/@Pointcut("execution(* com.woniuxy.springaop.service.impl.*ServiceImpl.*(..))")public void pointcut(){}/*** 前置通知* @param joinPoint 连接点*/@Before(value = "pointcut()")public void beforeAdvice(JoinPoint joinPoint){System.out.println("我的编号:9527叫前置通知,我执行在目标方法执行之前!!!");Object target = joinPoint.getTarget();System.out.println("目标对象" + target);String methodName = joinPoint.getSignature().getName();System.out.println("目标方法" + methodName);Object[] args = joinPoint.getArgs();System.out.println("目标方法所接收的参数:" + Arrays.toString(args));//开启事务}/*** 后置返回通知* @param joinPoint 连接点* @param result 目标方法的返回结果*/@AfterReturning(value = "pointcut()",returning = "result")public void afterReturningAdvice(JoinPoint joinPoint,Object result){System.out.println("我是后置返回通知,我执行在目标方法正常返回结果之后!!!");System.out.println("目标方法的返回是:" + result);}/*** 后置异常通知* @param joinPoint 连接点* @param throwable 目标方法抛出的异常*/@AfterThrowing(value = "pointcut()",throwing = "throwable")public void afterThrowingAdvice(JoinPoint joinPoint,Throwable throwable){System.out.println("我是后置异常通知,我执行在目标方法抛出异常之后!!!");System.out.println("抛出的异常是:" + throwable.getMessage());}/*** 后置通知* @param joinPoint 连接点*/@After(value="pointcut()")public void afterAdvice(JoinPoint joinPoint){System.out.println("我是后置通知,我执行在目标方法,结束之前,类似于try-catch-fanally中的fanally()");}/*** 定义更多的切入点*/
// @Pointcut("execution(* com.woniuxy.springaop.service.impl.*ServiceImpl.*(..))")
// public void pointcut01(){}
// @Pointcut("execution(* com.woniuxy.springaop.service.impl.*ServiceImpl.*(..))")
// public void pointcut02(){}
}
环绕通知@Around(了解)
–环绕通知:是5种溶质类型中最厉害的一种,因为它可以:修改参数,修改返回,甚至可以决定目标方法是否需要继续执行。
它 约等于 @Before + @After ,也就是前置通知和后置通知;
记住:能用@Before + After,千万不要使用@Around。
不推荐使用!
/*** 环绕通知 约等于 Before + After* NB:* 1、修改目标方法执行的参数* 2、修改目标方法执行的返回* 3、甚至可以控制到目标方法是否需要执行**** @param joinPoint* @return*/@Around(value="pointcut()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("我是环绕通知,我约等于:@Before + @After");//参数Object[] args = joinPoint.getArgs();args[0] = null; //将目标方法的第1个参数修改为NULL//该方法就相当于method.invoke(obj,args);Object obj = joinPoint.proceed(args);obj = 1;//所有方法,返回结果固定为1return obj;}
AOP在项目中的应用(面试题)
1、事务控制;
2、日志记录;
3、异常处理;
4、敏感词过滤;
SM整合
明确依赖包:
Spring各种依赖包,Mybatis依赖包,mysql-连接数据库的依赖包,整Junit的依赖包,Lombok的依赖包,Log4J的依赖包。
Spring各种依赖包
<!--导入spring框架的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version><scope>runtime</scope></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version></dependency><!--spring兼容Junit4--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.9.RELEASE</version><scope>runtime</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.9.RELEASE</version><scope>runtime</scope></dependency>
Mybatis的依赖包
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.3</version>
</dependency>
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.3</version>
</dependency>
连接Mysql数据库的依赖包
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>
Log4J依赖包
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
整合步骤
第一步
导入依赖关系;
第二步
准备项目结构;
第三步
编写Spring框架的配置文件(重点);
applicationContext.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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
Spring框架配置文件的步骤
1、配置数据库连接池,提供JDBC连接;
–数据源:就是数据的来源,来源可能很多:数据库,NOSQL, Excel, 其他系统
当然在我们这里,通常是指:关系型数据库;
–商业连接池:
C3P0, DBCP(阿帕奇), Druid(阿里-德鲁伊)
–DBCP
DBCP的配置文档
http://commons.apache.org/proper/commons-dbcp/configuration.html
–(1)引入DBCP依赖包
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-dbcp2</artifactId><version>2.7.0</version>
</dependency>
–(2)创建mysql.properties文件
jdbc.url=jdbc:mysql://localhost:3306/sm?characterEncoding=UTF8
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=123456
–(3)在applicationContext.xml中,配置数据源;
<!--将properties 配置文件,引入到Spring容器中--><context:property-placeholder location="mysql.properties"></context:property-placeholder><!--配置DataSource 数据源连接池--><bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"><!--配置JDBC连接数据库,最基本的连接信息--><property name="url" value="${jdbc.url}"></property><property name="driverClassName" value="${jdbc.driverClassName}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property><!--额外配置--><!--连接池中设置默认连接数为:5--><property name="initialSize" value="5"></property><!--连接池中,最大的连接数为:50--><property name="maxTotal" value="50"></property><!--设置连接池中,空闲区的连接最多为:10--><property name="maxIdle" value="10"></property><!--设置是否支持预编译--><property name="poolPreparedStatements" value="true"></property><!--设置预编译对象,默认为:20--><property name="maxOpenPreparedStatements" value="20"></property><!--创建连接时,校验连接是否有效--><property name="testOnCreate" value="true"></property><!--设置校验语句--><property name="validationQuery" value="select now() from dual"></property><!--取消自动提交--><property name="defaultAutoCommit" value="false"></property><!--取消连接只读--><property name="defaultReadOnly" value="false"></property><!--设置 SQL查询的超时时间为:2S钟,凡是超过2S以上的语句,都会报错!!!--><property name="defaultQueryTimeout" value="2"></property></bean>
2、配置SessionFactory 挺SQLsession;
SessionFactory:根据数据源,产生SQLSession对象;
<!--配置SessionFactory--><bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property><!--给JavaBean 定义别名:放置的是 JavaBean 所在的目录--><property name="typeAliasesPackage" value="com.woniuxy.sm.bean"></property><!--额外的配置--><property name="configuration"><bean class="org.apache.ibatis.session.Configuration"><!--开启延迟加载--><property name="lazyLoadingEnabled" value="true"></property><property name="aggressiveLazyLoading" value="false"></property></bean></property></bean>
3、配置MapperScanner 扫描SQL映射文件;
MapperScanner 扫描器 : 扫描 *Mapper.xml这种配置文件;
<!--配置MapperScanner扫描器--><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--放置Mapper接口以及配置文件所在路径,如果有多个路径,可以使用 , 或 ; 进行分割--><property name="basePackage" value="com.woniuxy.sm.mapper"></property><!-- 将映射文件 和 SessionFactory 进行连接,通过jdk代理为Mapper接口,动态产生代理类 --><property name="sqlSessionFactoryBeanName" value="sessionFactory"></property></bean>
4、配置事务管理器提供事务管理;
事务管理器:一个专门用来管理事物的容器/工具;
学习阶段,我们采用DAO来手动的控制事务,但是那种方式没办法保证,这个业务逻辑是处于事务控制下。
<!--事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean>
5、使用AOP切面技术管理事务,控制事务的管理范围。
(1)使用XML进行事务控制
<!--定义事务切面上的通知--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!--查询方法,以及其他的非法命名方法,统一连接只读--><tx:method name="*" read-only="true"></tx:method><tx:method name="save*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="add*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="insert*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="update*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="modify*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="delete*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/><tx:method name="remove*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"/></tx:attributes></tx:advice><!--定义切面--><aop:config><aop:pointcut id="pointcut1" expression="execution(* com.woniuxy.sm.service.impl.*ServiceImpl.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"></aop:advisor></aop:config>
(2)使用注解进行事务控制
第四步
安装Lombok插件
最好采用,本地磁盘安装,安装的包是:lombok-plugin-0.32-2020-1.zip;
在setting -> plugins -> 设置 -> install plugin from disk
在安装好后,重启IEDA。
在pom.xml中引入Lombok插件。
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency>
@Data 该注解定义在JavaBean上,作用:给JavaBean产生getter()和setter()方法、无参构造器、toString()、hashCode()、equals();
@NoArgsConstructor()产生无参构造器;
@Getter产生 getter();
@Setter() 产生 setter();
@ToString 产生toString();
@RequireArgsContructor + @NonNull 可以用来定义无参构造器;
package com.woniuxy.sm.bean;
import lombok.*;
import java.io.Serializable;
import java.util.Date;
/*** 用户实例类*/
@Data
@RequiredArgsConstructor
public class UserBean implements Serializable {private Integer id;@NonNullprivate String userName;@NonNullprivate String loginName;@NonNullprivate String password;private int age;private Date birthday;private RoleBean roleBean;
}
package com.woniuxy.sm.bean;
import lombok.Data;
import java.io.Serializable;
/*** 角色实体类*/
@Data
public class RoleBean implements Serializable {private Integer id;private String roleName;
}
第五步
定义业务层接口,并提供实现类
package com.woniuxy.sm.service;
import com.woniuxy.sm.bean.UserBean;
public interface IUserService {/*** 新增用户* @param userBean*/void saveUserBean(UserBean userBean);/*** 修改用户* @param userBean*/void updateUserBean(UserBean userBean);/*** 根据ID查询用户* @param id* @return*/UserBean getOneById(Integer id);
}
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.bean.UserBean;
import com.woniuxy.sm.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {@Overridepublic void saveUserBean(UserBean userBean) {}@Overridepublic void updateUserBean(UserBean userBean) {}@Overridepublic UserBean getOneById(Integer id) {return null;}
}
第六步
开启容器的自动扫描功能
<!--开启容器自动扫描功能-->
<context:component-scan base-package="com.woniuxy.sm"></context:component-scan>
第七步
编写持久层代码
package com.woniuxy.sm.mapper;
import com.woniuxy.sm.bean.UserBean;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {/*** 新增用户* @param userBean*/void saveUserBean(@Param("u") UserBean userBean);/*** 修改用户* @param userBean*/void updateUserBean(@Param("u")UserBean userBean);/*** 根据ID查询用户* @param id* @return*/UserBean getOneById(@Param("id")Integer id);
}
编写配置文件UserMapper.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.woniuxy.sm.mapper.UserMapper"><insert id="saveUserBean" useGeneratedKeys="true" keyProperty="u.id">insert into user_info(user_name,login_name,password,age,birthday)values (#{u.userName},#{u.loginName},#{u.password},#{u.age},#{u.birthday})</insert><update id="updateUserBean">update user_info<set><if test="u.userName != null">user_name = #{u.userName},</if><if test="u.loginName != null">login_name = #{u.loginName},</if><if test="u.password != null">password = #{u.password},</if><if test="u.age != null">age = #{u.age},</if><if test="u.birthday != null">birthday = #{u.birthday},</if></set><where>id = #{u.id}</where></update><select id="getOneById" resultType="UserBean">select id,user_name as userName,login_name as loginName,age,birthday from user_info where id = #{id}</select>
</mapper>
第八步
在pom.xml中,添加配置路径;
<!--指定配置文件的位置--><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources>
事务
事务:一个具有明确边界的,而且执行顺序是有序的一段序列。
Eg:张三个李四转钱;
Eg:一次HTTP请求;
四大特性
原子性:事务是一个完整的个体,不能再次分离(要么统一成功,要 么统一失败);
隔离性:事务和事务之间是相互隔离的,互不影响的。
一致性:同一个事务提交范围内,数据需要保证准确性以及完整性(质量守恒);
持久性:事务一旦提交,结果具有永久性。
事务的隔离级别
事务隔离级别和数据库级别是一个意思。数据库的隔离级别(mysql)有四个:
降低了隔离界别,除了会引发幻读,不可重复度读,以及脏读意外的情况,还有可能会引发两种数据更新丢失的问题。
更新丢失
更新丢失(Lost update):
如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失了,这就叫做更新丢失。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
更新丢失的解决方案:上数据库锁。
第一类更新丢失
事务A撤销时,把已经提交的事务B的更新数据覆盖了。
第二类更新丢失
事务A覆盖事务B已经提交的数据,造成事务B所做的操作的操作丢失。
隔离级别与更新丢失的情况
正因为:读已提交,可能会导致第二类更新丢失,以及会导致不可重复读,所以它的数据一致性的问题,比可重复读高,所以我们一般使用:可重复读,来作为我们默认隔离级别。
事务的传播特性
事务的传播:当多个同时具有事务能力的方法,在进行相互调用时,事务如何控制,或者如何传播的问题。
记住两点:
1、REQUIRED:不管怎么样,一定要有事务,通常用于CUD;
2、SUPPORTS:有事务,就支持事务,没有事务,就非事务方式执行。
Spring框架提供的事务处理方案(面试题)
编程式事务解决方案
在业务层的代码中,使用TransactionTemplate ,PlatformTransactionManager 提供的功能,去管理实务。
public class SimpleService implements Service {// single TransactionTemplate shared amongst all methods in this instanceprivate final TransactionTemplate transactionTemplate;// use constructor-injection to supply the PlatformTransactionManagerpublic SimpleService(PlatformTransactionManager transactionManager) {this.transactionTemplate = new TransactionTemplate(transactionManager);}public Object someServiceMethod() {return transactionTemplate.execute(new TransactionCallback() {// the code in this method executes in a transactional contextpublic Object doInTransaction(TransactionStatus status) {updateOperation1();return resultOfUpdateOperation2();}});}
}
注意:这种方法在很早以前就已经废弃了。废弃的原因是它把事务控制代码,和核心代码再度进行了耦合。
声明式事务解决方案
使用AOP的思想,将事务控制定义在切面中,而核心代码中就没有任何的事务逻辑代码。
1、在apploicationContext.xml中。
<!--开启注解驱动支持--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
2、更换Maven依赖
将spring-jdbc 更换为spring-orm的依赖关系。
<!--Spring的提供JDBC连接支持,以及事务管理依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.2.9.RELEASE</version><scope>runtime</scope></dependency>
3、添加一个依赖关系
<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.9.RELEASE</version></dependency>
4、采用@Transactional 注解完成对事务的声明。
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.bean.UserBean;
import com.woniuxy.sm.mapper.UserMapper;
import com.woniuxy.sm.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(readOnly = true)
@Service
public class UserServiceImpl implements IUserService {@Resourceprivate UserMapper userMapper;@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic void saveUserBean(UserBean userBean) {userMapper.saveUserBean(userBean);}@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic void updateUserBean(UserBean userBean) {userMapper.updateUserBean(userBean);}@Overridepublic UserBean getOneById(Integer id) {return userMapper.getOneById(id);}
}
Log4J日志框架
1、导入依赖关系
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
2、编写log4j.properties配置文件
log4j.rootLogger=DEBUG, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%5p [%t] - %m%n
#Print OUT SQL
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
3、在applicationContext.xml中,开启日志记录
<!--配置SessionFactory--><bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property><!--给JavaBean 定义别名:放置的是 JavaBean 所在的目录--><property name="typeAliasesPackage" value="com.woniuxy.sm.bean"></property><!--额外的配置--><property name="configuration"><bean class="org.apache.ibatis.session.Configuration"><!--开启延迟加载--><property name="lazyLoadingEnabled" value="true"></property><property name="aggressiveLazyLoading" value="false"></property><!--开启Log4j的日志记录--><property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property></bean></property></bean>
重点配置
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
锁
我们都知道降低隔离级别,会导致数据不一致的问题,隔离越低,准确性越差!
怎么解决呢?
方案:悲观锁,乐观锁。
悲观锁
悲观锁是 数据库中一种非常严格的行级锁,悲观锁的概念是:程序悲观的认为,当某一个用户在操作数据库中数据时,一定存在有其他用户跟他一起操作同样的一批数据,而且是同时,
于是为了解决这种问题,程序直接针对数据库底层的数据进行上锁。上锁之后的效果是:用户A 针对某些数据上了锁,其他用户就无法针对相同的数据进行查看以及操作效果等同于,在代码中使用synchronized。
具体的步骤: 在你的查询语句中 添加
for update;
语句举例:
select * from user_info for update;
上述的这条语句,会导致其他用户无法查看user_info中的数据,除非:当前使用者已经 提交事务,回滚事务,关闭事务
这种方式,准确性高,但效率太低! 通常适用在: 购买火车票。
乐观锁
乐观锁:是解决数据一致性问题,最为频繁的一种锁。它不是针对数据库底层上锁,相反它可以理解是一种逻辑锁。
乐观锁的概念:程序乐观的认为:当某一个用户在操作一批数据时,一定没有人和他同时操作同一批数据。
乐观锁的解决方案:添加版本控制;
添加版本控制后,每次提交都会将自己的version+1,
然后与数据库版本号进行对比,看是否大于数据库的版本号,如果大于,将正常提交,如果不大于,则提交不成功!
当然Mybatis中没有任何的乐观锁提供,需要程序员自己定义逻辑,如果是Hibernate或SpringDataJPA他们都默认提供乐观锁支持。
乐观锁的具体实现
创建JavaBean:商品类
package com.woniuxy.sm.bean;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class GoodsBean implements Serializable {private Integer id;private String goodsName;private BigDecimal price;/*** 库存量*/private Integer storeNum;/*** 版本号*/private Integer version;
}
订单类
package com.woniuxy.sm.bean;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
@Data
@RequiredArgsConstructor
public class OrderBean implements Serializable {private Integer id;@NonNullprivate String orderNo;/*** 商品*/@NonNullprivate GoodsBean goodsBean;/*** 购买者*/@NonNullprivate UserBean userBean;
}
创建业务层的接口
package com.woniuxy.sm.service;
import com.woniuxy.sm.bean.GoodsBean;
import com.woniuxy.sm.bean.OrderBean;
import com.woniuxy.sm.bean.UserBean;
public interface IGoodsService {/*** 根据ID查询商品* @param id* @return*/GoodsBean getOneById(Integer id);/*** 购买商品,产生订单* @param orderBean*/String buyGoods(OrderBean orderBean);
}
创建两个持久层的接口
package com.woniuxy.sm.mapper;
import com.woniuxy.sm.bean.GoodsBean;
import org.apache.ibatis.annotations.Param;
public interface GoodsMapper {/*** 根据ID查询商品* @param id* @return*/GoodsBean getOneById(Integer id);/*** 更新商品(库存量,以及版本号)* @param goodsBean* @return 数据库受影响的行数*/int updateGoodsBean(@Param("g") GoodsBean goodsBean);
}
package com.woniuxy.sm.mapper;
import com.woniuxy.sm.bean.OrderBean;
import org.apache.ibatis.annotations.Param;
public interface OrderMapper {/*** 保存订单* @param orderBean* @return*/int saveOrderBean(@Param("o") OrderBean orderBean);
}
并分别编写对应的配置文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.woniuxy.sm.mapper.GoodsMapper"><select id="getOneById" resultType="GoodsBean">select id,goods_name as goodsName,price,store_num as storeNum,versionfrom goods_info where id = #{id}</select><update id="updateGoodsBean">update goods_info<set><if test="g.storeNum != null">store_num = #{g.storeNum} - 1,</if><if test="g.version != null">version = #{g.version} + 1,</if></set><where>id = #{g.id} and version = #{g.version}</where></update>
</mapper>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.woniuxy.sm.mapper.OrderMapper"><insert id="saveOrderBean">insert into order_info (order_no,fk_goods_id,fk_user_id) values(#{o.orderNo},#{o.goodsBean.id},#{o.userBean.id})</insert>
</mapper>
编写业务层实现类的代码
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.bean.GoodsBean;
import com.woniuxy.sm.bean.OrderBean;
import com.woniuxy.sm.mapper.GoodsMapper;
import com.woniuxy.sm.mapper.OrderMapper;
import com.woniuxy.sm.service.IGoodsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(readOnly = true)
@Service
public class GoodsServiceImpl implements IGoodsService {@Resourceprivate GoodsMapper goodsMapper;@Resourceprivate OrderMapper orderMapper;@Overridepublic GoodsBean getOneById(Integer id) {return goodsMapper.getOneById(id);}/*** 乐观锁的逻辑* @param orderBean* @return*/@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic String buyGoods(OrderBean orderBean) {//先查询数据库中版本信息,以及库存量 > 0GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());if(goodsBean.getStoreNum() > 0){int currentVersion = orderBean.getGoodsBean().getVersion() + 1;int dbVersion = goodsBean.getVersion();//库存量 >0 的情况下, 判断当前的版本号 是否 > 数据库中版本号if(currentVersion > dbVersion){//更新数据,并返回受影响的行数int rows = goodsMapper.updateGoodsBean(goodsBean);if(rows > 0){return "恭喜你,购买成功!!!";} else{throw new RuntimeException("手速太慢,请重新操作!!!");}}else{throw new RuntimeException("手速太慢,请重新操作!!!");}}else{return "你凉了,商品已售罄!";}}
}
编写测试类
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.bean.GoodsBean;
import com.woniuxy.sm.bean.OrderBean;
import com.woniuxy.sm.bean.UserBean;
import com.woniuxy.sm.service.IGoodsService;
import com.woniuxy.sm.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class GoodsServiceImplTest {@Resourceprivate IGoodsService goodsServiceImpl;@Resourceprivate IUserService userServiceImpl;@Testpublic void getOneById() {}@Testpublic void buyGoods() {//选择1件商品GoodsBean goodsBean = goodsServiceImpl.getOneById(2);//选择1个用户UserBean userBean = userServiceImpl.getOneById(1);//产生订单OrderBean orderBean = new OrderBean("567567",goodsBean,userBean);//购买String result = goodsServiceImpl.buyGoods(orderBean);System.out.println(result);System.out.println("----------------------------------------------");//选择1个用户UserBean userBean02 = userServiceImpl.getOneById(2);//产生订单OrderBean orderBean02 = new OrderBean("456789",goodsBean,userBean02);//购买String result02 = goodsServiceImpl.buyGoods(orderBean02);System.out.println(result02);}
}
乐观锁的补偿机制
乐观锁,是通过版本控制的方法,来解决多个人,同时因为降低隔离级别,而导致数据不一致的问题,因为它在解决问题,往往可能抛出:你操作手速太慢,需要再次操作!
所以需要对它进行一定的补偿!
补偿代码:
/*** 再次购买* @param orderBean* @return true 表示购买成功,false 表示商品已售罄*/public boolean reBuy(OrderBean orderBean){GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());if(goodsBean.getStoreNum() > 0){int rows = goodsMapper.updateGoodsBean(goodsBean);if(rows > 0){//产生订单orderMapper.saveOrderBean(orderBean);return true;} else{reBuy(orderBean);}}else{return false;}return false;}
乐观锁逻辑代码:
/*** 乐观锁的逻辑* @param orderBean* @return*/@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic String buyGoods(OrderBean orderBean) {//先查询数据库中版本信息,以及库存量 > 0GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());if(goodsBean.getStoreNum() > 0){int currentVersion = orderBean.getGoodsBean().getVersion() + 1;int dbVersion = goodsBean.getVersion();//库存量 >0 的情况下, 判断当前的版本号 是否 > 数据库中版本号if(currentVersion > dbVersion){//更新数据,并返回受影响的行数int rows = goodsMapper.updateGoodsBean(goodsBean);if(rows > 0){//产生订单orderMapper.saveOrderBean(orderBean);return "恭喜你,购买成功!!!";} else{
// throw new RuntimeException("手速太慢,请重新操作!!!");boolean flag = reBuy(orderBean);if (flag) return "恭喜你,购买成功!!!";else return "你凉了,商品已售罄!";}}else{
// throw new RuntimeException("手速太慢,请重新操作!!!");boolean flag = reBuy(orderBean);if (flag) return "恭喜你,购买成功!!!";else return "你凉了,商品已售罄!";}}else{return "你凉了,商品已售罄!";}}
日志切面
系统日志切面
<!--日志桥梁框架--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.30</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version><scope>test</scope></dependency>
在applicationContext.xml中,开启切面的动态代理支持
<!--开启切面的动态代理支持--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
定义一个切面,并在切面中@Slf4注解
package com.woniuxy.sm.aspects;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Slf4j
@Component
@Aspect
public class SystemLogAspect {@Pointcut("execution(* com.woniuxy.sm.service.impl.*ServiceImpl.*(..))")public void pointcut(){}/*** 前置通知* 记录:目标方法的接收参数* @param joinPoint*/@Before(value="pointcut()")public void beforeAdvice(JoinPoint joinPoint){Object target = joinPoint.getTarget();//目标方法String meth = joinPoint.getSignature().getName();//接收的参数Object[] args = joinPoint.getArgs();log.info(target + "--" + meth + "()---args:{} ",Arrays.toString(args));}/*** 后置返回* 记录:目标方法的返回结果* @param joinPoint* @param result*/@AfterReturning(value = "pointcut()",returning = "result")public void afterReturningAdvice(JoinPoint joinPoint,Object result){Object target = joinPoint.getTarget();//目标方法String meth = joinPoint.getSignature().getName();log.info(target + "--" + meth + "()---result:{}",result);}/*** 后置异常通知* 记录:目标方法抛出的异常信息* @param joinPoint* @param exception*/@AfterThrowing(value = "pointcut()",throwing = "exception")public void afterThrowingAdvice(JoinPoint joinPoint,Exception exception){Object target = joinPoint.getTarget();//目标方法String meth = joinPoint.getSignature().getName();log.error(target + "--" + meth + "()---exception:{}",exception);}
}
记录操作日志
操作日志:记录某个人,在某个时间,在某个模块,执行了个什么样的事,它影响了什么数据,所有对数据库会造成影响的地方,都需要记录!CUD操作,R除外。
定义一个JavaBean
package com.woniuxy.sm.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OptLogBean implements Serializable {private Integer id;private String userName;private Date optTime;private String menuName;private Integer optType;private String data;
}
定义业务层接口
package com.woniuxy.sm.service;
import com.woniuxy.sm.bean.OptLogBean;
public interface IOptLogService {/*** 保存操作日志* @param logBean*/void saveLogBean(OptLogBean logBean);
}
定义接口实现类
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.bean.OptLogBean;
import com.woniuxy.sm.service.IOptLogService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(readOnly = true)
public class OptLogServiceImpl implements IOptLogService {//自己装配Mapper@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic void saveLogBean(OptLogBean logBean) {System.out.println("OptLogServiceImpl ----> " + logBean);}
}
定义日志枚举
package com.woniuxy.sm.enums;
import lombok.Getter;
@Getter
public enum OptTypeEnum {SAVE(0),UPDATE(1),DELETE(2);/*** 类型属性(枚举外部使用)*/private int type;private OptTypeEnum(int type){this.type = type;}
}
定义日志注解:
package com.woniuxy.sm.annos;
import com.woniuxy.sm.enums.OptTypeEnum;
import java.lang.annotation.*;
/*** 定义三个元注解*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {/*** 菜单名称* @return*/String menuName();/*** 操作类型* 0-新增* 1-修改* 2-删除* @return*/OptTypeEnum optType();
}
操作日志切面
package com.woniuxy.sm.aspects;
import com.woniuxy.sm.annos.MyLog;
import com.woniuxy.sm.bean.OptLogBean;
import com.woniuxy.sm.service.IOptLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class OptLogAspect {@Resourceprivate IOptLogService logServiceImpl;/*** 后置返回通知* 记录:操作日志* @param joinPoint* @param result* @param log 凡是使用@ MyLog 的地方,都是该切面的切入点*/@AfterReturning(value = "@annotation(log)",returning = "result")public void afterReturningAdvice(JoinPoint joinPoint, Object result, MyLog log){Object[] args = joinPoint.getArgs();OptLogBean optLogBean = new OptLogBean(null,"老蒲",new Date(),log.menuName(),log.optType().getType(), Arrays.toString(args));logServiceImpl.saveLogBean(optLogBean);}
}
在业务层的方法上,使用日志注解
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.annos.MyLog;
import com.woniuxy.sm.bean.UserBean;
import com.woniuxy.sm.enums.OptTypeEnum;
import com.woniuxy.sm.mapper.UserMapper;
import com.woniuxy.sm.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(readOnly = true)
@Service
public class UserServiceImpl implements IUserService {@Resourceprivate UserMapper userMapper;@MyLog(menuName = "用户管理",optType = OptTypeEnum.SAVE)@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic void saveUserBean(UserBean userBean) {userMapper.saveUserBean(userBean);}@MyLog(menuName = "用户管理",optType = OptTypeEnum.UPDATE)@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic void updateUserBean(UserBean userBean) {userMapper.updateUserBean(userBean);}@Overridepublic UserBean getOneById(Integer id) {return userMapper.getOneById(id);}
}
package com.woniuxy.sm.service.impl;
import com.woniuxy.sm.annos.MyLog;
import com.woniuxy.sm.bean.GoodsBean;
import com.woniuxy.sm.bean.OrderBean;
import com.woniuxy.sm.enums.OptTypeEnum;
import com.woniuxy.sm.mapper.GoodsMapper;
import com.woniuxy.sm.mapper.OrderMapper;
import com.woniuxy.sm.service.IGoodsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(readOnly = true)
@Service
public class GoodsServiceImpl implements IGoodsService {@Resourceprivate GoodsMapper goodsMapper;@Resourceprivate OrderMapper orderMapper;@Overridepublic GoodsBean getOneById(Integer id) {return goodsMapper.getOneById(id);}/*** 乐观锁的逻辑* @param orderBean* @return*/@MyLog(menuName = "商品管理",optType = OptTypeEnum.SAVE)@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)@Overridepublic String buyGoods(OrderBean orderBean) {//先查询数据库中版本信息,以及库存量 > 0GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());if(goodsBean.getStoreNum() > 0){int currentVersion = orderBean.getGoodsBean().getVersion() + 1;int dbVersion = goodsBean.getVersion();//库存量 >0 的情况下, 判断当前的版本号 是否 > 数据库中版本号if(currentVersion > dbVersion){//更新数据,并返回受影响的行数int rows = goodsMapper.updateGoodsBean(goodsBean);if(rows > 0){//产生订单orderMapper.saveOrderBean(orderBean);return "恭喜你,购买成功!!!";} else{
// throw new RuntimeException("手速太慢,请重新操作!!!");boolean flag = reBuy(orderBean);if (flag) return "恭喜你,购买成功!!!";else return "你凉了,商品已售罄!";}}else{
// throw new RuntimeException("手速太慢,请重新操作!!!");boolean flag = reBuy(orderBean);if (flag) return "恭喜你,购买成功!!!";else return "你凉了,商品已售罄!";}}else{return "你凉了,商品已售罄!";}}/*** 再次购买* @param orderBean* @return true 表示购买成功,false 表示商品已售罄*/public boolean reBuy(OrderBean orderBean){GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());if(goodsBean.getStoreNum() > 0){int rows = goodsMapper.updateGoodsBean(goodsBean);//判断是否提交成功if(rows > 0){//产生订单orderMapper.saveOrderBean(orderBean);return true;} else{reBuy(orderBean);}}else{return false;}return false;}
}
在日志切面中,使用了一种新的定义切入点的方法,使用注解来定义。
@annotation表示使用注解,必须要定义在方法的上方。
@within 表示使用注解,必须要定义在类的上方。
SpringMVC
MVC
1、模型、视图、控制器
2、它是一种架构模式,是表现层的一种架构模式。
3、MVC来源(发展):HTML(企业网站的信息展示) —> CGI(公共网关接口,它是进程级别的技术,因此非常消耗资源)---->Servlet ----->JSP(视图展示+控制器) +MODEL(JavaBean) 这个阶段被称为 WEB层架构模式一阶段---->WEB层架构模式二阶段(MVC)。
MVC优点:
1、松散 表现层的耦合关系,采用3个模块分类关注的形式,来松散表现层的关系。比如:
模型(MODEL)只关注 数据的传输以及业务功能的实现;
视图(VIEM)只关注 页面的展示;
控制器(Controller) 只关注页面的请求的接收以及 响应的返回。
2、成本低,部署快, 程序员秩序员只需要关注Java代码即可,不用关心前端。前后端各自部署,互相不干扰。
3、维护性高,因为MVC 是3各模块,相互独立,所以可以分别进行维护。
SpringMVC定义
是一种MVC架构模式下的表现层框架。
跟Servlet一样,遵循请求响应模式;
同样使用HTTP通讯协议,完成前后端的数据交互。
请求响应模式定义
Controller 控制器, 以前二阶段就是你们的Servlet来承担这个角色。
Servlet缺陷
1、太过依赖Servlet API ,强制要求必须继承 HTTPServlet;
2、太过依赖tomcat容器, 这就导致开发或测试非常的麻烦;
3、太过依赖具体的页面记住,例如:jsp;
但是现在在这个框架中,它就完美的解决了上述的问题。
SpringMVC控制器
1、核心(前端控制器)
该控制器位于整个框架的最前端,它实际上就是一个Servlet,它的作用:
(1)接收页面的请求;
(2)调度请求到不同的控制器上去;
2、应用控制器
处理映射器、视图解析器、文件上传解析器、处理适配器…
这些应用控制器都是SpringMVC提供好了的,不需要程序员操作,它们每一种都有它们自己的责任:
(1)处理映射器:完成页面的请求 与 页面控制器之间的映射关系,相当于@WebServlet所干的事情;
(2)视图解析器:完成后端的返回 视图名称 到 具体视图资源的解析任务;
(3)文件上传解析器:完成文件上传的任务;
(4)处理适配器:完成页面请求 到 页面控制器之间的适配关系。
SpringMVC框架, 除了采用MVC之外,还采用一种叫做:服务到工作者模式,请求到达该框架的每一步,都有一个Controller来完成对应的任务。
3、页面控制器
SpringMVC的详细架构图(重点)
SpringMVC框架的执行流程:
步骤:
1、前端放弃HTTP请求,到达 核心控制器去。
2、核心控制器将请求转发到 处理映射器上去,问询映射关系(有的就返回关系,没有的就返回404);
3、核心控制器就将请求转发到处理适配器上去,去做适配工作;
4、适配完毕之后,请求将会转发到 页面控制器上去,处理业务流程,并返回ModelAndView;
5、响应到达核心控制器上,由 核心控制器 将 ModelAndView中的viewName交给视图解析器让它解析成真正的视图资源;
6、核心控制器将Model数据 ,向 真正的视图资源填充,渲染;
7、核心控制器将渲染后的视图,返回给前端进行展示。
SpringMVC框架入门(配置版 -了解)
实现 登陆 到 首页!!!
导入依赖
<dependencies><!-- 导入springmvc框架的相关JAR包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.9.RELEASE</version></dependency><!-- Lombok的引入 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency><!-- 引入Servlet API --><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency></dependencies>
SpringMVC验证
格式验证
判断某一个输入框是否有值,某一个输入是否有电话号码,某一个输入是否有身份证(可以靠js+正则表达式);
内容验证
判断某一个账号是否已经存在(靠JS + 后端服务器)。
注意:springMVC的验证属于格式验证。
springMVC验证
作为一个了解往上的内容,(视公司的情况而定)
SpringMVC自身并没有任何的验证框架,它采用的是Hibernate的验证框架
Hibernate 框架是一个纯粹的面向对象的框架,它所有的操作都是针对Bean对象来完成,所以这里的验证,也可以被称为:Bean Validate
具体使用
1、导入Hibernate的验证依赖
<!-- 导入hibernate 验证框架 --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.4.1.Final</version></dependency>
2、修改spring-mvc.xml,让SpringMVC框架支持Hibernate的验证服务。
<!--开启SpringMVC框架的注解驱动支持--><mvc:annotation-driven conversion-service="conversionServiceFactoryBean" validator="localValidatorFactoryBean"></mvc:annotation-driven><!--配置Spring框架的验证工厂--><bean id="localValidatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!--配置验证服务:由Hibernate框架提供--><property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property></bean>
3、使用注解对Bean进行验证格式约束。
注意:更多规范请参考(https://docs.jboss.org/hibernate/validator/5.4/reference/en-US/html_single/#section-builtin-constraints)。
package com.woniuxy.springmvc.bean;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserBean implements Serializable {private Long id;@NotNull(message = "用户名不能为null!")@NotEmpty(message = "用户名必须要有值!")@Size(min = 2,max = 8,message = "用户名的长度:[2,8]位!")private String userName;@NotEmpty(message = "登录名必须要有值!")@Size(min = 6,max = 8,message = "登录名的长度:[6,8]位!")private String loginName;@NotEmpty(message = "密码必须要有值!")@Size(min = 6,max = 12,message = "密码的长度:[6,12]位!")private String password;@Max(value = 60,message = "年龄过大,不能注册!")@Min(value = 18,message = "未成年人,不能注册!")private Integer age;/*** 是否婚配*/@AssertFalse(message = "你大爷的,你都结婚,还来?")private boolean marray;/*** 个人收入*/@Digits(integer = 10,fraction = 2,message = "你钱太多了,别吹NB了!")@Min(value = 50000,message = "多找爸妈要点钱再来,或来蜗牛培训一段时间再说!")private double income;/*** 个人爱好*/private String[] hobbys;/*** 出生日期*/@Past(message = "还没出生的小屁孩,滚蛋!")private Date birthday;
}
4、页面控制器的写法
package com.wnxy.springmvc.controller;import com.wnxy.springmvc.bean.UserBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;import javax.validation.Valid;
import java.util.List;@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping(value = "/register")public String register(UserBean userBean){System.out.println(userBean);return "sysmag/main";}/*** springMVC验证* @param userBean* @param result 判断验证结果* @return* @param model*/@RequestMapping(value = "/validator")public String validator(@Validated UserBean userBean, BindingResult result, Model model){//判断是否格式错误if(result.hasErrors()){List<ObjectError> errs = result.getAllErrors();//获取全部错误//输入哪些属性错误,出的是什么错,有哪些提示码是什么(lamba表达式)errs.forEach((e)->{System.out.println(e.getObjectName());System.out.println(e.getDefaultMessage());System.out.println(e.getCode());});//如果有错误,就去错误页面model.addAttribute("errs",errs);return "validator";}//注册成功,返回登录页面System.out.println(userBean);return "index";}
}
Json
前后端分离时,如何处理JSON数据
前后端交互:采用数据格式,常见的是XML,JSON;
现成的JSON框架:Jackson(默认支持) FastJson(阿里巴巴);
1、导入JSON依赖
<!-- 引入Json的工具类Jar包 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.11.1</version></dependency>
2、编写json.jsp页面
将jquery-1.8.3.min.js 放置在webapp/static/js目录下:
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/23Time: 14:15To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="header.jsp"%>
<html>
<head><title>Title</title><script type="text/javascript" src="static/js/jquery-1.8.3.min.js"></script><script>/*页面加载完成之后需要执行的代码*/$(function () {$('#btn01').click(function () {/*发送Ajax请求*/$.ajax({type: "POST",url: "stus/add",data: {stuName:"一护",stuNo:"4578",age:"50"},success: function(msg){console.info(msg);}});});});</script>
</head>
<body><button id="btn01">新增</button><button></button><button></button>
</body>
</html>
3、在spring-mvc.xml中释放静态资源
<!--释放静态资源--><mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
4、编写JavaBean
package com.woniuxy.springmvc.bean;
import lombok.Data;
import java.io.Serializable;
@Data
public class StudentBean implements Serializable {private Long id;private String stuName;private String stuNo;private Integer age;
}
5、编写返回对象
package com.woniuxy.springmvc.bean;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class ResultMsg<T> implements Serializable {/*** 状态码*/@NonNullprivate String code;/*** 提示信息*/@NonNullprivate String msg;/*** 返回给用户数据*/private T data;
}
6、编写页面控制器
@ResponseBody 的作用:是通过MappingJackson2HttpMessageConverter 完成:后端Java对象到JSON对象之间的序列化操作。并序列化的结果通过 响应体 完成数据传输。
package com.woniuxy.springmvc.controller;
import com.woniuxy.springmvc.bean.ResultMsg;
import com.woniuxy.springmvc.bean.StudentBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("stus")
@Controller
public class StudentController {/*** 新增学生* 但是不跳转页面* @param studentBean* @return** @ResponseBody 作用:将Java对象 序列化成JSON对象* 并通过响应体完成数据的传输*/@RequestMapping(value="/add",produces = {"application/json;charset=utf-8"})@ResponseBodypublic ResultMsg saveStudentBean(StudentBean studentBean){ResultMsg resultMsg = new ResultMsg("200","保存成功");System.out.println(studentBean);return resultMsg;}
}
7、前端发送JSON数据
需要在webapp/static/js 下引入 jquery.json.js
8、修改json.jsp页面
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/23Time: 14:15To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="header.jsp"%>
<html>
<head><title>Title</title><script type="text/javascript" src="static/js/jquery-1.8.3.min.js"></script><script type="text/javascript" src="static/js/jquery.json.js"></script><script>/*页面加载完成之后需要执行的代码*/$(function () {//后端响应JSON数据$('#btn01').click(function () {/*发送Ajax请求*/$.ajax({type: "POST",url: "stus/add",data: {stuName:"一护",stuNo:"4578",age:"50"},success: function(msg){console.info(msg);}});});//前端发送JSON数据$('#btn02').click(function () {var user = {stuName:"一护",stuNo:"4578",age:"50"};//将JS对象,转成JSON对象var json = $.toJSON(user);/*发送Ajax请求*/$.ajax({type: "POST",url: "stus/update",contentType:"application/json;charset=utf-8",//告诉后端,传递过来的数据是JSONdata: json,success: function(msg){console.info(msg);}});});});</script>
</head>
<body><button id="btn01">新增(返回JSON)</button><button id="btn02">新增(发送&返回JSON)</button><button></button>
</body>
</html>
9、修改页面控制器,使用@RequestBody接收JSON数据
@RestController === @ResponseBody + @Controller
@RequestBody 通过MappingJackson2HttpMessageConverter 完成页面JSON数据,到后端Java对象数据的转换工作。
package com.woniuxy.springmvc.controller;
import com.woniuxy.springmvc.bean.ResultMsg;
import com.woniuxy.springmvc.bean.StudentBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("stus")
@RestController //@ResponseBody + @Controller
public class StudentController {/*** 修改学生* @param studentBean* @return* @RequestBody 作用:将消息体中的 JSON对象,反序列化为Java对象*/@RequestMapping(value="/update",produces = {"application/json;charset=utf-8"})public ResultMsg updateStudentBean(@RequestBody StudentBean studentBean){ResultMsg resultMsg = new ResultMsg("200","保存成功");System.out.println(studentBean);return resultMsg;}/*** 新增学生* 但是不跳转页面* @param studentBean* @return** @ResponseBody 作用:将Java对象 序列化成JSON对象* 并通过响应体完成数据的传输*/@RequestMapping(value="/add",produces = {"application/json;charset=utf-8"})public ResultMsg saveStudentBean(StudentBean studentBean){ResultMsg resultMsg = new ResultMsg("200","保存成功");System.out.println(studentBean);return resultMsg;}
}
PostMan邮递员
前后端分离之后,前端都不是Java程序员的任务,你不需要写页面?你如何测试你的接口?
Postman 是一个测试工具,它可以模拟页面完成HTTP请求的发送。
双击运行,并跳过登录!
发送get请求:
发送Post请求(表单数据):
发送Post请求(JSON数据):
Rest FUL 风格
Rest ful (Representational State Transfer)描述性状态转移风格
此风格 不是一种技术,也不是一种框架,它只是一种提交方式的改变
之前,只能采用GET/POST提交,针对资源操作:users/add users/update users/delete
HTTP协议:在协议中提供了N多种方式,用来对资源进行CRUD
URI(Universal Resource Identifier) : 统一资源标识符 作用:使用名称 将资源做了统一并唯一的标识
概述
2000年,由HTTP协议的制造者Roy Fielding提出
资源:现实生活中,每一样东西,我们可以对它进行一个名词性的描述!
资源可大可小,大可以以某一些集合,某一些团体为单位:比如 users orders goods
小也可以小成某一个具体的东西,比如:users/1 orders/2020-11-23/9856
users/张三/18/女
如何对资源做描述,答案是:采用URI
如何对资源做操作
HTTP的4种标准方法:
GET: 对资源的查询 有就是有 无就是无
POST: 对资源的新增 从无到有
PUT: 对资源的修改 有就是有
DELETE: 对资源的删除 一直都是无
幂等性: 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的。
GET PUT DELETE
POST 非幂等性的
描述性状态转移: 转移的是资源的状态
为啥需要使用Rest Full
1、便于前后端分离的项目 (不管前端是什么类型,后端只提供统一的访问入口)
2、同一资源,只有一个接口
3、便于服务器集群 (tomcat 做横向扩展之后,在tomcat 中取得数据,都是一样)
实现步骤
1、先定义RestController接口
package com.woniuxy.springmvc.controller;
import com.woniuxy.springmvc.bean.ResultMsg;
import com.woniuxy.springmvc.bean.StudentBean;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RequestMapping("students")
@RestController
public class RestFulController {/*** 新增* @param studentBean* @return*/@PostMapping(value = "/more",produces = {"application/json;charset=utf-8"})public ResultMsg saveStudentBean(StudentBean studentBean){ResultMsg resultMsg = new ResultMsg("200","操作成功");System.out.println(studentBean);return resultMsg;}/*** 前端:http://localhost:8080/mvc/students/1 students/2 1 2 代表的是id** 修改* @param studentBean* @param id* @return*/@PutMapping(value="/{id}",produces = {"application/json;charset=utf-8"})public ResultMsg updateStudentBean(StudentBean studentBean,@PathVariable("id") Long id){ResultMsg resultMsg = new ResultMsg("200","操作成功");System.out.println(studentBean);return resultMsg;}/*** 删除* @param id* @return*/@DeleteMapping(value="/{id}",produces = {"application/json;charset=utf-8"})public ResultMsg deleteStudentBean(@PathVariable("id") Long id){ResultMsg resultMsg = new ResultMsg("200","操作成功");System.out.println(id);return resultMsg;}/*** 查询* @param id* @return*/@GetMapping(value="/{id}",produces = {"application/json;charset=utf-8"})public ResultMsg getStudentBeanById(@PathVariable("id") Long id){ResultMsg resultMsg = new ResultMsg("200","操作成功");System.out.println(id);StudentBean studentBean = new StudentBean();studentBean.setStuName("小二");resultMsg.setData(studentBean);return resultMsg;}/*** uri === students/all/张三/18/4578* @param stuName* @return*/@GetMapping(value="/all/{stuName}/{age}/{stuNo}",produces = {"application/json;charset=utf-8"})public ResultMsg findAllStudents(@PathVariable("stuName") String stuName,@PathVariable("age")Integer age,@PathVariable("stuNo") String stuNo){ResultMsg resultMsg = new ResultMsg("200","操作成功");System.out.println(stuName);System.out.println(age);System.out.println(stuNo);StudentBean studentBean = new StudentBean();studentBean.setStuName("小二");StudentBean studentBean02 = new StudentBean();studentBean02.setStuName("小二");List<StudentBean> datas = new ArrayList<>();Collections.addAll(datas,studentBean,studentBean02);//将集合转入到 数据返回对象 并返回页面resultMsg.setData(datas);return resultMsg;}
}
2、在web.xml中配置,支持PUT、DELETE请求过滤器(支持使用表单方式传输数据);
<!-- 解决PUT,DELETE无法使用消息体 传递数据的一个过滤器 --><!-- 来自于:spring-web.jar --><filter><filter-name>formContentFilter</filter-name><filter-class>org.springframework.web.filter.FormContentFilter</filter-class></filter><filter-mapping><filter-name>formContentFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
3、编写restful.jsp页面,完成前后端数据的交互
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/23Time: 14:15To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="header.jsp"%>
<html>
<head><title>Title</title><script type="text/javascript" src="static/js/jquery-1.8.3.min.js?version=1.1"></script><script type="text/javascript" src="static/js/jquery.json.js?version=1.0"></script><script>/*页面加载完成之后需要执行的代码*/$(function () {//根据名称查询学生$('#btn05').click(function () {/*发送Ajax请求*/$.ajax({type: "GET",url: "students/all/张三/18/4578",success: function(msg){console.info(msg);}});});//根据ID查询数据$('#btn04').click(function () {/*发送Ajax请求*/$.ajax({type: "GET",url: "students/1",success: function(msg){console.info(msg);}});});//删除$('#btn03').click(function () {/*发送Ajax请求*/$.ajax({type: "DELETE",url: "students/1",success: function(msg){console.info(msg);}});});//修改$('#btn02').click(function () {/*发送Ajax请求*/$.ajax({type: "PUT",url: "students/1",data: {stuName:"一护",stuNo:"4578",age:"50"},success: function(msg){console.info(msg);}});});//新增$('#btn01').click(function () {/*发送Ajax请求*/$.ajax({type: "POST",url: "students/more",data: {stuName:"一护",stuNo:"4578",age:"50"},success: function(msg){console.info(msg);}});});});</script>
</head>
<body><button id="btn01">新增学生</button><button id="btn02">修改学生</button><button id="btn03">删除学生</button><button id="btn04">根据ID查询学生</button><button id="btn05">根据名称查询学生</button>
</body>
</html>
异常的处理
以前是如何处理异常:抛出,抓。
处理异常的方式
所有的异常:底层依次,向上层去抛,一直抛到表现层为止。采用AOP切面的方式,统一处理异常(将异常不是直接抛给页面,而是将异常转换为:友好的提示信息,例如:当前系统繁忙,请稍后重试。)
异常:系统在开发,运行过程中抛出来的问题。
各层都有可能抛出异常,底层不要去抓异常,相反是不断的向上抛出,抛到表现层为止。
注意:异常不能抛到页面上。
Tomcat容器处理系统异常
系统异常:405、404、403、400;
405:方法不被允许;
404:资源找不到;
400:参数错误;
403:没有授权;
解决系统提示的状态码错误信息
解决步骤:
1、编写状态码对应的错误页面;
2、在web.xml中配置error-page提示信息。
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/24Time: 10:29To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>请求方法错误,请联系管理员!(蒲老师 13398199549)
</body>
</html>
SpringMVC框架处理异常
1、使用xml配置文件处理异常(了解)
<!--配置异常解析器--><bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!--默认异常页面--><property name="defaultErrorView" value="errors/exception"></property><!--异常页面上,可以使用ex 来获得数据--><property name="exceptionAttribute" value="ex"></property><property name="exceptionMappings"><props><!--异常,对应 页面--><prop key="java.lang.NullPointerException">errors/npException</prop><!--有其他的就继续--></props></property></bean>
使用AOP切面编程思想去解决问题(掌握)
第一套方案:在异常页面中显示异常信息;
第二套方案:不在异常页面中显示异常信息。
注意:两种方案,2选其一,如果同时定义:第一套方案将会覆盖第二套方案。
1、第一套方案(适合于页面跳页面的系统)
异常页面:
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/24Time: 10:46To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>这是SpringMVC框架的默认异常页面,抛出的异常是:${ex}
</body>
</html>
exception部分,使用@ControllerAdvice + @ExceptionHandler:
package com.woniuxy.springmvc.exception;
import com.woniuxy.springmvc.bean.ResultMsg;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class ExceptionHanlder {/*** @ExceptionHandler 标记下面的方法,就是一个异常处理方法* @param e* @return*/@ExceptionHandlerpublic ModelAndView hanlder(Exception e){ResultMsg msg = new ResultMsg("10000","未知错误");ModelAndView mv = new ModelAndView();if(e instanceof NullPointerException){msg.setCode("10001");msg.setMsg("系统繁忙,请稍后重试!");}//下面可以继续判断异常的种类,并修改提示信息mv.addObject("ex",msg);mv.setViewName("errors/exception");return mv;}
}
2、第二套方案(不存在跳转页面,适用于前后端分离的系统)
注意:异常页面与第一套方案相同。
使用@RestControllerAdvice + @Exception:
package com.woniuxy.springmvc.exception;
import com.woniuxy.springmvc.bean.ResultMsg;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/*** 不跳转页面*/
@RestControllerAdvice
public class ExceptionHanlder {@ExceptionHandlerpublic ResultMsg hanlder(Exception e){ResultMsg msg = new ResultMsg("10000","未知错误");if(e instanceof NullPointerException){msg.setCode("10001");msg.setMsg("系统繁忙,请稍后重试!");}//继续做异常的判断return msg;}
}
总结:SpringMVC 框架处理异常的思想:采用AOP面向切面的思想来完成的,它的切入点定义到Controller的方法上。
SpringMVC框架的有点(面试题)
1、SpringMVC采用MVC的架构模式,以及服务到工作者的架构模式,将表现层通过多种内置组件,定义的更加清晰,让页面控制更加精炼。
2、由于SpringMVC是Spring框架的7大功能的中的MVC模块功能,所以它天生就支持Spring框架。
3、SpringMVC的配置虽然多,但是都比较简单,比较独立(你需要什么就可以自己选择配置什么)。
4、代码的可重用性很高(通常SpringMVC的配置文件,几乎不支持任何系统的配置)。
{
console.info(msg);
}
});
});
//新增
$(’#btn01’).click(function () {
/发送Ajax请求/
$.ajax({
type: “POST”,
url: “students/more”,
data: {
stuName:“一护”,
stuNo:“4578”,
age:“50”
},
success: function(msg){
console.info(msg);
}
});
});
});
新增学生 修改学生 删除学生 根据ID查询学生 根据名称查询学生 ```
异常的处理
以前是如何处理异常:抛出,抓。
处理异常的方式
所有的异常:底层依次,向上层去抛,一直抛到表现层为止。采用AOP切面的方式,统一处理异常(将异常不是直接抛给页面,而是将异常转换为:友好的提示信息,例如:当前系统繁忙,请稍后重试。)
异常:系统在开发,运行过程中抛出来的问题。
各层都有可能抛出异常,底层不要去抓异常,相反是不断的向上抛出,抛到表现层为止。
注意:异常不能抛到页面上。
Tomcat容器处理系统异常
系统异常:405、404、403、400;
405:方法不被允许;
404:资源找不到;
400:参数错误;
403:没有授权;
[外链图片转存中…(img-WH8FCF2V-1607306797675)]
解决系统提示的状态码错误信息
解决步骤:
1、编写状态码对应的错误页面;
2、在web.xml中配置error-page提示信息。
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/24Time: 10:29To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>请求方法错误,请联系管理员!(蒲老师 13398199549)
</body>
</html>
SpringMVC框架处理异常
1、使用xml配置文件处理异常(了解)
<!--配置异常解析器--><bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!--默认异常页面--><property name="defaultErrorView" value="errors/exception"></property><!--异常页面上,可以使用ex 来获得数据--><property name="exceptionAttribute" value="ex"></property><property name="exceptionMappings"><props><!--异常,对应 页面--><prop key="java.lang.NullPointerException">errors/npException</prop><!--有其他的就继续--></props></property></bean>
使用AOP切面编程思想去解决问题(掌握)
第一套方案:在异常页面中显示异常信息;
第二套方案:不在异常页面中显示异常信息。
注意:两种方案,2选其一,如果同时定义:第一套方案将会覆盖第二套方案。
1、第一套方案(适合于页面跳页面的系统)
异常页面:
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2020/11/24Time: 10:46To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>这是SpringMVC框架的默认异常页面,抛出的异常是:${ex}
</body>
</html>
exception部分,使用@ControllerAdvice + @ExceptionHandler:
package com.woniuxy.springmvc.exception;
import com.woniuxy.springmvc.bean.ResultMsg;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class ExceptionHanlder {/*** @ExceptionHandler 标记下面的方法,就是一个异常处理方法* @param e* @return*/@ExceptionHandlerpublic ModelAndView hanlder(Exception e){ResultMsg msg = new ResultMsg("10000","未知错误");ModelAndView mv = new ModelAndView();if(e instanceof NullPointerException){msg.setCode("10001");msg.setMsg("系统繁忙,请稍后重试!");}//下面可以继续判断异常的种类,并修改提示信息mv.addObject("ex",msg);mv.setViewName("errors/exception");return mv;}
}
2、第二套方案(不存在跳转页面,适用于前后端分离的系统)
注意:异常页面与第一套方案相同。
使用@RestControllerAdvice + @Exception:
package com.woniuxy.springmvc.exception;
import com.woniuxy.springmvc.bean.ResultMsg;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/*** 不跳转页面*/
@RestControllerAdvice
public class ExceptionHanlder {@ExceptionHandlerpublic ResultMsg hanlder(Exception e){ResultMsg msg = new ResultMsg("10000","未知错误");if(e instanceof NullPointerException){msg.setCode("10001");msg.setMsg("系统繁忙,请稍后重试!");}//继续做异常的判断return msg;}
}
总结:SpringMVC 框架处理异常的思想:采用AOP面向切面的思想来完成的,它的切入点定义到Controller的方法上。
SpringMVC框架的有点(面试题)
1、SpringMVC采用MVC的架构模式,以及服务到工作者的架构模式,将表现层通过多种内置组件,定义的更加清晰,让页面控制更加精炼。
2、由于SpringMVC是Spring框架的7大功能的中的MVC模块功能,所以它天生就支持Spring框架。
3、SpringMVC的配置虽然多,但是都比较简单,比较独立(你需要什么就可以自己选择配置什么)。
4、代码的可重用性很高(通常SpringMVC的配置文件,几乎不支持任何系统的配置)。
5、可拓展性(SpringMVC依旧是免费开源的产品,如果功能不够,你可以自己修改它的源码)。
Spring学习记录01相关推荐
- 我的Spring学习记录(二)
本篇就简单的说一下Bean的装配和AOP 本篇的项目是在上一篇我的Spring学习记录(一) 中项目的基础上进行开发的 1. 使用setter方法和构造方法装配Bean 1.1 前期准备 使用sett ...
- 【ArcGIS学习记录01】--利用CRU TS数据集绘制降雨量分布图
[ArcGIS学习记录01]–利用CRU TS数据集绘制降雨量分布图 注:仅作为本人的学习记录方便以后复习查阅. 一.介绍 CRU TS 是目前使用最广泛的气候数据集之一,由英国国家大气科学中心 (N ...
- JavaScript学习记录01快速入门、基本语法、严格检查模式
文章目录 JavaScript学习记录01快速入门.基本语法.严格检查模式 1.1什么是JavaScript 1.2认识JavaScript框架 1.3快速入门 1.4基本语法入门 1.5数据类型简介 ...
- JavaEE——Spring学习笔记01【Ioc开发的模式】
JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...
- Spring学习记录(九)---通过工厂方法配置bean
1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. 1 package com.guigu.spring.factory; 2 3 public c ...
- 【Spring学习】01
Spring学习01 Spring概述 简介 优点 组成 Spring快速入门 Spring程序开发步骤 Spring概述 简介 ·Spring框架是由于软件开发的复杂性而创建的.Spring使用的是 ...
- Spring 学习记录 冷兵器时代的故事
这篇文章的内容和 Spring 没什么关系,但还是分类到 Spring 学习了. 首先,我们来将一个故事. 很久很久以前,冷兵器时代,人们用弓和箭打仗. 版本1 public class BowAnd ...
- Spring学习记录
Spring相关 目录 文章目录 Spring相关 目录 前言 工厂设计模式 静态工厂模式 通用工厂模式 ApplicationContext ClassPathXmlApplicationConte ...
- jadx学习记录01
原计划是先学习 okHttp 和拦截器,再用 Android Studio 来 demo 下,奈何 Android Studio 装完 sync 一直失败,后续换个电脑再试下.一并整理 okHttp ...
- echarts引入geo地图数据,前端学习记录01
最近遇到项目需要使用使用geoJson数据在echarts上进行地图的绘制,仅使用此文记录学习的过程. 首先需要在项目中引入echart相关依赖,在package.json中添加如下依赖: " ...
最新文章
- Failed to register Grid Infrastructure type ora.mdns.type
- MySQL带DISTINCT关键字的查询
- 关于 tomcat 集群中 session 共享的三种方法
- tcp_handle_req: Made 4 read attempts but message is not complete yet - closing connection
- 花高价招来的阿里P8,我从他那里总结了8大硬核能力,4个经典案例,真香
- bash shell简介及变量
- summernote使用实例,解决了小图标方框显示问题
- Python 告诉你,情人节该送什么礼物?
- 查linux服务器CPU多少C,在linux 下怎么查看服务器的cpu和内存的硬件信息
- 解决360 安装补丁智能忽略的问题!
- 当棋牌遇到Web3,Immortal Games能让国际象棋流行起来么
- elementUI中input增加自定义图片
- 设计模式---003代理模式(转载自我的老师 Alley-巷子)
- 【ARC 123B】Increasing Triples(贪心)
- Sentinel-2(哨兵2号)SNAP预处理
- 浅谈面向对象和面向过程
- Android使用Put方式提交数据
- CentOS7部署文件双向同步工具(unison)
- 【四足机器人--摆动相足端位置速度轨迹规划】(4.1)FootSwingTrajectory(bezier曲线计算脚的摆动轨迹)代码解析
- python清洗文本非法字符_Python 文本字符串清理
热门文章
- 基于simulink的微电网虚拟同步发电机vsg控制系统仿真
- python函数详细讲解_小白必看的Python函数讲解
- ups计算软件_一篇文章读懂UPS
- 如何用excel快速实现“平均值±标准差”
- 桌面的计算机图标误删了怎么恢复,删除桌面图标-如何恢复桌面图标不小心将某个程序的桌面图标给删了,怎么恢复呢 爱问知识人...
- 学计算机的人掉头发吗,学计算机真的会秃顶吗?为什么?
- 阿里云国际版账户登录不上去什么原因?
- 彻底搞懂js中的this指向
- 抖音招商团长入驻条件
- 多极神经元切片手绘图,神经组织切片手绘图片