SSM

小德 2021-12-02 12:57:59
Categories: Tags:

SSM

1_Spring

1 什么是Spring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
是一个轻量级的企业级应用框架
企业应用开发的"一站式"选择,贯穿于表现层、业务层、持久层
优点
低侵入式设计
独立于各种应用服务器
依赖注入特性将组件关系透明化,降低耦合度
面向切面编程特性允许将通用任务进行集中式处理
与第三方框架的良好整合

Spring设计理念
是面向Bean的编程

Spring两大核心技术?

控制反转(IoC:Inversion of Control)/依赖注入(DI:Dependency Injection)

依赖注入 是控制反转的 实现手段。

面向切面编程(AOP:Aspect Oriented Programming)

2 IOC概念

1
2
3
4
5
6
7
8
9
10
11
12
13
IoC是什么
  Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

  ●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

DI —Dependency Injection,即“依赖注入”

IoC和DI由什么关系呢?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

IOC 是通过DI 来实现的,DI 是IOC 实现的手段。

3 为什么要进行控制反转?

1
2
3
4
5
6
7
8
9
10
用户模块业务层调用数据层

用户模块业务层与数据层高度耦合,怎么解决?

public class UserServiceImpl implements UserService {
// 实例化所依赖的 UserDao 对象
private UserDao dao = new UserDaoImpl();

}

4 第一个Spring程序

1
2
3
4
下载依赖jar包,并引入到项目中
编写Spring 配置文件
编写测试代码通过Spring进行属性注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

</dependencies>
1
2
3
4
ApplicationContext ac
=new ClassPathXmlApplicationContext("applicationContext-mybatis.xml");
Student s=ac.getBean("student",Student.class);
System.out.println(s);

2_SpringIOC

1 值注入的方式

1 set 方法注入

1
2
3
4
5
<bean id="student" class="com.xmx.pojo.Student">
<property name="sid" value="100"/>
<property name="sname" value="张三"/>
<property name="age" value="100"/>
</bean>

2 构造方法注入

1
2
3
4
<bean id="student" class="com.xmx.pojo.Student">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="张三"/>
</bean>

2_1构造方法注入

1
2
3
4
5
<bean id="student" class="com.xmx.pojo.Student">
<constructor-arg name="sid" value="1"/>
<constructor-arg name="sname" value="张三"/>
<constructor-arg name="age" value="100"/>
</bean>

​ 引用类型的注入

1
2
3
4
<bean id="teacher" class="com.xmx.pojo.Teacher">
<property name="tname" value="金平"/>
<property name="book" ref="book"/>
</bean>

3 p 命名空间 注入

1
2
3
4
5
6
7
8
使用p命名空间可以用bean 元素的属性代替<property/>元素。

命名空间中 加入
xmlns:p="http://www.springframework.org/schema/p"

<bean id="book" class="com.xmx.pojo.Book" p:bid="111" p:bname="平凡的世界">
</bean>

其他类型注入:

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
<!--数组类型属性注入-->
<property name="array">
<array>
<value>arr1</value>
<value>arr2</value>
</array>
</property>

<!--list类型属性注入-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>

<!--map类型属性注入-->
<property name="map">
<map>
<entry key="map-key1" value="map-value1"></entry>
<entry key="map-key2" value="map-value2"></entry>
</map>
</property>

<!--set类型属性注入-->
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>

使用<value></value>注入空字符串值
使用<null/>注入null

使用<value>标签实现
注意特殊字符的处理

分别使用<list>、<set>、<map>、<props>标签实现
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
<bean id="teacher" class="com.xmx.entity.Teacher">
<property name="tid" value="100"/>
<property name="tname" value="你好"/>
<property name="citys">
<array>
<value>长沙</value>
<value>北京</value>
</array>
</property>
<property name="schools">
<list>
<value>长郡</value>
<value>雅礼</value>
</list>
</property>
<property name="homes">
<set>
<value>长沙</value>
<value>广州</value>
</set>
</property>
<property name="fs">
<map>
<entry key="语文" value="100"/>
<entry key="数学" value="100"/>
</map>
</property>
<property name="ps">
<props>
<prop key="driver">com.jdbc.driver</prop>
<prop key="root">root</prop>
</props>
</property>
<property name="st">
<null/>
</property>
<property name="xh">
<value></value>
</property>
</bean>

4 引用类型的自动注入

1
2
3
4
5
6
7
8
9
10
11
12
13
1.默认按照类型
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byType">

2.byName 按照名称注入:
<bean id="xx" class="..." autowire="byName">
<property> </property>
</bean>
3.byType 按照类型注入:
<bean id="xx" class="..." autowire="byType">
<property> </property>
</bean>


5 注解注入

一个接口,两个实现类A,B, 用@Autowired默认类型注入接口的实现类,实现类A,B类型一样,所以区分不了注入的是哪个,1.给其中一个加别名@Component(“注入位置变量名”),就可以区分。2.给两个实现类取一个不同@Component(“xxx”),通过@Autowired搭配@Qualifier(“xxx”)进行按照名称注入。

1.导入命名空间

1
2
3
4
xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

2.扫描包中注解标注的类:
<context:component-scan base-package=”service,dao” />

3.注解解析:

1)set方法注入 @Value(“张宏”)
2)引用类型的注入,默认按照类型注入 @Autowired

使用@Resource注解实现组件装配

3)别名 @Qualifier(“bb”) 搭配@Autowired使用,按照名称注入

4)**(不在XML文件写bean)加了这个注解,bean才能被扫描到**

@Component:实现Bean组件的定义, 不知道 用什么注解 @Repository:用于标注DAO类
@Service:用于标注业务类
@Controller:用于标注控制器类

5)@Scope(“prototype”) 标注bean的作用范围。

4.Spring 的新注解

