spring笔记

Spring

1.IOC and DI

​ 将new的权力交给spring,由工厂提供对象,同时通过DI维护对象与对象之间的关系

2.AOP

​ 底层原理:动态代理的封装

​ 通过产生动态代理来实现附加操作,来减少代码冗余

​ 切面 = 通知(额外功能) + 切入点(加在哪里,哪些service类的哪些方法)

​ 通知和切入点可以随意组合成切面

1. 引言

项目管理框架,不是替换别的框架,而是将框架进行整合管理

  • 众多设计模式:工程 代理 策略 单例…

  • 开源

  • 轻量级

  • SSH:Struct2 + Spring +Hibernate

  • SSM: SpringMvc + Spring + Mybatis

对组件(controller service Dao )进行对象管理(创建 使用 销毁),entity通常不交给spring

1
2
3
4
原来: new 去创建,然后调用
userDao = new UserDaoImpl();
userDao.save()
现在:xml配置bean,然后直接取出来
  1. 导入依赖 org.springframework下的spring-webmvc

  2. 配置applicationContext.xml

    bean来管理对象的创建,class指定类UserDaoImpl,id唯一标识在spring容器中取出来(最好首字母小写)

    1
    <bean id="userDao" class="init.UserDaoImpl"></bean>
  3. 启动工程,绑定xml文件,取出bean

1
2
3
4
5
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("init/applicationContext.xml");
UserDao usertest = (UserDao)context.getBean("userDao");
usertest.save("小明");
}

2. 核心思想

  1. IOC(Inversion of Control)控制反转

    由手动new变为配置文件配置,交给spring工厂。在tomcat中,也是该思想帮助我们创建了response对象

    原理Class.forName(“UserDaoImpl”).newInstance() 反射调用构造方法

  2. AOP面向切面

DI:dependency Injection IOC的补充

​ 存在嵌套service调用dao,也就是serviceImpl中需要拿到daoImpl。

1
2
3
4
5
6
7
8
9
10
11
12
public class UserServiceImpl implements UserService{
private UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public void save(String name) {
userDao.save(name);
}
}

​ 都要先声明一个成员对象,然后

原来:new UserDaoImpl();

现在:需要set方法,在配置中完成赋值操作(注入) set注入

1
2
3
4
5
ref:工程里的标识 bean的id,name:注入哪个属性
<bean id="userService" class="userServiceImpl">
<property name="userDao" ref="userDao" />
</bean>

IOC 创建对象,DI 维护对象与对象之间的关系

3. 注入

除了一个对象的注入,其他的用的很少

  1. set 注入 property标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1. 对象注入 ref
<property name="userDao" ref="userDao" />
2. String、日期、八种基本类型 value注入
<property name="name" value="xiao"></property>
<property name="data" value="2012/12/12 23:54:57"></property> 日期需要/ :格式
3. array list set
<property name="hobbys">
<array> 这里不同
<value>听歌</value> 字符串用value
<ref bean="userDao"></value> 类用ref
</array>
</property>
map
<property name="card">
<map>
<entry key="1" value="123"></entry>
<entry key-ref="userDao" key-ref="userDao"></entry>
</map>
</property>
properties 无序键值对集合
<property name="info">
<props>
<prop key="driver">com.mysqljdbc.Driver</prop>
<prop key="姓名">jdbc:mysql://localhost:3306/test</prop>
</props>
</property>
  1. 构造注入 constructor-arg 标签
1
2
3
4
使用公开的有参构造方法  index代表参数的位置
<constructor-arg index="0" name="name" value="小明"/>

如果想单独赋值某些元素,就要对应的构造方法,很麻烦!!
  1. 自动注入 autowire = ”byType“ “byName”

底层是set 需要在组件标签上开启

Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。

问题: 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了

这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。

SmsService 接口有两个实现类: SmsServiceImpl1SmsServiceImpl2,且它们都已经被 Spring 容器所管理。

1
2
3
4
5
6
7
8
9
10
11
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

4. 工厂特征

