Spring学习笔记

本文详细介绍了Spring框架的核心概念,包括IoC的控制反转和DI的区别,以及AOP的面向切面编程。通过实例演示如何手写实现IoC和AOP,探讨了Spring中的Bean作用域、懒加载、FactoryBean和事务管理。还深入剖析了Spring IoC和AOP源码,以及其在实际项目中的应用和配置方式,涉及Spring MVC、事务声明式支持等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring学习笔记

第一部分:Spring 概述

1.1 Spring 简介

  • 轻量级开源框架
  • 核心是IOC和AOP
  • 使⽤最多的 Java EE 企业应⽤开源框架

1.2 Spring 发展历程

EJB->Expert One-to-One J2EE Design and Development论文->Spring

1.3 Spring 的优势

  • ⽅便解耦,简化开发

通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的
过度程序耦合。⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更
专注于上层的应⽤。

  • AOP编程的⽀持

通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过
AOP轻松应付。

  • 声明式事务的⽀持

@Transactional
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼
开发效率和质量。

  • ⽅便程序的测试
  • ⽅便集成各种优秀框架
  • 降低JavaEE API的使⽤难度
  • 源码是经典的 Java 学习范例

1.4 Spring 的核⼼结构

在这里插入图片描述

主要包括⼏个⼤模块:

Spring核⼼容器(Core Container)

容器是Spring框架最核⼼的部分,它管理着Spring应⽤中
bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。
基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼
容器之上。
⾯向切⾯编程(AOP) /Aspects

Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应
⽤系统中开发切⾯的基础,与DI⼀样, AOP可以帮助应⽤对象解耦。
数据访问与集成(Data Access/Integration)

Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专
注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外, Spring AOP为数据访问
提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、 MyBatis等。该模块由
JDBC、 Transactions、 ORM、 OXM 和 JMS 等模块组成。
Web

该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅
案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
Test

为了使得开发者能够很⽅便的进⾏测试, Spring提供了测试模块以致⼒于Spring应⽤的测
试。 通过该模块, Spring为使⽤Servlet、 JNDI等编写单元测试提供了⼀系列的mock对象实现。

第二部分:核心思想

IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向于理论化, spring在技
术层次把这两个思想做了⾮常好的实现(Java)

1.1 IoC

IOC:即控制反转,它是⼀个技术思想,不是⼀个技术实现

控制:指的是对象创建(实例化、管理)的权利
反转:控制权交给外部环境了(spring框架、 IoC容器)

1.1.1 IoC解决了什么问题

IoC解决对象之间的耦合问题

在这里插入图片描述

1.1.2 IoC和DI的区别

IOC和DI描述的是同⼀件事情,只不过⻆度不⼀样罢了

IOC是站在对象的角度,对象实例化及其管理的权利靠给了容器;

DI是站在容器的角度,容器会把对象依赖的其他对象注入进来

1.2 AOP

1.2.1 什么是AOP

AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程

在这里插入图片描述

1.2.2 AOP在解决什么问题

在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复

1.2.3 为什么叫做⾯向切⾯编程

「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑
「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个
⾯的概念在⾥⾯

第三部分:⼿写实现 IoC 和 AOP

1.1 问题分析

1)问题⼀:service 层实现类在使⽤ dao 层对象时,直接在
service层new出dao层对
象,然⽽⼀个 new 关键字却将service 和 dao 层具体的⼀个实现类
耦合在了⼀起,如果说技术架构发⽣⼀些变动, dao 层的实现要使⽤其它技术,
⽐如 Mybatis,思考切换起来的成本?每⼀个 new 的地⽅都需要修改源代码,重新编译,⾯向接⼝开发
的意义将⼤打折扣?
(2)问题⼆: service 层代码没有竟然还没有进⾏事务控制 ?!如果转账过程中出现异常,将可能导致
数据库数据错乱,后果可能会很严重,尤其在⾦融业务。

1.2 解决思路

针对问题⼀:

实例化对象的⽅式除了 new 之外,还有什么技术?反射 (需要把类的全限定类名配置在xml
中)
考虑使⽤设计模式中的⼯⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在⼯⼚中使
⽤反 射技术实例化对象,⼯⼚模式很合适

