==>Spring框架
传统编码方式,代码相互间依赖性高,耦合性高,移植性差。比如删除一个dao实现类,service到controller层都会报错。
体系结构:
1、test部分
2、Core container 核心容器
Bean:loC部分,如何创建对象,控制反转
Core:核心API
Context:上下文,读取配置文件等内容
spEL:表达式部分
3、Aop 面向切面编程
4、Aspects 切面
5、国际化部分 提供了各种语言支持的版本
6、message 消息部分
7、DataAccess 数据访问
transaction:集成了事务管理机制
orm框架:集成了mybatis、hibernate
JDBC框架:spring data
8、Web部分
SpringMVC框架、MVC框架
Servlet
一、 Beans 部分loC
一、JavaBean两种类型:
1、实体类:封装数据的JavaBean
满足如下要求:(1)提供公共的无参构造函数、(2)属性必须私有、(3)对私有属性封装成get及set方法。
2、封装业务的JavaBean serviceimpl
满足如下要求:(1)提供公共的无参构造函数、(2)提供公共的业务方法
二、自己创建Baen的缺点:耦合性强。
所以我们把对象的创建、管理的权限交给Spring框架管理——控制反转loC。
二、 创建Bean对象
【注】Spring 创建的 Bean 默认是单例模式
scope="prototype" 可以设定原型模式:每获取一次创建一个新的bean对象。
1 引入jar依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.16.RELEASE</version>
</dependency>
2 创建spring-context.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.lg.dao.impl.UserDaoImpl"></bean>
</beans>
3 通过ApplicationContext来获取Spring创建的Bean
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-context.xml");
//getBean中的名字要和xml中bean 的 id 一致
UserDao userDao=(UserDaoImpl) applicationContext.getBean("userDao");
2.1 通过Spring解耦
public class UserServiceImpl implements UserService {
private UserDao userDao=null;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public String addUser() {
System.out.println("userservice:调用........");
return userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ud" class="com.lg.dao.impl.UserDaoImpl"></bean>
<bean id="us" class="com.lg.service.impl.UserServiceImpl">
<!-- 给UserDaoServiceImpl的属性通过set方法赋值-->
<property name="userDao" ref="ud"></property>
</bean>
</beans>
id:称为对象的标识,不能重复,因为我们创建的对象都在同一容器中。
2.2 依赖注入方式 DI
在Spring中给属性赋值称为依赖注入—— DI。
方式一:set方法,设值注入;
<bean id="ud" class="com.lg.dao.impl.UserDaoImpl"></bean>
<bean id="us" class="com.lg.service.impl.UserServiceImpl">
<!--给UserDaoServiceImpl的属性userDao通过set方法赋值-->
<property name="userDao" ref="ud"></property>
</bean>
方式二:构造注入:通过构造函数进行注入。要求类必须有构造函数,以创建Student对象为例:
<bean id="lg" class="com.lg.entity.Stu">
<constructor-arg name="stuName" value="刘刚"></constructor-arg>
<constructor-arg name="stuAge" value="18"></constructor-arg>
</bean>
方式三:自动注入
<bean id="userDao" class="com.lg.dao.impl.UserDaoImpl"></bean>
<!--通过byName找到id名与实体类中属性名相同的bean,为实体类中的属性赋值-->
<bean id="userService" class="com.lg.service.impl.UserServiceImpl" autowire="byName"></bean>
2.3 Bean的创建时间
1、饿汉模式,在启用BeanFactory时就创建,默认是饿汉模式,提前创建好Bean,随用随取。
2、懒汉模式:启动BeanFactory时不创建bean,调用getBean() 方法时创建。
设置方式:
<beans default-lazy-init="true"> </beans>
2.4 Bean的作用域
1、默认单例模式
2、scope=“prototype” 原型模式;
五大作用域:singleton、Prototype、request、session、globalsession
2.5 Bean的不同类型属性的注入
<!-- 创建UserBean-->
<bean id="kb" class="com.lg.entity.User">
<property name="id" value="200101"></property>
<property name="pwd" value="123456"></property>
<property name="sex" value="男"></property>
<property name="age" value="20"></property>
<!-- 两种时间格式 推荐使用斜杠 -->
<!--<property name="birthday" value="2001-2-20"></property>-->
<property name="birthday" value="2001/2/20"></property>
<!-- 容器类型赋值-->
<!-- 数组-->
<property name="hobby">
<array>
<value>编程</value>
<value>打豆豆</value>
<value>打篮球</value>
</array>
</property>
<!-- set集合-->
<property name="phones">
<set>
<value>15840666666</value>
<value>15888888888</value>
<value>15888887777</value>
</set>
</property>
<!-- list集合-->
<property name="names">
<list>
<value>刘刚</value>
<value>全能刚</value>
</list>
</property>
<!-- map集合-->
<property name="countries">
<map>
<entry key="USA" value="美国"></entry>
<entry key="KR" value="韩国"></entry>
<entry key="RUSSIA" value="俄罗斯"></entry>
<!-- <entry key="book" value-ref="id"></entry>-->
</map>
</property>
<!-- Properties-->
<property name="files">
<props>
<prop key="username">root</prop>
<prop key="driverClass">com.mysql.JDBC.Driver</prop>
</props>
</property>
<!-- 对象集合 两种方式-->
<!-- 方式1-->
<property name="bookList">
<list>
<bean id="javaSE" class="com.lg.entity.Book">
<property name="isnb" value="java11"></property>
<property name="bookName" value="java从入门到放弃"></property>
</bean>
<bean id="db" class="com.lg.entity.Book">
<property name="isnb" value="java22"></property>
<property name="bookName" value="闪酷跑路"></property>
</bean>
</list>
</property>
<!-- 第二种方式 引入外部 bean 对象-->
<property name="bookList2">
<list>
<ref bean="javaSE"></ref>
</list>
</property>
</bean>
2.6 Bean的生命周期
【注】只有设值注入才走set方法,构造注入不走set方法。
多例无销毁方法,等待垃圾回收机制GC去销毁。单例模式销毁时,先去找是否实现接口的destroy方法,再去找XML中配置的 destroy-method方法。
设置生命周期的两种方式:
方式一:XML配置方式,在实体类中自定义初始化与销毁的方法,手动在xml中调用。
<bean id="lg" class="com.lg.entity.Stu" init-method="" destroy-method="">
<constructor-arg name="stuName" value="刘刚"></constructor-arg>
<constructor-arg name="stuAge" value="18"></constructor-arg>
</bean>
方式二:实体类中实现接口 implements InitializingBean, DisposableBean 两个接口
重写接口中的初始化(new的时候调用)、销毁方法(销毁的时候调用)
自动调用。
三、代理设计模式
作用:复杂的功能不让我们调用,只让我们调用简单的代理的方法。
3.1 静态代理模式
局限性:只能代理一种类型。
【案例】通过代理模式执行找房子方法
public interface HomeDao {
public Integer getHome();
}
public class HomeDaoImpl implements HomeDao {
public Integer getHome() {
System.out.println("找到了房子");
return 1;
}
}
代理类:
public class HomeDaoProxy implements HomeDao {
private HomeDao homeDao;
public HomeDao getHomeDao() {
return homeDao;
}
public void setHomeDao(HomeDao homeDao) {
this.homeDao = homeDao;
}
public Integer getHome() {
return homeDao.getHome();
}
}
测试:通过代理执行找房子方法。
public class TestProxy {
public static void main(String[] args) {
HomeDaoImpl homeDao=new HomeDaoImpl();
HomeDaoProxy proxy=new HomeDaoProxy();
proxy.setHomeDao(homeDao);
proxy.getHome();
}
}
3.2 动态代理模式
动态代理可以代理任何类型。
3.2.1 JDK动态代理
反射原理,需要实现接口。
public class DyProxy<T> implements InvocationHandler {
//声明属性,接收所代理的对象
private T target;
public T getTarget() {
return target;
}
public void setTarget(T target) {
this.target = target;
}
public DyProxy() {
}
public DyProxy(T target) {
this.target = target;
}
//执行invoke方法就是执行所要代理的方法
//proxy——所代理的对象
//method——代理所执行的方法
//args——方法参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o=null;
System.out.println("动态代理方法开始执行。。。");
try {
o=method.invoke(target, args);
} catch (Exception e) {
System.out.println("动态代理执行出现异常");
e.printStackTrace();
}finally {
System.out.println("动态代理执行结束");
}
return null;
}
}
public class TestProxy {
public static void main(String[] args) {
DeptDaoImpl deptDao = new DeptDaoImpl();
InvocationHandler invocationHandler = new DyProxy<DeptDao>(deptDao);
DeptDao dao = (DeptDao) Proxy.newProxyInstance(DeptDao.class.getClassLoader(), new Class[]{DeptDao.class}, invocationHandler);
dao.login();
}
}
3.2.2 cglib 动态代理
【原理】代理对象是被代理对象的子类
过滤器原理,无需实现任何接口,需要编写过滤器。
实现步骤:
1、添加jar依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
2、编写被代理类及方法
3、编写代理类(方法过滤器 implements MethodInterceptor)
public class CglibProxy implements MethodInterceptor {
//enhancer对象用来生成代理对象的子类对象、这样就能回调被代理对象的方法
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//继承代理对象,enhancer现在为被代理对象的子类
enhancer.setSuperclass(clazz);
//设置回调的方法,替代被代理对象回调这些方法
enhancer.setCallback(this);
//生成代理对象
return enhancer.create();
}
//过滤器方法、拦截被代理对象里的方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置执行");
//调用被代理对象的方法
Object result = methodProxy.invokeSuper(o, objects);
//执行之后
System.out.println("后置执行");
return result;
}
}
4、测试
public class TestCglib {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//得到代理对象
Student student = (Student) proxy.getProxy(Student.class);
//执行方法会被代理类中的过滤器拦截
student.sayHello();
}
}
3.3.3 JDK动态代理与cglib动态代理区别
spring框架同时使用了JDK动态代理及cglib动态代理。
1、原理上的区别:
JDK动态代理:JDK利用反射机制生成一个代理接口的匿名类
cglib动态代理:继承被代理对象,回调被代理对象中的方法
2 、使用上的区别:
目标对象实现了接口,默认使用JDK动态代理,也可以指定cglib动态代理。没有实现接口的话只能使用cglib动态代理。
3、效率上区别:
jdk8以上,JDK动态代理效率高。
jdk6以后,对jdk动态代理进行了优化,当调用次数少时,jdk动态代理高;大量调用时,cglib效率高。
【特别强调】cglib不能对final类、final修饰的方法进行代理。
因为final类不能被继承,final修饰的方法不能被重写。
四、Aop
4.1 概念
oop:面向对象编程
Aop: 面向切面编程:将那些多个类都用到的的公共业务进行封装,封装后的结果称为切面。
【Aop原理】内部由动态代理技术执行,当执行核心功能时,代理去执行 核心功能相关的业务功能。因为执行核 心功能不但包括方法,还包括异常处理、事务处理等业务。
A—— aspect 切面
O—— 面向
P—— 编程
【本质】Aop是对oop的一种提升。
连接点:把程序中的核心方法(切入点)和通用业务进行封装,封装成 JoinPoint 对象,代理会把此对象传入到通 用业务中,在通用方法中就可以获取核心方法相关信息。
切入点:Pointcut,就是核心方法。
通知/增强:在不同的时间点所执行的“公共业务”,称为通知或增强
1、在执行核心方法前所执行的公共业务,称为 前置增强。
2、在执行核心方法后所执行的公共业务,称为 后置增强。
3、在执行核心方法发生异常时所执行的公共业务,称为 异常处理增强。
4、在执行核心方法的 finally{ } 所执行的业务功能,称为 最终增强。
5、环绕增强,以上四种增强/通知的总称。
织入:将切入点与增强连接到一起,封装成连接点,这时会自动创建出动态代理对象,动态代理对象将会替我们执 行核心方法的配置好的一些业务(增强)。
4.2 springAop的使用步骤
【注】目标对象实现了接口,默认使用JDK动态代理,也可以指定cglib动态代理。没有实现接口的话只能使用cglib动态代理。
1、添加jar依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
2、编写公共业务类
public class DoException {
//首先声明日志记录对象
private final Logger logger = Logger.getLogger(DoException.class);
//1、异常处理增强:当核心方法发生异常时,应该调用执行的公共业务
public void catchException(JoinPoint joinPoint, RuntimeException e) {
System.out.println("发生异常,异常信息:" + e.getMessage());
//记录到日志中
//获取核心方法的类\核心方法名
logger.error("异常处理增强:" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法发生了异常,异常信息如下:"
+ e.getMessage());
}
//2、最终增强,执行finally{}时执行的方法
public void runFinally(JoinPoint joinPoint) {
logger.debug("最终增强:" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法执行结束");
}
}
public class Oper {
private final Logger logger = Logger.getLogger(Oper.class);
//前置增强
public void beforeOper(JoinPoint joinPoint) {
logger.debug("前置增强:执行了" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法" +
",方法参数列表如下:" + Arrays.toString(joinPoint.getArgs()));
}
//后置增强
public void afterOper(JoinPoint joinPoint, Object result) {
//System.out.println(1/0);
logger.debug("后置增强:" + joinPoint.getTarget() + "类中的" + joinPoint.getSignature() + "方法执行结束," +
"方法的返回值" + result);
}
}
3、在spring的配置文件中添加Aop相关的命名空间
4、配置文件中配置
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean id="dao" class="com.lg.dao.impl.UserDaoImpl"></bean>
<bean id="service" class="com.lg.service.impl.UserServiceImpl">
<property name="userDao" ref="dao"></property>
</bean>
<!--创建公共业务类的bean-->
<bean id="exception" class="com.lg.aop.MyExce"></bean>
<bean id="oper" class="com.lg.aop.MyOper"></bean>
<aop:config>
<!--创建切入点(也就是核心方法)-->
<aop:pointcut id="pointcut" expression="execution(public void deleteUser())"/>
<!--配置切面 第一个-->
<aop:aspect ref="oper">
<!--织入:将切入点与增强方法关联到一起,创建出连接点,这时会自动产生 动态代理 对象 -->
<!-- 配置前置增强 后置增强-->
<aop:before method="beforeEnhance" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="afterEnhance" pointcut-ref="pointcut"></aop:after-returning>
</aop:aspect>
<!--配置切面 第二个-->
<aop:aspect ref="exception">
<!--配置异常处理增强、最终增强-->
<aop:after-throwing method="exceEnhance" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
<aop:after method="finallyEnhance" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
5、测试
【注意】loC获取的bean对象必须用接口去接收,否则默认JDK的动态代理方法将会报错。
public class TestAop {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
UserService service = (UserService) applicationContext.getBean("us");
//UserServiceImpl service2 = (UserServiceImpl) applicationContext.getBean("us");
service.addUser();
}
}
4.3 环绕增强
环绕增强实现了前四种增强,不能与前四种一起用。
1、编写公共业务类
ProceedingJoinPoint 继承 JoinPoint 相当于对连接点再次封装
public class Around {
private final Logger logger = Logger.getLogger(Arround.class);
//环绕增强处理
public Object doArround(ProceedingJoinPoint joinPoint) {
//存放切入点返回值
Object result = null;
//编写前置增强代码
logger.debug("环绕增强-前置增强执行了:" + joinPoint.getTarget() + "中的" + joinPoint.getSignature() +
"方法,参数是:" + Arrays.toString(joinPoint.getArgs()));
//执行核心方法
try {
//执行核心方法,将核心方法从连接点中取出来执行
result = joinPoint.proceed();
logger.debug("环绕增强-后置增强:"+joinPoint.getTarget()+"方法执行结束,返回值为:"+result);
} catch (Throwable throwable) {
//环绕增强
logger.error("环绕增强-异常处理增强:"+joinPoint.getTarget()+"方法中发生异常,异常信息:"+throwable.getMessage());
throwable.printStackTrace();
}finally {
//最终增强
logger.debug("环绕增强-最终增强"+joinPoint.getSignature()+"执行结束。");
}
return result;
}
}
2、配置文件中配置切面
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean id="dao" class="com.lg.dao.impl.UserDaoImpl"></bean>
<bean id="service" class="com.lg.service.impl.UserServiceImpl">
<property name="userDao" ref="dao"></property>
</bean>
<!-- 创建公共业务类的bean-->
<bean id="around" class="com.lg.aop.Arround"></bean>
<aop:config>
<!-- ****************** 创建切入点 ——> 环绕增强-->
<aop:pointcut id="aroundPointcut" expression="execution(public String addUser())"/>
<!-- ************ 配置切面 环绕增强-->
<aop:aspect ref="around">
<aop:around method="doArround" pointcut-ref="aroundPointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
4.4 实现接口方式织入增强
1、实现前置增强接口
public class BeforeAdvice implements MethodBeforeAdvice {
//前置增强,无需在xml中配置织入增强,动态代理会自动识别执行
public void before(Method method, Object[] objects, Object o) throws Throwable {
}
}
2、xml 头部信息
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
"
4.5 通用配置切入点
通用方式语法:
execution( 访问修饰符 - 返回值类型 - 所在包和类(非必填)- 方法名 - 参数(非必填,只写参数类型))
例如:
(1)excution( * com.lg.aop.addUser() )—— 返回值类型不限*, com.lg.aop 包下的任何addUser()方法;
(2)excution(void com…*.addUser() )—— 访问修饰符任意的,返回值类型是void的,com包及其子包下的所有类,所有addUser() 方法;
(3)excution(* addUser(…))—— 访问修饰符任意,返回值任意,所有的addUser()方法,参数类型任意;
(4)excution(* addUser(…))—— 所有的addUser()方法,且方法参数任意。
【注】com.lg…* 表示 lg 包及其子包下的,所有类*
com.lg.* 表示 lg 包及下的所有类*
<aop:pointcut id="aroundPointcut2"
expression="execution(public * com.lg..*.addUser(..))"/>
五、spring与mybatis的整合
将一些原来配置到mybatis中的配置现在配置到spring-context.xml中
1、新建项目
2、添加jar依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
</dependencies>
3、改变映射文件存放位置
pom.xml中
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
spring-context.xml中 也需要设置Mapper.xml的存放位置,见第五步。
4、编写连接数据库的配置文件 database.properties
【注意】整合时,xml中不能用${username}来获取数据库名,
因为,在xml中${username}获取的是计算机的用户名。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/question_bank?useSSL=false&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
maxWait=5000
initialSize=2
maxActive=6
minIdle=2
5、创建spring的配置文件 spring-context.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 1、 导入数据库配置文件 classpath:指定在根目录resources-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 2、 配置druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 通过设值注入给属性赋值-->
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 配置初始化连接对象大小、最小活跃数、最大活跃数-->
<property name="initialSize" value="${initialSize}"></property>
<property name="minIdle" value="${minIdle}"></property>
<property name="maxActive" value="${maxActive}"></property>
<!-- 配置最大等待时间-->
<property name="maxWait" value="${maxWait}"></property>
<!-- 配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<!-- 配置一个连接在池中最小生存时间-->
<property name="minEvictableIdleTimeMillis" value="300000"></property>
</bean>
<!-- 3、 创建 SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引入连接池-->
<property name="dataSource" ref="dataSource"></property>
<!-- 告知接口对应的mapper映射文件所在位置-->
<property name="mapperLocations">
<list>
<value>classpath:com/lg/dao/impl/*.xml</value>
</list>
</property>
<!--配置实体类的 别名 -->
<property name="typeAliasesPackage" value="com.lg.entity"></property>
</bean>
<!--4、配置MapperScannerConfigurer 在dao中通过这些配置就能得到sqlSession-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
<!--<property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
<property name="basePackage" value="com.lg.dao"></property>
<!-- 配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<bean id="us" class="com.lg.service.impl.UserServiceImpl">
<!--这里的ref 写成dao的小写,因为程序已经为我们自动创建了id 为 userDao这个bean对象-->
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
【原理】(1)创建druid连接池;
(2)根据连接池创建出sqlSessionFactory对象
(3)配置MapperScannerConfigurer:将我们配置的sqlSessionFactory引入进来,然后自动调用sqlSessionFastory.openSession()方法创建出,sqlSesson对象;然后再配置告诉dao所在的包,检测出所有的dao接口,再自动调用sqlSession.getMapper方法,创建出所有的dao实现类的对象bean,这些bean的默认名字就是接口的小写,那个userDao就是自动创建出来的其中一个bean。
6、编写dao及Mapper.xml文件
我们所有的dao接口的实现类对象bean都不用通过sqlSession.getMapper( *Mapper.xml) 获取了,通过配置,spring框架已经给我们创建好放在内存中了。这些bean的id名字默认都是dao接口的小写。
如下面的userDao,就是自动创建好的一个放在内存中的bean对象。
<bean id="us" class="com.lg.service.impl.UserServiceImpl">
<!--这里的ref 写成dao的小写,因为程序已经为我们自动创建了id 为 userDao这个bean对象-->
<property name="userDao" ref="userDao"></property>
</bean>
7、编写service及实现类
六、事务
spring中默认集成了事务管理,每一个insert、delete、update都是一个独立的事务。
spring框架默认捕获的是runtimeException 如果spring框架默认捕获 CheckException 事务发生异常也会提交。解决方案:rollback-for=“Exception”
1、脏读:事务A读取了事务B改变了但是没有提交的数据,最终事务B回滚了;
2、不可重复读:事务A在多次读取的过程中,事务B对数据进行了更改提交,导致事务A多次读取的数据不一样
3、幻读:事务A将表中分数修改为具体的ABCDE级别,就在这时事务B又插入了一条数据,导致当事务A修改完成 后发现还有一条数据没有修改过来,就像发生幻觉一样。
6.1 事务的传播行为
6.2 事务的隔离级别
isolation,读取数据的问题。
【概念】一个事务与其他事务间的隔离程度,影响程度越小,隔离级别越高。
【作用】保证事务与事务间相互不影响,避免各种并发问题。
mySql默认隔离级别:可重复读。
6.3 XML配置事务步骤
这是 声明式事务!! 还有编程式事务 两种。
事务回滚时机:
(1)必须被拦截到—— 配置方式、注解方式;
(2)spring框架必须自动捕获到异常才会回滚 —— 如果发生异常被我们自己的catch捕获了,将不会被回滚;
如果被catch了,还要回滚的话需要在catch中发生异常;
最简单方式:不捕获异常;
根据场景自己定义异常,让事务回滚。
1、创建一个事务管理器DatasourceTransactionManager,为其绑定数据源。
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource属性-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2、配置事务通知
<!-- 6、 配置事务通知 管理某些方法 事务隔离级别 及 传播行为-->
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="insertUsers" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRES_NEW" read-only="false"/>
</tx:attributes>
</tx:advice>
3、配置切面:事务实质上是一种增强/通知
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(public * insertUsers(..))"/>
<aop:advisor advice-ref="txManager" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
完整代码:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" 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
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1、 导入数据库配置文件 classpath:指定在根目录resources-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 2、 配置druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 通过设值注入给属性赋值-->
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 配置初始化连接对象大小、最小活跃数、最大活跃数-->
<property name="initialSize" value="${initialSize}"></property>
<property name="minIdle" value="${minIdle}"></property>
<property name="maxActive" value="${maxActive}"></property>
<!-- 配置最大等待时间-->
<property name="maxWait" value="${maxWait}"></property>
<!-- 配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<!-- 配置一个连接在池中最小生存时间-->
<property name="minEvictableIdleTimeMillis" value="300000"></property>
</bean>
<!-- 3、 创建 SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引入连接池-->
<property name="dataSource" ref="dataSource"></property>
<!-- 告知接口对应的mapper映射文件所在位置 相当于mapper注册-->
<property name="mapperLocations">
<list>
<value>classpath:com/lg/dao/impl/*.xml</value>
</list>
</property>
<!-- 配置实体类的 别名 -->
<property name="typeAliasesPackage" value="com.lg.entity"></property>
</bean>
<!-- 4、配置MapperScannerConfigurer 通过配置能够获得所有dao接口的实现类对象bean-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
<!-- <property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
<property name="basePackage" value="com.lg.dao"></property>
<!-- 配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 5、 创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource属性-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 6、 配置事务通知 管理某些方法 事务隔离级别 及 传播行为-->
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="insertUsers" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRES_NEW"
read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 7、事务也是一种增强 使用事务需要配置切面-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(public * insertUsers(..))"/>
<aop:advisor advice-ref="txManager" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
<bean id="us" class="com.lg.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
七、注解配置
【注意】注解配置,需要在XML配置文件中告诉注解所要扫描的包;配置事务管理器;配置在注解中引入事务管理器。
类上方:
@service注解默认创建该service实体类的对象存放到内存中,默认对象名是实体类的小写字母
@Service(“usi”) 指定创建的对象名
@Component 通用
@Scope(“prototype”)多例模式
属性上方:
@Autowired注解应用在属性上方,按类型找属性对象并赋值
@Qualifier(“dd”) 一般与Autowired注解结合使用,首先按类型找,再按照属性名找
@value注入简单基本类型数据(8种基本+String)
事务:
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)————事务,可以加在类上、方法上
7.1 注解Ioc 、事务
@Service
//@Scope("prototype")
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class UserServiceImpl implements UserService {
@Autowired
// @Qualifier("dd")
private UserDao userDao;
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public Boolean insertUsers(List<User> userList) {
Integer temp = 0;
for (User user : userList) {
temp += userDao.insertUser(user);
}
if (temp == userList.size()) {
return true;
}
return false;
}
}
7.2 注解Aop
1、注解配置切面类
@Aspect //身份:切面
@Component //创建切面类的bean,当前类属于未知
public class MyAdvice {
@Pointcut("execution(public * com..*.*(..))")
public void p1(){}
@Before("p1()")
public void doBefore(JoinPoint joinPoint){
System.out.println("前置增强:"+joinPoint.getTarget()+"类的"+joinPoint.getSignature()+"方法执行。。。");
}
@AfterReturning(value = "p1()",returning = "returning")
public void doAfter(JoinPoint joinPoint,Object returning){
System.out.println("后置增强:"+joinPoint.getTarget()+"类的"+joinPoint.getSignature()+"方法执行,返回值:"+returning);
}
@AfterThrowing(value = "p1()",throwing = "ex")
public void doException(JoinPoint joinPoint,Exception ex){
System.out.println("发生异常,异常信息:"+ex.getMessage());
}
@After("p1()")
public void doFinally(JoinPoint joinPoint){
System.out.println("最终增强:"+joinPoint.getSignature()+"方法执行完毕");
}
}
7.3 总配置文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" 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
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1、 导入数据库配置文件 classpath:指定在根目录resources-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 2、 配置druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 通过设值注入给属性赋值-->
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 配置初始化连接对象大小、最小活跃数、最大活跃数-->
<property name="initialSize" value="${initialSize}"></property>
<property name="minIdle" value="${minIdle}"></property>
<property name="maxActive" value="${maxActive}"></property>
<!-- 配置最大等待时间-->
<property name="maxWait" value="${maxWait}"></property>
<!-- 配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<!-- 配置一个连接在池中最小生存时间-->
<property name="minEvictableIdleTimeMillis" value="300000"></property>
</bean>
<!-- 3、 创建 SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引入连接池-->
<property name="dataSource" ref="dataSource"></property>
<!-- 告知接口对应的mapper映射文件所在位置 相当于mapper注册-->
<property name="mapperLocations">
<list>
<value>classpath:com/dao/impl/*.xml</value>
</list>
</property>
<!-- 配置实体类的 别名 -->
<property name="typeAliasesPackage" value="com.entity"></property>
</bean>
<!-- 4、配置MapperScannerConfigurer 通过配置能够获得所有dao接口的实现类对象bean存放在内存中,这些bean的默认名是接口的小写-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
<!-- <property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
<property name="basePackage" value="com.dao"></property>
<!-- 配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 5、 创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource属性-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 6、类中事务引入事务管理器-->
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
<!-- 7、告诉哪些包下有被注解的类、属性、方法。-->
<context:component-scan base-package="com"></context:component-scan>
<!-- 8、启用注解 Aop,Aop默认使用XML配置方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
八、Spring-test & Junit
1、引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2、测试使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class TestSpringJunit {
@Autowired
private UserDao userDao;
@Test
public void m1(){
User user = userDao.selectUserByUserName("pg");
System.out.println(user);
}
}
==>SpringMVC 框架
M V C模式优点:
(1)低耦合,层次间不相互依赖,有利于程序变更;
(2)利于团队开发。
MVC框架都有哪些:Struts Struts2 springMVC
一、SpringMVC 开发流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOGSG2xx-1606655045684)(picture\7.png)]
1、引入jar依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
2、配置前端控制器
在web.xml中配置。
名字:DispatctherServlet,由SpringMVC提供;
功能:使用DispatctherServlet接收,拦截的所有网络请求;
(1)解析:请求路径中的方法名,对应着控制器的方法
(2)适配:响应结果所对应的视图
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置核心控制器Servlet-->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 核心控制器需要读取的配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 / 拦截所有请求,不包括jsp -->
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3、配置springMVC.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
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">
<!-- 配置Servlet扫描的父包,子包的类、方法、属性都有注解-->
<context:component-scan base-package="com.qf.controller"></context:component-scan>
<!-- 配置:mvc也开启使用注解实现-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置:视图解析器,-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 到根目录下找-->
<property name="prefix" value="/"></property>
<!-- 后缀为.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 在第一个拦截后,没找到,放行,再到tomcat默认的的servlet中,这个servlet帮我们找html资源-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
4、配置后端控制器
@Controller //1、身份是控制器,2、使用注解方式创建出该类对象
public class UserController {
//在方法上添加注解:请求路径所对应的方法
@RequestMapping("/login")
public String login() {
System.out.println("调用了登录方法");
return "login";
}
@RequestMapping("/register")
public String register(){
System.out.println("调用了注册方法");
return "register";
}
}
5、配置视图解析器
解析控制器返回的字符串所对应的视图位置
<!-- 配置:视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 到根目录下找-->
<property name="prefix" value="/"></property>
<!-- 后缀为.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
二、控制器获取表单数据
表单:
<form method="post" action="addUser2">
<p>编号:<input type="text" name="id"/></p>
<p>编号:<input type="text" name="username"/></p>
<p>编号:<input type="text" name="password"/></p>
<input type="submit" value="添加用户">
</form>
2.1 基本数据类型传参参数
【注意】形参字段名要与表单name属性一致!
复选框可以采用数组接收。
@RequestMapping("/addUser")
public String addUser(Integer id, String username, String password) {
System.out.println("id:" + id + ",username:" + username + ",password:" + password);
return "login";
}
2.2 对象类型传参
底层采用set方法注入
【注意】对象属性名要与表单name属性一致,大小写也必须一致。
复选框可以采用对象数组接收。
@RequestMapping("/addUser2")
public String addUser2(User user) {
System.out.println(user);
return "login";
}
2.3 地址传参
JSP 页面:
<script>
window.location.href="addUser3/liugang/18"
</script>
controller:
@RequestMapping("/addUser3/{username}/{age}")
public String addUser2(@PathVariable("username")String name,@PathVariable("age") Integer age){
System.out.println(name+"----------"+age);
return "login";
}
三、配置过滤器解决乱码问题
web.xml 文件中添加过滤器
<!-- 3、配置过滤器解决乱码问题-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
四、控制器传值给View
@Controller
@RequestMapping("/user.do")
//设置存入request域的同时,存入session域
@SessionAttributes({"username", "password"})
public class UserModel {
//1、存入model中的同时 数据存入 session 域
//存入model(model存放在 request域)
@RequestMapping("/getUser5")
public String getUserList5(Model model) {
model.addAttribute("username", "刘刚");
model.addAttribute("password", "123456");
return "forward:/user3.jsp";
}
//2、直接存到request域
//3、直接存到session域
@RequestMapping("/getUser")
public String getUserList(HttpServletRequest request, HttpSession session) {
List<User> list = new ArrayList<User>();
list.add(new User(1, "pg", "123456"));
list.add(new User(2, "kobe", "123456"));
request.setAttribute("ul", list);
session.setAttribute("uu", list);
return "forward:/user.jsp";
}
//4、存到model对象中,相当于存放到request域
@RequestMapping("/getUser2")
public String getUserList2(Model model) {
List<User> list = new ArrayList<User>();
list.add(new User(1, "pg", "123456"));
list.add(new User(2, "kobe", "123456"));
model.addAttribute("uuu", list);
return "forward:/user.jsp";
}
//5、存到 modelAndView 对象中,相当于存放到request域
@RequestMapping("/getUser3")
public ModelAndView getUserList3() {
List<User> list = new ArrayList<User>();
list.add(new User(1, "pg", "123456"));
list.add(new User(2, "kobe", "123456"));
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("forward:/user2.jsp");
modelAndView.addObject("list", list);
return modelAndView;
}
}
五、解决静态资源访问不到
在springMVC的配置文件中加入mvc:default-servlet-handler配置
分析:web.xml 中 / 拦截所有请求到DispatcherServlet,找对应的映射,
如果没有找到,不报异常,请求继续往下走,到DefaultServlet中,
DefaultServlet 会到项目下按照路径寻找静态资源
六、JSON处理
SpringMVC默认使用的JSON工具是jackson
6.1 常用 JSON 注解
1、@ResponseBody: 把响应给客户端的对象转成JSON格式
2、@RequestBody: 把客户端提交的JSON转成java对象
3、@RestController : @Controller+@ResponseBody
4、@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
设置在前端解析时间的格式,东八区
@JsonFormat(pattern = "yyyy-MM-dd mm:hh:ss",timezone = "GMT+8")
private Date birthday;
5、@JsonProperty 给属性起别名
@JsonProperty("u_name")
private String username;
6、@JsonIgnore:忽略某个属性,转json时,此属性不转
7、JsonInclude:当属性值为空或empty时,此属性不转JSON
@JsonInclude(JsonInclude.Include.NON_NULL)
private String addr;
二、相关使用方法,如下:
1、导入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
2、@ResponseBody 注解(方法上),表名该方法自动将数据转换成JSON格式,返给AJAX请求。
3、@RestController注解(类上方),具有 @Controller & @ResoponseBody 双重功效。表示类下所有方法都返 回JSON字符串
4、@RequestBody注解(方法形参列表中),作用:把前端AJAX提交的JSON数据,转成user对象.
function tj() {
var xhr = new XMLHttpRequest();
url = "/HelloSpringMVC_war_exploded/json/ajax";
var user = '{"username": "刘刚", "password": "123456"}';
xhr.open("post", url, true);
xhr.setRequestHeader("content-type","application/json");
xhr.send(user)
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
alert(xhr.responseText)
}
}
}
//要求:JSON里name 和user对象的属性同名
@RequestMapping(value ="/ajax" , method=RequestMethod.POST,
produces="text/html;charset=utf-8")
@ResponseBody
public String getAjax(@RequestBody User user) {
System.out.println(user);
return "响应数据";
}
6.2 自定义序列化(转JSON)的规则
1、extends JsonSerializer 接口
public class MySeralize extends JsonSerializer<Double> {
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
//value 就是转JSON的属性值
//gen 按照自定义的规则,生成新值
//BigDecimal.ROUND_HALF_UP 四舍五入
String newValue= BigDecimal.valueOf(value).setScale(2,BigDecimal.ROUND_HALF_UP).toString();
gen.writeNumber(newValue);
}
}
2、在重写的public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException 方法中去定义自己的序列化规则
3、gen.writeNumber(newValue) 输出自定义后的新值
4、在具体的属性上应用
@JsonSerialize(using = MySeralize.class)
private double salary;
七、SpringMVC集成 fastjson
步骤:
1、引入fastjson依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
2、注册fastjson替换掉原有的jsckson
<mvc:annotation-driven>
<!-- 安装FastJson,转换器 -->
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 声明转换类型:json -->
<property name="supportedMediaTypes">
<list>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
3、使用fastjson
@RequestBody、@ResponseBody、@RestController 使用方式不变
@JSONField(name = "tea_No") //别名
private int tea_Id;
@JSONField(serialize = false) //不转换成JSON
private String tea_Name;
@JSONField(format = "yyyy-MM-dd HH:mm:ss") //格式化
private Date birthday;
@JSONField(serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
private String addr;
//使用自定义的序列化规则
@JSONField(serializeUsing = MySeralize2.class)
private Double salary;
自定义序列化规则:
public class MySeralize2 implements ObjectSerializer {
@Override
public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
//参数 o,是要转换的值
//jsonSerializer 输出器:把转换后的值进行输出
//转成Double
double doubleChange=(Double)o;
BigDecimal bdChange=BigDecimal.valueOf(doubleChange).setScale(2,4);
String strChange="¥"+bdChange;
jsonSerializer.write(strChange);
}
}
八、异常解析器
概念: 控制器里的方法(Hander)被执行时,发生异常,并不能被代理对象自动调用增强来处理。如果每个 hander中的方法都 try-catch 捕获的话那么代码将会很冗余。
解决方案: 配置统一的异常解析器,当hander发生异常时,异常解析器进行统一的捕获、处理。
处理方法: 针对不同的异常,创建不同的异常界面。
实现步骤:
1、定义类实现HanderExceptionResover 接口
2、实现接口里的方法
多路捕获异常
声明异常界面:用于展示异常信息
//自定义的异常解析器
//当handler发生异常后,此异常解析器就能捕获异常,捕获后执行resolveException方法
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o,Exception e) {
//判断异常类型,跳转/转发到不同的错误页面即可
ModelAndView modelAndView=new ModelAndView();
if(e instanceof SQLException){
modelAndView.addObject("meg","数据库发生异常");
modelAndView.setViewName("forward:/error/dberror.jsp");
}
if(e instanceof NullPointerException){
modelAndView.addObject("meg","发生未知异常");
modelAndView.setViewName("forward:/error/exception.jsp");
}
return modelAndView;
}
}
3、在springMVC配置文件中配置异常解析器即可
<bean class="com.qf.springmvc.utils.MyExceptionResolver"></bean>
8.2 实际开发中异常的处理方案
(1)在dao中抛出异常;
(2)在service中捕获:采用Aop切面异常处理增强;
(3)在控制器方法中(Hander),采用配置异常解析器,做统一处理
九、拦截器
如果有多个hander中有相同的逻辑代码,这是我们可以抽取出来交由拦截器统一处理。
1、实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("调用hander之前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("hander执行结束");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面渲染完成。。。");
}
}
2、springMVC.xml中配置拦截器
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user.do/**"/>
<mvc:exclude-mapping path="/user.do/getUser"/>
<bean class="com.qf.util.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
十、rest风格,称为restful
可读性更强的编码规范
1、每个资源都有唯一的标识URL
@Controller
@RequestMapping("/user") //唯一标识
public class UserController {
@RequestMapping("/testSession") //用户下的具体请求操作
public String testSession(Model model){
model.addAttribute("userName","刘刚");
model.addAttribute("userId","liugang");
model.addAttribute("pwd","123456");
return "forward:/users.jsp";
}
3、传参时: 资源?id=1&name=zhaoyan 不采用此种方式
users/1 采用路径传参
3、不同的请求采用不同的 http-method
- get:查询操作
- post:添加操作
- delete:删除操作
- put:修改操作
在后台控制器中,不再使用RequestMapping 使用具体的@PostMapping("/")
十一、跨域问题
报错:Access-Control-Allow-Origin
原因:跨域问题,浏览器不允许在提交网络请求时,有和当前服务器 协议、IP地址、端口号中任何一个不同的,将不 提交请求,称为同源策略。
跨域实现:
1、后台:通过注解设置允许的域
@CrossOrigin("http://127.0.0.1:8020")
public class RestUserController {
2、前端、
方式1:xhr.withCredentials=true;
方式2:jsonp方式
function query() {
var xhr=new XMLHttpRequest();
var url="http://localhost:8085/springmvcpro_war/restUser/queryUser/1000"
//xhr.withCredentials=true;
xhr.jsonpCallback="myCallBack";
xhr.open("get",url,true);
xhr.send();
xhr.onreadystatechange=function () {
if(xhr.status==200 && xhr.readyState==4){
var strData=xhr.responseText;
var jsonData=JSON.parse(strData);
document.write(jsonData.userId+"---"+jsonData.userName);
}
}
}
function query2(){
var url="http://localhost:8085/springmvcpro_war/restUser/queryUser/1000";
$.ajax({
type:"get",
url:url,
async:true,
jsonpCallback:"myCallBack",
success:function(strData){
alert(strData.userName);
//var jsonData=JSON.parse(strData);
//document.write(jsonData.userId+"---"+jsonData.userName);
}
});
}
//定义一个空函数
function myCallBack(dt){
}
十二、StringMVC执行流程
2:Handermapping在程序启动前执行,将requestMapping注解里的路径、以及请求方法和对应的Ioc容器中的 bean注册到map集合中;
3:根据处理请求:HanderAdapter进行适配, 获取请求头中的信息, 找到具体的hander以及拦截器的bean。如果有拦截器的话先进行拦截器,再进行参数绑定 执行方法 将参数、视图路径 放到ModelAndView 通过后置拦截返回DispatcherServlet。