默认单例,多次getBean还是同一个。 struct2用成员对象存储信息,所以这时要多例 bean中scope = “prototype”

原理: 反射 + 构造方法

生命周期

单例:工厂启动时创建,正常关闭工程时销毁 context.close()

多例:getBean时创建,创建完成后脱离spring的管理,jvm去销毁

1
2
自己写的方法
init-method="" destroy-method=""

好处:

  1. 解耦合,更换类只需要修改配置文件
  2. 减少内存占用
  3. 建立对象与对象之间的关系 打开配置文件就可看到

5. 代理

代理对象:完成传化,也可以附加操作,同时也可以中断

​ 好处:原始业务逻辑不变,同时可以附加

  1. 代理对象和原始逻辑对象实现相同的接口
  2. 代理对象 依赖 原始业务逻辑对象

原始逻辑中,除了具体事务,还有开启事务、提交事务、回滚事务的方法。后面这部分是重复的

在代理中,负责处理这些额外的方法,同时需要调用原始对象。总体的处理逻辑不变

1
2
3
4
5
public void save(){
sout("开启事务")
userService.save(name)
out("结束事务")
}

静态代理

每一个业务对象都开发一个代理对象,上面的逻辑

动态代理

在程序运行中自动生成

getSqlSession().getMapper(UserMapper.class).getUser(); 这一步也是通过代理实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 类加载器
2. Class[] classes = {UserService.class} 目标对象的接口数组
3. new InvocationHandleer() 附加操作 额外功能
proxy = (UserServic)Proxy.newProxyInstance(classLoader, classes, new InvocationHandleer(){
// 代理过程 处理代理的实例 这里执行附加操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 附加 开启事务,打印信息等操作
log(method.getName());
// 反射, 执行具体事务 哪个目标类的method
Object result = method.invoke(new UserServiceImpl(), args);
return result;
}
public void log(String msg){
System.out.println("执行了"+msg);
}
}); 生成动态对象

porxy.save(); 直接调用

封装成通用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ProxyInvocationHandler implements InvocationHandler {

// 被代理的接口, 一个动态代理 可以代理 多个类,只要是同一接口
private Object target;

public void setTarget(Object target) {
this.target = target;
}

// 返回代理类
public Object getProxy(){
// 类加载器 被代理类接口 InvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}

// 代理过程 处理代理的实例 这里执行附加操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//附加
log(method.getName());
Object result = method.invoke(target, args);
return result;
}

public void log(String msg){
System.out.println("执行了"+msg);
}
}

---------
// 真实角色
ServiceImpl service = new ServiceImpl();

ProxyInvocationHandler phi = new ProxyInvocationHandler();
// 设置要代理的对象
phi.setTarget(service);
// 得到代理对象 是一个接口
Service proxy = (Service) phi.getProxy();

proxy.add();

6. AOP

Aspect Oriented Programing 核心就是动态代理

附加操作:如日志打印,每个controller都加的话代码冗余

通过动态代理完成附加操作(通知、Advice)。开发通知类,配置切入点

通知(Advice):前置通知、后置通知、环绕通知、异常通知

切入点(pointcut):指定通知应用在哪里。一般用于业务层

切面(Aspect):通知(Advice)+ 切入点(pointcut)

  1. 导入依赖

  2. 开发通知 继承接口 MethodIntercept MethodBeforeAdvice ..

    1
    2
    3
    4
    5
    public class Log implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
    System.out.println("执行了"+method.getName()+"方法");
    }
    }
  3. 配置切面 .xml

    • 注册通知类bean

      1
      <bean id="logbefore" class="aop.Log"></bean>
    • 组装切面,aop输入时会导入一些依赖。先定义切入点,切哪个类的哪个方法;再把通知类和切入点组装

      1
      2
      3
      4
      5
      <aop:config>
      <!-- 切入点 execution:执行的位置 -->
      <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserserviceImpl.*(..))"></aop:pointcut>
      <aop:advisor advice-ref="logbefore" pointcut-ref="pointcut"></aop:advisor>
      </aop:config>

    被切的类都会创建代理对象,之后.getBean(“UserserviceImpl”)返回的是proxy,和5中动态代理一样,但每个被切的类不用单独创建

