Spring 学习笔记
Spring
整体架构
spring framework体系结构及模块jar依赖关系
Spring Framework 5 模块组成、体系结构、整体架构 - Jacian - 博客园
spring 配置文件头
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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/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">
<context:component-scan base-package="com.xtyuns.*"/>
<!--context、aop、transaction-->
</beans>
spring-context
spring-context 是整个 spring 生态的基础, 它提供了最基本的 IOC 容器。
总结
-
IOC 容器实现了对象的统一管理, 将对象的控制权掌握在了 spring 自己手中。
-
通过在 beans 中登记那些交给 IOC 容器管理的对象, 告诉了容器你是个什么东西, 你需要什么东西。
-
DI 是控制反转(IOC)思想的一种实现方式, 它解决了对象依赖的问题, 因此我们可以将 DI 理解为 IOC 思想下的产物。
IOC(Inversion of Control,控制反转)的一个重点是在系统运行中动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
-
DI 的实现是通过反射来实现的。
-
反射可以理解为: Runtime Programming
IOC
- spring beans 的默认创建是非懒加载、单例模式的对象。
- 我们可以将 spring 认为是两种使用方式: xml 配置和注解。注解可用于那些我们自己编写的类中,而 xml 配置声明那些由第三方提供的类,如 SDK 等。
DI
- 通过构造方法注入
- 通过 setter 注入
- 基于字段的依赖注入(不推荐使用)
基于字段注入的缺点
推荐文章: Spring IOC不再推荐使用属性注入
-
基于字段注入 private 类型的属性, 导致了所在类强依赖于 spring 的 IOC, 否则该属性一定为 null。
-
一个类中存在大量基于字段注入的属性, 可能使开发者无法意识到这个类过于庞大。
-
在使用依赖注入时, 受影响的类应该暴露出依赖的需求。通过构造方法注入的方式通过构造方法透露出了依赖项的需求, 通过 setter 注入的方式则通过 setter 方法透露出了依赖项的需求, 而通过字段注入的方式可能使外界忽略掉这个依赖性。
我们已经看到,基于字段的注入应该尽可能地避免,因为它有许多缺点,无论它看起来多么优雅。推荐的方法是使用基于构造函数和基于setter的依赖注入。对于必需的依赖,建议使用基于构造函数的注入,设置它们为不可变的,并防止它们为null。对于可选的依赖项,建议使用基于sett的注入。
注解笔记
<bean></bean>
<!--对应注解-->
@Component
@Controller
@Service
@Repository
<constructor-arg />
<property />
<!--对应注解-->
@Autowired
spring-aop
概念
切面(Aspect): 含有通知/增强功能的类
通知(Advice): 增强功能就是通知,它是在连接点上执行的动作
-
前置通知
-
后置通知
- 后置最终通知
- 后置返回通知
- 后置异常通知
-
环绕通知
环绕通知是最强大的通知,它可以实现以上四种任意一种通知,环绕通知类似于 JavaEE 中的 Filter,可以作用于连接点的前后。
切入点(Pointcut): 连接点的集合即为切入点,类似于(css 选择器):querySelectAll()
连接点(Join Point): 增强功能所插入的点就是连接点,它是一个确切的位置,类似于(css):querySelectAll()[0]
目标对象(Target Object): 被通知/增强的对象,也就是连接点所属的对象,它是业务核心的逻辑部分
织入(Weaving): 把切面应用到目标对象来创建新的代理对象的过程即为织入。这个过程可以在编译时(例如使用 AspectJ 编译器) 、类加载时或运行时中完成。Spring 和其他纯 Java AOP 框架一样,是在运行时完成织入的。
代理(AOP Proxy): 织入的产物,也就是 AOP 框架所创建的对象,由 JDK 动态代理或 CGLIB 实现
引入(Introduction): 声明额外的方法或者某个类型的字段到任何被通知的对象上,例如,通过引入来使目标对象实现某个接口
可以大致的将环绕通知理解为 Filter:
public Object around(ProceedingJoinPoint pjp) {
前置通知();
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable e) {
后置异常通知(e);
} finally {
后置最终通知();
}
后置返回通知(result);
return result;
}
使用 AOP 的步骤
- 定义切面
- 定义切点
- 定义通知,并指定切点
切入点表达式
Spring Doc: AspectJ 5 pointcut expression Examples
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
/*
? 表示可选, 同时要遵守空格的位置
modifiers-pattern: 权限修饰符, 通配符: 直接省略即可, 不能使用 * 作为通配符
ret-type-pattern: 返回值类型, 通配符: *
declaring-type-pattern: 全限定类名, 通配符: *
name-pattern: 方法名, 通配符: *
param-pattern: 参数类型, () 空参、(..) 任意数量的参数、(*) 拥有一个任意类型的参数、(*,String) 拥有两个参数且第二个参数类型为 String
throws-pattern: 异常类型, 通配符: *
*/
使用注解定义 AOP
<aop:aspectj-autoproxy/>
package com.xtyuns.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LoggerAdvice {
@Pointcut("execution(* com.xtyuns.dao.impl.*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println(pjp.getSignature().getName() + " begin()...");
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(pjp.getSignature().getName() + " result: " + result);
return result;
}
}
事务
DataSourceTransactionManager
- 使用 @Transaction 注解
- 使用 xml 配置事务: tx:advice-->tx:attributes + aop:config--> aop:pointcut/aop:advisor
<!--定义事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--方法一: 使用注解声明事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--方法二: 使用 xml 声明事务-->
<!-- <!–配置事务拦截方法–>-->
<!-- <tx:advice id="txAdvice" transaction-manager="transactionManager">-->
<!-- <tx:attributes>-->
<!-- <tx:method name="save*" rollback-for="Exception"/>-->
<!-- <tx:method name="remove*" rollback-for="Exception"/>-->
<!-- <tx:method name="modify*" rollback-for="Exception"/>-->
<!-- <tx:method name="*" read-only="true" propagation="SUPPORTS"/>-->
<!-- </tx:attributes>-->
<!-- </tx:advice>-->
<!-- <!–织入事务–>-->
<!-- <aop:config>-->
<!-- <aop:pointcut id="txPoint" expression="execution(* com.xtyuns.service.impl.*.*(..))"/>-->
<!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>-->
<!-- </aop:config>-->
事务传播机制
PROPAGATION | 含义 |
---|---|
REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。 |
SSM 整合
整体结构
- Spring 的核心就是 IOC、DI, 我们可以根据注解来实现 beans 容器的功能
- MyBatis 的核心是 SqlSession, 但是它是非线程安全的, 因此需要我们提供一个 SqlSessionFactoryBean
- SpringMVC 的核心是 DispatcherServlet, 它是 Spring 的一个子容器, 主要功能就是分发请求和响应
- 总体来讲 webapp 工程是根基, Spring 是对 webapp 的扩展, 使用 IOC 容器降低了类之间的耦合性。
- MyBatis 基于映射器和 SQL 映射文件(或注解) 为整个项目提供了数据持久层的支持(动态代理), 与此同时 MyBatis-Spring 为 IOC 容器提供了一个扩展 bean(SqlSessionFactoryBean), 它使我们更方便的使用 MyBatis 所提供的映射器。
- SpringMVC 是 Spring 在 JavaEE 方面上的增强, 它通过 DispatcherServlet 提供了 MVC 的分层服务 (Model 提供数据的存储和传递服务, View 通过视图解析器进行处理, Controller 通过 @RequestMapping、@RequestAdapter 等内容提供请求的处理)。
核心文件配置
src/main/resources
- db.properties
- spring-context.xml
- spring-mvc.xml
src/main/webapp
- WEB-INF/web.xml
pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xtyuns.inpn</groupId>
<artifactId>SSM-phone</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SSM-phone Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>9.0.40</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>9.0.40</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>com/xtyuns/mapper/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
db.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/table
username=root
password=root
spring-context.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--扫描非 Controller 组件-->
<context:component-scan base-package="com.xtyuns">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置数据源-->
<context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!--配置事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--MyBatis 配置-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/xtyuns/mapper/*.xml"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.xtyuns.mapper"/>
</bean>
</beans>
spring-mvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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">
<!--扫描所有 Controller-->
<context:component-scan base-package="com.xtyuns.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--启用 mvc 注解驱动-->
<mvc:annotation-driven/>
<!--未映射请求交给 tomcat 中的 default servlet 处理-->
<mvc:default-servlet-handler/>
<!--配置视图解析器-->
<!--配置上传文件解析器-->
<!--配置拦截器-->
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--这里不能配置为 /*, 否则将覆盖 web 容器中的 JspServlet-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
</web-app>