SSM框架
Spring
基本概念
- 在没有Spring之前,程序是通过
public UserService user = new UserService()
的形式,来给一个变量赋予引用值,user变量定义后就可以直接user.xxx()
的来调用方法. - 在有Spring之后,可以直接使用
public UserService user
的形式给来定义变量,而省略new UserService()
这一部分,这部分由Spring来进行. - 由Spring创建对象,称其为”控制反转”,Spring创建对象的过程中,发现对象里面还有一个成员变量需要赋值,赋值的过程称为”依赖注入”.
控制反转
简称为IOC,英文全称:Inversion of Control,为什么原本自己new一个对象,现在要交由Spring交由Spring来管理,如需详细了解好处点击进入,我的理解就是解耦合,提升代码的灵活性- -
那么问题就来了,现在由Spring来new对象,总得告诉Spring大佬,应该new哪个对象出来,告诉Spring有两种方式如下:
- 配置
XML
文件 - 使用
注解
告诉了Spring大佬new哪个对象后,如果对象里面有成员变量,还得告诉大佬,得给这个成员变量赋值,具体XML实例如下:
1 | <bean id="mainUI" class="com.jungle.www.controller.MainUI"> |
bean
标签:告诉Spring我要开始创对象
id
:指定对象名
class
:类的全路径,写哪个类,就会创建哪个类的对象
property
标签:需要Spring赋值的成员变量
name
:需要赋值的成员变量名ref
:代表引用其他bean的id名所具有的值这里面有两个bean,所以创建了两个对象,名字由id指定,分别名叫:
mainUI
和userService
,其中mainUI实例里有一个叫jungle
的成员变量,这个成员变量的值是通过ref来引用了userService对象的值,上述的配置,等同于以下:
1
2
3 public void MainUI{
private UserServiceImpl jungle = new UserServiceImpl();
}
上面的例子里的成员变量,是引用类型,也就是一个对象,那么基本数据类型又如何解决赋值问题?集合又怎样解决赋值问题?系统的归纳如下:
属性赋值
value注入基本类型
<bean id="mainUI" class="com.MainUI"> <property name="str" value="李白"/> </bean>
1
2
3
4
5
6
7
8
9
10
11
> 上面的效果,创建了`com.MainUI`类(com是包名,MianUI是类名)的名叫`mainUI`的实例,并且给里面的str成员变量赋值为 李白,等同于如下:
>
> ```java
> public void MainUI{
> private String str;
> /*Spring注入值时,是通过setter方法来的,因此需要注入的成员变量必须提供setter方法*/
> public void setStr(String str){
> this.str=str;
> }
> }
ref 注入已有的bean
- 最开始的例子就是使用ref来给一个对象注入值的,在官方源码有一句话:The name of the property, following JavaBean naming conventions,对于对象的值的注入,一般就是通过引入其他的bean的id名来注入的.
List、Set、Map
<property name="list"> <list> <value>J</value> <value>G</value> </list> </property>
1
2
3
4
5
6
7
8
9
- ```xml
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property><property name="map"> <map> <entry key="dock" value="yellow"/> </map> </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
27
28
29
30
31
32
## 依赖注入
英文简称DI
英文全称: Dependency Inject
#### 注入方式
- setter注入
- 常用方式,必须提供setter方法,也就是上面给成员变量时赋值所用到的方式
- 构造器注入
- <constructor-arg name="参数名字" index="参数序号" value="参数值" ref="引用的bean" >
- 注:name||index任选一项即可,value||ref任选一项即可
## 属性注入实例
### 1.项目整体结构及运行效果图
![](https://cdn.jsdelivr.net/gh/Griffonage/Figurebed/img/20200909141952.png)
### 2.导入依赖
既然使用Spring框架,就应该导入Spring的东西,在eclipse是导入jar包,在idea是导入maven依赖方式进行的,需要导入的依赖如下:
```xml
<!--SpringFrameWork-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
当导入此依赖时,maven会自动将相关依赖导入,不需要我们去自己导入,依赖关系如下图所示:
3.各模块主要代码
1 | //User类 |
resources文件夹下的applicationContext.xml文件
1 | <!--UserServiceImpl类中的名叫obj的成员变量引用此值--> |
完整项目链接
对象的实例化方式
也可以称作bean的实例化方式
上面通过bean来实例化对象时,默认采用的是无参构造来进行实例化,还有其他几种方法,也需要知道以下,下面总结一下:
默认构造器实例化
<bean id="mainUI" class="com.jungle.com.MainUI">
效果:创建了com.jungle.com.MainUI类的名叫mainUI的实例
注意:需要com.jungle.com.MainUI类里具有无参构造!
静态工厂实例化
<bean id="today" class="com.jungle.com.service" factory-method="getObject"/>
效果:通过com.jungle.com.service类的名叫getObject的静态方法,创建一个名叫today的实例
实例工厂实例化
//Factory类 public class Factory { public Robot getRobot(){ return new Robot(); } }
1
2
3
4
5
6
7
8
- ```java
//Robot类
public class Robot {
public void say(){
System.out.println("i am a robot");
}
}> 第一个bean > > - id:以后读取的时候,就是读取这个id来得到对象 > - factory-bean:指定生产者 > - factory-method:指定生产者所使用的方法 > > 第二个bean:创建一个com.Factory类的叫factory的对象1
2
3<!--XML-->
<bean id="robot" factory-bean="factory" factory-method="getRobot"/>
<bean id="factory" class="com.Factory"/>
Spring的FactoryBean接口实例化
类实现
FactoryBean<T>
接口,重写getObject()
和getObjectType()
方法配置XML文件,bean的实例为getObject方法的返回值
具体实例如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//java代码
public class Factory implements FactoryBean<User> {
public User getObject() throws Exception {
return new User(1,"李白","123");
}
public Class<?> getObjectType() {
return User.class;
}
}
//xml配置
<bean id="user" class="com.jungle.www.controller.Factory"/>
//注:在测试类中,使用getbean("user")即可得到User的实例
核心API
读取文件时,就是如下表格所示API来实现
BeanFactory |
IOC容器的基本接口 |
---|---|
ApplicationContext |
扩展的IOC容器接口 |
ClassPathXmlApplicationContext | 基于xml配置文件的IOC容器 |
看一下下面的关系图,可能理解的更深
上面的AnonotationConfigApplicationContext
在读取注解的配置时会用到,,图片来源于点击进入,作者很不错,有很多的源码的具体分析。
bean的作用域
概念
在上面的属性注入实例中,多次使用getbean获取到的实例,总会是同一个实例,这和bean的作用域有关,获取实例如下图所示:
总结归纳如下:
英文名 | 解释 |
---|---|
prototype | 原型,每次创建一个实例 |
singleton | 单例[默认],一个bean的定义,只有一个实例,不是一个类只有一个实例 |
request | 一个请求一个实例(仅适用于WebApplicationContext环境) |
session | 一个会话一个实例(仅适用于WebApplicationContext环境) |
globalSession | 用于Protlet应用环境(仅适用于WebApplicationContext环境) |
修改作用域
XML实现
在bean的里面配置scope="prototype或者singleton"
注解实现
使用注解@Scope
来指定,如下例所示
@Component @Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE) public class BeanExample { private String test; public String getTest() { return test; } public void setTest(String test) { this.test = test;} }
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
下图所示,为使用XML修改作用域后,测试的结果![](https://cdn.jsdelivr.net/gh/Griffonage/Figurebed/img/20200909150733.png)
## bean的生命周期
如下图所示,[图片来源](https://www.cnblogs.com/zrtqsk/p/3735273.html)
![](https://cdn.jsdelivr.net/gh/Griffonage/Figurebed/img/20200909153438.png)
![](https://cdn.jsdelivr.net/gh/Griffonage/Figurebed/img/20200909153510.png)
### 重写Bean生命周期内的方法
> 注意:为了更加符合历史发展规律,许多例子都是使用XML配置来实现的,一般来说,都有对应的注解方式来解决
- 在bean的XML配置上写`init-method`和`destroy-method`
- 实现Java类实现`InitializingBean`和`DisposableBean`及其方法
- 在容器关闭的时候销毁
- ```java
//例:
public class Cycle2 implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("开始....");
}
@Override
public void destroy() throws Exception {
System.out.println("结束了....");
}
}
//初始化很容易出结果,销毁测试需要手动关闭,才能看到效果,如下所示
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ClassPathXmlApplicationContext applicationContext= (ClassPathXmlApplicationContext)context;
applicationContext.close();
配置方式的演变
ps:下面的代码实现,是在IDEA
中使用Maven
来进行构建和管理,不会Maven
的可以去网站学习
方式一[纯XML实现]
项目结构
APP类
1 | public class App |
MianUI类
1 | public class MainUI { |
- 在此类中,声明了UserService类的userService变量,区别于传统的赋值方式,这里仅仅只是声明了该变量,根据java的编程规范,未赋值的引用变量,默认值为null.
Spring要做的,就是将userService赋值,而采用xml配置方式,是需要setter方法的,所以在最后添加了一个setUserService方法
UserServiceImpl类
1 | public class UserServiceImpl implements UserService { |
- 代码分析:有一个变量userDAO需要赋值
UserDAOImpl类
1 | public class UserDAOImpl implements UserDAO { |
- 此代码中,有一个名叫qr的成员变量需要赋值,而这里还执行了数据库的查询操作,需要在pom.xml文件中引入jar包
User类
1 |
|
- 在这里,使用了lombok来给私有的成员变量建立getter、setter方法和空参构造及有参构造
applicationContext.xml文件配置
1 | <!--在App类中开始启动,需要得到MainUI类的实例--> |
druid.properties文件
1 | jdbc.classDriver=com.mysql.jdbc.Driver |
pom.xml文件配置
1 | <!--数据库_驱动--> |
完整代码已上传:
链接:https://pan.baidu.com/s/1X64xcNhcqxOkr6sntraiBw
提取码:upuc
方式二[注解]
通过注解方式可以更加快速配置,只需要在类上面加上注解即可
- 注册springbean
@Controller
加在Controller层的实现类上@Service
加在Service层的实现类上@Repository
加在Dao层的实现类上@Component
加类上,该类就会被Spring识别
- 自动依赖注入
@Autowired
默认按类型装配@Resource
默认按名称装配(具体细致区别参考此处)@Value
从配置文件中读取值
- 生命周期
@PostConstruct
、@PreDestroy
- 作用域范围
@Scope
方式三[java配置]
注解:
@Bean
- 标记某个方法返回值为spring的bean,方法参数为依赖注入的请求
@Configuration
- 标记一个类为配置类 applicationContext.xml
@Component
- 将普通pojo实例化到spring容器中
@ComponentScan
- 打开包扫描功能,并且指定扫描的包 等价于 <context:component-scan base-package=”xxx”>
@PropertySource
- 指定配置文件所在位置
AOP
Aspect Oritented Programming AOP 面向切面编程
Object Oritened Proguramming OOP 面对对象编程
概念
- 具有横切性质的系统功能,例如:日志记录、性能统计、事务管理、安全检查等等。散布在系统的各个类中。需要一种机制能将这类功能自动加入到需要的位置中去。这种机制就是AOP。
名词
中文名 | 英文名 | 解释 |
---|---|---|
连接点 | joinpoint | 需要加入功能的位置(方法) |
切入点 | pointcut | 执行加入功能的连接点,从连接点选出的需要加入功能的连接点 |
通知 | advice | 需要实现的功能 |
切面 | aspect | 切入点和通知的组合 |
目标对象 | target | 连接点(方法)所在的对象 |
织入 | weave | 将切面应用到目标对象的过程 |
步骤
1) 编写service类
2) 编写通知 , 实现MethodBeforeAdvice接口
1 | //method 要执行的方法 |
3)配置xml
- 配置service
- 配置通知
- 配置切入点,class= org.springframework.aop.support.JdkRegexpMethodPointcut ,配置属性pattern=> service的方法
- 配置切面,class=org.springframework.aop.support.DefaultPointcutAdvisor 连接切入点和通知
- 包装service类, class= org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
- 获取bean,调用方法,将会看到通知执行了
1 |
|
通知类型
- 前置通知 : 方法执行之前 MethodBeforeAdvice
- 后置通知 : 方法执行之后 AfterReturningAdvice
- 环绕通知 : 方法执行前后 MethodInterceptor
- 异常通知 : 抛出异常时
- 最终通知 : finally执行时
注:面向接口编程 1)解耦,修改实现类 2)默认使用接口的方式生成代理
依赖
aspectj
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
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
#### 配置方式
##### XML实现
通知类实现通知接口,如:implements MethodBeforeAdvice、implements MethodInterceptor、 implements AfterReturningAdvice
事务通知类需要有三个方法,分别对应切面的配置
resources文件夹下:aop.xml(注释掉的为日志通知,没注释的切面是事务通知)
```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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--目标对象,下面的两个知识点都需要-->
<bean id="userService" class="com.woniu.www.service.impl.UserServiceImpl"/>
<!--通知: 实现了打日志的功能-->
<bean id="beforeExecution" class="com.woniu.www.component.BeforeExecution"/>
<bean id="afterExecution" class="com.woniu.www.component.AfterExecution"/>
<bean id="interceptorExecution" class="com.woniu.www.component.InterceptorExecution"/>
<!--切入点:选出了需要增加功能的方法-->
<bean id="pointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="com.woniu.www.service.impl.UserServiceImpl.addUser"/>
</bean>
<!--打印日志-->
<!--切面:连接切入点和通知,让打日志功能在切入点的位置执行-->
<!-- <bean id="aspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">-->
<!--需要增加功能的方法-->
<!-- <property name="pointcut" ref="pointCut"/>-->
<!--功能-->
<!-- <property name="advice" ref="beforeExecution"/>-->
<!-- <property name="advice" ref="afterExecution"/>-->
<!-- <property name="advice" ref="interceptorExecution"/>-->
<!-- </bean>-->
<!--通知-->
<bean id="txAdvice" class="com.woniu.www.component.TransactionAdvice"/>
<!--切面配置-->
<aop:config>
<aop:aspect ref="txAdvice">
<!--切入点-->
<aop:pointcut id="servicePointcut" expression="execution(* com.woniu.www.service.impl.*.*(..) )" />
<!--前置通知-->
<aop:before method="begin" pointcut-ref="servicePointcut"/>
<!--后置通知-->
<aop:after-returning method="commit" pointcut-ref="servicePointcut"/>
<!--异常通知-->
<aop:after-throwing method="roolback" pointcut-ref="servicePointcut"/>
</aop:aspect>
</aop:config>
<!--包装userService-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
Java注解实现
下面的例子分为纯java配置和带部分xml配置,具体体现在测试类的调用上
功能:实现了两个事务,一个正常,一个会进行回滚
aop.xml配置
1 |
|
TxConfig类
1 |
|
TxAspect类
1 |
|
产生事务类
1 |
|
测试类
1 | public class App |
切入点表达式语法
execution (* com.sample.service.impl..*.*(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
execution(): 表达式主体。
第一个号:表示返回类型,号表示所有的类型。
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
第二个号:表示类名,号表示所有的类。
*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
AOP与代理模式的关系
- AOP使用代理模式实现
- 我们编写的业务对象是被代理的对象;代理对象是由Spring容器动态生成,实现了业务对象的接口
- 当使用AOP时,
- 由Spring容器为被拦截的目标对象生成代理类,内部持有目标对象的引用,并加入通知功能
- 将生成的bean放入到spring容器中
- 当获取bean时,得到是spring生成的代理类实例
- 调用时,首先执行代理类的代码,再有代理类调用被代理类
事务管理
AOP的典型应用就是Spring声明式事务管理
- 编程式事务管理 使用spring提供的
TransactionTemplate
来实现事务管理 - 声明式事务管理 使用注解
@Transactional
,利用AOP来实现事务管理
步骤
加依赖 spring-tx 、 spring-jdbc
配置类
- 数据源
- 数据源代理
- queryRunner
- 事务管理器 (引用数据源代理)
- 事务模板(引用事务管理器)
编码时
1
2
3
4
5
6
7
8
9
10
11transactionTemplate.execute(new TransactionCallBakWithoutResult(){
....doInTransactionWithoutResult(TransactionStatus status){
try{
//需要被事务管理的代码
}catch(Exception e){
//标记回滚
status.setRollbackOnly()
}
}
})
声明式
加依赖 spring-tx 、 spring-jdbc
配置类
- 数据源
- 数据源代理
- queryRunner
- @EnableTransactionManagement
编码时
1
2
3
4
5
public void test2(){
//需要事务管理的代码
//抛出异常时自动回滚
}实例
配置类(两个都可以)
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
public class AppConfig {
private String classDriver;
private String url;
private String username;
private String password;
public QueryRunner queryRunner(){
return new QueryRunner(dataSourceProxy());
}
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(classDriver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
//数据源的代理,Spring能够去控制事务的打开和提交等待
public TransactionAwareDataSourceProxy dataSourceProxy(){
return new TransactionAwareDataSourceProxy(druidDataSource());
}
//事务管理器
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSourceProxy());
}
//事务模板
public TransactionTemplate transactionTemplate(){
return new TransactionTemplate(transactionManager());
}
}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
public class AppConfigTwo {
private String classDriver;
private String url;
private String username;
private String password;
public QueryRunner queryRunner(){
return new QueryRunner(dataSourceProxy());
}
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(classDriver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
//数据源的代理,Spring能够去控制事务的打开和提交等待
public TransactionAwareDataSourceProxy dataSourceProxy(){
return new TransactionAwareDataSourceProxy(druidDataSource());
}
//事务管理器
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSourceProxy());
}
}事务类
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 UserServiceImpl implements UserService {
private QueryRunner queryRunner;
private TransactionTemplate transactionTemplate;
public void test() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
queryRunner.update("insert into user values(default,?,?)","abc","qwe");
if( null != status){
throw new RuntimeException("error");
}
queryRunner.update("update user set user_password=?","123456");
} catch (SQLException e) {
e.printStackTrace();
status.setRollbackOnly();
}
}
});
}
public void testTwo() throws SQLException{
queryRunner.update("insert into user values(default,?,?)","abc","qwe");
if(null != queryRunner){
throw new RuntimeException("test2");
}
queryRunner.update("update user set user_password=?","123456");
}
}
测试类
1 | public class App |
SpringMVC
基本概念
- Model:模型
- 数据+功能 模型类+Service+DAO
- View:视图
- 展示页面 JSP / HTML+AJAX
- Controller:控制器
- 控制流程 @Controller控制器
配置方式一:XML
加依赖
Spring Web MVC依赖
org.springframework spring-webmvc
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.4.RELEASE</version> </dependency>
1
2
3
4
5
6
7
8
9
10
11- servlet API支持
- `javax.servlet javax.servlet-api`
- ```XML
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
JSTL支持
javax.servlet jstl
taglibs standard
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.1.2</version> <type>jar</type> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> <type>jar</type> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
- 参数验证
- `org.hibernate.validator hibernate-validator`
- ```XML
<!--参数验证-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
JSON类型转换
com.fasterxml.jackson.core jackson-core
com.fasterxml.jackson.core jackson-databind
com.fasterxml.jackson.core jackson-annotations
<!--下面的jackson版本放在properties标签之间,对于同种类型的多个依赖,建议使用此方式,便于后期维护--> <jackson.version>2.10.0</jackson.version> ... <!--JSON类型转换--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
### 建包
### 配置web.xml
#### 配置web.xml版本
检查一下web.xml最上方的头文件,版本是否过低,可以将上方内容改为如下:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--内容区-->
</web-app>
配置监听器
作用:在tomcat启动时加载Spring容器
方式
1
2
3<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
配置servlet
作用:接受并分发请求
方式
1
2
3
4
5
6
7
8
9<servlet>
<servlet-name>mvcmylove</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvcmylove</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
配置filter
作用:过滤请求字符编码集
方式
1
2
3
4
5
6
7
8
9
10
11
12
13<!--filter-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
配置成功代码
1 |
|
配置applicationContext
- 作用:根容器的配置文件, 包括非Web的SpringBean
新建文件,选择XML Configuration File –>Spring Config,命名为applicationContext.xml即可,创建完成放着就行,暂时不配置
配置mvcmylove-servlet.xml
- 命名规范:{servlet-name}-servlet.xml
- 作用:子容器的配置文件,包括Controller、ViewResolver的Web相关的bean
- 注意:子容器可以访问根容器,根容器不能访问子容器
新建文件,选择XML Configuration File –>Spring Config,命名为mvcmylove-servlet.xml即可,创建完成放着就行,暂时不配置
配置包扫描路径
<context:component-scan base-package="com.jungle.www.controller"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> 出现:Namespace 'context' is not bound后,可以手动在上面加:
>
> xmlns:context="http://www.springframework.org/schema/context"
>
> 或者让idea自动添加
#### 配置内容协商视图解析器
- 作用:根据请求自动返回jsp或者json
```xml
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><!--没提示就自己复制上去,复制上去后不报红即可-->
<property name="viewResolvers">
<list>
<!-- home => /WEB-INF/page/home.jsp -->
<!--内部资源视图解析器,JSP-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
配置启动mvc注解
1 | <mvc:annotation-driven/> |
配置静态资源映射
- 作用:解决WEB-INF文件下的资源无法访问问题
1 | <!--mapping : 访问路径 location : 本地路径--> |
配置成功的代码
1 |
|
测试环境搭建
出现:No bean named ‘cacheManager’ available的报错
原因:xml文件头声明有问题
解决方案:使用下面的直接将原来的替换掉即可
1 |
|
1 |
|
使用ip+端口号+项目名+请求名,访问成功跳转,即说明配置成功
补充概念
ViewResolver 视图解析器
作用
Controller将请求处理结果放入到ModelAndView后,DispatcherServlet会根据ModelAndView选择合适的视图进行渲染。ViewResolver就是负责挑选出合适的视图,ViewResolver接口定义了resolverViewName方法,根据viewName创建合适类型的View实现。
在前面的配置mvcmylove-servlet.xml中,就有对viewResolvers的配置
使用场景
- jsp解析器
- thymeleaf解析器
- json解析器
配置方式二:Java
- AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
- 提供根容器配置文件
- 提供子容器配置文件
- 配置DispatcherServlet的url-pattern
- 提供Filter
- WebCtxConfig extends WebMvcConfigurerAdapter
- @Configuration @ComponentScan @EnableWebMVC
- 视图解析器
- 静态资源处理
- 文件上传
配置两个java文件即可
WebCtxConfig.java
1 |
|
AppInitializer.java
1 | public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
功能使用
自动注入对象
对于需要注入的对象,加
@Autowired
注解即可例:
1
2
public Service service;
form传递
假如数据库中有字段user_id
,user_name
,userpassword
,当用户登录的时候,会将名字和密码发送过来,这时我们只要新建一个实体类(最好新建目录),请求参数为实体类,即可自动绑定值,例如下所示:
1 |
|
前面在xxx-servlet配置里,配置的跳转路径是WEB-INF,所以这个请求会去WEB-INF/page下面找,且加上后缀名(也是配置的),也就是WEB-IN/page/index.jsp
跳转除了这种默认的方式,还可以使用
forward:/xxx
redirect:xxx
- 区别在于,不写前面的方式,默认会去配置的路径下找,写了前面的方式,除了可以跳转配置的路径,还可以进行类自身的跳转
页面的取值方式
- 像上面的实体类来接收值,在前端jsp页面,可以直接使用
${类名(首字母小写)}
取得值,例:${login}
controller层的设置值的方式
HttpServletResponse response
照样可以设置值给页面ModelMap的方式,例:
1
2ModelMap modelMap = new ModelMap();
modelMap.put("account",userLogin.getAccount());
参数校验
需要依赖
<!--参数验证--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.17.Final</version> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 对实体类中的成员变量加注解 :
- @NotNull
- @NotBlank
- @Email
- @Length
- @Future
- @Past
- @Pattern ...
- ```java
@NotBlank
private String account;
在需要校验的参数对象前@Valid
public String getUser(@Valid Login userLogin){}
方法签名,BindingResult参数,获取校验结果
具体实例如下:
实体类:
1 |
|
Controller层的类
1 |
|
JSP直接:${mes}
取值即可取到实体类设置的值
测试的网址:http://localhost:8080/getUser?password=5555
效果:account:账户千万不能为空哟;
在controller层的类中,可以将前面的account去掉的
参数类型转换
当界面的数据传到controller层的时候,是字符串类型,无法直接将字符串类型的日期转换成日期类型,通过下面的方式,会自动将请求的字符串类型转换成日期类
- @InitBinder 参数 WebDataBinder
1 |
|
参数校验和参数类型转换可以变得更加精简,具体做法是建立一个抽象类,将主要代码抽取到抽象类中,需要的类再继承即可
json自动转换
- @ResponseBody
- @RestController
静态资源处理
1 | <!--解决静态资源访问限制问题--> |
全局异常处理
- @ControllerAdvice (针对于所有的Controller的通知)
- @ExceptionHandler(异常处理器)
实例:
1 |
|
只要出现异常,就会进入这里,
Model
仅在此使用ModelMap
在其他场景下都可以使用,这里直接上HttpServletRequest也不错,SpringMVC里都可以使用HttpServletRequest的
参数封装
文件上传
加依赖
commons-fileupload commons-fileupload
1
2
3
4
5<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>配置xxx-servlet.xml
1
2
3
4
5
6
7
8<!--文件上传的配置-->
<!--下面的id名字不要改变-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--最大的上传文件总大小-->
<property name="maxUploadSize" value="10485760"/>
<!--最大的上传文件单个大小-->
<property name="maxUploadSizePerFile" value="2097152"/>
</bean>编写jsp页面
1
2
3
4
5
6
7<%--method=post--%>
<%--enctype=multipart/form-data--%>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="avatar">
<input type="text" name="username">
<input type="submit" value="上传一下">
</form>编写Controller, 参数为MultipartFile
1 | //JAVA代码 |
文件下载
1 | //设置响应头,控制浏览器下载该文件 |
拦截器
自带的,所以不用加依赖
类implements HandlerInterceptor
1 | public class LoginInterceptor implements HandlerInterceptor { |
配置xxx-servlet.xml
1 | <mvc:interceptors> |
Mybatis
ORM
- Object-Relation-Mapping 对象-关系-映射
概念
mybatis是一个优秀的持久化框架,支持定制化的SQL语句、储存过程和高级映射。避免了所有JDBC代码和手动设置参数。
mybatis vs hibernate
- mybatis 支持 定制化SQL,hibernate自动生成所有的SQL
- mybatis比较灵活,支持SQL调优;hibernate可以跨数据库,理论上可以无缝数据库迁移
- mybatis适用于业务更改频繁,需要SQL调优的系统;hibernate适用于单表查询较多的系统,比如后台
作用
- 可以直接写SQL,封装了除SQL以外的其他的ORM功能
缓存
查询数据时将查询结果存放到内存(缓存区)中。
每次查询数据时,先判断缓存区中是否存在数据,
- 如果存在,就从缓存区中获取数据
- 如果不存在,就从数据库中获取数据,将数据存放到缓存区中,给下次访问使用
好处
- 避免频繁与数据库交互,提高数据访问效率。提升系统性能
一级缓存
一级缓存是SqlSession自带的。SqlSession对象被创建,一级缓存就存在了。
如果SqlSession对象关闭或调用清理方法,会导致缓存失效。
缓存底层实现就是通过HashMap实现的。
一级缓存的介质:内存
一级缓存介绍
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。
每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户
- MyBatis一级缓存的生命周期和SqlSession一致。
- MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。
- MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。
1 | <setting name="localCacheScope" value="SESSION"/> |
二级缓存
二级缓存介质——内存,硬盘
二级缓存SqlSessionFactory进行管理的
配置二级缓存介绍
在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。
二级缓存 -> 一级缓存 -> 数据库
项目基本结构
使用方式
建项目+加依赖(mybatis、mysql-connector)
创建配置文件mybatis-config.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
<configuration>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/class26"/>
<property name="username" value="root"/>
<property name="password" value="root123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/user.mapper.xml"/>
</mappers>
</configuration>单表配置
1
2
3
4
5
6
7
8
9
<mapper namespace="maiz">
<delete id="delete" >
delete from user where user_id=#{userId}
</delete>
</mapper>- namespace ,可以任意填
启动框架
- 读取配置
- 构建SQLSessionFactory
- 开启SQLSession
- 执行数据库操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class App
{
public static void main( String[] args ) throws IOException {
//读配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//构建SQLSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
sqlSession.delete("maiz.delete",16);
sqlSession.commit();
}
}
==>传参
一个参数,自动绑定
多个参数,需要传入对象(或map)将多个参数封装
xml中,参数需要用#{属性名}获取
1
select * from user where username=#{username} and password=#{password}
<==结果映射
- resultType : 将结果映射到一个对象上,默认 数据库字段与实体类的属性名完全匹配才能映射
- resultMap : 将结果映射到一个配置好的resultMap
- <resultMap id=”” type=”{基于某个类型进行映射}”>
Q : 下划线转为驼峰式的问题
1 | <settings> |