Spring

锐锋产乎钝石,明火炽乎暗木

1. 简介

1.1 定义

Spring认证框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。

1.2 背景

  • 2002年,Spring框架雏形:interface21框架
  • 2004年3月24日,发布1.0正式版本
  • Rod Johnson,Spring Framework创始人,音乐学家,轮子理论
  • SSH:Struts,Spring,Hibernate或SpringMVC,Spring,Hibernate
  • SSM:SpringMVC,Spring,Mybatis
  • SpringBoot:约定大于配置
  • SpringCloud

1.3 理念

使现有技术更加容易使用,本身是一个大杂烩,整合了现在的技术框架

1.4 获取

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>

1.5 优点

  • 开源、免费的框架(容器)
  • 轻量级、非入侵式的框架
  • 控制反转(IOC)、面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

1.6 组成

图片

2. IOC(Inversion of Control思想,DI是其一种实现方法)

有很多实现方式,注解零配置(约定大于配置)、XML配置

2.1 核心内容

IOC控制反转是一种设计思想,没有IOC的程序,对象的创建和对象间的依赖关系完全依赖,有IOC后,对象的创建,管理,装配由Spring控制

2.2 IOC创建对象

在配置文件加载的时候,bean就被实例化了

  • 构造器注入
1
2
3
4
5
6
7
8
    <bean id="hello" class="com.xxy.entity.Hello">
<!--使用下标赋值index-->
<!-- <constructor-arg index="0" value="spring"></constructor-arg>-->
<!--不建议使用类型type,不能处理多个参数-->
<!-- <constructor-arg type="java.lang.String" value="spring1"></constructor-arg>-->
<!--使用参数名 name-->
<constructor-arg name="name" value="spring2"></constructor-arg>
</bean>
  • set方式注入【依赖注入DI】
    • 实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.xxy.entity;

import java.util.*;

public class Student {
private String name;
private Hello hello;
private int age;
private String[] book;
private List<String> hobbys;
private Map<String,String> cards;
private Set<String> games;
private Properties info;
private String wife;

//。。。省略toString和getset方法
}

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 version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="hello" class="com.xxy.entity.Hello">
<property name="name" value="x"></property>
</bean>

<bean id="student" class="com.xxy.entity.Student">
<!--普通值注入-->
<property name="name" value="zs"></property>
<property name="age" value="23"></property>
<!--Bean注入-->
<property name="hello" ref="hello"></property>
<!--数组注入-->
<property name="book">
<array>
<value>《西游记》</value>
<value>《红楼梦》</value>
</array>
</property>
<!--List注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
</list>
</property>
<!--Map注入-->
<property name="cards">
<map>
<entry key="身份证" value="22"></entry>
<entry key="银行卡" value="122"></entry>
</map>
</property>
<!--Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>CS</value>
</set>
</property>
<!--null注入-->
<property name="wife">
<null></null>
</property>
<!--Properties注入-->
<property name="info">
<props>
<prop key="学号">202211228</prop>
<prop key="sex"></prop>
</props>
</property>
</bean>
</beans>


  • 其他方式注入

注意p和c标签不能直接使用,需要导入命名空间

1
2
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
  • p标签注入,相当于property
1
2
3
4
5
6
7
8
9
10
11
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello1" class="com.xxy.entity.Hello" p:name="s">

</bean>

</beans>
  • c标签注入,相当于构造器注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello1" class="com.xxy.entity.Hello" p:name="s">

</bean>
<bean id="hello2" class="com.xxy.entity.Hello" c:name="s2">

</bean>
</beans>

3. Spring 配置

3.1 别名

1
<alias name="hello" alias="hello1"></alias>

3.2 Bean的配置

1
2
3
4
5
6
    <!--id:bean唯一标识符,相当于对象名-->
<!--class:bean对象所对应的全限定名:包名+类型-->
<!--name:也是别名,比alias功能更丰富,可以同时取多个-->
<bean id="userService" class="com.xxy.service.UserService">
<property name="iUserDao" ref="userDaoMysql"></property>
</bean>

3.3 import

用于合并多个配置文件,多用于团队开发

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"></import>
<import resource="beans1.xml"></import>
</beans>


3.4 bean作用域

scope默认是单例,singleton,prototype每次get产生一个新对象,其余requese、session、application这些只在web中使用

1
2
3
4
5
6
<bean id="hello1" class="com.xxy.entity.Hello" p:name="s" scope="singleton">

</bean>
<bean id="hello2" class="com.xxy.entity.Hello" c:name="s" scope="singleton">

</bean>

3.5 Bean自动装配

  • 在XML显示的配置

byName需要保证bean的id唯一,byType需要保证bean的class唯一,而且都要和set方法的值或类型一致

1
2
3
4
5
6
<bean id="hello1" class="com.xxy.entity.Hello" p:name="s" scope="singleton" autowire="byName">

</bean>
<bean id="hello2" class="com.xxy.entity.Hello" c:name="s" scope="singleton" autowire="byType">

</bean>
  • 在java中显示配置

jdk1.5支持注解,spring2.5支持注解

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

<context:annotation-config/>

</beans>

@Autowired可以直接在属性上使用,也可以在Set方式上使用,通过ByType实现

序号 注解 解释
1 @Qualifier(value = “hell01”) @Autowired环境复杂,可以通过此注解指定唯一bean注入
2 @Resource 首先通过byName寻找,找不到通过byType寻找
3 @Nullable 标记这个注解,说明允许为空
  • 隐式的自动装配

