Spring
AOP
简述
概念:Aspect-Oriented Programming,一般称为面向切面编程。
目的:用于将那些与业务无关的,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被称为切面(Aspect)
用途:权限认证、日志、事务处理等,作为面向对象的一种补充
好处:减少重复代码,提高系统可维护性,降低模块间的耦合度
原理:使用的是动态代理
动态代理
JDK动态代理
只提供接口代理,不支持类代理
运行时生成动态代理类 $proxy.class
代理类实现了目标类接口,并且实现接口类所有方法增强代码
调用时,通过代理类先去调用处理类进行增强,再通过反射方式调用目标类的方法,从而实现AOP
CGLIB动态代理
没有实现接口
通过ASM在运行时动态生成目标类的一个子类 ,(还有相关类,主要是为了增强调用时效率)会生成多个
会重写父类所有的方法增强代码
调用时,先代理类进行增强,再直接调用父类对应方法 ,从而实现AOP
类被标记为final,无法使用CGLIB做动态代理的
除了目标子类代理类,还有个FastClass(路由类),可以(但不是必须)让本类方法调用进行增强,不会像jdk代理
相关方法
是否是代理对象:
AopUtils.isAopProxy(AopContext.currentProxy());
是否是cglib代理对象:
AopUtils.isCglibProxy(AopContext.currentProxy())
是否是jdk代理对象:
AopUtils.isJdkDynamicProxy(AopContext.currentProxy());
相关名词
切面 Aspect 切面类,管理切点和通知
连接点 Join Point 被增强的业务方法
通知 Advice 需要增加到业务方法中的公共代码和逻辑
切点 Pointcut 决定那些方法需要增强,那些不需要。结合切点表达式
目标对象 Target Object 增强的对象
织入 Weaving Aspectj独有的,Spring aop织入方式是动态代理。
编译器
类加载期
运行期
其他
Advisor 顾问。是Pointcut和Advice的一个结合
AspectJ
Spring AOP使用
Spring AOP 提供了AspectJ的支持,
但只用到了AspectJ的切点解析和匹配,及@Aspect、@Before等注解
在容器启动时需要生成代理实例,在方法的调用上也会增加栈的深度,使得Spring AOP 性能没有AspectJ那么好
AspectJ本身
SpringIOC
控制反转
new实例类,耦合度太高,维护不方便
引入IOC,将创建对象控制权交给了Spring,控制了对象的创建权利,Spring IOC去创建
要使用对象:需要通过DI(依赖注入)@Autowired,从容器中自动注入
优点
实现机制
工厂模式
BeanFactory.getBean(String beanName)
反射
通过反射,实例化创建Bean对象 BeanFactory.getBean(String className)
Bean
作用域
作用域:Prototype/Singleton/Request/Session/Global Session
Prototype:原型模式,每次获取bean都会创建以一个新的实例,因此不存在线程安全的问题。
Singleton:不同的线程访问同一个bean,如果这个bean中含有实例变量,并且线程具有对实例变量的写操作时,就会产生线程安全问题
解决:使用ThreadLocal ,为每个线程创建独立的变量副本,互相隔离互不影响
只要Bean是无状态的则一定是线程安全的
加载方式
使用
@Component
注解 和@ComponentScan
对其进行扫描在配置类中(
@Configuration
),使用@Bean
注解,将方法返回值加载到容器中类上声明
@Import(User.class)
注解
Autowired
required属性
@Autowired(required = true)
默认是true,表示注入的时候,该bean必须存在,否则会注入失败
@Autowired(required = false)
忽略当前要注入bean,如果有直接注入,没有跳过,不会报错
容器启动过程中,会初始化bean,spring核心之一(IOC)
当前容器不能注入自己,这样就会不停的注入自己,陷入死循环,从而找不到要注入的bean
Spring框架提供的三级缓存来专门解决循环依赖
构造函数注入会进入死循环
依赖注入方式
一、Field Injection
二、Construction Injection (推荐)
三、Setter Injection
对比
注入方式 | 可靠性 | 可维护性 | 可测试性 | 灵活性 | 循环关系检测 | 性能影响 |
---|---|---|---|---|---|---|
Field Injection | 不可靠 | 差 | 差 | 灵活 | 不检测 | 启动快 |
Constructor Injection | 可靠 | 好 | 好 | 不灵活 | 自动检测 | 启动慢 |
Setter Injection | 不可靠 | 差 | 好 | 灵活 | 不检测 | 启动快 |
Resource
和Autowire区别
Transactional
回滚rollbackFor()。默认是RuntimeException
IOException不是RuntimeException子类,不会进行事务回滚
RequestParam & RequestBody
RequestParam
RequestBody
JavaConfig
注解的方式开发,代替xml
@Configuration
@Bean
@ComponentScan
@EnableWebMvc
@ImportResource
@PropertySource
依赖
spring-boot-starter-parent
Java版本
<properties> <java.version>1.8</java.version> </properties>源码文件编码
<properties> <project.build.sourceEncoding>GBK</project.build.sourceEncoding> </properties>依赖管理
打包支持
动态识别资源
<resource> <directory>src/main/resources</directory> <includes> <include>**/*.*</include> </includes> <filtering>true</filtering> </resource>识别插件设置。如exec plugin,surefire,Git commit ID等
设别不同的配置。如application-dev.properties和 application-dev.yml
有些继承自其父级spring-boot-dependencies
事务
不生效
Bean没有纳入Spring容器管理
(不是动态代理的Bean),里面方法是没有事务管理的
自调用
调用当前类的方法,比如用this调用当前类的的方法
原因:this.methodA(),没有走TestService的代理类,所以事务会失效
解决
methodA()和methodB()分别放到不同的类中
自己注入自己,用注入的实例调用
获取动态代理类,调用自己类的方法 且设置暴露当前代理对象到本地线程(
@EnableAspectJAutoProxy(expostProxy=true)
)@Service public class TestService{ @Transactional public void methodA(){ } public void methodB() { ((TestService)AopContext.currentProxy()).methodA(); } }
异常没有抛出了,被try catch了
抛出的不是RuntimeException,而且没有指定rollbackFor=异常类型
事务方法不是public的,不会被动态代理
事务方法内启用新线程进行异步操作
数据库不支持事务
五种事务隔离级别
隔离级别 | 说明 |
---|---|
DEFAULT | 使用数据库默认的事务隔离级别 |
READ_COMMITTED | 读已提交:防止脏读。但会出现不可重复读和幻读 |
READ_UNCOMMITTED | 读未提交:最低隔离级别事务。会产生脏读、不可重复读和幻读 |
REPEATABLE_READ | 可重复读:可以防止脏读、不可重复读。但会幻读 |
SERIALIZABLE | 串行化:最高隔离级别最可靠 |
七种事务传播行为
传播行为 | 说明 |
---|---|
REQUIRED | 若存在事务,则支持当前事务,否则开启一个新的事务 |
SUPPORTS | |
MANDATORY | |
REQUIRES_NEW | 总是新开,挂起当前事务 |
NOT_SUPPORTED | |
NEVER | |
NESTED |
Other
SpringBoot属性加载顺序
命令行参数,ex: java -jar -Dspring.profile.active
操作系统环境变量
application.properties或application.yml
@Configuration注解的类,@PropertySource注解的属性