环绕通知

MethodInterceptor

计算运行时间 、处理异常

image-20221206190215566

切入点表达式

image-20221206190819126

image-20221206190916792

image-20221206191123649

7. 复杂对象

  • 简单对象:可以直接new的对象,因此可以直接通过< bean >交给spring
  • 复杂对象:不能直接new,接口Connction、抽象类Calendar

Implements FactoryBean<类名>

image-20221207202622749

8.整合mybatis

spring:项目管理

mybatis:持久层CRUD。把mybatis中的对象创建交给spring

mybatis写法: .xml包含 数据源 以及 mapper注册 。 sqlSessionFactory =》sqlSession =》 usermapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1.配置文件mybatis.xml 包含数据源信息(数据库账号密码等)
2.从配置文件.xml得到SqlSessionFactory,再openSession生成SqlSession执行sql语句 (封装成工具类)
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
try {
// get factory
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

// get SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}

3.编写Dao接口,接口实现类(.xml)

4.注册Mapper.xml
<mappers>
<mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>

5.测试,调用工具类得到SqlSession,再调用getMapper,该mapper执行函数完成查询
// 获得对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// getMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

8.1拿到sqlSessionFactory

SqlSessionFactoryBuilder 读配置文件,构建Factory

sqlSessionFactory 核心对象, 因此整合的核心就是接管这个!!!

sqlSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sqlSessionFactory 是接口,因此利用第七节的方法完成配置,并把xml文件位置通过依赖注入到bean中

spring将这个类封装了,叫做SqlSessionFactoryBean
注入.xml,细粒度化,把dataSource分开了,同时配置mapperLocations;mybatis.xml中配置日志等其他对象(可以不要)
<!--使用spring数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--mapper-->
<property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"></property>
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/> (可以不要)
</bean>

到这一步拿出sqlSessionFactory,就可以取出sqlSession.getmapper操作数据库
但我们的核心是在Dao部分,所以不想每次拿Dao时都先拿出sqlSessionFactory,想直接拿出Dao

8.2整和Dao

1
2
factory中指向mapper文件
<property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"></property>

进一步封装,就可以直接getBean(UserDao)

1
2
3
4
5
6
7
8
9
10
11
sqlSessionFactory和userDao绑定,就可以直接getMapper,等价 @Mapper,每一个都要写。@Repository没有用
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
<property name="mapperInterface" value="com.kuang.mapper.UserDao"></property>
</bean>

这里每一个Dao都要配置,直接只写一个mapperscan可以解决 等价 @MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

或者将sqlsession作为一个成员放到UserDaoImpl中,间接调用,狂神的课程

1
2
3
4
5
6
7
8
9
10
11
12
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}

public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUser();
}
}
1
2
3
4
5
6
7
8
9
<!--sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

<!--实现类-->
<bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"></property>
</bean>

9.Service事务

1
用DataSourceTransactionManager实现事务,自己写try-catch  ->  写环绕通知  ->  用spring的环绕通知(细粒度)

service直接注入Dao即可,但需要处理事务

9.1.编程式

每个impl中注入事务管理对象实现

JDBC中

1
2
3
connection.setAutoCommit(false);
connection.commit();
connection.rollback();

datasouce中可以取出connection,但是service中的connection和dao中的可能不是同一个

image-20221208142531461

DataSourceTransactionManager:全局事务管理,使得service和Dao连接对象相同,控制数据源的线程安全问题

1
2
3
4
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

serviceImpl中,添加transactionManager, .commit() .rollback()。每一个serviceImpl都要写,用AOP解决冗余

image-20221208143608607

9.2 声明式事务

aop实现事务处理

自己写:环绕通知,大家都一样的,所以spring封装了

image-20221208145557943

封装后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--配置事务通知,等价于自己写的环绕通知 tx:advice为特殊标签 对通知做细粒度管控-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--在什么情况下添加事务, 细粒度-->
<tx:method name="add"/>
<tx:method name="delete"/>
<tx:method name="update*"/> 方法名需要对应上
</tx:attributes>
</tx:advice>