针对问题⼆:
service 层没有添加事务控制,怎么办?没有事务就添加上事务控制,⼿动控制 JDBC 的
Connection 事务,但要注意将Connection和当前线程绑定(即保证⼀个线程只有⼀个
Connection,这样操作才针对的是同⼀个 Connection,进⽽控制的是同⼀个事务,考虑ThreadLocal

1.3 手写改造实现

针对问题一的改造:通过对象工厂来实例化需要的bean

public class BeanFactory {
/**
* ⼯⼚类的两个任务
* 任务⼀:加载解析xml,读取xml中的bean信息,通过反射技术实例化bean对象,然后放⼊
map待⽤
* 任务⼆:提供接⼝⽅法根据id从map中获取bean(静态⽅法)
*/
private static Map<String,Object> map = new HashMap<>();
static {
InputStream resourceAsStream =
BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//bean");
// 实例化bean对象
for (int i = 0; i < list.size(); i++) {
Element element = list.get(i);
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
Class<?> aClass = Class.forName(clazz);
Object o = aClass.newInstance();
map.put(id,o);
}
// 维护bean之间的依赖关系
List<Element> propertyNodes =
rootElement.selectNodes("//property");
for (int i = 0; i < propertyNodes.size(); i++) {
Element element = propertyNodes.get(i);
// 处理property元素
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
String parentId =
element.getParent().attributeValue("id");
Object parentObject = map.get(parentId);
Method[] methods = parentObject.getClass().getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
if(("set" + name).equalsIgnoreCase(method.getName()))
{
// bean之间的依赖关系(注⼊bean)
Object propertyObject = map.get(ref);
method.invoke(parentObject,propertyObject);
}
}
// 维护依赖关系后重新将bean放⼊map中
map.put(parentId,parentObject);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static Object getBean(String id) {
return map.get(id);
}
}

针对问题二的改造:增加事务管理类,封装事务操作,通过AOP动态代理自动完成事务操作

public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// 开启事务
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// 提交事务
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// 回滚事务
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}

public class ConnectionUtils {
/*private ConnectionUtils() {
}
private static ConnectionUtils connectionUtils = new
ConnectionUtils();
public static ConnectionUtils getInstance() {
return connectionUtils;
}*/
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); //存储当前线程的连接
/**
* 从当前线程获取连接
*/
public Connection getCurrentThreadConn() throws SQLException {
/**
* 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取⼀个连接绑定到
当前线程
*/
Connection connection = threadLocal.get();
if(connection == null) {
// 从连接池拿连接并绑定到线程
connection = DruidUtils.getInstance().getConnection();
// 绑定到当前线程
threadLocal.set(connection);
}
return connection;
}
}


/**
*代理工厂
**/
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager
transactionManager) {
this.transactionManager = transactionManager;
}
public Object getProxy(Object target) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[]
args) throws Throwable {
Object result = null;
try{
// 开启事务
transactionManager.beginTransaction();
// 调⽤原有业务逻辑
result = method.invoke(target,args);
// 提交事务
transactionManager.commit();
}catch(Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
// 异常向上抛出,便于servlet中捕获
throw e.getCause();
}
return result;
}
});
}
}

第四部分:Spring IOC 应⽤

1.1 IOC基础应用

BeanFactory:BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范

ApplicationContext:ApplicationContext是BeanFactory的⼀个⼦接⼝,是容器的⾼级接⼝,⽐
BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml, java配置类)等等

1.1.1 xml模式下实例化bean的三种方式
  • ⽅式⼀:使⽤⽆参构造函数

    在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建
    失败。

    <!--配置service对象-->
    <bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
    </bean>
    
  • ⽅式⼆:使⽤静态⽅法创建

    <!--使⽤静态⽅法创建对象的配置⽅式-->
    <bean id="userService" class="com.lagou.factory.BeanFactory"
    factory-method="getTransferService"></bean>
    
  • ⽅式三:使⽤实例化⽅法创建

<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="beanFactory"
class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" factorymethod="getTransferService"></bean>
1.1.2 bean的作用域