@Configuration 指定当前类是 一个Spring 配置类, 当创建容器时,从该类上 加载注解(相当于beans的配置文件)
@ComponentScan 指定Spring初始化 扫描的包 (相当于下面的扫描语句)<context:component-scan base-package=”com.lpc”/>
@Bean 把返回的对象放在配置文件beans中
@PropertySource() 加载 .properties 文件
@Import 导入其他配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Spring的原始注解: 主要是用来 替代 bean的操作。

@Autowired
@Qualifier("userDao")
private UserDao dao;

@Resource(name="userDao")
private UserDao dao;

使用上面的注解 不能 全部替代xml 配置文件。


Spring的新注解:
@Configuration 指定当前类是 一个Spring 配置类, 当创建容器时,从该类上 加载注解
@ComponentScan 指定Spring初始化 扫描的包
<context:component-scan base-package="com.lpc"/>
@Bean 用在方法上,该Spring 配置Bean
@PropertySource() 加载 .properties 文件
@Import 导入其他配置类

6 autowide 和 resources 区别

1
2
3
4
5
6
7
8
9
10
11
注入引用类型的值:
@Autowired
注入值的时候:首先按照类型注入,如果有相同的类型,我们可以按照名称注入。如果按照名称注入:要@Qualifier( "mysq1")注解配合
@Resources注入值:
首先按照名称注入,再按照类型注入。

区别:
1 Autowired是 Spring的注解,Resources是Java的注解
2 Autowired首先按照类型匹配,Resources首先按照名称匹配
3 Autowired根据名称匹配,要配合@Qualifier注解
Resources配合自己的name属性

7 注入数据源

xml文件方式

1
2
3
4
5
6
7
8
9
10
11
导入依赖
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 加载数据库配置文件 -->
<context:property-placeholder location="classpath:database.properties"/>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}"></property>
<property name="jdbcUrl" value="${url}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>


@Test
public void test1() throws SQLException {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource=ac.getBean("dataSource",DataSource.class);
Connection conn=dataSource.getConnection();
System.out.println(conn);
conn.close();
}

注解方式

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
@Configuration
@ComponentScan("com.lpc")
@Import({DataSourceConfig.class})
public class SpringConfig {
}

@Configuration
@PropertySource("classpath:database.properties")
public class DataSourceConfig {
@Value("${driver}")
private String driver;
@Value("${url}")
private String url;
@Value("${user}")
private String user;
@Value("${password}")
private String password;

@Bean("dataSource")
public DataSource getDataSource(){
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setJdbcUrl(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
}

@Test
public void test3() throws SQLException {
ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);
Lj lj=ac.getBean("lj",Lj.class);
System.out.println(lj.toString());

DataSource dataSource=ac.getBean("dataSource",DataSource.class);
Connection conn=dataSource.getConnection();
System.out.println(conn);
conn.close();
}

8 bean 的5种作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

在Spring 容器当中,一共提供了5种作用域类型,在配置文件中,通过属性scope来设置bean的作用域范围。

1. singleton:
当Bean的作用域为singleton的时候,Spring容器中只会存在一个共享的Bean实例,所有对Bean的请求只要id与bean的定义相匹配,则只会返回bean的同一实例。单一实例会被存储在单例缓存中,为Spring的缺省作用域。

2 prototype:

每次对该Bean请求的时候,Spring IoC都会创建一个新的作用域。
对于有状态的Bean应该使用prototype,对于无状态的Bean则使用singleton

3 request:
Request作用域针对的是每次的Http请求,Spring容器会根据相关的Bean的
定义来创建一个全新的Bean实例。而且该Bean只在当前request内是有效的。

4 session:

针对http session起作用,Spring容器会根据该Bean的定义来创建一个全新的Bean的实例。而且该Bean只在当前http session内是有效的。

5 global session:

类似标准的http session作用域,不过仅仅在基于portlet的web应用当中才有意义。Portlet规范定义了全局的Session的概念。他被所有构成某个portlet外部应用中的各种不同的portlet所共享。在global session作用域中所定义的bean被限定于全局的portlet session的生命周期范围之内。

2 多个配置文件导入

Spring 配置文件可以配置多个 分模块开发

如多个 配置文件 applicationContext.xml applicationContext-dao.xml applicationContext-service.xml

导入其他配置文件

1
<import resource="classpath:applicationContext-entity.xml"/>

读取的时候,可以读取多个,

1
2
3
4
String[] config={"applicationContext.xml","applicationContext-dao.xml","applicationContext-service.xml"};
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
使用通配符 读取?
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext*.xml");

3_SpringAOP

1 什么是 AOP?

1
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

2 为什么需要 AOP

1
2
开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?
在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

3 AOP 实现的分类

1
2
3
4
5
6
7
8
9
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。

AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码:

按照 AOP 框架修改源代码的时机,可以将其分为两类:

静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。

动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

代理模式

1
2
3
4
5
代理模式 分为:  静态代理和动态代理

代理,就是你委托别人帮你办事,所以代理模式也有人称作委托模式的。

代理模式的目的: 代理就是对目标方法进行增强。

1 静态代理

1
2
3
4
5
静态代理,代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,
这样,当我们需要调用被代理类的方法时,可以通过调用代理类的方法来做到。

静态代理实现的步骤:
1 定义接口。
1
2
3
4
5
6
7
8
9
10
11
12
public interface PersonService {
void savePerson();
}

public class PersonServiceImpl implements PersonService {
@Override
public void savePerson() {
System.out.println("添加人");
}
}

正常情况,开闭原则。
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
2 写增强类,代理类,代理类必须实现和被代理类同样的接口


public class MyTransaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
}

public class PersonServiceProxy implements PersonService {
//目标类
private PersonService personService;

//增强类
private MyTransaction transaction;

public PersonServiceProxy(PersonService personService, MyTransaction transaction) {
this.personService = personService;
this.transaction = transaction;
}

@Override
public void savePerson() {
transaction.beginTransaction();
personService.savePerson();
}
}


