1、Spring 概述
1.1 框架
框架概念
框架:指软件框架,是在实际开发中解决项目需求的技术集合,简化代码编写,缩短开发周期,对固定人员要求不高,对系统升级具有灵活的标准。
框架仅仅是框架,是一种已经编写好的固定规范,并不是系统架构,一个架构可以拥有多个框架
1.2 框架的作用
- 提高开发效率
- 增强代码可重用性
- 节约维护成本
- 提高编写规范
- 解耦底层实现原理
1.3 框架的必要性
- Spring(包括:springmvc、spingBoot、spingData等)和mybatis是企业开发的最为基础的两个框架
- 从企业角度:几乎都会使用,个人:可以实现持久层操作时代码变得非常精简
1.4 Sping 官网
https://spring.io
2、Spring简介
Spring是分层的JavaSE/EE 应用的轻量级开源框架。
是以 IOC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核提供了展现层(Web层)SpringMVC和持久层Spring JDBC
以及业务层事务管理等众多的企业级应用技术,也整合了许多第三方框架、类库
- 老的框架组合
- Struts2+Spring+Hibernate(SSH)
- SpringMVC+Spring+MyBatis(SSM)
- 新的框架
- Spring+SpringBoot+MyBatis(新SSM)
2.1 Spring发展历史
- 1997 IBM提出类EJB的思想
- 1998 SUM制定开发标准规范EJB1.0
2.1.1 了解EJB
- EJB 可以像是一个Web Service,编写的EJB业务组件放置在EJB容器上,然后提供给客户端访问,EJB提供了很多的规范,而这些规范只有在EJB容器上才能正常运行,EJB是一个重量级框架
- Spring 容器取代了原来的EJB容器,因此Spring框架为核心的应用无需EJB的支持,可以在web容器运行,Spring容器不同于EJB,而是一个POJOBean。
- 目前Spring最新版本spring 5.3.1 通用版(GA)
2.2 Spring的优势
- 方便解耦,简化开发
- 通过Spring提供的IOC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的程序过度耦合,用户也不必再为单例模式,属性文件解析等这些底层的需要写代码,可以更加专注上层的应用
- AOP编程的支持
- 通过Spring 的AOP功能,方便进行面向切面的编程。
- 声明式事务的支持
- 可以将我们从繁琐的事务管理代码解脱出来,通过声明式灵活的进行事物的管理,提高开发效率和质量
- 方便程序的测试
- 可以用非容器依赖的编程方式经行几乎所有的测试工作
- 方便集成各种优秀的框架
- Spring可以降低各种框架的使用框架,提供了对各种框架的支持
- 降低Java EE API 的使用难度
- Spring 对Java EE API 进行了封装
- Spring 源码是经典实践规范
- 源码是JAVA技术最佳的时间范例
2.3 Spring 框架体系
3、控制反转IOC–Inverse Of Control
3.1 程序的耦合
3.1.1 什么是耦合
- 耦合性:也叫耦合度,是对模块之间关联程序的度量
- 在软件工程中,耦合指的就是对象之间的依赖关系,对象之间的耦合越高,则模块的独立性和可重用性越差,并且维护成本越高。
- 软件设计通常用耦合度和内聚度作为衡量模块独立性的标准,划分模块的一个标准就是高内聚低耦合。
3.1.2 内聚和耦合
- 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展,内聚是从功能角度来度量模块内的练习,一个好的内聚模块是只做一件事,它描述的是模块内的功能联系
- 耦合是软件结构中各个模块之间相互连接的一种度量,耦合强弱取决于模块之间的复杂程度、进入或访问一个模块的点以及通过接口的数据的复杂成都,程序讲究的是低耦合,高内聚。就是同 一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依赖度却不要那么紧密。
3.1.3 框架
框架:指软件框架,使用框架会大大节约时间,方便维护,但是框架也有一定的规范我们必须遵循这个规范
3.2 工厂模式解耦
3.2.1 解耦及其必要性
解耦:解除耦合,消除任何依赖关系,如果两个模块没有任何依赖关系,则表示他们是独立的,不会有任何交叉或者协同工作的可能,解耦并不是消除代码之间的耦合,而是减少其依赖关系。
低耦合是开发中的一个最基本的要求,可以使独立性提高,增加模块的复用性,也会在后期维护成本、项目的升级,减少重构的风险
3.2.2 思路
第一:使用反射创建对象 第二:创建对象用到全限定类名配置在配置文件中
3.2.3 示例
在JDBC中一般通过反射进行注册通过反射的方式和通过注册驱动的方式
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");//此处只是一个字符串
- 第一种方式,就会导致驱动进行注册两次,如果修改数据库时,就要更改源代码,但是第二种class的方式则不会
- 使用一些不是频繁创建对象时,采用反射的方式则更加合理,而反 射创建时需要提供创建类的全限定类名,这个名称如果写在java代码中,造成的结果就是修改仍然 避免不了修改源代码。所以,我们需要使用配置文件,把要创建类的全限定名配置在配置文件中。
3.3 设计模式 – 工厂模式
工厂模式是最常用的实例化对象模式,是用工厂的方法代替new创建对象的一种设计模式
我们以MyBatis的SqlSession接口为例,它有一个实现类DefaultSqlSession,如果我们要创建该接口的 实例对象:
SqlSession sqlSession=new DefaultSqlSession();
可是,实际情况是,通常我们都要在创建SqlSession实例的时候做点初始化的工作,比如解析XML,封装 连接数据库的信息等等。
在创建对象时,如果有一些不得不做的初始化操作时,我们首先想到的是,可以用构造方法,这样的创建对象时就写成
SqlSession sqlSession=new DefaultSqlSession(传入配置文件的路径);
但是,如果创建SqlSession实例时所做的初始化工作不是简单赋值这样的事情,可能是很长的一段代 码,如果也写入构造方法,那你的代码就不是特别的规范。
SqlSessionFactory factory=new SqlSessionFactory().build("mybatis-config.xml"); SqlSession sqlSession=factory.openSession();
所以,MyBatis框架在使用时为我们提供了SqlSessionFactory工厂类,通过openSession()方法获取到SqlSession对象,openSession()同时有很多重载,用于实现不同的需求。它支持传入Connection参数 来保证连接的一致性,支持传入true或false来保证事务的提交时机不同等待。
3.3.1 运用工厂模式解耦
步骤1:创建业务层接口和实现类
package bdit.service;
/**
* @Package: com.bdit.service
* @ClassName: AccountService
* @Author: Ling
* @Date: 2020/11/13 6:55
* @Description:业务层
*/
public interface AccountService {
void saveAcccount();
}
package bdit.service.impl;
import bdit.dao.AccountDao;
import bdit.factory.BeanFactory;
import bdit.service.AccountService;
/**
* @Package: com.bdit.service.impl
* @ClassName: AccountServiceImpl
* @Author: Ling
* @Date: 2020/11/13 6:56
* @Description:业务层实现类
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;//调用dao层
@Override
public void saveAcccount() {
accountDao= (AccountDao)BeanFactory.getBean("accountDao");
accountDao.saveAccount();
}
}
步骤2:创建持久层接口和实现类
import bdit.dao.AccountDao;
/**
* @Package: com.bdit.dao.impl
* @ClassName: AccountDaoImpl
* @Author: Ling
* @Date: 2020/11/13 7:00
* @Description:账户持久层实现类
*/
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount() {
}
}
package bdit.dao;
/**
* @Package: com.bdit.dao
* @ClassName: AccountDao
* @Author: Ling
* @Date: 2020/11/13 6:58
* @Description:账户持久层接口
*/
public interface AccountDao {
public void saveAccount();
}
步骤3:配置文件beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!--配置service-->
<bean id="accountService" class="bdit.service.impl.AccountServiceImpl">
</bean>
<!--配置dao-->
<bean id="accountDao" class="bdit.dao.impl.AccountDaoImpl"></bean>
</beans>
步骤4:工厂类
package bdit.factory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Package: com.bdit.factory
* @ClassName: BeanFactory
* @Author: Ling
* @Date: 2020/11/13 7:05
* @Description:步骤:
* 1、解析xml,获取所有的对象的全限定名
* 2、反射创建对象,存储到容器中
* 容器:有查找的需求,使用map集合
* 3、用户需要对象时,从容器根据id之间获取
*/
public class BeanFactory {
//创建map对象,key:String,Value:Object类型,反射创建好的对象
private static Map<String,Object> map = new HashMap<>();
//静态代码块,类加载时执行,只会被执行一次
static{
//解析xml,获取所有对象的全限定名
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"));
//获取根节点
Element element = doc.getRootElement();
//获取根节点下的所有子节点
List<Element> elementList = element.elements();
for (Element e:elementList){
String idvalue = e.attributeValue("id");
String classvalue = e.attributeValue("class");
//反射创建对象,存储到容器中
Class cla = Class.forName(classvalue);
Object obj = cla.getDeclaredConstructor().newInstance();
//存储到容器中
map.put(idvalue,obj);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Object getBean(String id){
return map.get(id);
}
}
步骤5:测试类
import bdit.factory.BeanFactory;
import bdit.service.AccountService;
import org.junit.Test;
/**
* @Package: PACKAGE_NAME
* @ClassName: Text
* @Author: Ling
* @Date: 2020/11/13 17:47
* @Description:
*/
public class Text {
@Test
public void testSave(){
AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
System.out.println(accountService);
accountService.saveAcccount();
System.out.println("成功");
// AccountService accountService = (AccountService) BeanFactory.getBean("accountDao");
// System.out.println(accountService);
// accountService.saveAcccount();
// System.out.println("成功");
}
}
4、关于IOC
4.1.1 IOC
- 全称是Inverse Of Control,控制反转,它不是一个技术,而是一种思想,作用是:消减代码的耦合,实现思想是利用了工厂的设计模式,把创建对象的代码从具体类中分离出来,交给工厂,从而降低了代码的依赖关系。
- 将原先的资源—app转换成了 资源–工厂–app
IOC作用:降低了代码之间的依赖关系,消减程序中的耦合
5、Spring的IOC
Spring中两大核心IOC和AOP
使用Spring IOC配置时,它支持纯XML配置或者纯注解配置以及XML和注解混合配置三种方式
5.1 案例:
本案例要解决 账户的业务层和持久层的依赖关系
步骤1 创建业务层接口和实现类
package com.bdit.service;
/**
* ClassName: AccountService
* Package: com.bdit.service
* Description: 账户的业务层接口
* date: 2020/11/12 16:37
* Version 1.0
*/
public interface AccountService {
/**
* 保存账户(此次只是模拟,并不是真正的保存)
*/
void saveAccount();
}
package com.bdit.service.impl;
import com.bdit.dao.AccountDao;
import com.bdit.dao.impl.AccountDaoImpl; import com.bdit.service.AccountService;
/**
* ClassName: AccountServiceImpl
* Package: com.bdit.service.impl
* Description: 账户业务层的实现类
* author: BDIT
* date: 2020/11/12 16:38
* Version 1.0
*/
public class AccountServiceImpl implements AccountService {
//调用DAO层
private AccountDao accountDao=new AccountDaoImpl();
@Override
public void saveAccount() {
accountDao.saveAccount();
}
步骤2:创建DAO层接口和实现类
package com.bdit.dao;
/**
* ClassName: AccountDao
* Package: com.bdit.dao
* Description: 账户持久层接口
* author: BDIT
* date: 2020/11/12 16:41
* Version 1.0
*/
public interface AccountDao {
/**
* 保存账户
*/
public void saveAccount();
}
package com.bdit.dao.impl;
import com.bdit.dao.AccountDao;
/**
* ClassName: AccountDaoImpl
* Package: com.bdit.dao.impl
* Description: 账户持久层实现类
* author: BDIT
* date: 2020/11/12 16:41
* Version 1.0
*/
public class AccountDaoImpl implements AccountDao { @Override
public void saveAccount() {
System.out.println("===========添加账户信息=============");
}
}
6、BeanFactroy 和 ApplicationContext
- BeanFactory 是Spring中IOC容器的顶层接口,而ApplicationContext是它的一个字接口,所以ApplicationContext具备BeanFactory提供的全部功能
- 通常情况下,称BeanFacotry是Spring IOC的基础容器,而ApplicationContext是容器的高级接口,它比BeanFactory多了很多重要的功能
- BeanFacory 和 ApplicationContext 区别
- 创建对象的时间点不一样
- ApplicationContext :是要一度配置文件,默认情况下就会创建对象
- BeanFactory:什么时候使用什么时候创建对象
- 创建对象的时间点不一样
6.1 ApplicationContext
- ClassPathXmlApplicationContext:
- 它是从类的根路径下加载配置文件
- FileSystemXmlApplicationContext:
- 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
- AnnotaionConfigcationContext:
- 当我们使用注解配置容器对象时,需要使用此类来创建Spring容器,它用来读取注解
6.2 IOC中Bean标签和管理对象
6.2.1 Bean 标签的对象获取
- 通过getBean方法
- 三种方式
- 第一步:创建Spring的容器实例
- 第二步:根据bean id属性获取对象
- 第三步: 执行方法
- 三种方式
- 代码示例(第一种):
System.out.println("===========第一种");
// 1、使用ApplicationcationContext接口,获取spring容器
ApplicationContext appl = new ClassPathXmlApplicationContext("spring-beans.xml");
// 2、根据bean id 属性获取对象
AccountService accountService = (AccountService)appl.getBean("accountService");
System.out.println(accountService);
AccountDao accountDao = (AccountDao)appl.getBean("accountDao");
System.out.println(accountDao);
- 代码示例(第二种):
System.out.println("===========第二种:通过字节码");
AccountService accountService1 = appl.getBean(AccountService.class);
System.out.println(accountService1);
AccountDao accountDao1 = appl.getBean(AccountDao.class);
System.out.println(accountDao1);
- 代码示例(第三种):
System.out.println("==========第三种:通过字节码和id");
AccountService accountService2 = appl.getBean("accountService",AccountService.class);
System.out.println(accountService2);
AccountDao accountDao2 = appl.getBean("accountDao",AccountDao.class);
System.out.println(accountDao2);
6.3 Bean 标签的作用和属性
- 作用:
- 用于配置对象,让Spring来创建
- 默认情况下调用的是类中的无参的构造方法,如果没有则无法创建
- 属性:
- id:给对象在容器中提供一个唯一的标识,用于获取对象
- class:指定类的全限定名,用于反射创建对象
- scope:指定对象的作用范围
- singleton:默认值,单例对象
- prototype:多例
- request:WEB项目中,Spring创建一个Bean对象,将对象存入到request域中
- session:WEB项目中,Spring创建一个Bean对象,将对象存入到session域中
- init-method:指定类中的初始化方法名称
- destroy-method:指定类中销毁方法名称
6.4 Bean 标签的生命周期
- 单例对象:scope=“singleton”
- 一个应用只有一个对象的实例,它的作用范围是整个应用
- 生命周期:
- 对象创建时:当使用对象事,创建新的对象实例
- 对象存活:只要容器在,对象就一直存活
- 对象销毁:当对象长时间不用,就会被java垃圾回收器所回收
- 总结:spring框架只负责创建,不负责销毁
6.5 Bean实例化
- 方式一:spring使用默认无参数的构造方法
- 默认情况下,spring会根据默认无参数的构造方法来创建类的对象,如果bean中没有默认的无参数构造方法,将会失败
- 方式二:spring管理静态工厂----使用静态工厂的方法创建对象
- 使用 StaticFactory类中的静态方法创建对象,并存入spring容器
工厂类
package bdit.factory;
import bdit.service.impl.AccountServiceImpl;
/**
* @Package: bdit.factory
* @ClassName: AccountDaoFactory
* @Author: Ling
* @Date: 2020/11/13 14:19
* @Description:
*/
public class AccountServiceFactory {
public static AccountServiceImpl accountServicefactory(){
return new AccountServiceImpl();
}
}
xml
<!--工厂-->
<bean id="accountService01" class="bdit.factory.AccountServiceFactory" factory-method="accountServicefactory"></bean>
测试
@Test
public void accountService01(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
AccountService accountService = context.getBean("accountService01",AccountService.class);
System.out.println(accountService);
}
- 方式三:spring管理实例工厂----使用实例工厂的方法创建对象
- 使用此工厂创建对象,必须有实现工厂类的对象,在调用方法
工厂类
public class InstanceFactory {
public AccountService createAccountService(){ return new AccountServiceImpl();
}
}
xml
<!--实例工厂bean-->
<bean id="instanceFactory" class="com.bdit.factory.InstanceFactory"></bean>
<!--业务bean
factory-bean属性:用于指定实例工厂bean的id factory-method属性:用于指定实例工厂中创建对象的方法
-->
<bean id="accountService3" factory-bean="instanceFactory" factory- method="createAccountService"></bean>
测试
@Test
public void test3(){ ApplicationContext context=new
ClassPathXmlApplicationContext("beans.xml");
AccountService accountService=context.getBean("accountService3",AccountService.class);
System.out.println(accountService);
7、Spring的依赖注入(DI)
7.1 依赖注入
- 依赖注入:DI(Dependency Injection) 就是让Spring框架给Bean对象的属性进行赋值,它是Spring框架核心IOC的具体体现
- 依赖注入就是利用框架把所需要的对象传递过来,而不需要我们去获取,IOC解耦的时候只是降低耦合,并不会直接消除
7.2 使用构造方法进行注入
- 使用类的构造方法,利用soring框架进行(仅需这三个)
pojo
package com.bdit.pojo;
import java.util.Date;
/**
* @Package: bdit.pojo
* @ClassName: User
* @Author: Ling
* @Date: 2020/11/13 19:01
* @Description:
*/
public class User {
private Integer id;
private String name;
private double scoure;
private Date britday;
//方法省略
}
xml
<!--通过 <constructor-arg>.... </constructor-arg> 标签 -->
<bean id="brithday" class="java.util.Date"></bean>
<!--构造方法(第一种,按照javabean中属性的位置来进行赋值(index))
index:下标(0开始) value:注入的属性值 ref:引用,可以是类也可以是值-->
<bean id="userOne" class="com.bdit.pojo.User">
<constructor-arg index="0" value="1001"></constructor-arg>
<constructor-arg index="1" value="甲一"></constructor-arg>
<constructor-arg index="2" value="99.63"></constructor-arg>
<constructor-arg index="3" ref="brithday"></constructor-arg>
</bean>
<!-- 第二种:通过数据类型来注入,但可能出现混乱的错误-->
<bean id="students" class="bdit.pojo.Students">
<constructor-arg type="java.lang.Integer" value="1001"/>
<constructor-arg type="java.lang.String" value="甲一"/>
<constructor-arg type="double" value="88.89"/>
<constructor-arg type="java.util.Date" ref="bridaydate"/>
</bean>
<!-- 第三种:通过javabean中属性的名称来注入 -->
<bean id="students" class="bdit.pojo.Students">
<constructor-arg name="id" value="1001"></constructor-arg>
<constructor-arg name="name" value="甲一"></constructor-arg>
<constructor-arg name="score" value="88.89"></constructor-arg>
<constructor-arg name="briday" ref="bridaydate"></constructor-arg>
</bean>
测试
@Test
public void userOne(){
ApplicationContext context = new ClassPathXmlApplicationContext("ex-spring.xml");
User u = context.getBean("userOne", User.class);
System.out.println(u);
}
7.3 使用Set方法注入
需要Set方法,必须是标准的完整的
pojo省略
xml
<!--通过 <property>.... </property> 标签 -->
<bean id="studentsSet" class="bdit.pojo.StudentsSet">
<property name="id" value="1002"></property>
<property name="name" value="甲二"></property>
<property name="score" value="72.98"></property>
<property name="briday" ref="bridaydate"></property>
</bean>
测试
@Test
public void userset(){
ApplicationContext context = new ClassPathXmlApplicationContext("ex-spring.xml");
UserSet u = context.getBean("userset", UserSet.class);
System.out.println(u);
}
7.4 业务层注入DAO实例
DAO层接口
package bdit.dao;
/**
* @Package: com.bdit.dao
* @ClassName: AccountDao
* @Author: Ling
* @Date: 2020/11/13 6:58
* @Description:
*/
public interface AccountDao {
public void saveAccount();
}
DAO层实现类
package bdit.dao.impl;
import bdit.dao.AccountDao;
/**
* @Package: com.bdit.dao.impl
* @ClassName: AccountDaoImpl
* @Author: Ling
* @Date: 2020/11/13 7:00
* @Description:
*/
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount() {
}
public AccountDaoImpl(){
System.out.println("============AccountDaoImpl 无参---单例");
}
}
业务层
package bdit.service;
/**
* @Package: com.bdit.service
* @ClassName: AccountService
* @Author: Ling
* @Date: 2020/11/13 6:55
* @Description:
*/
public interface AccountService {
void saveAcccount();
}
业务层实现类
package bdit.service.impl;
import bdit.dao.AccountDao;
import bdit.dao.impl.AccountDaoImpl;
import bdit.service.AccountService;
/**
* @Package: com.bdit.service.impl
* @ClassName: AccountServiceImpl
* @Author: Ling
* @Date: 2020/11/13 6:56
* @Description:业务层
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;//实现Dao层的引入Impl中
public AccountServiceImpl(AccountDaoImpl accountDao) {
System.out.println("===================构造方法");
}
@Override
public void saveAcccount() {
accountDao.saveAccount();
}
}
xml
<!--
通过Impl的注入、
在Impl中定义Dao的实体类,创建出Dao的构造参数或者Set方法
并在业务层中,引用dao-->
<bean id="accountdao" class="bdit.dao.impl.AccountDaoImpl"></bean>
<bean id="accountService" class="bdit.service.impl.AccountServiceImpl">
<constructor-arg index="0" ref="accountdao"/>
<!-- <property name="accountDao" ref="accountdao"/>-->
</bean>
测试
// 通过以Dao为id引入Impl中的注入
@Test
public void servicedao(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans-two.xml");
AccountService accountService = context.getBean("accountService",AccountService.class);
System.out.println(accountService);
}
7.4 注入集合
就是给类中的集合成员赋值,它用的是set方式注入,只不过数据类型都是集合
JavaBean
package bdit.pojo;
import java.util.*;
/**
* @Package: bdit.pojo
* @ClassName: GatherArray
* @Author: Ling
* @Date: 2020/11/13 16:31
* @Description:
*/
public class GatherArray {
private List<String> mylist;
private Set<String> myset;
private Map<Integer,String> mymap;
private Properties myproperties;
private String[] myarray;
//省略方法
}
xml
<!--集合、数组-->
<bean id="usergaar" class="bdit.pojo.UserGaAr">
<property name="mylist">
<list>
<value>list</value>
<value>1111</value>
<value>2222</value>
</list>
</property>
<property name="myarray">
<set>
<value>array</value>
<value>2222</value>
<value>3696</value>
</set>
</property>
<property name="myset">
<set>
<value>myset</value>
<value>2222</value>
<value>2222</value>
</set>
</property>
<property name="myproperties">
<props>
<prop key="1">proper</prop>
<prop key="2">2222</prop>
<prop key="3">3333</prop>
</props>
</property>
<property name="mymap">
<map>
<entry key="0" value="map"></entry>
<entry key="1" value="1212"></entry>
<entry key="2" value="3232"></entry>
</map>
</property>
</bean>
测试
// 数组、集合的注入
@Test
public void gatherArray(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans-two.xml");
GatherArray gatherArray = context.getBean("gatherArray",GatherArray.class);
System.out.println(gatherArray);
System.out.println(gatherArray.getMylist());
System.out.println(gatherArray.getMyset());
System.out.println(gatherArray.getMymap());
System.out.println(gatherArray.getMyproperties());
System.out.println(gatherArray.getMyarray());
System.out.println(Arrays.toString(gatherArray.getMyarray()));
}
8、 Spring 基于注解的开发
8.1 bean标签和注解的对于关系
最开始基础的配置
- DAO层的接口
package com.bdit.dao;
public interface AccountDao {
public void saveAccount();
}
- DAO接口的实现类
package com.bdit.dao.impl;
import com.bdit.dao.AccountDao;
import org.springframework.stereotype.Component;
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount() {
}
public AccountDaoImpl(){
System.out.println("DAO层 ==");
}
}
- 业务层接口
package com.bdit.service;
import org.springframework.stereotype.Component;
@Component
public interface AccountService {
void saveAcccount();
}
- 业务层实现类
package com.bdit.service.impl;
import com.bdit.dao.AccountDao;
import com.bdit.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public AccountServiceImpl(){
System.out.println("============= 业务层默认的构造方法 ==========");
}
@Override
public void saveAcccount() {
accountDao.saveAccount();
}
}
- 测试类
public class TextAccount {
}
- beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
</beans>
- 目录结构
8.1.1 创建对象的(标记于业务层)
- @Component:作用就和在xml配置文件中的 标签实现的功能是一样的
- 作用:用于把当前类对象存入Spring容器(key=value形式存在)中
- 属性:
- value:1、用于指定bean的id,当未在 @Component(value = “”)中定义是,value就是当前类名,且首字母小写
- 2、如果只有一个则value可以省略直接是:@Component(”accountService“)
- @Component三个子注解
- @Controller(一般用在表现层)
- @Service(一般用在业务层)
- @Repository(一般用在持久层)
- 【以上三个注解(@Controller @Service @Repository) 作用和属性与Component作用、属性一模一样,这三个是Spring框架提供的明确三层使用的注解,使三层对象更加清晰】
- @PostConstruct和@PreDestory和@Resouce注解都是jdk提供的,它们位 于javax.annotation包中
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
应用:
- 在 package com.bdit.service.impl.AccountServiceImpl中加入
@Component //将 AccountServiceImpl 类反射创建一个对象存入Spring容器中
public class AccountServiceImpl implements AccountService {}
- xml文件中
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--告诉Spring在创建容器时,就要扫描的包,实现其里面的注解
配置的标签不在bean中而是在 context 名称空间和约束中 -->
<context:component-scan base-package="com.bdit"/>
<!--在此时的xml中可以是最原始的状态,也可以是这种-->
</beans>
- 测试类中
@Test
public void text01(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
AccountService a = context.getBean("accountServiceImpl",AccountService.class);
System.out.println(a);
}
8.1.2 用于注入数据
用于注入数据
作用和xml配置文件中的bean标签内的 标签作用一致
-
@Autowired:
-
作用:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配,就可以注入成功
-
如果IOC容器中没有任何bean类型和要注入的变量匹配,则报错
-
如果IOC容器中有多个类型匹配时,先用过数据类型去匹配,这是会匹配到多个,再通过变量名称确定唯一
- 例如:
@Autowired private AccountDao accountDao02 = null; 访问修饰符 数据类型 变量名称 数据类型
-
放置位置:可以是变量上,也可以是方法上
-
注意:在使用注解注入的时,set方法不是必须的了
Spring的IOC容器是一个 MAP( key(String) value(Object) ) 结构
- @Qualifier:
- 作用:在按照类中注入的基础之上再按照名称注入,再给类成员注入的时候不能单独使用,如果通过构造方法注入,该注解则需要放在构造方法参数的前面
- 属性:
- value:用于指定注入的ID
- @Resource:
- 作用:直接按照bean的id注入可以独立使用,不能用在构造方法上,此时类中也不要提供有参数的构造方法。
- 属性:
- name:用于指定bean的id
- type:用于指定注入的class 例如: @Resource(type = AccountDaoImpl.class)
- 注意:
- 以上只能注入其它bean类型的数据,基本数据类型、String类型无法使用上述注解完成,集合的注入只能通过xml来实现
- @Value
- 作用:用于注入基本上类型和String类型的数据
- 属性:
- value:用于指定数据的值,可以使用spring中的spel(el表达式)
spel写法:${表达式}
- value:用于指定数据的值,可以使用spring中的spel(el表达式)
业务层接口实现类
package com.bdit.service.impl;
import com.bdit.dao.AccountDao;
import com.bdit.dao.impl.ACCountDaoImpl02;
import com.bdit.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Component //将 AccountServiceImpl 类反射创建一个对象存入Spring容器中
public class AccountServiceImpl implements AccountService {
//第一种(其余都可不变)
@Autowired
private AccountDao accountDao02;
//第二种(方式一)
@Autowired
@Qualifier("accountDao02")
public AccountServiceImpl(AccountDao accountDao) {
this.accountDao=accountDao;
System.out.println("===================AccountServiceImpl的构造方法,不是dao的构造方法==================");
}
//第二种(方式二)
@Autowired
public AccountServiceImpl(@Qualifier("accountDao02")AccountDao accountDao) {
this.accountDao=accountDao;
System.out.println("===================AccountServiceImpl的构造方法,不是dao的构造方法==================");
}
//第三种
@Resource(name = "accountDao02" )//(方式一)
@Resource(type = ACCountDaoImpl02.class )//(方式二)
public void setAccountDao(AccountDao accountDao) {
System.out.println("===========SetAccountDao");
this.accountDao = accountDao;
}
public AccountServiceImpl(){
System.out.println("============= 业务层默认的构造方法 ==========");
}
@Override
public void saveAcccount() {
accountDao.saveAccount();
System.out.println("============ AccountServiceImpl 中实现 AccountService.class 的 saveAcccount() ===========");
}
}
-
DAO层实现类
- 实现两个实现类,数据类型相同,但value值不一样
第一个
package com.bdit.dao.impl;
import com.bdit.dao.AccountDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
/**
* @Package: com.bdit.dao.impl
* @ClassName: AccountDaoImpl
* @Author: Ling
* @Date: 2020/11/13 7:00
* @Description:
*/
@Repository("accountDao01")
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount() {
System.out.println("============ AccountDaoImpl 中实现 AccountDao.class 的 saveAcccount() ===========");
}
public AccountDaoImpl(){
System.out.println("============ DAO层 AccountDaoImpl默认的构造方法 ============");
}
}
第二个
package com.bdit.dao.impl;
import com.bdit.dao.AccountDao;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
/**
* @Package: com.bdit.dao.impl
* @ClassName: ACCountDaoImpl02
* @Author: Ling
* @Date: 2020/11/16 13:17
* @Description:
*/
@Repository("accountDao02")
public class ACCountDaoImpl02 implements AccountDao {
@Override
public void saveAccount() {
System.out.println("ACCountDaoImpl02");
}
}
- 测试类
public class TextAccount {
@Test
public void text03(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
AccountService a = context.getBean("accountServiceImpl",AccountService.class);
a.saveAcccount();
}
}
注入总结
- 第一种:
- @Autowired //作用:自动按照类型注入
- private AccountDao accountDao02;//需要定义注入的变量名称
- 第二种
- @Autowired
- @Qualifier(“accountDao02”)
- 注意:使用 @Qualifier 放在构造方法时,最好把 @Autowired 放在构造方法之上
例如:
private AccountDao accountDao;
@Autowired
public AccountServiceImpl(@Qualifier(value = "accountDao02") AccountDao accountDao) {
this.accountDao=accountDao;
System.out.println("===================构造方法");
}
- 第三种
@Resource(name="accountDao02")
private AccountDao accountDao;
或者
@Resource(type = AccountDaoImpl.class)
private AccountDao accountDao;
注意:
private AccountDao accountDao;
@Autowired
@Resource(name = "accountDao02" )//或@Resource(type = ACCountDaoImpl02.class )
public void setAccountDao(AccountDao accountDao) {
System.out.println("===========SetAccountDao");
this.accountDao = accountDao;
}
8.1.3 用于改变作用范围
用于改变作用范围的
- 作用:和bean标签中的scope属性实现的功能是一样的
- Scope
- 作用:指定bean的作用范围
- 属性:
- value:指定范围的取值,常用取值:singleton单例 prototype多例(默认是单例)
- 业务层实现类
@Component
@Scope("prototype")//默认不写为单例
public class AccountServiceImpl implements AccountService {其余不变}
- 测试类
public class TextAccount {
@Test
public void text04(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
AccountService a = context.getBean("accountServiceImpl",AccountService.class);
a.saveAcccount();
}
}
8.1.4 用于改变生命周期
用于生命周期的
作用:与bean标签中的 init-method 和 destroy-methode 的作用是一样的
@PreDestroy:作用:用于指定销毁方法
@PostConstruct:作用:用于指定初始化方法
- 业务层实现类
package com.bdit.service.impl;
import com.bdit.dao.AccountDao;
import com.bdit.dao.impl.ACCountDaoImpl02;
import com.bdit.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@Component
//@Scope("prototype")
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
@Autowired
@Resource(type = ACCountDaoImpl02.class )
public void setAccountDao(AccountDao accountDao) {
System.out.println("===========SetAccountDao");
this.accountDao = accountDao;
}
public AccountServiceImpl(){
System.out.println("============= 业务层默认的构造方法 ==========");
}
@Override
public void saveAcccount() {
accountDao.saveAccount();
System.out.println("============ AccountServiceImpl 中实现 AccountService.class 的 saveAcccount() ===========");
}
@PostConstruct
public void Accountinit(){
System.out.println("======================== 初始化方法init ========================");
}
@PreDestroy
public void Accountdestory(){
System.out.println("======================== 销毁方法 ========================");
}
}
- 测试类
public class TextAccount {
@Test
public void text04(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
AccountService a = context.getBean("accountServiceImpl",AccountService.class);
a.saveAcccount();
}
}
8.1.5 Spring对Junit的支持
- junit运行的时候底层使用了Runner对象,有一个默认的Runnit对象
- spring对junit,其实是自己实现了一个Runner对象
- 好处
- 配置完成后不需要我们手动启动Spring
- 可以在junit中使用@AutoWried等方式诸如对象,直接对其进行调用测试
//明确Runner对象,使用Spring框架中的Runner对象,替换Junit中的Runner对象
@RunWith(SpringJUnit4ClassRunner.class)
//明确Spring框架启动的入口,xml配置文件的位置,必须以classpath开头
@ContextConfiguration(locations ="classpath:beans.xml")
示例:
//明确Runner对象,使用Spring框架中的Runner对象,替换Junit中的Runner对象
@RunWith(SpringJUnit4ClassRunner.class)
//明确Spring框架启动的入口,xml配置文件的位置,必须以classpath开头
@ContextConfiguration(locations ="classpath:beans.xml")
public class StudentTest {
@Autowired
private IStudentService studentService;
@Test
public void testFindAll(){
List<Student> list=studentService.findAll();
for(Student student:list){
System.out.println(student);
}
}
}
9、AOP
9.1 AOP概述
- AOP:全称是 Aspect Oriented Programming,面向切面编程
- AOP 就是把程序中重复的代码抽取出来,在需要执行的时候和,在使用动态代理技术,在不改变源代码的基础上,对我们已有的方法进行增强
- AOP是对OOP的一种补充
9.2 Spring中AOP的相关
- Joinpoint(连接点)
- 是指那些需要被拦截到的点,在Sspring中,这些点指的就是方法,因为Spring只基于方法的连接
- PointCut(切入点)
- 既是指对那些连接点进行拦截的定义
- Advice(通知/增强)
- 是指对拦截到的连接点之后所要做的事情
- 通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知
- Introducation(引入----引介)
- 是一种特殊的通知,可以在不修改类代码的前提下,引介可以在运行期间动态的增加一些方法或着属性
- Target(目标对象)
- 代理的目标对象
- Weaving(织入)
- 就是把增强应用到目标对象创建新的代理对象的过程
- spring采用动态代理的织入,而Aspectj采用编译器织入和类装载期织入
- Proxy(代理)
- 一个类被AOP织入增强后,就产生一个结果代理类
- Aspect(切面)
- 就是切入点和通知(引介)的结合
9.3 基于XML的AOP配置
- 导入 aspect 和环绕通知的jar包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!-- 环绕通知jar包 -->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
- 在spring配置文件中导入约束
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
"
>
</beans>
- 切面类
package com.bdit.aspect;
import com.bdit.pojo.Usera;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @Package: com.bdit.aspect
* @ClassName: LogginAspect
* @Author: Ling
* @Date: 2020/11/17 16:17
* @Description:切面类
*/
public class LogginAspect {
// 基于XML的AOP配置
private Logger loggerasper = LogManager.getLogger(LogginAspect.class);
/**
* @Description: 设置前置通知输出
* @return:
*/
public void beforeks(){
loggerasper.info("==========AOP 前置通知 开始日志==========");
}
/**
* @Description: 设置后置通知输出
* @return:
*/
public void afterRunningHz() {
loggerasper.info("==========AOP 后置通知 日志记录成功==========");
}
/**
* @Description: 设置后置通知(异常)输出
* @return:
*/
public void afterThrowingHz(){
loggerasper.info("==========AOP 后置异常通知 日志输出==========");
}
/**
* @Description: 设置后置最终输出
* @return:
*/
public void afterHz(){
loggerasper.info("==========AOP 后置最终通知 日志输出==========");
}
/**
* 环绕通知:
* Spring提供了接口 ProceedingJoinPoint(一定要有) 可以作为环绕通知方法的参数
* 再环绕通知执行时,可以通过该接口的实例获得目标类的对象,直接执行目标类中的方法
*
* 使用环绕通知时,目标方法的返回值类型只能是引用类型(基本数据类型要写其包装类)
* @Author: Ling
* @Date: 2020-11-17 17:11:17
*/
public void aroundHuanRao(ProceedingJoinPoint proceedingJoinPoint){
//获取方法执行所需的参数
Object[] obj = proceedingJoinPoint.getArgs();
Usera usera = (Usera)obj[0];
System.out.println(usera);
System.out.println(proceedingJoinPoint.getTarget());
loggerasper.info("==================开启 环绕通知 事务==================");
try{
proceedingJoinPoint.proceed(obj); //执行目标方法
loggerasper.info("=============提交 环绕通知 事务==================");
} catch (Throwable throwable) {
throwable.printStackTrace();
loggerasper.info("==============异常 环绕通知 事务================");
}finally {
loggerasper.info("==============关闭 环绕通知 事务==============");
}
}
}
- 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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
"><!-- 基于XML的AOP配置 -->
<!--
添加aop的约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
-->
<bean id="useraServiceImpl" class="com.bdit.service.impl.UseraImpl"></bean>
<!--配置切面类-->
<bean id="logginAspect" class="com.bdit.aspect.LogginAspect"></bean>
<!--配置AOP
aop:config 作用:用于声明aop的开始-->
<aop:config >
<!-- aop:aspect 作用:用于配置切面类
属性:
id:给切面类提供一个唯一标识
ref:引用配置好的切面类-->
<aop:aspect id="logginAspectYing" ref="logginAspect">
<!-- aop:pointcut 作用:用于配置 切入点表达式 ,就是指定对哪些方法进心增强
属性:
id:用于切入点表达式提供为一个表示
expression:用于定义切入点表达式
-->
<aop:pointcut id="cutexpression" expression="execution(* com.bdit.service.impl.*.*(..))"></aop:pointcut>
<!--配置前置通知类型
aop:before 作用:用于配置 前置通知 指定正强方法在切入点方法执行之前进心增强
属性:
method:用于指定通知类中的增强方法名称
pointcut-ref:用于指定 切入点表达式 的引用
pointcut:用于指定切入点表达式-->
<aop:before method="beforeks" pointcut-ref="cutexpression"/>
<!--配置后置通知
aop:after-returning 作用:配置后置通知
属性:
method:用于指定通知类中的增强方法名称
pointcut-ref:用于指定切入点表达式的引用
pointcut:用于指定切入点表达式
执行的时间点:切入点方法正常执行后执行,它和异常通知只能有一个执行-->
<aop:after-returning method="afterRunningHz" pointcut-ref="cutexpression"/>
<!--配置后置异常通知
aop:after-throwing 作用:配置后置异常通知
属性:
method:用于指定通知类中的增强方法名称
pointcut-ref:用于指定切入点表达式的引用
pointcut:用于指定切入点表达式
执行的时间点:切入点方法执行过程中产生异常后执行异常通知
与后置通知,只执行一个
-->
<aop:after-throwing method="afterThrowingHz" pointcut-ref="cutexpression"/>
<!--配置后置最终通知
aop:after 作用:配置后置最终通知
属性:
method:用于指定通知类中的增强方法名称
pointcut-ref:用于指定切入点表达式的引用
pointcut:用于指定切入点表达式
执行的时间点:无论切入点方法执行有没有异常都会最后执行
-->
<aop:after method="afterHz" pointcut-ref="cutexpression"/>
<!-- 环绕配置
环绕通知:
说明:它是Spring框架提供的
属性:
method:用于指定通知类中的增强方法名称
pointcut-ref:用于指定切入点表达式的引用
pointcut:用于指定切入点表达式
注意:大多数情况下,环绕都是单独使用的
-->
<aop:around method="aroundHuanRao" pointcut-ref="cutexpression"/>
</aop:aspect>
</aop:config>
</beans>
- 业务层
package com.bdit.service.impl;
import com.bdit.pojo.Usera;
import com.bdit.service.IUseraServicr;
/**
* @Package: com.bdit.service.impl
* @ClassName: UseraImpl
* @Author: Ling
* @Date: 2020/11/17 16:16
* @Description:
*/
public class UseraImpl implements IUseraServicr {
@Override
public void transfer(String source, String target, double money) {
/**
* 基于XML的AOP配置
* 没有异常,后置通知输出,异常后置通知不输出
* System.out.println(10/0); 产生异常:后置通知不输出,后置异常通知输出
* @Author: Ling
* @Date: 2020-11-17 16:56:22
*/
System.out.println(10/0);
System.out.println("----------------转账----------------");
}
@Override
public Integer save(Usera usera) {
System.out.println("----------------增加----------------");
return null;
}
@Override
public Integer delect(Integer integer) {
System.out.println("----------------删除----------------");
return null;
}
@Override
public Integer update(Usera usera) {
System.out.println("----------------修改----------------");
return null;
}
@Override
public Usera queryById(Integer integer) {
System.out.println("----------------按照ID查询----------------");
return null;
}
@Override
public Usera queryAll() {
System.out.println("----------------查询全部----------------");
return null;
}
}
- 测试
package com.bdit.text;
import com.bdit.pojo.Usera;
import com.bdit.service.IUseraServicr;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Package: com.bdit.text
* @ClassName: Text01
* @Author: Ling
* @Date: 2020/11/17 16:37
* @Description:
*/
public class Text01 {
/**
* 基于XML的AOP配置
* 1、配置好 增强方法 (例如:LogginAspect类)
* 2、在 XML中进行定义
* 3、在测试类中 引用接口实现类中的方法(有些需在是实现类中改变代码看到效果------后置异常类)
* 4、注意:大多数情况下,环绕都是单独使用的 使用环绕通知时,目标方法的返回值类型只能是引用类型(基本数据类型要写其包装类)
* @Author: Ling
* @Date: 2020-11-17 16:57:25
*/
@Test
public void textaop(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");
IUseraServicr iUseraServicr = context.getBean(IUseraServicr.class);
iUseraServicr.transfer("甲一","甲二",200);
}
@Test
public void texthr(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");
IUseraServicr iUseraServicr = context.getBean(IUseraServicr.class);
Usera u = new Usera();
iUseraServicr.save(u);
}
}
9.4 切入点表达式
- AspectJ:在Spring2.x以上的版本,可以使用基于AspectJ注解活着基于xml配置的AOP
- AspectJ表达式说明
- execution:匹配方法的执行
- 表达式语法: execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 写法说明:
- 全匹配写法: public void com.bdit.service.impl.AccountServiceImpl.save(com.bdit.pojo.Account)
- 访问修饰符可以省略: void com.bdit.service.impl.AccountServiceImpl.save(com.bdit.pojo.Account)
- 返回值可以使用*号,表示任意类型: * com.bdit.service.impl.AccountServiceImpl.save(com.bdit.pojo.Account)
- 包名可以使用 * 号,表示任意包,包有几级,就写几个* :* . * . * .*.AccountServiceImpl.save(com.bdit.pojo.Account)
- 使用…来表示当前包,及其子包:* com…AccountServiceImpl.save(com.bdit.pojo.Account)
- 类名也可以使用*号,表示任意类 : * com… * .save(com.bdit.pojo.Account)
- 方法名也可以使用*号,表示任意方法 * com… * . * (com.bdit.pojo.Account)
- 参数列表可以使用*,表示任意参数类型,但是必须有参数 * com… * . *( * )
- 参数列表可以使用*,标识任意参数类型,有参数无参数都可以 * com…* .*(…)
- 全通匹配 * * … * . *(…) 通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切入业务层的实现类。
9.5 基于半注解半xml的aop文件
**在xml中配置要指定扫描的包和 xml启用Spring对AOP注解的支持 **
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
"><!-- 基于半注解半XML的AOP配置 -->
<!--扫描指定的包-->
<context:component-scan base-package="com.bdit"/>
<!--设置支持对aop注解的支持-->
<aop:aspectj-autoproxy/>
</beans>
切面类
package com.bdit.aspect;
import com.bdit.pojo.Usera;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Package: com.bdit.aspect
* @ClassName: LogginAspect
* @Author: Ling
* @Date: 2020/11/17 16:17
* @Description:切面类
*/
@Component
@Aspect //声明为切面类
public class LogginAspect {
// <!-- 基于半注解半XML的AOP配置 -->
private Logger loggerasper = LogManager.getLogger(LogginAspect.class);
/**
* @Pointcut(value = "execution(* com.bdit.service.impl.*.*(..))")
* public void lei(){};
* 这种优化于每次在方法之上定义
* 引用时:
* @通知名(value="lei()")即可
*/
@Pointcut(value = "execution(* com.bdit.service.impl.*.*(..))")
public void lei(){};
/**
* @Description: 设置前置通知输出
* @return:
*/
// @Before(value = "execution(* com.bdit.service.impl.*.*(..))")
@Before(value = "lei()")
public void beforeks(){
loggerasper.info("==========AOP 前置通知 开始日志==========");
}
/**
* @Description: 设置后置通知输出
* @return:
*/
@AfterReturning(value = "execution(* com.bdit.service.impl.*.*(..))")
public void afterRunningHz() {
loggerasper.info("==========AOP 后置通知 日志记录成功==========");
}
/**
* @Description: 设置后置通知(异常)输出
* @return:
*/
@AfterThrowing(value = "execution(* com.bdit.service.impl.*.*(..))")
public void afterThrowingHz(){
loggerasper.info("==========AOP 后置异常通知 日志输出==========");
}
/**
* @Description: 设置后置最终输出
* @return:
*/
@After(value="execution(* com.bdit.service.impl.*.*(..))")
public void afterHz(){
loggerasper.info("==========AOP 后置最终通知 日志输出==========");
}
/**
* Spring 提供了 一个 ProceedingJoinPoint
* 在环绕通知时,可以通过该接口的实例获取目标类的对象,直接执行目标类的方法
*
* 【使用环绕通知时,目标方法返回值类型只能是引用类型(基本数据类型要写包装类)】
*
* @Description: 设置环绕通知
* @return:
*/
@Around(value="execution(* com.bdit.service.impl.*.*(..))")
public void aroundHuanRao(ProceedingJoinPoint joinPoint){
Object[] obj = joinPoint.getArgs();
Usera a = (Usera)obj[0];
System.out.println(joinPoint.getTarget());
System.out.println(a);
loggerasper.info("============ AOP 开启环绕通知 日志输出 ===========");
try{
joinPoint.proceed(obj);//执行目标方法
loggerasper.info("============ AOP 环绕通知 提交 日志输出 ==============");
} catch (Throwable throwable) {
throwable.printStackTrace();
loggerasper.info("============ AOP 环绕通知出现异常 日志输出 ==============");
}finally {
loggerasper.info("============ AOP 环绕通知销毁 日志输出 ==============");
}
}
}
【其余几乎都一样】
9.6 纯注解的AOP配置
- Spring的配置类
package com.bdit.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
* @Package: com.bdit.config
* @ClassName: Spring
* @Author: Ling
* @Date: 2020/11/17 20:11
* @Description:
*/
@Configuration
@ComponentScan(basePackages = {"com.bdit"})
@EnableAspectJAutoProxy //启用Spring对AOP注解的支持
public class SpringConfig {
}
- 配置切面类
package com.bdit.aspect;
import com.bdit.pojo.Usera;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Package: com.bdit.aspect
* @ClassName: LogginAspect
* @Author: Ling
* @Date: 2020/11/17 16:17
* @Description:切面类
*/
@Component
@Aspect //声明为切面类
public class LogginAspect {
// <!-- 基于全注解的AOP配置 -->
private Logger loggerasper = LogManager.getLogger(LogginAspect.class);
/**
* @Pointcut(value = "execution(* com.bdit.service.impl.*.*(..))")
* public void lei(){};
* 这种优化于每次在方法之上定义
* 引用时:
* @通知名(value="lei()")即可
*/
@Pointcut(value = "execution(* com.bdit.service.impl.*.*(..))")
public void lei(){};
/**
* @Description: 设置前置通知输出
* @return:
*/
// @Before(value = "execution(* com.bdit.service.impl.*.*(..))")
@Before(value = "lei()")
public void beforeks(){
loggerasper.info("==========AOP 前置通知 开始日志==========");
}
/**
* @Description: 设置后置通知输出
* @return:
*/
@AfterReturning(value = "lei()")
public void afterRunningHz() {
loggerasper.info("==========AOP 后置通知 日志记录成功==========");
}
/**
* @Description: 设置后置通知(异常)输出
* @return:
*/
@AfterThrowing(value = "lei()")
public void afterThrowingHz(){
loggerasper.info("==========AOP 后置异常通知 日志输出==========");
}
/**
* @Description: 设置后置最终输出
* @return:
*/
@After(value = "lei()")
public void afterHz(){
loggerasper.info("==========AOP 后置最终通知 日志输出==========");
}
/**
* Spring 提供了 一个 ProceedingJoinPoint
* 在环绕通知时,可以通过该接口的实例获取目标类的对象,直接执行目标类的方法
*
* 【使用环绕通知时,目标方法返回值类型只能是引用类型(基本数据类型要写包装类)】
*
* @Description: 设置环绕通知
* @return:
*/
@Around(value = "lei()")
public void aroundHuanRao(ProceedingJoinPoint joinPoint){
Object[] obj = joinPoint.getArgs();
Usera a = (Usera)obj[0];
System.out.println(joinPoint.getTarget());
System.out.println(a);
loggerasper.info("============ AOP 开启环绕通知 日志输出 ===========");
try{
joinPoint.proceed(obj);//执行目标方法
loggerasper.info("============ AOP 环绕通知 提交 日志输出 ==============");
} catch (Throwable throwable) {
throwable.printStackTrace();
loggerasper.info("============ AOP 环绕通知出现异常 日志输出 ==============");
}finally {
loggerasper.info("============ AOP 环绕通知销毁 日志输出 ==============");
}
}
}
- 测试类
package com.bdit.text;
/**
* @Package: com.bdit.text
* @ClassName: Text01
* @Author: Ling
* @Date: 2020/11/17 16:37
* @Description:
*/
import com.bdit.config.SpringConfig;
import com.bdit.pojo.Usera;
import com.bdit.service.IUseraServicr;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class Text01 {
/**
* 全注解的AOP配置
* 1、配置好 增强方法 (例如:LogginAspect类)
* 2、准备Sspring配置类
* @Author: Ling
* @Date: 2020-11-17 16:57:25
*/
/*@Test
public void textaop(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");
IUseraServicr iUseraServicr = context.getBean(IUseraServicr.class);
iUseraServicr.transfer("甲一","甲二",200);
}*/
@Autowired
private IUseraServicr iUseraServicr;
@Test
public void textaopsave(){
iUseraServicr.save(new Usera());
}
}
10、事务
10.1 Spring事务
- JavaEE事务处理位于业务层,Spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现
- PlatformTransactionManager 此接口是Spring的事务管理器,提供了我们常用的操作事物的方法
- 获取事物的状态的信息:
- TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
- 提交事务:
- void commit(TransactionStatus var1) throws TransactionException;
- 回滚事务:
- void rollback(TransactionStatus var1) throws TransactionException;
- 获取事物的状态的信息:
- 一般使用其实现类
- 真正管理事务的对象 org.springframework.jdbc.datasource.DataSourceTransactionManager 使用JDBC或MyBatis进行 持久化数据时使用。
- org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate持久化数据 时使用
10.2 TransactionDefinition
它是事务的定义信息对象,里面包含如下方法:
- 获取事务对象名称: String getName();
- 获取事务的隔离级别: int getIsolationLevel();
- 获取事务的传播行为: int getPropagationBehavior();
- 获取事务超时时间: int getTimeout();
- 获取事务是否只读: boolean isReadOnly();
10.3 事物的隔离级别
事务隔离级别反映事务提交并发访问时的处理态度:
-
ISOLATION_DEFAULT :默认级别,归属下列某一种
-
ISOLATION_READ_UNCOMMITTED :可以读取未提交的数据
-
ISOLATION_READ_COMMITTED:只能读取已提交的数据,解决脏读问题(Oracle默认级别)
-
ISOLATION_PERPEATABLE_READ :是否读取其它事务提交修改后的数据,解决不可重复读问题(MySQ默认的级别)
-
ISOLATION_SERIALIZABLE :是否读取其它事务提交添加好的数据,解决幻读问题,最高的隔离级别,不允许并发访问,效率低
10.4 事物的传播行为
- REQUIRED:如果当前没有事务,就创建一个事务,如果已经存在一个事务中,加入到这个事务中,一 般选择(默认值)
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则嵌套事务内执行,如果当前没有事务,则执行REQUIRED类似的操作。
10.5 超时时间
默认值为-1,就是用不超时,但可以设置,单位为秒
10.6 是否只读事务
查询时反馈的数据最好为只读------获取事务是否只读: boolean isReadOnly();
10.7 TransactionStatus
此接口提供的是事务的运行状态,方法如下:
- 获取事务是否为新的事务 boolean isNewTransaction();
- 获取是否是存储点 boolean hasSavepoint();
- 设置事务回滚 void setRollbackOnly();
- 获取事务是否回滚 boolean isRollbackOnly();
- 刷新事务 void flush();
- 事务是否完成 boolean isCompleted();
11、Spring事务配置相关标签及属性
- 事务配置:<tx:advice ></ tx:advice> 通知标签
- 属性id:自定义唯一标识
- transaction-manager属性:事务管理类,配置事物的管理类的id属性
- 事务属性配置:<tx:attributes ></tx:attributes >
- 子标<tx:method ></tx:method >签属性:
- name:方法名(业务层的方法名,支持通配符*)
- read-only:是否只读事务,查询都是只读,其他是非只读,默认配置为false
- propagation:事物的传播行为,默认配置为REQUIRED
- isolation:事物的隔离级别,默认配置为DEFAULT
- timeout:事物的超时时间,默认为-1
- no-rollback-for:遇到什么异常不回滚,配置异常类名,多个类名逗号分开
- rollback-for:遇到什么异常回滚
- 子标<tx:method ></tx:method >签属性:
- aop切面配置 <aop:config >
- 子标签 <aop:advisor ></aop:advisor > 属性:
- advice-of:引用通知,配置 tx:advice 标签的属性值
- pointcut-ref:引入切入点
- 子标签 <aop:advisor ></aop:advisor > 属性:
11.1 Spring基于xml的事务配置
基于转账的案例
-
<tx:advice ></tx:advice >标签详解
-
作用:配置事物的通知
-
出现位置:beans标签内部
-
属性:id:为事务通知提供一个唯一标识
transacyion-manager:为事务通知指定一个事务管理器ID的引用
-
-
<tx:attributes ></tx:attributes >标签详解
- 作用:配置事务的属性
- 的位置:要去写在<tx:advice ></tx:advice >标签内部
-
<tx:method ></tx:method >标签
- 作用:用于配置每个需要事务支持的方法,所使用的特征
- 出现位置:要求写在<tx:attributes ></tx:attributes >标签内部
- 属性:name:指定方法的名称支持通配符的配置方式,例如:* 表示所有的方法,find* 表示以find开头的方法
- read-only:指定是否为只读事务,默认值false,只有查询方法可以设置为true
- propagation:指定事务的传播行为,默认值是REQUIRED,表示必须有事务。查询方法设置为 SUPPORTS,表示有事务就支持,没有事务就以非事务进行。
- timeout:指定事务超时的时间,默认值是-1,表示永不超时,取值为正整数,以秒为单位
- isolation:事务隔离级别,默认值是DEFAULT,表示采用数据库的默认隔离级别,不同的数据库默认隔 离级别不一样,mysql为REPEATABLE_READ,oracle为 READ COMMITTED
- rollback-for:用于指定一个异常,当该异常产生时,事务回滚。产生其他异常时,事务不回滚,没有 默认值,即产生任何异常都回滚。
- no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其它异常,事务回滚。没 有默认值,即产生任何异常都回滚。
-
<aop:advisor ></aop:advisor >标签
- 作用: 用于建立通知和切入点表达式的关系
- 出现位置: 要求写在标签内部
- 属性:
- id:唯一标识
- advice-ref:用于指定通知的引用
- pointcut-ref:用于指定切入点表达式的引用
- pointcut:用于指定切入点表达式
- order:当配置多个advisor,用于指定执行的优先级
-
javabean
package com.bdit.pojo;
import lombok.Data;
import java.io.Serializable;
/**
* @Package: com.bdit.pojo
* @ClassName: Account
* @Author: Ling
* @Date: 2020/11/18 12:58
* @Description:
*/
@Data
public class Account implements Serializable {
private int aid;
private String name;
private Double money;
}
- dao接口实现类
package com.bdit.mapper;
import com.bdit.pojo.Account;
/**
* @Package: com.bdit.mapper
* @ClassName: IAccountMapper
* @Author: Ling
* @Date: 2020/11/18 13:00
* @Description:
*/
public interface IAccountMapper {
public Account queryByName(String name);
public int update(Account account);
}
- 业务层接口
package com.bdit.service;
import com.bdit.pojo.Account;
/**
* @Package: com.bdit.service.impl
* @ClassName: IAccountService
* @Author: Ling
* @Date: 2020/11/18 19:39
* @Description:
*/
public interface IAccountService {
public void transfer(String source, String target, double money);
public int update(Account account);
}
- 业务层接口实现类
package com.bdit.service.impl;
import com.bdit.mapper.IAccountMapper;
import com.bdit.pojo.Account;
import com.bdit.service.IAccountService;
/**
* @Package: com.bdit.service.impl
* @ClassName: AccountServiceImpl
* @Author: Ling
* @Date: 2020/11/18 13:01
* @Description:
*/
public class AccountServiceImpl implements IAccountService {
private IAccountMapper accountMapper;
public void setAccountMapper(IAccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Override
public void transfer(String source, String target, double money) {
Account accountSource = accountMapper.queryByName(source);
Account accountTarget = accountMapper.queryByName(target);
accountSource.setMoney(accountSource.getMoney() - money);
accountMapper.update(accountSource);
System.out.println(10/0);
accountTarget.setMoney(accountTarget.getMoney() + money);
accountMapper.update(accountTarget);
}
@Override
public int update(Account account) {
return accountMapper.update(account);
}
}
- 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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--配置DAO层-->
<!--将数据库链接交给spring框架,通过文件加载器来加载bd.properties-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataService" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置mybatis的Sqlsession工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据
将 数据库的链接有关的数据,注入工厂中-->
<property name="dataSource" ref="dataService"></property>
<!--配置别名-->
<property name="typeAliasesPackage" value="com.bdit.pojo"></property>
</bean>
<!--创建DAO代理实现类的扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bdit"></property>
</bean>
<!--配置业务层-->
<bean id="accountService" class="com.bdit.service.impl.AccountServiceImpl">
<property name="accountMapper" ref="IAccountMapper"></property>
</bean>
<!-- 配置事务
第一步:配置事务管理器
第二步:配置事务的通知
第三步:配置事物的属性
第四步:配置aop切入点表达式和事务通知关联-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataService"/>
</bean>
<!--配置事务的通知-->
<tx:advice id="txAdice" transaction-manager="transactionManager">
<!--配置事物的属性-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<aop:pointcut id="mypointcut" expression="execution(* com.bdit.service.impl.*.*(..))"/>
<!--配置切入点和事务通知关联-->
<aop:advisor advice-ref="txAdice" pointcut-ref="mypointcut"/>
</aop:config>
</beans>
测试
package com.bdit.text;
import com.bdit.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Package: com.bdit.text
* @ClassName: Text1
* @Author: Ling
* @Date: 2020/11/17 10:31
* @Description:
*/
public class Text1 {
//aop事务 xml 实现 转账
@Test
public void text(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
IAccountService accountService = context.getBean("accountService",IAccountService.class);
accountService.transfer("张三","李四",200);
}
}
11.2 Spring基于注解的事务注解
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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--开启ioc容器的注解扫描-->
<context:component-scan base-package="com.bdit"/>
<!--配置DAO层-->
<!--将数据库链接交给spring框架,通过文件加载器来加载bd.properties-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataService" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置mybatis的Sqlsession工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据
将 数据库的链接有关的数据,注入工厂中-->
<property name="dataSource" ref="dataService"></property>
<!--配置别名-->
<property name="typeAliasesPackage" value="com.bdit.pojo"></property>
</bean>
<!--创建DAO代理实现类的扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bdit"></property>
</bean>
<!--DAO层结束-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataService"/>
</bean>
<!--开启Spring事务扫描器-->
<tx:annotation-driven/>
</beans>
业务层
package com.bdit.service.impl;
import com.bdit.mapper.IAccountMapper;
import com.bdit.pojo.Account;
import com.bdit.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @Package: com.bdit.service.impl
* @ClassName: AccountServiceImpl
* @Author: Ling
* @Date: 2020/11/18 13:01
* @Description:
*/
@Service("accountService")
@Transactional(transactionManager = "transactionManager",propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountMapper accountMapper;
public void setAccountMapper(IAccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Override
public void transfer(String source, String target, double money) {
Account accountSource = accountMapper.queryByName(source);
Account accountTarget = accountMapper.queryByName(target);
accountSource.setMoney(accountSource.getMoney() - money);
accountMapper.update(accountSource);
System.out.println(10/0);
accountTarget.setMoney(accountTarget.getMoney() + money);
accountMapper.update(accountTarget);
}
@Override
public Account queryByName(String name) {
return accountMapper.queryByName(name);
}
@Override
public int update(Account account) {
return accountMapper.update(account);
}
}
测试类
package com.bdit.text;
import com.bdit.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Package: com.bdit.text
* @ClassName: Text1
* @Author: Ling
* @Date: 2020/11/17 10:31
* @Description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class Text1 {
//aop事务 半xml半注解 实现 转账
@Autowired
private IAccountService accountService;
@Test
public void text(){
accountService.transfer("张三","李四",200);
}
}
11.3 注解详解
@Transactional
/**
此注解是Spring配置事务的核心注解,无论是注解驱动开发还是注解和XML混合开发,只要涉及注解配
置事务的地方,都需要使用此注解
通过源码可以看到,该注解可以出现在接口上、类、方法上,分别表示:
接口上:当前接口的所有实现类中重写接口的方法有事务支持
类上:当前类中所有方法有事务支持
方法上:当前方法有事务支持
优先级:
方法上>类上>接口上
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {}
11.4 全注解
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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--开启ioc容器的注解扫描-->
<context:component-scan base-package="com.bdit"/>
<!--配置DAO层-->
<!--将数据库链接交给spring框架,通过文件加载器来加载bd.properties-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataService" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置mybatis的Sqlsession工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据
将 数据库的链接有关的数据,注入工厂中-->
<property name="dataSource" ref="dataService"></property>
<!--配置别名-->
<property name="typeAliasesPackage" value="com.bdit.pojo"></property>
</bean>
<!--创建DAO代理实现类的扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bdit"></property>
</bean>
<!--DAO层结束-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataService"/>
</bean>
<!--开启Spring事务扫描器-->
<tx:annotation-driven/>
</beans>
- MyBatisConfig
package com.bdit.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
/**
* @Package: com.bdit.config
* @ClassName: MyBatisConfig
* @Author: Ling
* @Date: 2020/11/18 23:01
* @Description:
*/
public class MyBatisConfig {
@Bean(value = "sqlSessionFactory")
public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("druidDataSource")DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.bdit.pojo");
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
scannerConfigurer.setBasePackage("com.bdit.mapper");
return scannerConfigurer;
}
}
DataSourceConfig
package com.bdit.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
/**
* @Package: com.bdit.config
* @ClassName: DataSourceConfig
* @Author: Ling
* @Date: 2020/11/18 23:02
* @Description:
*/
@PropertySource(value = {"classpath:db.properties"})
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(value = "druidDataSource")
public DataSource createDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
TransactionManagerConfig
package com.bdit.config;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
* @Package: com.bdit.config
* @ClassName: TransactionManagerConfig
* @Author: Ling
* @Date: 2020/11/18 23:11
* @Description:事务管理器配置类
*/
public class TransactionManagerConfig {
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
SpringConfig
package com.bdit.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @Package: com.bdit.config
* @ClassName: SpringConfig
* @Author: Ling
* @Date: 2020/11/18 23:12
* @Description:
*/
@Configuration
@ComponentScan(basePackages = {"com.bdit"})
@Import({DataSourceConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement //开启Spring注解事务的支持
public class SpringConfig {
}
业务层
package com.bdit.service.impl;
import com.bdit.mapper.IAccountMapper;
import com.bdit.pojo.Account;
import com.bdit.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @Package: com.bdit.service.impl
* @ClassName: AccountServiceImpl
* @Author: Ling
* @Date: 2020/11/18 13:01
* @Description:
*/
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountMapper accountMapper;
public void setAccountMapper(IAccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Override
public void transfer(String source, String target, double money) {
Account accountSource = accountMapper.queryByName(source);
Account accountTarget = accountMapper.queryByName(target);
accountSource.setMoney(accountSource.getMoney() - money);
accountMapper.update(accountSource);
// System.out.println(10/0);
accountTarget.setMoney(accountTarget.getMoney() + money);
accountMapper.update(accountTarget);
}
@Override
public Account queryByName(String name) {
return accountMapper.queryByName(name);
}
@Override
public int update(Account account) {
return accountMapper.update(account);
}
}
测试类
package com.bdit.text;
import com.bdit.config.SpringConfig;
import com.bdit.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Package: com.bdit.text
* @ClassName: Text1
* @Author: Ling
* @Date: 2020/11/17 10:31
* @Description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Text1 {
//aop事务 半xml半注解 实现 转账
@Autowired
private IAccountService accountService;
@Test
public void text(){
accountService.transfer("张三","李四",200);
}
}
12、SpringMVC
12.1 三层架构
- Java开发架构一般都是基于两种形式,一种是C/S架构,也就是客户端/服务器。另一种是B/S架构,也就是浏 览器/服务器。
- 在JavaEE开发中,几乎全部都是基于B/S架构的开发。那么在B/S架构中,系统标准的三层架 构包括:表现层、业务层、持久层。
- 表现层:也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请 求web层,web需要接收http请求,完成http响应。
- 表现层包括展示层和控制层,控制层负责接收请求,展示层负责结果的展示。 表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。 表现层的设计一般都使用MVC模式(MVC是表现层的设计模式,和其它层没有关系)
- 业务层: 也就是我们常说的service层,它负责业务逻辑处理,和我们开发项目的需求息息相关。Web 层依赖业务层,但业务层不依赖web层。 业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要 保证事务一致性。(也就是我们说的事务应该放到业务层来控制)
- 持久层: 也就是我们常说的dao层,负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化 到数据库中。通俗的将,持久层就是和数据库交互,对数据库表进行增删改查操作的。
12.2 MVC
- MVC全名是 Model View Controller,是模型(model)—视图(view)—控制器(controller)的 缩写,是一种用于设计创建web应用程序表现层的模式。
- MVC中每个部分各司其职: Model(模型): 通常指的是我们的数据模型,作用一般情况用于封装数据 View(视图): 通常指的就是我们的jsp或者 html,作用一般就是展示数据的 通常视图是依赖模型数据创建的。
- Controller(控制器): 是应用程序中处理用户交互的部分,作用一般就是处理程序逻辑的。
12.3 SpringMVC 的执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MA998iBb-1606224110780)(C:\Users\Lenovo\Desktop\流程.PNG)]
- 用户发送请求到前端控制器DispatcherServlet
- DispatcherServlet收到请求调用到HandlerMapping处理器映射器
- 处理器映射器找到具体的处理器(可以根据xml、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapater处理器适配器
- HandlerAdapter 经过适配调用具体的处理器(Controller 也叫后端控制器)
- Controller执行完成后返回ModelAndVies
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将MmodelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体的View
- DispathcherServlet根据View进行渲染视图(即将模型数据填充至视图中)DispatcherServlet响应用户
12.4 SpringMVC组件解析
- 前端控制器:DispatcherServlet
- 用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中 心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
- 处理器映射器:HandlerMapping HandlerMapping
- 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现 不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- 处理器适配器:HandlerAdapter
- 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型 的处理 器进行执行。
- 处理器:Handler
- 它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
- 视图解析器:View Resolver
- View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图 名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用 户。
- 视图:View
- SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用 户,需要由程序员根据业务需求开发具体的页面
12.5 SpringMVC注解解析
@RequestMapping
作用:用于建立请求URL和处理请求方法之间的对于关系
位置:类上,请求URL的第一级访问目录,不写,默认是应用的根目录
方法上:请求URL的第二级访问目录,与类上使用的@RequestMapping标注的一级目录一起组成访问的虚拟目录
属性:value:用于指定请求的url,他和path属性的作用是一样的
method:用于请求的方式
如果请求方式和方法处理请求的方式不匹配时,出现405
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和 配置的一模一样
12.6 MVC实现 HELLO
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置spring的核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置初始化参数,也就是指定spring的核心配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--配置注解扫描器-->
<context:component-scan base-package="com.bdit.controlle"/>
<!--配置视图解析器:如何将控制器返回的字符串结果,转换为一个物理的视图文件-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--指定视图文件所在路径-->
<property name="prefix" value="/WEB-INF/views/"/>
<!--配置视图文件的技术-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
HelloControlle.java
package com.bdit.controlle;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @Package: com.bdit.controlle
* @ClassName: HelloControlle
* @Author: Ling
* @Date: 2020/11/19 10:49
* @Description:
*/
@Controller
@RequestMapping(value = {"/user"}) // 根标签,访问时必须是 user/hellow
public class HelloControlle {
//value:通过url(hello)访问
//method:确定访问方式,RequestMethod.POST 确定访问类型是 post(访问类型不是post则爆405)
//params:数据、参数,里面的数据必须出现在url地址中
@RequestMapping(value = {"/hello"},method = RequestMethod.GET,params = {"name=aa","pass!=100"})
public String welcom(){
System.out.println("======================= 用户访问信息到达,接收成功 =======================");
return "successon";//指访问的jsp等
}
}
successon.jsp
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/19
Time: 11:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>successon</title>
</head>
<body>
<h3>successon(在HellcControlle.class中return)</h3>
</body>
</html>
index.jsp
<html>
<body>
<h2>Hello World!</h2>
<a href="user/hello?name=aa&pass!=100">HelloWold</a>
</body>
</html>
12.7 MVC命名空间引入
命名空间:xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
12.8 组件扫描
SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中, 如果使用@Controller注解标注的话,就需要使用进行组件扫描。
12.9 SpringMVC的xml配置解析
SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文 件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图 解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
可以看到该解析器的默认设置,如下:
REDIRECT_URL_PREFIX = "redirect:" --重定向前缀
FORWARD_URL_PREFIX = "forward:" --转发前缀(默认值)
prefix = ""; --视图名称前缀
suffix = ""; --视图名称后缀
视图解析器 我们可以通过属性注入的方式修改视图的的前后缀
<!--配置内部资源视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
12.10 SpringMVC的相关组件、SpringMVC的注解和配置
SpringMVC的相关组件
- 前端控制器:DispatcherServlet
- 处理器映射器:HandlerMapping
- 处理器适配器:HandlerAdapter
- 处理器:Handler
- 视图解析器:View Resolver
- 视图:View
SpringMVC的注解和配置
- 请求映射注解:@RequestMapping
- 视图解析器配置:
- REDIRECT_URL_PREFIX = “redirect:”
- FORWARD_URL_PREFIX = “forward:”
- prefix = “”;
- suffix = “”;
13、SpringMVC的请求和响应
13.1 SpringMVC的数据响应方式
- 页面跳转
- 直接返回字符串
- 返回ModelAndView对象
- 回写数据
- 直接返回字符串
- 返回对象或集合
13.2 基本配置
- spring-mvc.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<context:component-scan base-package="com.bdit"/>
<!--配置视图解析器:如何将控制器返回的字符串结果,转换为一个物理的视图文件-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 数据响应 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</list>
</property>
</bean>
<!--在方法上添加@ResponseBody就可以返回JSON格式的字符串,但这样配置比较麻烦,配置的代码比较多,因此 ,我们可以使用mvc的注解驱动代替上述配置
-->
<mvc:annotation-driven/>
<!--放行静态资源
mapping表示的是映射路径或规则
location标识目录的路径
-->
<!-- <mvc:resources mapping="/images/**" location="/images/"/>-->
<!-- <mvc:resources mapping="/css/**" location="/css/"/>-->
<!-- <mvc:resources mapping="/js/**" location="/js/"/>-->
<!-- <mvc:default-servlet-handler/>-->
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
13.3 数据响应
数据响应-------页面跳转
package com.bdit.controller;
import com.bdit.pojo.Account;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Package: com.bdit.controller
* @ClassName: ControllerTwo
* @Author: Ling
* @Date: 2020/11/19 16:24
* @Description:
*/
@Controller
@RequestMapping(value = {"/conTwo"})
public class ControllerTwo {
//数据响应---跳转页面
@RequestMapping(value = {"/controller01"})
public ModelAndView controller01(){
//封装响应数据和视图
ModelAndView mav = new ModelAndView();
//相应的数据,默认放在请求域中
mav.addObject("accountName","甲一");
mav.addObject("accountName","甲二");
//设置视图名
mav.setViewName("controllers");
return mav;
}
//数据响应---跳转页面
@RequestMapping(value = "/controller02")
public ModelAndView controller02(ModelAndView mav){
mav.addObject("accountName","甲三");
mav.setViewName("controllers");
return mav;
}
//数据响应---跳转页面
@RequestMapping("/controller03")
public String controller03(Model model){
model.addAttribute("accountName","model");
return "controllers";
}
//数据响应---跳转页面
@RequestMapping(value = "/controller04")
public String controller04(HttpServletRequest httpServletRequest){
//响应的数据放在请求域中
httpServletRequest.setAttribute("accountName","甲四");
return "controllers";
}
}
controllers.jsp
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/19
Time: 17:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Controller</title>
</head>
<body>
<h3>Controller----succeed</h3>
${accountName}
</body>
</html>
数据响应—回写数据
ControllerTwo.java
//数据响应---回写数据
@RequestMapping(value = "/controller05")
@ResponseBody
public String controller05(){
return "甲五";
}
//数据响应---回写数据
@RequestMapping(value = "/controller06")
@ResponseBody
public void controller06(HttpServletResponse resp) throws IOException {
resp.getWriter().print("06-----甲六");
}
//数据响应---回写数据-直接回写JSON格式字符串
@RequestMapping("/controller07")
@ResponseBody
public String controller07() throws JsonProcessingException {
Account account = new Account();
account.setAid(1001);
account.setName("甲七");
account.setMoney(100.0);
ObjectMapper obj = new ObjectMapper();
String json = obj.writeValueAsString(account);
return json;
}
@RequestMapping("/controller07_1")
public String controller07_1(HttpServletRequest httpServletRequest) throws JsonProcessingException {
Account account = new Account();
account.setAid(1001);
account.setName("甲七");
account.setMoney(100.0);
ObjectMapper obj = new ObjectMapper();
String json = obj.writeValueAsString(account);
httpServletRequest.setAttribute("accountName",json);
return "jsontwo";
}
//数据响应--回写数据--返回对象或集合(需在 spring-mvc.xml声明)
@RequestMapping("/controller08")
@ResponseBody
public Account controller08(){
Account account = new Account();
account.setAid(1002);
account.setName("甲八");
account.setMoney(96.36);
return account;
}
@RequestMapping(value = "/controller09")
@ResponseBody
public List<String> controller09(){
List<String> list = new ArrayList<>();
list.add("一一");
list.add("二二");
list.add("三三");
list.add("四四");
return list;
}
@RequestMapping(value = "/controller10")
@ResponseBody
public List<Account> controller10(){
List<Account> accountList = new ArrayList<>();
Account account = new Account();
account.setAid(1003);
account.setName("五五");
account.setMoney(153.36);
accountList.add(account);
Account account1 = new Account();
account1.setAid(1004);
account1.setName("六六");
account1.setMoney(963.36);
accountList.add(account1);
return accountList;
}
}
index.jsp
<a href="contro01">controller</a><hr/>
<a href="conTwo/controller01">ModelAndView01--null</a><hr/>
<a href="conTwo/controller02">ModelAndView02--ModelAndView</a><hr/>
<a href="conTwo/controller03">ModelAndView03--model</a><hr/>
<a href="conTwo/controller04">ModelAndView04--HttpServletRequest</a><hr/>
<a href="conTwo/controller05">ModelAndView05--xiangying--null</a><hr/>
<a href="conTwo/controller06">ModelAndView06--xiangying-HttpServletResponse</a><hr/>
<a href="conTwo/controller07">ModelAndView07--xiangying--json</a><hr/>
<a href="conTwo/controller07_1">ModelAndView07_1--xiangying--json</a><hr/>
<a href="conTwo/controller08">ModelAndView08--xiangying--duixiang/jihe</a><hr/>
<a href="conTwo/controller09">ModelAndView09--xiangying--duixiang/jihe</a><hr/>
<a href="conTwo/controller10">ModelAndView10--xiangying--duixiang/jihe</a><hr/>
controller.jsp
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/19
Time: 17:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Controller</title>
</head>
<body>
<h3>Controller----succeed</h3>
${accountName}
</body>
</html>
jsontwo.jsp
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/19
Time: 21:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>jsontwo</title>
</head>
<body>
<h3>jsontwo</h3>
${json}
</body>
</html>
13.4 数据请求
客户端请求参数的格式是:name=value&name=value…
服务器端要获得请求的参数,有时还需要对数据进行封装,
SpringMVC可以接收如下类型的参数: 基本类型参数 POJO类型参数 数组类型参数 集合类型参数
Controller中方法的参数名称要和请求参数的name一致,参数值会自动映射匹配,并且能够自动做类型 转换。 自动类型转换是指从String向其它类型转换
ContorllerThree.java
package com.bdit.controller;
import com.bdit.pojo.Account;
import com.bdit.pojo.Vo;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Arrays;
import java.util.List;
/**
* @Package: com.bdit.controller
* @ClassName: ContorllerThree
* @Author: Ling
* @Date: 2020/11/19 20:26
* @Description:
*/
@Controller
@RequestMapping(value = {"/conThree"})
public class ContorllerThree {
//SpringMVC的请求--获得请求参数--基本类型参数
@RequestMapping(value = "/contorller01")
public String contorller01(String name,int age){
System.out.println("姓名:"+name+" 年龄:"+age);
return "qingqiu";
}
//SpringMVC的请求--获得请求参数--pojo类型参数
//Controller中方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配
@RequestMapping(value = "/contorller02")
public String contorller02(Account account){
System.out.println(account);
return "qingqiu";
}
//SpringMVC的请求--获得请求参数--数组类型参数
@RequestMapping(value = "/contorller03")
public String contorller03(String[] str){
System.out.println(str.toString()+" yi");
System.out.println(Arrays.asList(str) +" er");
return "qingqiu";
}
// SpringMVC的请求--获得请求参数--集合类型参数
//获得集合参数时,需要将集合参数包装到一个POJO中才可以,method = RequestMethod.POST
@RequestMapping(value = "/contorller04")
@ResponseBody
public void contorller04(Vo vo){
List<Account> list = vo.getList();
for (Account a : list){
System.out.println(a);
}
// return "qingqiu";
}
}
Vo.java
package com.bdit.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Package: com.bdit.pojo
* @ClassName: Vo
* @Author: Ling
* @Date: 2020/11/19 21:24
* @Description:
*/
@Data
public class Vo implements Serializable {
private List<Account> list;
}
qingqiu.jsp
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/19
Time: 20:34
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>qingqiu</title>
</head>
<body>
<h3>请求页面</h3>
</body>
</html>
index.jsp
<a href="conThree/contorller01?name=conThree01&age=20">qingqiu----jibencanshu</a><hr/>
<a href="conThree/contorller02?aid=1005&name=乙一&money=12.36">qingqiu----json</a><hr/>
<a href="conThree/contorller03?str=AA&str=BB">qingqiu----shuzhu</a>
<form action="conThree/contorller04" method="get">
<input type="text" name="list[0].aid"><br/>
<input type="text" name="list[0].name"><br/>
<input type="text" name="list[0].money"><br/>
<input type="text" name="list[1].aid"><br/>
<input type="text" name="list[1].name"><br/>
<input type="text" name="list[1].money"><br/>
<input type="submit" value="提交">
</form>
13.5SpringMVC请求—静态资源访问的开启
当有静态资源需要加载时,比如jquery文件,图片、css等,默认情况下是无法加载的。因为SpringMVC 的前端控制器DispatcherServlet的url-parttern配置的是/,代表对所有的资源进行过滤操作,我们可以 通过两种方式指定放行静态资源: 在spring-mvc.xml配置文件中指定放行的资源
<!--放行静态资源
mapping表示的是映射路径或规则
location标识目录的路径
-->
<!-- <mvc:resources mapping="/images/**" location="/images/"/>-->
<!-- <mvc:resources mapping="/css/**" location="/css/"/>-->
<!-- <mvc:resources mapping="/js/**" location="/js/"/>-->
<mvc:default-servlet-handler/>
14、 SpringMVC重定向和请求转发的字符串
- 返回值String类型,同过springmvc框架使用重定向页面跳转(数据会丢失,依然使用servlet中的重定向)
- 语法格式:redirect:请求的地址
- 返回值为String类型,通过springmvc框架实现请求转发,数据不会丢失
- forward:请求的地址
- @RequestParam注解,当前请求中的参数名和方法中的参数名不一致时,可以通过@RequestParam 注解进行参数绑定
@Controller
@RequestMapping(value = "/account")
public class resultTes1 {
/**
* redirect 重定向,只能定向到方法上不能到jsp,会丢失数据(仍是servlect的重定向方法)
* @return
*/
@RequestMapping(value = "/result")
public String resultRedirect(ModelMap modelMap){
System.out.println("========================resultRedirect=====================");
modelMap.addAttribute("date",new Date());
return "redirect:/account/cdx";
}
@RequestMapping(value = "/cdx")
public String cdx(ModelMap modelMap){
System.out.println("====================zf====================");
Date date=(Date) modelMap.getAttribute("date");
System.out.println(date);
return "success";
}
/**
* forward 请求转发,数据不会丢失
* @return
*/
@RequestMapping(value = "/forward")
public String resultForward(HttpServletRequest request){
request.setAttribute("data","aa");
System.out.println("====================forward===============");
return "forward:/account/zf";
}
@RequestMapping(value = "/zf")
public String zf(HttpServletRequest request){
System.out.println("===========zf===========");
String data = (String) request.getAttribute("data");
System.out.println(data);
return "success";
}
/**
* @ResquestParam 代表的是:当前请求中的参数名和方法中的参数名不一致时,可以通过@RequestParam注解进行参数绑定
* @param bid 方法属性中的,对应javabean中的aid
* @param namee 方法属性中的,对应javabean中的 name
* @param moneyss 方法属性中的,对应javabean中的 money
* @return
*/
@RequestMapping(value = "/param")
public String param(@RequestParam("aid") int bid,@RequestParam("name") String namee,@RequestParam("money") String moneyss){
return "success";
}
indes.jsp
<a href="account/result"> 重定向 </a>
<a href="account/forward"> 请求转发 </a>
14.1 JSON格式数据实现AJAX交互
JSON数据是企业开放中数据交换最常用的一种方式,它是轻量级,格式比较清晰。
交互:前端和后端的互动
- 前端传递JSON字符串到后台,后台如何能够自动转换为pojo对象。
- @RequestBody注解,将JSON字符串转换为POJO对象
- 用于获取请求(消息)体的内容,不适用于GET请求方式
- 后台return对象,可以通过@ResponseBody注解,将后台的对象转换为JSON格式的字符串。
代码演示,页面
<%@page pageEncoding="UTF-8" contentType="text/html; UTF-8" %>
<html>
<head>
<title>vue+axios</title>
</head>
<body>
<h2>SpringMVC框架和AJAX进行交互 JSON</h2>
<div id="app">
<input type="button" @click="clickAccount()" value="发送json数据">
{{accoutList}}
</div>
</body>
</html>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<script>
new Vue({
el:'#app', data:{
accoutList:[]
},
methods:{
clickAccount:function () {
var params={id:1001,name:'小明',money:666.66};
//发送ajax axios.post('ajax/textAjax',params).then(response=>{
//成功this.accoutList=response.data; console.log(response.data);
}).catch(error=>{ console.log(error);
});
}
}
});
</script>
Controller
package com.bdit.controller;
import com.bdit.pojo.Account;
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;
import java.util.ArrayList; import java.util.List;
/**
* @RestController:相当于在类上标记了@Controller,和相当于在每个处理器方法上标记了@ResponseBody
* @RestController和@Controller就是多了@ResponseBody
* ClassName: AjaxJsonController
* Package: com.bdit.controller
* Description: TODO
* author: BDIT
* date: 2020/11/23 11:12
* Version 1.0
*/ @RestController
@RequestMapping(value = {"/ajax"}) public class AjaxJsonController {
/**
* @RequestBody
* @param account
* @return
*/
@RequestMapping(value = {"/textAjax"})
public Account testAjax(@RequestBody Account account){ System.out.println("account====>"+account); List<Account> list=new ArrayList<>();
Account account1=new Account(); account1.setId(1002);
account1.setName("哈哈哈1");
account1.setMoney(11111.3);
Account account2=new Account(); account2.setId(1003); account2.setName("哈哈哈2"); account2.setMoney(12221.3);
Account account3=new Account(); account3.setId(1004); account3.setName("哈哈哈4"); account3.setMoney(1441111.3); list.add(account1); list.add(account2); list.add(account3);
return account1;
}
}
14.2 REST和RESTful
REST(Representational State Transfer):表现层状态转移,是一种软件架构风格。REST并没有一个明确的标准,而更像是一种设计的风格。它本身并没有什么实用性,其核心价值在于如何设计出符合REST风格的API。
- 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一 张图片、一首歌曲、一种服务、总之就是一个就具体的存在,可以用一个URI(统一资源定位符)指向 它,每种资源对应一个特定的URI,要获取这个资源,访问它的URI就可以,因此URI即为每个资源 的独一无二的识别符。
- 表现层(Representational):把资源具体呈现出来的形式,叫做它的表现层。 比如:文本可以用txt 格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式。
- 状态转化 (State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。 HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必 须通过某种手段,让服务器端发生“状态转化”,而这种转化是建立在表现层之上的,所以就是“表现 层状态转化”。具体说,就是HTTP协议里面,四个表示操作方式的动词,GET、POST、PUT、
DELETE。他们分别对应四种基本操作,GET用来获取资源,POST用来新建资源,PUT用来更新资 源,DELETE用来删除资源。
【RESTful就是一个资源定位及资源操作的风格,不是标准也不是协议,只是一种风格,基于这个风格设 计的软件可以更加简洁、更有层次】
14.3 使用RESTful操作资源
先定义对象
http://localhost:8080/springmvc/account/1 操作对象account,查询GET
http://localhost:8080/springmvc/account 新增 POST
http://localhost:8080/springmvc/account 更新 PUT
http://localhost:8080/springmvc/account/1 删除 DELETE
14.4 传统方式操作资源
操作啥(原来URL)?操作谁(传入的参数)
url中先定义动作,然后传递的参数表明这个动作操作的是哪个对象
先定位动作,然后定位对象
http://localhost:8080/springmvc/account/findById?id=1 查询
http://localhost:8080/springmvc/account/saveAccount 新增
http://localhost:8080/springmvc/account/updateAccount 更新
http://localhost:8080/springmvc/account/delete?id=1 删除
14.5 HiddenHttpMethodFilter过滤器
由于浏览器form表单只支持GET与POST请求。而DELETE,PUT等method并不支持,Spring3.0之后添 加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给处理器的方法,使得支持GET、POST、PUT与DELETE请求。
package com.bdit.controller;
import com.bdit.pojo.Account;
import org.springframework.web.bind.annotation.*;
/**
* @Package: com.bdit.controller
* @ClassName: Aone
* @Author: Ling
* @Date: 2020/11/23 15:14
* @Description:
*/
@RestController
public class Aone {
@RequestMapping(value = {"/account/{id}"},method = RequestMethod.GET)
public String gett(@PathVariable("id")Integer id){
System.out.println("GET================="+id);
return "info";
}
@RequestMapping(value = {"/account/{id}"},method = RequestMethod.DELETE)
public String deflect(@PathVariable("id") Integer integer){
System.out.println(integer+"========删除============");
return "info";
}
@RequestMapping(value = {"/account"},method = RequestMethod.PUT)
public String findById(Account account){
System.out.println(account+"========修改============");
return "info";
}
@RequestMapping(value = {"/account"},method = RequestMethod.POST)
public String query(Account account){
System.out.println(account+"========查询============");
return "info";
}
}
<%--
Created by IntelliJ IDEA.
User: Ling
Date: 2020/11/15
Time: 11:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h2>Hello World!</h2>
<a href="${pageContext.request.contextPath}/account/1">GET</a>
<form action="${pageContext.request.contextPath}/account/1" method="post">
<!--
1、该表单的请求方式还必须是post
2、添加一个隐藏域,属性名是_method,值:put或者delete
3、form表单只支持get和post请求,所有put和delete请求的处理器方法必须标记@ResponseBody或者在类上标记@RestController
-->
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="DELETE--删除">
</form>
<form action="${pageContext.request.contextPath}/account" method="post">
<!--
1、该表单的请求方式还必须是post
2、添加一个隐藏域,属性名是_method,值:put或者delete
3、form表单只支持get和post请求,所有put和delete请求的处理器方法必须标记@ResponseBody或者在类上标记@RestController
-->
账户名:<input type="text" name="name"><br/>
money:<input type="text" name="money"><br/>
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="Put请求--更新">
</form>
<form action="${pageContext.request.contextPath}/account" method="post">
<!--
1、该表单的请求方式还必须是post
2、添加一个隐藏域,属性名是_method,值:put或者delete
3、form表单只支持get和post请求,所有put和delete请求的处理器方法必须标记@ResponseBody或者在类上标记@RestController
-->
账户名:<input type="text" name="name"><br/>
money:<input type="text" name="money"><br/>
<input type="submit" value="POST请求--查询">
</form>
</body>
</html>
15、SpringMVC实现文件上传
15.1 SpringMVC实现本地的上传
- 注意:
- from表单的请求方式必须是 post
- 添加from表单参数 enctype(enctype=“multipart/from-data”)
- 添加依赖:commons-fileupload,commons-io
本地上传
- 引入依赖
<!--本地上传依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- spring-mvc.xml配置文件上传解析器和相关参数
<!-- 配置本地上传的解析器,其id必须是 multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传文件的一些参数,例如:最大文件限制-->
<property name="maxUploadSize">
<!-- 限制最大上传大小 -->
<value>5242880</value>
</property>
</bean>
- 页面配置
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/load" method="post">
<p>姓名:<input type="text" name="name"></p>
<p>文件:<input type="file" name="multipartFile"></p>
<p><input type="submit" value="提交"></p>
</for
- Controller
package com.bdit.file;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* @Package: com.bdit.file
* @ClassName: Fileuplode
* @Author: Ling
* @Date: 2020/11/24 10:53
* @Description:本地上传
*/
@Controller
public class Fileuplode {
/**
* 本地上传
* @return
*/
@RequestMapping(value = "/load")
public String uplode(MultipartFile multipartFile, HttpServletRequest request){
//通过UUID来编写新的文件名称
String uuid = UUID.randomUUID().toString();
System.out.println(uuid);
//获取文件名称
String filename = multipartFile.getOriginalFilename();
System.out.println(filename);
//获取上传路径
String realPath = request.getSession().getServletContext().getRealPath(request.getContextPath()+"/upload/");
//创建一个file对象
File file = new File(realPath,uuid+filename);
//实现上传
try{
multipartFile.transferTo(file);
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
}
15.2 跨服上传
- 引入依赖
<!--跨服上传依赖-->
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-core -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.19.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-client -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19.4</version>
</dependency>
-
可以通过修改Tomcat配置等来实现跨服
-
Tomcat默认不能跨服
- 在 tomcat/conf/web.xml 中,找到 defaultServlet配置添加
<init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param>
-
增加一个tomcat并且更改其端口号和本地地址
- 创建一个web项目,并在其webapp目录下创建一个upload目录,在upload目录下创建一个新的文件,否则,这个upload目录则不会编译
-
-
Controller代码
package com.bdit.file;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
/**
* @Package: com.bdit.file
* @ClassName: FileRealm
* @Author: Ling
* @Date: 2020/11/24 12:02
* @Description:跨服上传
*/
@Controller
public class FileRealm {
@RequestMapping(value = "/realm")
public String realm(MultipartFile multipartFile, HttpServletRequest request){
//32位随机数 .replace("-"," ")
String uuid = UUID.randomUUID().toString();
//文件名称
String filename = multipartFile.getOriginalFilename();
//获取文件的后缀
String str = filename.substring(filename.lastIndexOf("."));
//跨服上传的地址
String serverUrl = "http://localhost:8081/springmvc_file_02/upload";
Client client = Client.create();
WebResource resource = client.resource(serverUrl+"/"+uuid+str);
try{
resource.put(String.class,multipartFile.getBytes());
}catch (IOException io){
io.printStackTrace();
}
return "success";
}
}
16、SpringMVC中异常的统一处理
- 需要先定义一个异常解析器
package com.bdit.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Package: com.bdit.exception
* @ClassName: MyHandlerExceptionResolver
* @Author: Ling
* @Date: 2020/11/24 11:53
* @Description:SpringMVC中异常的统一处理,需要在spring-mvc.xml中配置
*/
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
/**
* @param httpServletRequest:请求对象
* @param httpServletResponse:响应对象
* @param o:处理器
* @param e:异常
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
e.printStackTrace();
ModelAndView mav = new ModelAndView();
mav.addObject("yichang","系统出错"+e.getMessage());
mav.setViewName("error");
return mav;
}
}
- 在spring-mvc.xml配置
<!--配置异常处理器-->
<bean class="com.bdit.exception.MyHandlerExceptionResolver"></bean>
17、SpringMVC拦截器
17.1 拦截器简介
- Servlet:处理Request请求和Response响应
- 过滤器(Filter):对Request请求起到过滤的作用,在请求到达Servlet之前,如果配置为 /*可以对所有的资源进行过滤处理(包括servlet、js/css等静态资源)
- 监听器(Listener):它是服务器组件,它随着web应用启动而启动,只初始化一次,然后会一直运行监视,随着web应用的停止而销毁
- 拦截器(Interceptor):是SpringMVC、Struts等表现出框架自己的,不会拦截jsp、html、css、images、js的访问,只会拦截访问的控制器方法(Handler),底层采用的是AOP思想
- 从配置的角度发现:servlet、filter、listener、是配置在web.xml中的,而interceptor是配置在表现层框架自己的配置文件中的
- 在Handler业务方法执行之前拦截一次
- 在Handler方法执行完毕后但是为跳转页面之前拦截一次
- 在跳转之后的页面又拦截一次
17.2 自定义拦截器
- 实现接口 HandlerInterceptor
- 重写方法
- preHandle:handler方法执行之前执行,返回true表示放行
- prostHandle:handler方法执行完毕但是还没有跳转页面
- afterCompletion:返回页面之后,响应完毕之后执行
BlockerHhelp.java
package com.bdit.blocker;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Package: com.bdit.blocker
* @ClassName: BlockerHhelp
* @Author: Ling
* @Date: 2020/11/24 15:13
* @Description:拦截器,需要在spring-mvc.xml中配置拦截器
*/
public class BlockerHhelp implements HandlerInterceptor {
/**
* 作用:请求到达拦截器,返回为true就放行,返回为false就拦截
* @return
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=================请求到达拦截器1======================");
return true;
}
/**
* 作用:Handler方法执行完成,但是还未进行页面跳转
* @return
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("================== 拦截器1 在Handler方法执行完成,但是还未进行页面跳转==================");
}
/**
* 作用:方法执行完成,响应执行完毕,进行一些垃圾回收等操作
* @return
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("================== 拦截器1 响应执行完毕====================");
}
}
spring-mvc.xml
<mvc:interceptors>
<mvc:interceptor>
<!–拦截当前目录下的所有子目录–>
<mvc:mapping path="/**"></mvc:mapping>
<bean class="com.bdit.blocker.BlockerHhelp"/>
</mvc:interceptor>
</mvc:interceptors>
index.jsp
<a href="${pageContext.request.contextPath}/blockquote">拦截器</a>
17.3 拦截链
拦截器链中如果有拦截器因为异常或者其它原因中断时,整个链中的拦截器的postHandle都不会 执行。
拦截器链执行时,拦截器正常流程 preHandle顺序执行 postHandle倒序执行 afterCompletion倒序执行
17.4 拦截器登录案例
- 需求
- 有一个登录页面,写一个Handler用于接收登录请求,实现页面跳转
- 登录页面有一个提交表单的动作,需要在Controller处理
- 判断用户名和密码是否正确 如果正确,向session中写入用户信息,跳转到登录成功页面
- 如果不正确,跳转到登录页面
- 拦截器
- 拦截用户请求,判断用户是否登录(登录页面跳转请求和登录提交请求不能拦截)
- 如果用户已经登录则放行 如果用户为登录则跳转到登录页面
spring-mvc.xml
<!--配置案例拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.bdit.blocker.AnliLanJieQi"/>
</mvc:interceptor>
</mvc:interceptors>
index.jsp
<p>拦截器登录案例</p>
<a href="${pageContext.request.contextPath}/findall/queryAll">查看所有登陆成功</a>
<form action="${pageContext.request.contextPath}/save">
<p>姓名:<input type="text" name="name"></p>
<p>密码:<input type="password" name="password"></p>
<p>登录:<input type="submit" value="登录"></p>
</form>
BlockerAnli.java
package com.bdit.blocker.anli;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @Package: com.bdit.blocker.anli
* @ClassName: BlockerAnli
* @Author: Ling
* @Date: 2020/11/24 15:41
* @Description:拦截器登录案例
*/
@Controller
public class BlockerAnli {
@RequestMapping(value = "/save")
public String save(String name, String password, HttpServletRequest request){
if ("jiayi".equals(name) && "1234".equals(password)){
request.getSession().setAttribute("name",name);
return "redirect:/findall/queryAll";
}else{
return "redirect:/index.jsp";
}
}
}
FindAll.java
package com.bdit.blocker.anli;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Date;
/**
* @Package: com.bdit.blocker.anli
* @ClassName: FindAll
* @Author: Ling
* @Date: 2020/11/24 15:46
* @Description:
*/
@Controller
@RequestMapping(value = "/findall")
public class FindAll {
@RequestMapping(value = "/queryAll")
public String queryAll(Model model){
model.addAttribute("model",new Date());
return "lanjieqi";
}
}
AnliLanJieQi.java
package com.bdit.blocker;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Package: com.bdit.blocker
* @ClassName: AnliLanJieQi
* @Author: Ling
* @Date: 2020/11/24 15:56
* @Description:
*/
public class AnliLanJieQi implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求的URL
String url = request.getRequestURI();
System.out.println("===拦截器中获取的请求的URL="+url);
//判断是否是请求登录,如果是就放行
if ("/springmvc_dom05_file/save".equals(url)){
return true;
}
//如果不是请求登录,则利用Session判断是否存储了用户名
Object obj = request.getSession().getAttribute("name");
if (obj!=null){
//如果有,就放行
return true;
}else{
//如果没有登录,跳转到登录页面
response.sendRedirect("/springmvc_dom05_file/");
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("================== 拦截器1 响应执行完毕====================");
}
}
spring-mvc.xml
<mvc:interceptors>
<mvc:interceptor>
<!–拦截当前目录下的所有子目录–>
<mvc:mapping path="/**"></mvc:mapping>
<bean class="com.bdit.blocker.BlockerHhelp"/>
</mvc:interceptor>
</mvc:interceptors>
index.jsp
<a href="${pageContext.request.contextPath}/blockquote">拦截器</a>
17.3 拦截链
拦截器链中如果有拦截器因为异常或者其它原因中断时,整个链中的拦截器的postHandle都不会 执行。
拦截器链执行时,拦截器正常流程 preHandle顺序执行 postHandle倒序执行 afterCompletion倒序执行
17.4 拦截器登录案例
- 需求
- 有一个登录页面,写一个Handler用于接收登录请求,实现页面跳转
- 登录页面有一个提交表单的动作,需要在Controller处理
- 判断用户名和密码是否正确 如果正确,向session中写入用户信息,跳转到登录成功页面
- 如果不正确,跳转到登录页面
- 拦截器
- 拦截用户请求,判断用户是否登录(登录页面跳转请求和登录提交请求不能拦截)
- 如果用户已经登录则放行 如果用户为登录则跳转到登录页面
spring-mvc.xml
<!--配置案例拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.bdit.blocker.AnliLanJieQi"/>
</mvc:interceptor>
</mvc:interceptors>
index.jsp
<p>拦截器登录案例</p>
<a href="${pageContext.request.contextPath}/findall/queryAll">查看所有登陆成功</a>
<form action="${pageContext.request.contextPath}/save">
<p>姓名:<input type="text" name="name"></p>
<p>密码:<input type="password" name="password"></p>
<p>登录:<input type="submit" value="登录"></p>
</form>
BlockerAnli.java
package com.bdit.blocker.anli;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @Package: com.bdit.blocker.anli
* @ClassName: BlockerAnli
* @Author: Ling
* @Date: 2020/11/24 15:41
* @Description:拦截器登录案例
*/
@Controller
public class BlockerAnli {
@RequestMapping(value = "/save")
public String save(String name, String password, HttpServletRequest request){
if ("jiayi".equals(name) && "1234".equals(password)){
request.getSession().setAttribute("name",name);
return "redirect:/findall/queryAll";
}else{
return "redirect:/index.jsp";
}
}
}
FindAll.java
package com.bdit.blocker.anli;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Date;
/**
* @Package: com.bdit.blocker.anli
* @ClassName: FindAll
* @Author: Ling
* @Date: 2020/11/24 15:46
* @Description:
*/
@Controller
@RequestMapping(value = "/findall")
public class FindAll {
@RequestMapping(value = "/queryAll")
public String queryAll(Model model){
model.addAttribute("model",new Date());
return "lanjieqi";
}
}
AnliLanJieQi.java
package com.bdit.blocker;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Package: com.bdit.blocker
* @ClassName: AnliLanJieQi
* @Author: Ling
* @Date: 2020/11/24 15:56
* @Description:
*/
public class AnliLanJieQi implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求的URL
String url = request.getRequestURI();
System.out.println("===拦截器中获取的请求的URL="+url);
//判断是否是请求登录,如果是就放行
if ("/springmvc_dom05_file/save".equals(url)){
return true;
}
//如果不是请求登录,则利用Session判断是否存储了用户名
Object obj = request.getSession().getAttribute("name");
if (obj!=null){
//如果有,就放行
return true;
}else{
//如果没有登录,跳转到登录页面
response.sendRedirect("/springmvc_dom05_file/");
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}