<!--配置切入-->
<aop:config>
<aop:pointcut id="tranPointCut" expression="execution(* com.kuang.mapper.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="tranPointCut"></aop:advisor>
</aop:config>

9.3事务传播

业务层中调用别的业务,可以把事务传播过去。

1
2
3
4
5
6
7
8
9
10
11
12
OrderService.save(){
userService.update();
}
<tx:method name="add" />
propagation=""
REQUIRED:外层没有则开启,有则融入 增删改 默认
SUPPORTS:外层没有不开,有则融入;实现事务传播 查询
REQUIRE_NEW: 自己开一个新的 隔开 银行日志,总是要个新的
NOT_SUPPORTED: 不用事务
isolation=""
read-only
time-out: 事务超时 -1永不超时 秒

行锁为一条数据,在同一事务中多次查询,不会受到别人update印象

表锁为表的数据,多一条少一条不受印象

image-20221208161551569

10.日志

log4j

ERROR WARN INFO DEBUG

配置文件在resource根目录下

1
log4j.logger.com.baizhi.dao=DEBUG 展示sql

11.注解

装配

service太多了,每次都要添加

前置条件:开启注解扫描

1
2
<!--指定注解生效的包-->
<context:component-scan base-package="com.kuang"/>

@component(value = “指定id”),装配到Spring中,等价与< bean>, 唯一标识为类名小写

  • Dao **@Repository (只是告诉spring要加这个bean,和mybatis无关) ** @Mapper 和 @MapperScan结合使用为了结合spring和mybatis。如果不加@Repository可能爆红 但能正常运行
  • Service @Service
  • controller @ Controller

@Scope(value=”singleton\prototype”) 单例多例

注入

对象注入 属性或者set方法上。在属性上用时可以不用添加set方法,底层会自动提供set方法

​ @Autowired 默认类型

​ @Qualifier(value = “cat1”) 指定名称

JavaEE

​ @Resource 默认名字,找不到再类型

基本类型注入

​ @Value(“XX”) @Value(“${spring.datasource.username}”)

事务

@transactional(propagation = , ) 添加事务,加载类或者单个方法 serviceimpl上加

不需要事务通知对象以及添加切面,但还是添加开启注解事务

1
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven > 
image-20221208170805881

Dao还是扫描进行装配的

P21整合SM

SpringMVC

MVC中的C层,控制器框架。代替struts2

0.Struct2

1.C层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ProductAction extends ActionSupport {
private List<Product> productList; // 成员变量传递结果 相当于model.add

public String list() {
// 处理获取商品列表的逻辑,这里直接模拟
productList = ProductService.getProductList();
return SUCCESS;
}

public List<Product> getProductList() {
return productList;
}
}

2.路由:封装servlet,Struts2的前端控制器DispatcherServlet接收并分发到对应的Action处理。

定义名为productList的Action,并将其关联到ProductAction类中的list()方法。当请求到达/productList时,Struts2会调用list()方法并将结果返回到productList.jsp页面中。

1
2
3
4
5
6
7
<struts>
<package name="product" extends="struts-default">
<action name="productList" class="com.example.ProductAction" method="list">
<result name="success">/WEB-INF/views/productList.jsp</result>
</action>
</package>
</struts>

3.V层:配置Struts2的视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Product List</title>
</head>
<body>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<s:iterator value="productList">
<tr>
<td><s:property value="id"/></td>
<td><s:property value="name"/></td>
<td><s:property value="price"/></td>
<td><s:property value="description"/></td>
</tr>
</s:iterator>
</tbody>
</table>
</body>
</html>

1.基本思想

在servlet基本思想中,编写一个控制器,就要去web.xml中去注册,在SpringMVC中,使用dispatchservlet拦截所有

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>loginout</servlet-name>
<servlet-class>com.xiao.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginout</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>

配置web.xml(拦截全部的请求) 配置springmvc.xml 编写controller(component-scan)