3.6 Java配置Spring

spring4之后成为了核心功能,以前是一个子项目,SpringBoot很常见

1
2
3
4
5
6
7
@Configuration
public class MyConfig {
@Bean
public User getUser() {
return new User();
}
}

4. 使用注解开发

在spring4之后,使用注解必须保证aop的包导入,然后是命名空间约束,维护较为复杂,不是所有场景能够使用

序号 注解 解释
1 @Component 组件,放在类上,说明被Spring管理
2 @Value(value = “li”) 属性注入,最好放在set方法上
3 @Repository 标记这个注解,说明是数据访问层
4 @Service 标识为业务逻辑层
5 @Controller 标识为控制层
6 @Scope(value = “singleton”) bean作用域
7 @Configuration 用java配置spring,标识为配置类,通常和@Bean一起使用,类似于beans

5. AOP(Aspect Oriented Programming)

5.1 代理模式

5.1.1 静态代理

一个真实角色会产生一个代理角色,开发效率会降低

  • 抽象角色
  • 代理角色
  • 真实角色
  • 客户

5.1.2 动态代理

代理类是动态生成的,Proxy,InvocationHandler,本质是使用反射机制实现的

  • 基于接口【Jdk动态代理】
  • 基于类【cglib动态代理】
  • Java字节码【javassist】

5.2 AOP(默认为JDK动态代理)

5.2.1 通过Spring的AOP自带的API实现

  • 导包
1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
  • 被代理的类
1
2
3
4
5
6
7
8
9
package com.xxy.service;

public interface IUserService {
void add();
void delete();
void update();
void select();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.xxy.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements IUserService{
public void add() {
System.out.println("增加");
}

public void delete() {
System.out.println("删除");
}

public void update() {
System.out.println("修改");
}

public void select() {
System.out.println("查询");
}
}

  • 代理
1
2
3
4
5
6
7
8
9
10
11
12
package com.xxy.utils;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的" + method.getName()+"被执行了");
}
}

  • 配置文件
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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="com.xxy"></context:component-scan>
<context:annotation-config/>
<bean id="userServiceImpl" class="com.xxy.service.UserServiceImpl"></bean>
<bean id="log" class="com.xxy.utils.Log"></bean>
<!--配置aop,需要aop约束-->
<aop:config>
<!--切入点,需要执行的位置 修饰符 全包名.类名.方法(参数)-->
<aop:pointcut id="pointcut" expression="execution(* com.xxy.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>


  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.xxy.test;

import com.xxy.config.MyConfig;
import com.xxy.entity.User;
import com.xxy.service.IUserService;
import com.xxy.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfiger.xml");
//只能是接口
IUserService userServiceImpl = (IUserService) context.getBean("userServiceImpl");
userServiceImpl.delete();
}
}

5.2.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
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))"
id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- 3.2.1 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!-- 3.2.3 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>

5.2.3 注解实现

1
2
 <bean id="annotationPointCut" class="com.xxy.utils.AnnotationPointCut"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.xxy.utils;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointCut {
@Before("execution(* com.xxy.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("执行前");
}
}

优先级:环绕前-方法执行前-方法-环绕后-方法执行后-最终执行

  • cglib动态代理
1
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

6. 整合Mybatis

6.1 导包

MyBatis-Spring MyBatis Spring Framework Spring Batch Java
3.0 3.5+ 6.0+ 5.0+ Java 17+
2.1 3.5+ 5.x 4.x Java 8+
2.0 3.5+ 5.x 4.x Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+
  • mybatis相关
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring</artifactId>
<groupId>com.xxy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring03</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis.version>3.2.6</mybatis.version>
<mysql.version>5.1.30</mysql.version>
<aspectjweaver.version>1.9.6</aspectjweaver.version>
<springjdbc.version>5.2.12.RELEASE</springjdbc.version>
<mybatis-spring.version>2.0.4</mybatis-spring.version>
</properties>
<!--导入依赖-->
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
<!--spring操作数据库还需要一个spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springjdbc.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
</dependencies>

<build>
<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

</project>

6.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
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">


<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/temp1?useUnicode=true&amp;characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- <property name="configLocation" value="classpath:mybatis-config.xml"></property>-->
</bean>

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

</beans>


6.3 编写实体类

1
2
3
4
5
public class User {
private String id;
private String name;
private int age;
//...省略getset、tostring方法
1
2
3
4
5
6
7
8
9
10
package com.xxy.mapper;

import com.xxy.entity.User;

import java.util.List;

public interface UserMapper {
public List<User> selectAllUser();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.xxy.mapper;

import com.xxy.entity.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;


public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;

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

public List<User> selectAllUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectAllUser();
}
}

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxy.mapper.UserMapper">

<select id="selectAllUser" resultType="com.xxy.entity.User">
select * from user
</select>
</mapper>

6.4 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.xxy.test;

import com.xxy.entity.User;
import com.xxy.mapper.UserMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
@Test
public void testSelectAllUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfiger.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
for (User user : userMapper.selectAllUser()) {
System.out.println(user);
}

}
}

7. 事务

ACID非常重要

7.1 声明式事务[不影响代码]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--propagation事务传播-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"></tx:method>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.xxy.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>

1
2
3
4
5
6
7
<!-- 事务管理器,依赖于数据源 --> 
<bean id="transactionManager" class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

7.2 编程式事务

会改变代码结构

8. 总结

学习了Spring框架,最主要是IOC和AOP的理解