文章内容输出来源:拉钩教育Java高薪训练营
自定义IoC&AOP框架
Spring框架整体回顾
简介
- Spring分层(Controller, Service, Dao)的full-stack轻量级开源框架
- 以IoC和AOP为内核,提供了展现层SpringMVC和业务层失误管理等众多的企业级应用技术
- 整合开源世界众多著名的第三方框架和类库
- 使用最多的Java EE企业应用开源框架
- spring.io
发展历程
- EJB
- Rod Johnson(spring之父)
- 2017.9 发布 Spring 5.x
优势
- 方便解耦,简化开发
- AOP编程的支持
- 声明式事务的支持(@Transactional)
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API 的使用难度(对JavaEE API进行封装)
- 源码是经典的Java学习范例
核心结构
模块化开发:数据处理模块、Web模块、AOP/Aspects模块、Core Container模块和Test模块。
框架版本
课程使用Spring Framework 5.1.12,JDK 11.0.5, Maven 3.6.x
Spring Framework不同版本对JDK有要求:
- JDK 8+ : 5.x
- JDK 6+ : 4.x
- JDK 5+ : 3.x
核心思想
IOC和AOP并不是Spring提出的,之前已经存在。Spring在技术层面把这两个思想进行了非常好的实现。
IoC
-
Inversion of Control (控制反转):由IoC容器帮助实例化对象并进行管理。
-
控制:对象创建(实例化、管理)的权力;反转:控制权交由外部环境(Spring框架、IoC容器)
-
解决对象之间的耦合问题
-
DI(Dependancy Injection)依赖注入
-
IoC与DI描述的是同一件事情 -> 对象实例化及依赖关系维护
- IoC站在对象的角度,权力反转给了容器
- DI站在容器的角度,容器会把对象依赖的其他对象注入
AOP
- Aspect oriented Programming 面向切面编程
- OOP(封装、继承、多态):垂直纵向的继承体系。无法解决的问题:横切逻辑代码(多个纵向顺序流程中出现的相同子流程代码)的代码重复;与业务逻辑混杂臃肿,不方便维护
- AOP:抽取横向逻辑,与业务逻辑拆分。在不改变原有逻辑情况下,增强横切逻辑代码,解耦合,避免重复
手写实现IoC和AOP
银行转账案例代码问题分析
- new关键字将service层的实现类TransferServiceImpl和Dao层的具体实现类JdbcAccountDaoImpl耦合在了一起。当需要切换Dao层实现时必须修改service代码。不符合面向接口开发的最优原则。
- service层没有添加事务控制,出现异常可能导致数据错乱。
问题解决思路
问题一思考
- 全限定类名+反射:配置在xml中
- 工厂模式解耦合
问题二思考
- 数据库事务归根结底是connect的事务(connection.commit, connection.rollback), 两次更新使用两个connect,则无法统一控制。-> connect绑定当前线程
- 事务控制不应该添加在dao层,而应该在service层。-> 动态代理增强
设计模式-工厂模式
-
简单工厂模式
INoodles noodles = SimpleNoodlesFactory.createNoodles(2); // type -
工厂方法
INoodlesFactory noodlesFactory = new LzINoolesFactory(); INoodles noodles = noodlesFactory.createNoodles();
设计模式-单例模式
-
饿汉式
private static final HungrySingleton instance = new HungrySingleton(); public static HungrySingleton getInstance() { return instance; } -
懒加载
private static LazySingleton instance; // 加synchronized关键字实现同步,保障线程安全 public static synchronized LazySingleton getInstance(){ if (instance == null) { instance = new LazySingleton(); } return instance; }
设计模式-代理模式
-
静态代理
// implements IRentingHouse private IRentingHouse rentingHouse; public RentingHouseProxy(IRentingHouse rentingHouse) { this.rentingHouse = rentingHouse; } @Override public void rentHouse(){ // do sth before rentingHouse.rentHouse(); // do sth after } -
动态代理
public Object getJdkProxy(Object obj) { return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), (o, method, args) -> { System.out.println("JdkProxy before"); Object result = method.invoke(obj, args); System.out.println("JdkProxy after"); return result; }); } public Object getCglibProxy(Object obj) { return Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> { System.out.println("CglibProxy before"); Object result = method.invoke(obj, objects); System.out.println("CglibProxy after"); return result; }); }JDK Proxy 要求委托对象必须实现接口,cglib则不需要
Spring IoC高级应用与源码分析
Spring IoC 基础
-
纯xml/xml+注解
-
JavaSE:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); //或者,但不推荐 //new FileSystemXmlApplicationContext("c:/beans.xml"); -
JavaWeb:
web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--配置Spring的IoC容器的配置文件--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--使用监听器启动Spring的IoC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>TransferServlet.java
@Override public void init() throws ServletException { WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); ProxyFactory proxyFactory = (ProxyFactory) webApplicationContext.getBean("proxyFactory"); transferService = (TransferService) proxyFactory.getJdkProxy(webApplicationContext.getBean("transferService")); }
-
-
纯注解
-
JavaSE:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class); -
JavaWeb:
@Configuration @ComponentScan({"carol"}) @PropertySource("classpath:jdbc.properties") public class SpringConfig { @Value("${jdbc.driver}") private String driverClassnmae; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") public DataSource createDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassnmae); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--告诉ContextLoaderListener知道我们使用注解的方式启动ioc容器--> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <!--配置Spring的IoC容器的配置文件--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>carol.SpringConfig</param-value> </context-param> <!--使用监听器启动Spring的IoC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
-
BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接口/基础容器;ApplicationContext是它的一个子接口/高级接口,拥有更多功能。
纯xml模式
applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
- spring-beans.xsd xml文件的规范
- xmlns namespace 命名空间
- xmlns:xxx xxx为自定义前缀,使用标签时需加入前缀 xxx:abc
实例化Bean的三种方式
-
使用无参构造函数
<bean id="connectionUtils" class="utils.ConnectionUtils" /> -
静态方法
<bean id="connectionUtils" class="factory.CreateBeanFactory" factory-method="getInstanceStatic" />public class CreateBeanFactory { // static修饰的⽅法 public static ConnectionUtils getInstanceStatic() { return new ConnectionUtils(); } } -
实例化方法
<bean id="createBeanFactory" class="factory.CreateBeanFactory" /> <bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance" />public ConnectionUtils getInstance() { return new ConnectionUtils(); }
Bean的作用范围及生命周期
<bean id="accountDao" class="dao.impl.JdbcAccountDaoImpl" scope="prototype">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
| 单例模式:singleton | 多例模式:prototype | |
|---|---|---|
| 对象出⽣ | 当创建容器时,对象就被创建了。 | 当使⽤对象时,创建新的对象实例。 |
| 对象活着 | 只要容器在,对象⼀直活着。 | 只要对象在使⽤中,就⼀直活着。 |
| 对象死亡 | 当销毁容器时,对象就被销毁了。 | ⻓时间不⽤时,被java的gc回收了。 |
| 总结 | 对象⽣命周期与容器相同 | spring框架只负责创建,不负责销毁 |
Bean标签属性
-
id:⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
-
class:⽤于指定创建Bean对象的全限定类名。
-
name:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
-
factory-bean:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
-
factory-method:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
-
scope:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
-
init-method:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
-
destory-method:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。
DI 依赖注入的xml配置
两种注入方式
-
构造函数注入,需提供有参构造函数
<bean id="abc" class="xxx"> <!--name:参数名称;index:参数索引位置--> <constructor-arg name="A" ref="ref2Others" /> <constructor-arg index="1" value="123" /> </bean> -
set方法注入(更多使用),需提供set方法
<bean id="abc" class="xxx"> <property name="A" ref="ref2Others" /> <property name="B" value="123" /> </bean>
数据类型分类
-
基本类型和String :
-
其他Bean类型:
-
集合类型的注入(Array, List, Set, Map, Properties)
<property name="myArray"> <array> <value>array1</value> <value>array2</value> <ref bean="song1"/> </array> </property> <property> <map> <entry key="key1" value="value1" /> <entry key="Key2" value-ref="PersonBean" /> <entry key-ref="keyBean"> <ref bean="valueBean"/> </entry> </map> </property> <property> <set> <!--与array相同, 可替换--> </set> </property> <property> <props> <prop key="pro1">value1</prop> </props> </property>
xml与注解相结合模式
- 引入注解功能,不需要引入额外的jar包
- xml文件依然存在,容器的启动仍然从加载xml开始
- 第三方jar中的bean使用xml,自己开发的bean使用注解
| xml | 注解 |
|---|---|
| @Component("accountDao") bean的id属性直接配置在注解后,不配置则默认id为类名(首字母小写) 为了分层开发,提供三个别名@Controller(控制层), @Service(服务层), @Repository(dao层) | |
| 标签的scope属性 | @Scope("prototype"),默认单例,注解加在类上 |
| 标签的init-method属性* | @PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法 |
| 标签的destroy-method属性* | @PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法 |
*:需要引入jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
- DI的注解实现方式
@Autowired
@Qualifier(value="jdbcAccountDaoImpl")
private AccountDao accountDao;
- 使用注解功能
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--开启注解扫描,base-package指定扫描的包路径-->
<context:component-scan base-package="carol" />
<!--引入外部资源文件-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--第三方jar中的bean定义在xml中-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
</beans>
纯注解模式
- @Configuration 注解,表名当前类是⼀个配置类
- @ComponentScan 注解,替代 context:component-scan
- @PropertySource,引⼊外部属性配置⽂件
- @Import 引⼊其他配置类
- @Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
- @Bean 将⽅法返回对象加⼊ SpringIOC 容器
- [参考](#Spring IoC 基础)
Spring IoC 高级特性
lazy-init延迟加载
- xml
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />
<beans default-lazy-init="true">
<!--可以被单个bean的lazy-init覆盖-->
</beans>
- 注解
@Lazy
- debug查询生成的bean
FactoryBean 和 BeanFactory
-
BeanFactory
是容器的顶级接口,定义了基础行为,负责生产和管理Bean的一个工厂。具体使用它下面的子接口。
-
FactoryBean
生成某一个类型的Bean实例并返回,可以帮助自定义Bean的创建过程。(有别于普通Bean)
package org.springframework.beans.factory; import org.springframework.lang.Nullable; public interface FactoryBean<T> { @Nullable T getObject() throws Exception; @Nullable Class<?> getObjectType(); default boolean isSingleton() { return true; } }Company.java
package carol.pojo; public class Company { private String name; private int scale; @Override public String toString() { return "Company{" + "name='" + name + '\'' + ", scale=" + scale + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScale() { return scale; } public void setScale(int scale) { this.scale = scale; } }CompanyFactoryBean.java
package carol.factory; import carol.pojo.Company; import org.springframework.beans.factory.FactoryBean; public class CompanyFactoryBean implements FactoryBean { private String companyInfo; // name + "," + scale public void setCompanyInfo(String companyInfo) { this.companyInfo = companyInfo; } @Override public Object getObject() { // 需要较复杂的操作 Company company = new Company(); String[] strings = companyInfo.split(","); company.setName(strings[0]); company.setScale(Integer.parseInt(strings[1])); return company; } @Override public Class<?> getObjectType() { return Company.class; } }applicationContext.xml
<bean id="companyBean" class="carol.factory.CompanyFactoryBean"> <property name="companyInfo" value="WAP,3000"/> </bean>结果
applicationContext.getBean("companyBean"); // Company applicationContext.getBean("&companyBean"); // CompanyFactoryBean
后置处理器
BeanPostProcessor
-
该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,见下方生命周期中下划线的两步。
-
定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每 个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判 断我们将要处理的具体的bean。
-
SpringBean的生命周期
-
实例化Bean
-
设置属性值
-
调用BeanNameAware的setBeanName方法
-
调用BeanFactoryAware的setBeanFactory方法
-
调用ApplicationContextAware的setApplicationContext方法
-
调用BeanPostProcessor的预初始化方法
-
调用注解@PostConstruct方法
-
调用InitializingBean的afterPropertiesSet方法
-
调用定制的初始化方法init-method(xml)
-
调用BeanPostProcessor的后初始化方法
-
prototype
将准备就绪的Bean交给调用者
-
singleton
放入Spring缓存池
销毁过程
- 调用注解@PreDestroy方法
- 调用DisposableBean的destory方法
- 调用定制的销毁方法destory-method(xml)
-
-
BeanFactoryPostProcessor
- 时机:BeanFactory产生之后,Bean实例化之前
- BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个 BeanDefinition
- BeanFactoryPostProcessor#ConfigurableListableBeanFactory -> getBeanDefinition
- 经典应用PropertyPlaceholderConfigurer : 替换属性占位符${jdbc.}
Spring IoC源码深度剖析
-
spring-framework源码构建
-
编译顺序:spring-core,spring-oxm,spring-context,spring-beans,spring-aspects,spring-aop
-
tasks-other-compileTestJava
-
Spring IoC 容器初始化主体流程
- IoC容器不只是map,map只是容器的一个成员,叫做单例池(singletonObjects)
- 容器是一组组件和过程的集合,包括BeanFactory, 单例池,BeanPostProcessor等等,以及之间的协作
-
Bean生命周期关键时机点
- 参考
- 构造器、初始化方法、Bean后置处理器的before&after方法:#refresh -> AbstractApplicationContext#finishBeanFactoryInitialization
- Bean工厂后置处理器初始化、方法执行:#refresh -> AbstractApplicationContext#invokeBeanFactoryPostProcessors
- Bean后置处理器初始化:#refresh -> AbstractApplicationContext#registerBeanPostProcessors
-
AbstractApplicationContext#refresh
BeanFactory创建流程
-
入口: AbstractRefreshableApplicationContext#refreshBeanFactory
-
实例化:DefaultListableBeanFactory beanFactory = createBeanFactory()
-
factory属性: allowBeanDefinitionOverriding, allowCircularReferences
-
时序图
BeanDefinition加载注册
-
读取xml为document
AbstractRefreshableApplicationContext#refreshBeanFactory AbstractXmlApplicationContext#loadBeanDefinitions AbstractBeanDefinitionReader#loadBeanDefinitions XmlBeanDefinitionReader#loadBeanDefinitions XmlBeanDefinitionReader#doLoadBeanDefinitions -
注册
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions DefaultBeanDefinitionDocumentReader#parseBeanDefinitions DefaultBeanDefinitionDocumentReader#parseDefaultElement DefaultBeanDefinitionDocumentReader#processBeanDefinition BeanDefinitionReaderUtils#registerBeanDefinition DefaultListableBeanFactory#registerBeanDefinition最终注册
Bean对象创建流程
Bean实例化
装配属性
lazy-init延迟加载机制
以上跳过容器初始化阶段,在第一次使用时才会创建
循环依赖
- 对象的相互依赖关系形成死循环
处理机制
- 单例bean构造器参数循环依赖:无法解决
- prototype原型bean循环依赖:无法解决
- 单例bean通过setXxx或者@Autowired进行循环依赖
- 三级缓存机制:1. 单例池(成型SpringBean的) 2. early(升级过程中可以做一些扩展操作) 3. singletonFactorys
- A在对象实例化之后立刻放到三级缓存,提前暴露自己
- B在创建过程中,发现依赖A,在三级缓存中找到尚未成型的A,升级到二级缓存
- B创建完成后会放入一级缓存
- A使用一级缓存中的B完成创建
三级缓存拿出来的是ObjectFactory, 可以进行一定扩展
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
Spring AOP高级应用与源码剖析
AOP术语
在什么地方插入什么横切逻辑代码
-
Joinpoint(连接点)
它指的是那些可以⽤于把增强代码加⼊到业务主线中的点,那么由上图中我们可以看出,这些点指的就是⽅法。在⽅法执⾏的前后通过动态代理技术加⼊增强的代码。在Spring框架AOP思想的技术实现中,也只⽀持⽅法类型的连接点。(方法开始时、结束时、正常运行完毕时、方法异常时等特殊时机)
-
Pointcut(切入点)
它指的是那些已经把增强代码加⼊到业务主线进来之后的连接点。由上图中,我 们看出表现层 transfer ⽅法就只是连接点,因为判断访问权限的功能并没有对 其增强。
-
Advice(通知/增强)
它指的是切⾯类中⽤于提供增强功能的⽅法。并且不同的⽅法增强的时机是不⼀样的。⽐如,开启事务肯定要在业务⽅法执⾏之前执⾏;提交事务要在业务⽅法正常执⾏之后执⾏,⽽回滚事务要在业务⽅法执⾏产⽣异常之后执⾏等等。那么 这些就是通知的类型。其分类有:前置通知 后置通知 异常通知 最终通知 环绕通知。
-
Target(目标对象)
它指的是代理的⽬标对象。即被代理对象。
-
Proxy(代理)
它指的是⼀个类被AOP织⼊增强后,产⽣的代理类。即代理对象。
-
Weaving(织⼊)
它指的是把增强应⽤到⽬标对象来创建新的代理对象的过程。spring采⽤动态代理织⼊,⽽AspectJ采⽤编译期织⼊和类装载期织⼊。
-
Aspect(切面)
它指定是增强的代码所关注的⽅⾯,把这些相关的增强代码定义到⼀个类中,这个类就是切⾯类。例如,事务切⾯,它⾥⾯定义的⽅法就是和事务相关的,像开启事务,提交事务,回滚事务等等,不会定义其他与事务⽆关的⽅法。我们前⾯的案例中TrasnactionManager 就是⼀个切⾯。
Spring中AOP的代理选择
动态代理技术:当被代理对象没有实现任何接口时,使用cglib,否则使用jdk。可以通过配置的方式强制使用cglib。
Spring中AOP实现
纯xml方式
-
pom.xml
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.12.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> -
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 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="logUtils" class="carol.utils.LogUtils" /> <aop:config > <!--配置切面--> <aop:aspect id="logAspect" ref="logUtils"> <!--切入点--> <!--expression="execution(* *..*.*(..))"---> <!--expression="execution(* carol.service.impl.*.*(..))"---> <aop:pointcut id="pt1" expression="execution(public void carol.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, int))"/> <!--方位--> <!--前置通知/增强--> <!--aop:before method="beforeMethod" pointcut-ref="pt1"--> <!--aop:after 最终通知,无论如何都执行--> <!--aop:after-returning returning="retValue" 正常执行--> <!--aop:after-throwing 异常执行--> <aop:around method="around" pointcut-ref="pt1" /> </aop:aspect> </aop:config> </beans>package carol.utils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * @author Carol */ public class LogUtils { public void beforeMethod(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); for (Object arg : args) { System.out.println(arg); } System.out.println("业务逻辑开始执行之前"); } public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("[around] before"); Object result = null; try { // 控制原有逻辑是否执行 result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); } catch (Exception e) { System.out.println("[around] exception"); } finally { System.out.println("[around] after"); } return result; } }
半xml+半注解
-
xml中开启对注解的支持
<!--开启spring对注解aop的⽀持--> <aop:aspectj-autoproxy/> -
Java
package carol.utils; import org.aspectj.lang.JoinPoint; 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; /** * @author Carol */ @Component @Aspect public class LogUtils { @Pointcut("execution(public void carol.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, int))") public void pt1() { } // @Before("pt1()") public void beforeMethod(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); for (Object arg : args) { System.out.println(arg); } System.out.println("业务逻辑开始执行之前"); } @Around("pt1()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("[around] before"); Object result = null; try { // 控制原有逻辑是否执行 result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); } catch (Exception e) { System.out.println("[around] exception"); } finally { System.out.println("[around] after"); } return result; } }
全注解
-
xml
<!--开启spring对注解aop的⽀持--> <aop:aspectj-autoproxy/> -
配置类
/** * @author Carol */ @Configuration @ComponentScan({"carol"}) @PropertySource("classpath:jdbc.properties") @EnableAspectJAutoProxy public class SpringConfig { }
Spring声明式事务的支持
-
四大特性:原子性、一致性、隔离性、持久性
-
隔离级别 (处理并发问题)
-
问题:脏读,不可重复读(update),虚读(幻读)(insert/delete)
脏读 不可重复读 幻读 Serializable(串行化) √ √ √ Repeatable read(可重复读) √ √ x Read Commited(读已提交) √ x x Read Uncommitted(读未提交) x x x -
MySQL默认是Repeatable read(可重复读)
-
-
事务的传播行为
A调用B,AB本身都被添加了事务控制,需要协商
-
PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务。如果已经存在一个事务中,加入其中。最常见。
-
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,则以非事务的方式执行。
-
Spring中事务的API
-
PlatformTransactionManager
事务管理器的顶级接口
public interface PlatformTransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
纯xml模式
-
pom.xml
<!--spring aop的jar包支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.12.RELEASE</version> </dependency> <!--第三方的aop框架aspectj的jar--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!--引入spring声明式事务相关--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.1.12.RELEASE</version> </dependency> -
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 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 http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd "> <!--第三方jar中的bean定义在xml中--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"/> </bean> <!--spring声明式事务配置,声明式事务无非就是配置一个aop,只不过有些标签不一样罢了--> <!--横切逻辑--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <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+注解
-
xml配置
<!--声明式事务的注解驱动--> <tx:annotation-driven transaction-manager="transactionManager"/> -
接口、类或者方法上加上@Transactional注解(优先级:方法>类>接口)
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
纯注解
-
在Spring的配置类上添加
@EnableTransactionManagement//开启spring注解事务的⽀持 public class SpringConfiguration { }
Spring AOP源码深度剖析
AOP代理对象创建流程
调用栈
o.sf.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
|
o.sf.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAterInitialization
|
o.sf.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
|
o.sf.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
|
o.sf.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
|
o.sf.aop.framework.DefaultAopProxyFactory#createAopProxy
|
o.sf.aop.framework.CglibAopProxy#getProxy
Spring声明式事务控制
@EnableTransactionManagement @Transactional