image-20221212145632004

  1. web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--1.注册DispatcherServlet-->
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    </servlet>
    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  2. springmvc.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--处理器映射器 定位到方法-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理器适配器 解析参数-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器:DispatcherServlet给他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    id="InternalResourceViewResolver">
    <!--前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--后缀-->
    <property name="suffix" value=".jsp"/>
    </bean>

    </beans>
    1
    2
    <!--注解驱动-->
    <mvc:annotation-driven /> 包含映射器、适配器 实验发现是必须要
  3. 编写controller

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    手动配置,这里的id就是访问的路径
    <bean id="/helloserv" class="com.kuang.controller.Helloservlet"></bean>

    或者 不可以同时配置

    <!-- 注解扫描 -->
    <context:component-scan base-package="com.kuang.controller"/>

    @Controller // 注入bean
    public class HelloServlet {

    @RequestMapping("/helloserv") // 访问路径
    public String hello(Model model){
    model.addAttribute("msg","hello annotation"); //model
    return "hello";//view
    }
    }
  • 在 Controller 中,根据业务逻辑查询出需要显示的动态数据,并将其封装成一个 Model 对象。
  • 将 Model 对象作为参数传递给模板引擎(根据返回值定位页面),通过 Thymeleaf 的表达式语言 ${} 将动态数据注入到 HTML 模板中。
  • Thymeleaf 模板引擎会根据 HTML 模板中定义的逻辑和数据,生成渲染后的 HTML 页面,并将其返回给客户端浏览器。
  • 客户端浏览器接收到 HTML 页面后,根据 HTML 标签和 CSS 样式渲染页面,并显示给用户。

想访问一个商品列表页面:

  1. controller定义toList接口,
  2. 加载数据到model里,return “toList”跳转到toList.html,thymeleaf会自动渲染数据进入页面对应的${}

2.跳转

servlet

  • forward 请求转发 地址栏不变,获得商品列表后转发到商品界面
  • redirect 重定向

controller -> 页面

  • 默认返回就是forward
  • 重定向return “redirect:/index.jsp” 不经过视图解析器

controller -> controller

  • return “forward:/path”
  • return “redirect:/path”

3.参数接受

vo:专门用来传值的对象

struts2:成员变量接收参数 多例

springmvc:

@RequestBody 反序列化 @ResponseBody 序列化成json

1
2
3
4
5
6
7
8
9
10
局部变量,无线程安全问题,单例