3 调用
@Test
public void test2(){
MyTransaction myTransaction=new MyTransaction();
PersonServiceImpl impl=new PersonServiceImpl();
PersonServiceProxy proxy=new PersonServiceProxy(impl,myTransaction);
proxy.savePerson();
}

2 静态代理的特点

1
2
3
4
5
6
7
8
9
10
11
1  由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
2 代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,


静态代理模式的缺点:

1、假设一个系统中有100个Service,则需要创建100个代理对象

2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码

3、由第一点和第二点可以得出:静态代理的重用性不强

3 动态代理

1
2
3
4
5
6
7
8
9
静态代理的缺点可以用 动态代理解决:

动态代理实现的目的和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的

动态代理分为JDK的动态代理和cglib动态代理

它俩有略微的差别:
JDK动态代理产生的代理类和目标类实现了相同的接口;
glib动态代理产生的代理类是目标对象的子类。
1 JDK动态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1 写接口
public interface PersonService {
void savePerson();
}

2 目标对象
public class PersonServiceImpl implements PersonService {
@Override
public void savePerson() {
System.out.println("添加人");
}
}

增强类

public class MyTransaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
生成代理的工具类。

需要两个核心的接口类。
java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类)

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,
每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用。


Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载

interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。


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
public class MyProxy {
// 目标对象 被代理的类
Object target ;
// 增强类
MyTransaction zq;

public MyProxy(Object target, MyTransaction zq) {
this.target = target;
this.zq = zq;
}
// 产生一个 代理类

public Object getProxyInstance(){
Object obj=Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
// 匿名内部类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
zq.beginTransaction();
Object returnvalue=method.invoke(target,args);
return returnvalue;
}
});
// 返回代理对象
return obj;
}
}
2 CGlib 动态代理
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>


1 实现一个业务类,注意,这个业务类并没有实现任何接口:

public class TeacherService {
public void addTeacher(){
System.out.println("添加老师");
}
}

2 定义增强类
public class MyTransaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
}


3 自定义 MethodInterceptor

public class MyMethodInterceptor implements MethodInterceptor {
// 增强类
private MyTransaction transaction;

public MyMethodInterceptor(MyTransaction transaction) {
this.transaction = transaction;
}

/**
*
* @param o cglib生成的代理对象
* @param method 被代理对象方法
* @param objects 方法入参
* @param methodProxy 代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
transaction.beginTransaction();
Object object = methodProxy.invokeSuper(o, objects);
return object;
}
}


4 定义生成代理的类

public class CGProxy {
public <T> T createProxy(T t,MyTransaction obj){
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(t.getClass());
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor(obj));
// 创建代理对象
T proxy= (T)enhancer.create();
return proxy;
}
}



5 测试:

@Test
public void test3(){
CGProxy proxy=new CGProxy();
// 代理类
TeacherService teacherService=new TeacherService();
// 增强类
MyTransaction myTransaction = new MyTransaction();

TeacherService t= proxy.createProxy(teacherService,myTransaction);
t.addTeacher();

}
3 JDK动态代理和CGLib的区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1)、JDK和CGLib的区别

JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

2)、Spring在选择用JDK还是CGLib的依据

当Bean实现接口时,Spring就会用JDK的动态代理
当Bean没有实现接口时,Spring使用CGLib来实现

可以强制使用CGLib(在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)

3)、JDK和CGLib的性能对比

使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理

5 AOP 术语

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AOP 领域中的特性术语:

切面(Aspect): 一个模块化的横切逻辑(或称为横切关注点),可能会横切多个对象。切面可理解为 (增强处理和切入点组成)

增强(Advice): 切面在某个特定连接点 上执行 的代码逻辑。

连接点(join point): 程序执行中 某个具体的 执行点。

切入点(PointCut): 对连接点的特征进行描述。可以使用正则表达式。

目标对象:(Target Object): 被一个或多个切面 增强的对象。

AOP代理对象(AOP Proxy): 由AOP框架所创建的对象,实现执行增强处理方法。

织入(Weaving): 将增强处理连接到 应用程序中 的类型或者 对象上的过程。

6 XML AOP 的使用 前置 后置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1  AOP 功能还依赖2个 jar包。和 AOP配置使用。

<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

2 做日志

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</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
public interface StudentService {
int addStudent();
}


public class StudentServiceImpl implements StudentService {
@Override
public int addStudent() {
System.out.println("添加了一个学生......");
return 100;
}
}


import org.apache.log4j.Logger;

public class MyLogger {
private static final Logger log=Logger.getLogger(MyLogger.class);
// JoinPoint 连接点 信息
public void qx(JoinPoint jp){
System.out.println("检查权限.........");
log.info("调用了"+jp.getTarget()+"的"+jp.getSignature().getName()
+"方法,方法入参是:"+ Arrays.toString(jp.getArgs()));
}

public void logger(JoinPoint jp,Object result){
System.out.println("记录日志.......");
log.info("调用了"+jp.getTarget()+"的"+jp.getSignature().getName()
+"方法,方法返回值是::"+ result);
}
}



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
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<bean id="studentService" class="com.lpc.service.StudentServiceImpl"/>

<!--增强类-->
<bean id="myLogger" class="com.lpc.config.MyLogger"/>

<!--配置切面-->
<aop:config>
<!--定义切入点-->
<aop:pointcut id="pt" expression="execution( public * addStudent())"/>
<!--引用包含增强bean-->
<aop:aspect ref="myLogger">
<aop:before method="qx" pointcut-ref="pt"/>
<aop:after-returning method="rz" pointcut-ref="pt" returning="result"/>
</aop:aspect>
</aop:config>
</beans>
1
2
3
4
5
6
@Test
public void test5(){
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService stu= ac.getBean("studentService",StudentService.class);
stu.addStudent();
}

execution 表达式的使用

execution 表达式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
常用的 execution 配置:

通过方法签名定义切点

execution(public * *(..))

匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;

execution(* com.baobaotao.Waiter.*(..))

第一个*代表返回任意类型 *代表Waiter类口中的所有方法;

execution(* com.baobaotao.*.*(..))

匹配com.baobaotao包下所有类的所有方法;

execution(* com.baobaotao..*.*(..))

匹配com.baobaotao包、子孙包下所有类的所有方法

execution(* com..*Dao.find*(..))

com的任何包下类名后缀为Dao的类,方法名必须以find为前缀

7 异常抛出 最终 增强

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
/**
* 定义包含增强方法的JavaBean
*/
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);