单例模式: singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
多例模式: prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象, spring框架只负责创建,不负责销毁。

1.2 Spring IOC⾼级特性

1.2.1 lazy-Init 延迟加载

设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,⽽是第⼀次向容器
通过 getBean 索取 bean 时实例化的

<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />

如果⼀个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不
会实例化bean,⽽是调⽤ getBean ⽅法实例化的。

应⽤场景

(1)开启延迟加载⼀定程度提⾼容器启动和运转性能

<bean id="testBean" class="cn.lagou.LazyBean" />

该bean默认的设置为:

<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>

(2)对于不常使⽤的 Bean 设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean 就占
⽤资源

1.2.2 FactoryBean
<bean id="companyBean" class="com.lagou.edu.factory.CompanyFactoryBean">
<property name="companyInfo" value="拉勾,中关村,500"/>
</bean>
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo; // 公司名称,地址,规模
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
// 模拟创建复杂对象Company
Company company = new Company();
String[] strings = companyInfo.split(",");
company.setName(strings[0]);
company.setAddress(strings[1]);
company.setScale(Integer.parseInt(strings[2]));
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

测试

Object companyBean = applicationContext.getBean("companyBean");
System.out.println("bean:" + companyBean);
// 结果如下
bean:Company{name='拉勾', address='中关村', scale=500}

如果需要获取FactoryBean本身,需要在id之前添加“&”,如Object companyBean = applicationContext.getBean("&companyBean");

1.2.3 后置处理器

BeanPostProcessor

Spring容器的实例化和依赖注⼊之后,该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法

BeanFactoryPostProcessor

BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,典型应
⽤:PropertyPlaceholderConfigurer

第五部分:Spring IOC源码深度剖析

1、源码阅读原则:

定焦原则:抓主线
宏观原则:站在上帝视⻆,关注源码结构和业务流程(淡化具体某⾏代码的编写细节)

2、源码阅读技巧:

  • 断点(观察调⽤栈)

  • 反调(Find Usages)

  • 经验(spring框架中doXXX,做具体处理的地⽅)

1.1 Spring IoC容器初始化主体流程

在这里插入图片描述

1.2 Spring IoC容器初始化主流程

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第⼀步:刷新前的预处理
prepareRefresh();
/*
第⼆步:
获取BeanFactory;默认实现是DefaultListableBeanFactory
加载BeanDefition 并注册到 BeanDefitionRegistry
*/ConfigurableListableBeanFactory beanFactory =
obtainFreshBeanFactory();
// 第三步: BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加
载器等)
prepareBeanFactory(beanFactory);
try {
// 第四步: BeanFactory准备⼯作完成后进⾏的后置处理⼯作
postProcessBeanFactory(beanFactory);
// 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执registerBeanPostProcessors(beanFactory);
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
// 第⼋步:初始化事件派发器
initApplicationEventMulticaster();
// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
onRefresh();
// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器
bean
registerListeners();
/*
第⼗⼀步:
初始化所有剩下的⾮懒加载的单例bean
初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
填充属性
初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、 init-method⽅法)
调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
*/
finishBeanFactoryInitialization(beanFactory);
/*
第⼗⼆步:
完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事
件 (ContextRefreshedEvent)
*/
finishRefresh();
}
......
}
}

在这里插入图片描述

在这里插入图片描述

1.3 BeanFactory创建流程

在这里插入图片描述

1.4 Bean创建流程

Bean创建⼦流程⼊⼝在
AbstractApplicationContext#refresh()⽅法的finishBeanFactoryInitialization(beanFactory)

1.5 lazy-init 延迟加载机制原理

普通 Bean 的初始化是在容器启动初始化阶段执⾏的,⽽被lazy-init=true修饰的 bean 则是在从容器⾥
第⼀次进⾏context.getBean() 时进⾏触发。 Spring 启动的时候会把所有bean信息(包括XML和注解)解
析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个
BeanDefinition 进⾏处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进
⾏初始化并依赖注⼊。