// restful风格
@RequestMapping("/test2/{pageNo}“)
@PathVariable int pageNo 将URL中的占位符参数绑定到控制器处理方法的入参

@RequestParam String name Url Form表
User user url中自动对应

@RequestBody User user Body中raw格式下的json参数

前端可以混用,一个在params,一个在data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestParam String resourceId,
@RequestParam String examId,
@RequestBody List<UpdateExamAnswer> examAnswerList,
@RequestParam(value = "writeType", defaultValue = "0") int writeType

return request({
url: BASE_URL + '/submit_all_save',
method: 'post',
params: {
resourceId, // 普通字符 拼接到url @RequestParam
examId,
writeType
},
data: questionRes // json格式 @RequestBody
})

此外,以上默认post为application/json格式发送接受数据

  1. application/x-www-form-urlencoded 前端URLSearchParams或Qs.Stringify(data) 后端@RequestParam或不加
1
2
3
4
5
6
7
8

数据格式:loginName=lst&password=1

百分号编码:
丁 十六进制下是0xE4B881占3字节
转成字符串‘E4B881’,占六字节
每两个字节前加上百分号前缀,得到字符串“%E4%B8%81”,占九个字节(十六进制下是0x244534254238253831)
把这九个字节拼接到数据包里,这样就可以传输“非ascii字符的 utf8编码的 十六进制表示的 字符串的 百分号形式”

3.multipart/form-data 前端FormData格式,后端@RequestParam或不加

4.返回参数:

request(单次请求) session(浏览器) application(全体用户共享)

1
2
3
4
5
6
7
forward跳转
Model model 对request的封装,作用一样
model.addAttribute("msg","hello annotation");
取出:${requestScope.msg}
redirect
1.地址栏拼接
2.session req.getSession.setAttribute

5.拦截器

javaweb:filter过滤器,可以拦截一切资源。springmvc只能拦截controller

编写拦截器类,再注册:注入bean,定义拦截的地方。(或者定义和拦截写在一起)

这里继承HandlerInterceptor,有处理前,(controller)处理后,清理后

AOP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class JWTInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 放行OPTIONS请求
if ("OPTIONS".equals(request.getMethod())) {
return true;
}

String token = request.getHeader("token");
HashMap<String, Object> map = new HashMap<>();
if(token == null){
map.put("msg","未携带token");
}else{
try{
JWTUtils.verity(token);
// 未出错 放行
return true;
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","token算法不一致");
}catch (Exception e){
e.printStackTrace();
map.put("msg","token无效");
}
}

map.put("code",500);
map.put("data",null);
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
1
2
3
4
5
6
7
8
<!--拦截器注入-->
<mvc:interceptors>
<!--可以写多个拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.kuang.config.JWTInterceptor"></bean> 或者分开 <ref bean = "beanname">
</mvc:interceptor >
</mvc:interceptors>

6.异常处理

编写异常处理类,注册bean就行。可以根据不同的异常if判断处理不同逻辑

image-20221216183658625

SSM

这里7.a scan可以只扫描service下的,因为mapper和controller都扫描了;mapper可以不使用注解(mapperscanconfig是和spring整合的;等价在springboot中使用了@Mapper或@MapperScan)

image-20221215092403942

image-20221216205759155

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	<!--扫描service下的包-->
<context:component-scan base-package="com.kuang.service"/>

<!-- 这里使用注解实现-->
<!--将service层装入-->
<!-- <bean id="bookServiceImpl" class="com.kuang.service.BookServiceImpl">-->
<!-- <property name="bookMapper" ref="bookMapper"></property>-->
<!-- </bean>-->

<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--开启注解事务生效-->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- 配置整合mybatis -->
<!-- 1.关联数据库文件 -->
<context:property-placeholder
location="classpath:database.properties"/>

<!-- 2.数据库连接池 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置连接池属性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>

<!-- 3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
<!--解释 : https://www.cnblogs.com/jpfss/p/7799806.html-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"/>
<!-- 给出需要扫描Dao接口包 -->
<property name="basePackage" value="com.kuang.dao"/>
</bean>

1.ab 让mvc在服务器启动时创建工厂(提供service Dao)。

实际项目中,可以把所有的文件整合到一起applicationContext,在servlet中配置

image-20221215093143429

image-20221216210140557

image-20221216210029653

C:\Users\13000\Desktop\study\vue-spring\2.vue-spring\7.springmvc\0代码———————————-\ssmbuild

spring.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<!-- 配置整合mybatis -->
<!-- 1.关联数据库文件 -->
<context:property-placeholder
location="classpath:database.properties"/>

<!-- 2.数据库连接池 -->
<!--数据库连接池
dbcp 半自动化操作 不能自动连接
c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
-->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置连接池属性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>

<!-- 3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
<!--解释 : https://www.cnblogs.com/jpfss/p/7799806.html-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"/>
<!-- 给出需要扫描Dao接口包 -->
<property name="basePackage" value="com.kuang.dao"/>
</bean>

<!--service相关-->
<!--配置声明式事务,开启注解事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven >

<!--扫描service下的包, 也可以手动写入bean-->
<context:component-scan base-package="com.kuang.service"/>
<!--将service层装入-->
<!-- <bean id="bookServiceImpl" class="com.kuang.service.BookServiceImpl">-->
<!-- <property name="bookMapper" ref="bookMapper"></property>-->
<!-- </bean>-->

springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- 注解扫描 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler/>
<!--注解驱动-->
<mvc:annotation-driven/>

<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>


web.xml
<servlet>
<servlet-name>dispatchservlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatchservlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>