public void afterThrowing(JoinPoint jp, RuntimeException e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
}
}


<!-- 声明增强方法所在的Bean -->
<bean id="theLogger" class="aop.ErrorLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增强方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 将afterThrowing()方法定义为异常抛出增强并引用pointcut切入点 -->
<!-- 通过throwing属性指定为名为e的参数注入异常实例 -->
<aop:after-throwing method="afterThrowing"
pointcut-ref="pointcut" throwing="e" />
</aop:aspect>
</aop:config>

当程序 运行异常会抛出异常,不抛出异常则 不会进入异常处理方法。
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
最终增强 是 无论目标 对象的方法是否正常运行 还是抛出异常。该增强都会被执行的一种增强方法。

其与Java中的 finally 代码块作用相似。

/**
* 定义包含增强方法的JavaBean
*/
public class AfterLogger {
private static final Logger log = Logger.getLogger(AfterLogger.class);

public void afterLogger(JoinPoint jp) {
log.info(jp.getSignature().getName() + " 方法结束执行。");
}
}


<!-- 声明增强方法所在的Bean -->
<bean id="theLogger" class="aop.AfterLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增强方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 将afterLogger()方法定义为最终增强并引用pointcut切入点 -->
<aop:after method="afterLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

8 环绕增强

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
/**
* 定义包含增强方法的JavaBean
*/
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);

public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
try {
Object result = jp.proceed();
log.info("调用 " + jp.getTarget() + " 的 "
+ jp.getSignature().getName() + " 方法。方法返回值:" + result);
return result;
} catch (Throwable e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
throw e;
} finally {
log.info(jp.getSignature().getName() + " 方法结束执行。");
}

}
}


<!-- 声明增强方法所在的Bean -->
<bean id="theLogger" class="aop.AroundLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增强方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 将aroundLogger()方法定义为环绕增强并引用pointcut切入点 -->
<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>


通过增强方法申明: ProceedingJoinPoint 类型的参数,可以获得连接点的信息,方法与 JointPoint 相同。

ProceedingJoinPoint 是 JointPoint 的子接口。其不仅封装了 目标方法和 入参数组,还封装了 被代理的目标对象。

通过 proceed()方法 可以调用真正的目标方法,从而达到对连接点的完全控制。

环绕增强是 最强大的 增强处理。

9 使用注解做增强

1
2
3
4
5
6
7
8
9
10
11
12
13
1 打开注解 AOP 的支持。

<!-- 注解扫描 -->
<context:component-scan base-package="service,dao,aop" />

//打开AOP的注解支持
<aop:aspectj-autoproxy />

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@Aspect
@Component
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);

@Pointcut("execution(* service.UserService.*.*(..))")
public void pointcut() {}

/**
* 前置增强
* @param jp
*/
@Before("pointcut()")
public void before(JoinPoint jp) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}

/**
* 后置增强
* @param jp
* @param returnValue
*/
@AfterReturning(pointcut = "pointcut()", returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法返回值:" + returnValue);
}

}


/**
* 通过注解实现异常抛出增强
*/
@Aspect
@Component
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);

@AfterThrowing(pointcut = "execution(* service.UserService.*.*(..))", throwing = "e")
public void afterThrowing(JoinPoint jp, RuntimeException e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
}

}


/**
* 通过注解实现最终增强
*/
@Aspect
@Component
public class AfterLogger {
private static final Logger log = Logger.getLogger(AfterLogger.class);

@After("execution(* service.UserService.*.*(..))")
public void afterLogger(JoinPoint jp) {
log.info(jp.getSignature().getName() + " 方法结束执行。");
}

}


/**
* 通过注解实现环绕增强
*/
@Aspect
@Component
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);

@Around("execution(* service.UserService.*(..))")
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
try {
Object result = jp.proceed();
log.info("调用 " + jp.getTarget() + " 的 "
+ jp.getSignature().getName() + " 方法。方法返回值:" + result);
return result;
} catch (Throwable e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
throw e;
} finally {
log.info(jp.getSignature().getName() + " 方法结束执行。");
}
}

}

4_Spring整合 MyBatis 框架

Spring 整合 Mybatis的基本思路就是 MyBatis的所有核心 接口和类 交给 Spring来处理

读取配置文件、组件的创建、组件之间的依赖关系以及整个框架的生命周期都由Spring容器统一管理

1 原始整合方式

1
2
3
4
5
6
Spring和MyBatis的整合步骤  (基础版本)
1 整合所需的依赖及配置
2 通过Spring配置文件配置数据源
3 通过Spring配置文件创建SqlSessionFactory
4 通过SqlSessionTemplate操作数据库

整合的简单版 操作过程

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.3</version>
</dependency>

<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>

<!-- MyBatis依赖 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>


</dependencies>