public void preInstantiateSingletons() throws BeansException {
// 所有beanDefinition集合
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 触发所有⾮懒加载单例bean的初始化
for (String beanName : beanNames) {
// 获取bean 定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在容器创建时初始化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否是 FactoryBean
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>)
getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof
SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new
PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
}else {
/*
如果是普通bean则进⾏初始化并依赖注⼊,此 getBean(beanName)接下来触发的逻辑
和
懒加载时 context.getBean("beanName") 所触发的逻辑是⼀样的
*/
getBean(beanName);
}
}
}

1.6 Spring IoC循环依赖问题

在这里插入图片描述

注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结
条件。

Spring中循环依赖场景有:

  • 构造器的循环依赖(构造器注⼊)

  • Field 属性的循环依赖(set注⼊)

    其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决
    属性循环依赖时, spring采⽤的是提前暴露对象的⽅法。

单例 bean 构造器参数循环依赖(⽆法解决)
prototype 原型 bean循环依赖(⽆法解决)

Spring通过setXxx或者@Autowired⽅法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对
象来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法
之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。

boolean earlySingletonExposure = (mbd.isSingleton() &&
this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//将初始化后的对象提前已ObjectFactory对象注⼊到容器中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
  • ClassA调⽤setClassB⽅法, Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring
    容器中。
  • Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
  • ClassB调⽤setClassA⽅法, Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了
    ClassA,因此可以获取到ClassA实例
    • ClassA通过spring容器获取到ClassB,完成了对象初始化操作。

这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。

在这里插入图片描述

在这里插入图片描述

第六部分:Spring AOP 应⽤

1.1 相关术语

在这里插入图片描述

1.2 代理方式

Spring 实现AOP思想使⽤的是动态代理技术
默认情况下, Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现
任何接⼝时, Spring会选择CGLIB。当被代理对象实现了接⼝, Spring会选择JDK官⽅的代理技术,不过
我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。

1.3 Spring中AOP的配置⽅式

xml模式

<!--
Spring基于XML的AOP配置前期准备:
在spring的配置⽂件中加⼊aop的约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd

Spring基于XML的AOP配置步骤:

第⼀步:把通知Bean交给Spring管理
第⼆步:使⽤aop:config开始aop的配置
第三步:使⽤aop:aspect配置切⾯
第四步:使⽤对应的标签配置通知的类型
⼊⻔案例采⽤前置通知,标签为aop:before
-->
<!--把通知bean交给spring来管理-->
<bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
<!--配置切⾯-->
<aop:aspect id="logAdvice" ref="logUtil">
<!--配置前置通知-->
<aop:before method="printLog" pointcut="execution(public *
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou
.pojo.Account))"></aop:before>
</aop:aspect>
</aop:config>>

五种通知类型:
前置通知
配置⽅式: aop:before标签
包名可以使⽤.表示任意包,但是有⼏级包,必须写⼏个
*
…TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Accou
nt)
包名可以使⽤…表示当前包及其⼦包
*
…TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account
)
类名和⽅法名,都可以使⽤.表示任意类,任意⽅法

  • …(com.lagou.pojo.Account)
    参数列表,可以使⽤具体类型
    基本类型直接写类型名称 : int
    引⽤类型必须写全限定类名: java.lang.String
    参数列表可以使⽤*,表示任意参数类型,但是必须有参数

  • .()
    参数列表可以使⽤…,表示有⽆参数均可。有参数可以是任意类型

  • .*(…)
    全通配⽅式:

  • .*(…)
    <aop:config proxy-target-class=“true”>

    <aop:aspectj-autoproxy proxy-target-class=“true”></aop:aspectjautoproxy>执⾏时机
    前置通知永远都会在切⼊点⽅法(业务核⼼⽅法)执⾏之前执⾏。
    细节
    前置通知可以获取切⼊点⽅法的参数,并对其进⾏增强。
    正常执⾏时通知
    配置⽅式
    异常通知
    配置⽅式

    <aop:before method=“printLog” pointcut-ref=“pointcut1”>
    </aop:before>

    <aop:after-returning method=“afterReturningPrintLog” pointcutref=“pt1”></aop:after-returning>执⾏时机
    异常通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏产⽣异常之后,异常通知执⾏。如果切
    ⼊点⽅法执⾏没有产⽣异常,则异常通知不会执⾏。
    细节
    异常通知不仅可以获取切⼊点⽅法执⾏的参数,也可以获取切⼊点⽅法执⾏产⽣的异常信息。
    最终通知
    配置⽅式
    执⾏时机
    最终通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏完成之后,切⼊点⽅法返回之前执⾏。
    换句话说,⽆论切⼊点⽅法执⾏是否产⽣异常,它都会在返回之前执⾏。
    细节
    最终通知执⾏时,可以获取到通知⽅法的参数。同时它可以做⼀些清理操作。
    环绕通知
    配置⽅式

    <aop:after-throwing method=“afterThrowingPrintLog” pointcut-ref=“pt1”>

    </aop:after-throwing>

    <aop:after method=“afterPrintLog” pointcut-ref=“pt1”></aop:after>4.2 XML+注解模式
    XML 中开启 Spring 对注解 AOP 的⽀持
    示例

    <aop:around method=“aroundPrintLog” pointcut-ref=“pt1”></aop:around>

xml+注解

<!--开启spring对注解aop的⽀持-->
<aop:aspectj-autoproxy/>

@Pointcut

@Before

@AfterReturning

@AfterThrowing

@After

@Around

注解

@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //开启spring对注解AOP的⽀持
public class SpringConfiguration {
}

1.7 Spring 声明式事务的⽀持

编程式事务: 在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务
声明式事务: 通过xml或者注解配置的⽅式达到事务控制的⽬的,叫做声明式事务

核心API:PlatformTransactionManager、TransactionStatus、TransactionDefinetion

xml配置方式

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--定制事务细节,传播⾏为、隔离级别等-->
<tx:attributes>
<!--⼀般性配置-->
<tx:method name="*" read-only="false"
propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
<!--针对查询的覆盖性配置-->
<tx:method name="query*" read-only="true"
propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--advice-ref指向增强=横切逻辑+⽅位-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(*
com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
</aop:config>

xml+注解方式

<!--配置事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManage
r">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启spring对注解事务的⽀持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)

纯注解方式

@EnableTransactionManagement//开启spring注解事务的⽀持
public class SpringConfiguration {
}

第七部分:Spring AOP源码深度剖析

1.1 代理对象创建

AOP源码分析类⽅法调⽤关系:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#i
nitializeBean
调⽤
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#a
pplyBeanPostProcessorsAfterInitialization
调用
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProce
ssAfterInitialization(后置处理器AbstractAutoProxyCreator完成bean代理对象创建)
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNec
essary
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy (在这⼀步把委托对象的aop增强和通⽤拦截进⾏合并,最终给代理对象)
调⽤
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
调⽤
org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader
)

1.2 Spring声明式事务控制

AbstractAutoProxyCreator是一个后置处理器

声明式事务分析:

@EnableTransactionManagement 注解
1)通过@import引⼊了TransactionManagementConfigurationSelector类
它的selectImports⽅法导⼊了另外两个类: AutoProxyRegistrar和
ProxyTransactionManagementConfiguration
2) AutoProxyRegistrar类分析
⽅法registerBeanDefinitions中,引⼊了其他类,通过
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引⼊
InfrastructureAdvisorAutoProxyCreator,
它继承了AbstractAutoProxyCreator,是⼀个
后置处理器类
3) ProxyTransactionManagementConfiguration 是⼀个添加了@Configuration注解的配置类
(注册bean)
注册事务增强器(注⼊属性解析器、事务拦截器)
属性解析器: AnnotationTransactionAttributeSource,内部持有了⼀个解析器集合
Set annotationParsers;
具体使⽤的是SpringTransactionAnnotationParser解析器,⽤来解析
@Transactional的事务属性
事务拦截器: TransactionInterceptor实现了MethodInterceptor接⼝,该通⽤拦截
会在产⽣代理对象之前和aop增强合并,最终⼀起影响到代理对象
TransactionInterceptor的invoke⽅法中invokeWithinTransaction会触发原有业
务逻辑调⽤(增强事务)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值