核心配置文件中的配置 applicationContext.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
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.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 ">

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<!--<property name="url">
<value><![CDATA[jdbc:mysql://127.0.0.1:3306/cvs_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai]]></value>
</property>-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/xmxj8?useUnicode=true
&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>

<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用数据源组件 -->
<property name="dataSource" ref="dataSource" />
<!-- 引用MyBatis配置文件中的配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<!-- 配置SQL映射文件信息 -->
<property name="mapperLocations">
<list>
<value>classpath:com/lpc/dao/*.xml</value>
</list>
</property>
</bean>
<!-- 配置SqlSessionTemplate -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

<context:component-scan base-package="com.lpc"/>
</beans>

接口

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
public interface StudentMapper {
List<Student> findAll();
}



<mapper namespace="com.lpc.dao.StudentMapper">
<select id="findAll" resultType="student">
select * from student
</select>
</mapper>




@Repository("studentMapper")
public class StudentMapperImpl implements StudentMapper {
@Autowired
private SqlSessionTemplate sqlSession;

@Override
public List<Student> findAll() {
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
return mapper.findAll();
}
}

测试。

2 扫描包的整合方式

1
2
3
4
5
6
使用MapperScannerConfigurer注入映射器
自动扫描指定包下的Mapper接口,并将它们直接注册为MapperFactoryBean


MapperScannerConfigurer递归扫描基准包下所有接口,若它们在SQL映射文件中定义过,则动态注册为映射器实现类

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
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.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 ">

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<!--<property name="url">
<value><![CDATA[jdbc:mysql://127.0.0.1:3306/cvs_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai]]></value>
</property>-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/xmxj8?useUnicode=true
&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>

<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用数据源组件 -->
<property name="dataSource" ref="dataSource" />
<!-- 引用MyBatis配置文件中的配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>


<!-- 配置DAO 用包扫描的方式-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> -->
<property name="basePackage" value="com.lpc.dao" />
</bean>

<context:component-scan base-package="com.lpc"/>
</beans>

在Dao 的接口上扫描进来就行。

3 映射文件和 接口不在同一个包下面处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用数据源组件 -->
<property name="dataSource" ref="dataSource" />
<!-- 引用MyBatis配置文件中的配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<!--如果不在一起 配置-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>


<!-- 配置DAO 用包扫描的方式-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> -->
<property name="basePackage" value="com.lpc.dao" />
<property name="annotationClass" value="org.springframework.stereotype.Repository" />
</bean>


5_Spring事物

1 声明式事物

1
2
3
4
5
6
7
8
9
10
11
12
1 为什么 Spring 要管理事物。

传统 硬编码方式

弊端
事务相关代码分散在业务方法中难以重用
复杂事务的编码难度高,增加了开发难度

Spring基于AOP实现声明式事务处理机制
优势
全在配置文件完成,将事务处理与业务代码分离,降低了开发和维护难度

1 声明式配置策略

1
2
3
4
5
6
7
8
9
10
11
声明式事务关注的核心问题是
对哪些方法,采取什么样的事务策略


配置步骤
1 导入命名空间
2 定义事务管理器
3 设置事务属性
4 定义事务切面


2 事物属性的说明

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
propagation:事务传播机制
REQUIRED(默认值)、REQUIRES_NEWS、MANDATORY、NESTED
SUPPORTS、NOT_SUPPORTED、NEVER
isolation:事务隔离级别
DEFAULT(默认值)
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE


REQUIRED(默认值) 可满足大多数的事务需求,可以作为首选的事务传播行为

timeout:事务超时时间
允许事务运行的最长时间,以秒为单位
超过给定的时间自动回滚,防止事务执行时间过长而影响系统性能
read-only:事务是否为只读
默认值为false,对于仅执行查询功能的事务设置为true,提高事务处理性能
rollback-for:设定能够触发回滚的异常类型
Spring默认只在抛出RuntimeException时才标识事务回滚
可以通过全限定类名自行指定需要回滚事务的异常
no-rollback-for:设定不触发回滚的异常类型
Spring默认CheckedException不会触发事务回滚
可以通过全限定类名自行指定不需回滚事务的异常

3 操作演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务传播特性 -->
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 定义切面 -->
<aop:config>
<aop:pointcut id="serviceMethod"
expression="execution(* cn.cvs.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>

2 使用注解声明事物

1 在Spring配置文件中配置事务管理类,并开启注解处理事务功能

1
2
3
4
5
6
7
8
<bean id="transactionManager" class="org.springframework.jdbc.datasource.
DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven />

事务管理器的id被定义为transactionManager,则<tx:annotation-driven/>不需要指定transaction-manager属性的值

2 在 类上面添加注解支持 使用@Transactional为方法添加事务支持

1
2
3
4
5
@Transactional
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
……
}

6_SpringMVC

1 MVC模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
视图(View)-对应组件:JSP或者HTML文件
控制器(Controller)-对应组件:Servlet
模型(Model)-对应组件:JavaBean


MVC优点
MVC三个模块相互独立,松耦合架构
多视图共享一个模型,大大提高代码的可重用性
控制器提高了应用程序的灵活性和可配置性
有利于软件工程化管理
MVC缺点
增加了系统结构和实现的复杂性,不适合小型规模的项目
视图层与模型之间需要控制器做中间的连接控制,所以效率较低


2 编写第1个 Spring MVC 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1  下载JAR文件
spring-web-5.2.2.RELEASE.jar
spring-webmvc-5.2.2.RELEASE.jar
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.3</version>
</dependency>

2 编写配置文件
创建Spring MVC的配置文件:springmvc-servlet.xml
在web.xml中配置DispatcherServlet,命名为springmvc


基本用到的 pom 依赖

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.3</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.3</version>
</dependency>

<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>

<!-- MyBatis依赖 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>

</dependencies>

Spring-MVC 核心配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--定义控制器,访问路径为/hello-->
<bean name="/hello" class="com.lpc.controller.HelloController"/>
<!-- 完成视图的对应 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

配置进入 web.xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Spring MVC的核心是一个Servlet,叫做前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 加载Spring MVC配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

基本的跳转

1
2
3
4
5
6
7
8
9
10
public class HelloController extends AbstractController {

@Override
protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest httpServletRequest,
javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
System.out.println("Spring MVC框架搭建成功。");
// 跳转到hello页面
return new ModelAndView("hello");
}
}
1
2
3
4
5
Spring提供了多种处理器映射的支持
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping


如果有多个请求时,uspringmvc-servlet.xml中需要配置多个映射关系,并建立多个Controller来进行请求处理,实现繁琐,如何解决?

1
2
3
4
不建议使用:
<bean name="/user" class="cn.cvs.controller.UserController"/>
<bean name="/supplier" class="cn.cvs.controller.SupplierController" />

解决方案: 使用Spring MVC提供的一键式配置方法:mvc:annotation-driven,通过注解的方式进行开发

3 通过注解编写 SpringMVC

总体步骤是:

1 导入依赖

2 配置 web.xml

3 编写 Controller

4 编写 Springmvc的核心文件.

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.3</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.3</version>
</dependency>

<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>

<!-- MyBatis依赖 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>


</dependencies>

编写 web.xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 加载Spring MVC配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

编写 controller

1
2
3
4
5
6
7
8
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("进来了,去Hello页面");
return "hello";
}
}

编写 配置文件

1
2
3
4
5
6
7
8
9
<!-- 注解扫描的包 -->
<context:component-scan base-package="com.lpc.controller"/>
<!---->
<mvc:annotation-driven/>
<!-- 完成视图的对应 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

扫描包 还可以配置为:

1
2
3
4
<!-- 注解扫描的包 -->
<context:component-scan base-package="com.lpc">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

注解中 mvc:annotation-driven 作用

1
2
3
4
5
6
7
8
9
10
一、mvc:annotation-driven的作用
Spring 3.0.x中使用了mvc:annotation-driven后,默认会帮我们注册默认处理请求,参数和返回值的类,其中最主要的两个类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter ,分别为HandlerMapping的实现类和HandlerAdapter的实现类,从3.1.x版本开始对应实现类改为了RequestMappingHandlerMapping和RequestMappingHandlerAdapter。

HandlerMapping的实现类的作用
实现类RequestMappingHandlerMapping,它会处理@RequestMapping 注解,并将其注册到请求映射表中。

HandlerAdapter的实现类的作用
实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。

当配置了mvc:annotation-driven/后,Spring就知道了我们启用注解驱动。然后Spring通过context:component-scan/标签的配置,会自动为我们将扫描到的@Component@Controller@Service@Repository等注解标记的组件注册到工厂中,来处理我们的请求。

注意: 如果配置了 / 所有东西都拦截 ,静态页面过不去的问题。

1
2
<!--静态页面,如html,css,js,images可以访问-->
<mvc:default-servlet-handler />
1
2
 <!--静态页面,如html,css,js,images可以访问-->
<mvc:default-servlet-handler />

SpringMVC的请求流程。

Spring MVC 的体系结构

1
2
3
4
5
6
7
8
9
10
11
12
13
DispatcherServlet(前端控制器)
Spring MVC最核心的类
web.xml中配置

Handler(处理器)
对应MVC中的C(Controller层)
作用:实际处理请求
标注了@RequestMapping的所有方法都可以看做是一个Handler
ModelAndView
模型对象信息

逻辑视图名

SpringMVC体系结构

4 SpringMVC中相关注解

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping 注解:

不仅可以作用于控制器的方法上,还可以标注到控制器类上
映射规则
通过请求URL进行映射
通过请求方法进行映射

@RequestMapping(value="/view", method=RequestMethod.GET)
@GetMapping
@PostMapping

@RequestMapping映射的请求信息必须保证全局唯一

5 SpringMVC 传入参数的方式

1 Request 请求对象方式

1

2 传入参数和 参数一致

1

3 对象的形式

1

4 参数名不一致是: @RequestParam

1

5 路径变量 @PathVariable获取路径中的参数接收

1
2
3
4
5
6
7
8
9
10
11
12
13
@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值

@PathVariable("xxx")
通过 @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)

@RequestMapping(value=”user/{id}/{name}”)
请求路径:http://localhost:8080/hello/show5/1/james

@RequestMapping("show5/{id}/3

{name}")
public ModelAndView test5(@PathVariable("id") Long ids ,@PathVariable("name") String names){

6 SpringMVC 中的出参处理

1 使用ModelAndView对象

1
2
3
4
5
6
7
8
9
10
ModelAndView
包含模型数据信息和视图信息
常用方法
添加模型数据
ModelAndView addObject(String attributeName,Object attributeValue);
ModelAndView addAllObjects(Map<String,?> modelMap);
设置视图
void setView(View view);
void setViewName(String viewName);

2 使用Model对象

1
2
3
4
5
6
7
Model
处理方法的入参为Model类型
数据结构:Map类型
常用方法:添加模型数据

Model.addAttribute(String attributeName, Object attributeValue);

3 使用Map 对象

1
2
3
4
5
6
7
8
9
10
Map
使用方法和Model一样

作为Spring MVC的标准用法,推荐使用Model

public String hello(Map<String,Object> map){
System.out.println("进来了,去Hello页面");
map.put("name","张三");
return "hello";
}

7 SpringMVC静态资源引入

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
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

静态资源引入 2种 方式:

采用<mvc:default-servlet-handler />

在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:

<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />


采用<mvc:resources />

<mvc:default-servlet-handler />将静态资源的处理经由Spring MVC框架交回Web应用服务器处理。而<mvc:resources />更进一步,由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能。

首先,<mvc:resources />允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中。通过location属性指定静态资源的位置,由于location属性是Resources类型,因此可以使用诸如"classpath:"等的资源前缀指定资源位置。传统Web容器的静态资源只能放在Web容器的根路径下,<mvc:resources />完全打破了这个限制。

其次,<mvc:resources />依据当前著名的Page Speed、YSlow等浏览器优化原则对静态资源提供优化。你可以通过cacheSeconds属性指定静态资源在浏览器端的缓存时间,一般可将该时间设置为一年,以充分利用浏览器端的缓存。在输出静态资源时,会根据配置设置好响应报文头的Expires 和 Cache-Control值。

在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303相应状态码,提示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端,以充分节省带宽,提高程序性能。


<mvc:resources location="/,classpath:/META-INF/publicResources/" mapping="/resources/**"/>

以上配置将Web根路径"/"及类路径下 /META-INF/publicResources/ 的目录映射为/resources路径。假设Web根路径下拥有images、js这两个资源目录,在images下面有bg.gif图片,在js下面有test.js文件,则可以通过 /resources/images/bg.gif 和 /resources/js/test.js 访问这二个静态资源。

<mvc:resources mapping="/static/**" location="/static/" />


<mvc:resources location="/img/" mapping="/img/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
处理方式:


<head>
<title>Title</title>
<link href="${pageContext.request.contextPath}/static/my.css" rel='stylesheet' type='text/css' />
</head>
<body>
<h1>我是 朱贼啊..............${name}</h1>
<h1>${pageContext.request.contextPath}</h1>
<p class="ys">总有梦</p>
</body>


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>

<link href="${ctx}/rs/css/style.css" rel='stylesheet' type='text/css' />

8 SpringMVC 拦截器

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
配置拦截器

<mvc:interceptors>
<!-- 日志拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/static/**" />
<bean class="拦截器java代码路径" />
</mvc:interceptor>
</mvc:interceptors>

<!--配置拦截器-->
<mvc:interceptors>
<!-- 非法登录拦截器 -->
<mvc:interceptor>
<!--拦截器匹配哪些请求-->
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/login.html"/>
<mvc:exclude-mapping path="/css/*"/>
<mvc:exclude-mapping path="/img/*"/>
<mvc:exclude-mapping path="/user/login"/>
<bean class="com.xmx.config.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

1)mvc:mapping 拦截器路径配置

2)mvc:exclude-mapping 拦截器不需要拦截的路径

要实现拦截器,要继承 HandlerInterceptor

1
2
3
4
5
6
7
8
9
10
11

boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;

void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;

void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
1
2
3
4
5
正常情况下,对于preHandle就是在在处理函数之前先执行,然后再执行处理函数,接着执行postHandle,最后再执行afterCompletion。afterCompletion无论是否出错是肯定要执行的,而postHandle则不是,不一定会执行。之后看源代码就知道他们的执行情况。

AsyncHandlerInterceptor接口则增添了afterConcurrentHandlingStarted方法,对于此还未研究,先不讨论。

HandlerInterceptorAdapter则默认实现了上述的接口,所以当我们仅仅要实现某个方法时,只需继承HandlerInterceptorAdapter,然后覆盖相应的方法。

9 转发和重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SpringMVC 中 默认就是转发:

转发的关键字 是: forward

return "index.jsp"; //这种方式默认的就是转发
return "forward:/index.jsp"; //这是全写的方式

重定向: return "redirect:index.jsp";

@RequestMapping("/hello1")
public String hello1(String username,String pwd){
if(username.equals("张三")&&pwd.equals("123")){
// 跳转到 /list
return "forward:list";
}else{
// 重定向到 world.html 登录页面
return "redirect:world.html";
}
}


7 URL匹配

1
2
3
其中/和/*的区别:
< url-pattern > / </ url-pattern > 不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类 。
< url-pattern > /* </ url-pattern > 会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

8 异常处理

局部异常处理

1
2
3
4
5
6
7
8
异常处理
HandlerExceptionResolver
resolveException()

局部异常处理
仅能处理指定Controller中的异常
@ExceptionHandler

局部异常

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
/**
* 一个抛出异常的接口
* @param account
* @param password
* @return
*/
@RequestMapping("/exLogin")
public String exLogin(String account, String password){
logger.debug("一个抛出异常的接口");
//调用service方法,进行用户匹配
SysUser user = sysUserService.login(account,password);
if(null == user){//登录失败
throw new NullPointerException("空指针异常!");
}else{
throw new RuntimeException("用户名或者密码不正确,跳转到错误页面!");
}
// return "redirect:/user/toMain";
}

/**
* 捕获异常信息,跳转到error页面
* @param e
* @param req
* @return
*/
@ExceptionHandler(value={Exception.class})
public String handlerException(Exception e, HttpServletRequest req){
req.setAttribute("e", e);
return "error";
}

全局异常处理

全局异常处理使用SimpleMappingExceptionResolver实现

1
2
3
4
5
6
7
8
<!--  全局异常处理 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.RuntimeException">error</prop>
</props>
</property>
</bean>
1
2
3
<body>
<h1>${exception.message }</h1>
</body>

9 Rest风格

10 JSR303校验

11 SpringMVC 单文件 多文件上传

1
2
3
4
5
6
文件上传需要用到的包

导入依赖jar包
commons-io-2.4.jar
commons-fileupload-1.2.2.jar

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>


commons-io可以不用自己导入,maven会自动导入对应版本的jar
1
https://www.cnblogs.com/hamawep789/p/10923722.html

12 SSM整合

1 SSM整合步骤

1 导入需要的 jar包,依赖。

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<dependencies>
<!-- Servlet jar包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- SpringWeb -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.3</version>
</dependency>
<!--Spring 环境-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<!--Spring 事物-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- c3p0数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.3</version>
</dependency>
<!--aop 依赖的jar包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--aop 依赖的jar包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>

<!-- MyBatis依赖 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>

</dependencies>

2 导入 各框架的核心配置文件

1
2
3
4
5
6
7
Spring 的核心配置文件: applicationContext.xml
数据源: database.properties
日志配置文件: log4j.properties

Mybatis 核心配置文件: mybatis-config.xml

SpringMVC核心配置: springmvc-servlet.xml

3 Spring 和 Springmvc 配置进web.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
61
62
63
1 启动Spring的启动文件。application在 resources中。
<!-- 1、启动Spring容器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2
<!-- Spring MVC的核心是一个Servlet,叫做前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 加载Spring MVC配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

3
<!-- 3、字符编码过滤器,一定要放在所有过滤器的最前面 -->
<filter>
<filter-name>CharacterEncodingFilter</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>
<init-param>
<!--请求编码和响应编码都为UTF-8-->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


<!--打包 mybatis 映射文件-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>

4 Spring核心文件 和 SpringMVC的配置

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
61
62
63
64
65
66
67
68
69
70
71
72
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.lpc.service"/>
<context:component-scan base-package="com.lpc.dao"/>

<!-- 读取数据库配置文件 -->
<context:property-placeholder location="classpath:database.properties"/>

<!-- 获取数据源(使用dbcp连接池) -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" scope="singleton">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
<property name="initialSize" value="${initialSize}"/>
<property name="maxActive" value="${maxActive}"/>
<property name="maxIdle" value="${maxIdle}"/>
<property name="minIdle" value="${minIdle}"/>
<property name="maxWait" value="${maxWait}"/>
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"/>
<property name="removeAbandoned" value="${removeAbandoned}"/>
<!-- sql 心跳 -->
<property name= "testWhileIdle" value="true"/>
<property name= "testOnBorrow" value="false"/>
<property name= "testOnReturn" value="false"/>
<property name= "validationQuery" value="select 1"/>
<property name= "timeBetweenEvictionRunsMillis" value="60000"/>
<property name= "numTestsPerEvictionRun" value="${maxActive}"/>
</bean>

<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- AOP 事务处理 开始 -->
<aop:aspectj-autoproxy />

<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.lpc.service..*(..))" id="transService"/>
<aop:advisor pointcut-ref="transService" advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<!-- AOP 事务处理 结束 -->

<!-- 配置mybitas SqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.cvs.dao" />
</bean>

</beans>
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<context:component-scan base-package="com.lpc.controller"/>
<mvc:resources mapping="/static/**" location="/static/" />

<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="features">
<list>
<!-- Date的日期转换器 -->
<value>WriteDateUseDateFormat</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- 配置多视图解析器 -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<!-- 是否启用参数支持,默认为true(支持),即xxx?format=json、xml等形式。 -->
<property name="favorParameter" value="true" />
<!-- favorPathExtension:是否支持扩展名,默认为true(支持),扩展名指xxx.json、xxx.xml等形式 -->
<property name="favorPathExtension" value="true" />
<!-- 默认ContentType -->
<property name="defaultContentType" value="application/json" />
<!-- 配置映射关系 -->
<!--扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用 -->
<property name= "mediaTypes">
<value>
json=application/json
xml=application/xml
html=text/html
</value>
</property>
</bean>
<!-- VIEW解析定义。内容协商视图解析器;根据contentNegotiationManager使用的不同mediaTypes决定不同的 view进行响应 默认使用json-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">

<!-- 内容协商管理器 用于决定media type -->
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<!-- 默认视图 解析 -->
<property name="defaultViews">
<list>
<bean class="com.alibaba.fastjson.support.spring.FastJsonJsonView">
<property name="charset" value="UTF-8"/>
</bean>
</list>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
</bean>



<!-- 配置interceptors -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.lpc.interceptor.SysInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

<!-- 全局异常处理 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">sysError</prop>
</props>
</property>
</bean>
</beans>

13 JSON数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 @ResponseBody


<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.4</version>
</dependency>

14 样式丢失

1
2
3
4
5
6
网络路径问题:

<c:set var="ctx" value="${pageContext.request.contextPath}"/>

${pageContext.request.contextPath}

网络路径

15 SpringMVC多视图配置

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

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
<property name="order" value="1"/>
</bean>
<!-- 配置freeMarker视图解析器(可解析HTML) -->
<bean id="viewResolverFtl" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<property name="contentType" value="text/html; charset=UTF-8"/>
<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="cache" value="true" />
<property name="suffix" value=".html" />
<property name="order" value="2"/>
</bean>

<!-- 配置freeMarker的模板路径 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">3600</prop>
<prop key="locale">zh_CN</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="date_format">yyyy-MM-dd</prop>
<prop key="number_format">#.##</prop>
</props>
</property>
</bean>

注意: 配置freeMarker视图解析器 要导入相应的 依赖。

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
1
2
3
4
5
6
配置freemaker的html视图解析器之前需要首先配置freemarker的路径。该路径中的templateLoderPath相当于视图解析器中的前缀。因此freemaker的视图解析器中是没有prefix属性。
配置freemarker视图解析器时要有viewClass属性
优先级问题:需要总是把InternalResourcceViewResolver放在freemarker后边,并且优先级设置相同,不然只能对同一个文件夹下的.html和.jsp文件生效(),对不同文件夹下的.html和.jsp总是使用优先级高的视图解析器去解析。


数字越低 优先级越高。

16 动态 数字导航

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
 <c:if test="${page<=5}">
<c:if test="${allPage<=5}">
<c:forEach var="i" begin="1" end="${allPage}">
${i}
</c:forEach>
</c:if>
<c:if test="${allPage>5}">
<c:forEach var="i" begin="1" end="5">
${i}
</c:forEach>
</c:if>
</c:if>


<c:if test="${page>5}">
<c:if test="${allPage>=page+2}">
<c:forEach var="i" begin="${page-2}" end="${page+2}">
${i}
</c:forEach>
</c:if>
<c:if test="${allPage<page+2}">
<c:forEach var="i" begin="${page-2}" end="${allPage}">
${i}
</c:forEach>
</c:if>
</c:if>