spring 基础
核心思想
1,IOC 和 AOP 是一种思想,而不是一种技术
2,在 spring 之前就已经存在了,之不是这两种思想在 spring 得到了非常好的技术实现
IOC 和 DI
1,Inversion of Control,控制反转
2,控制:创建(实例化、管理)对象的权力,反转:把控制权交出来(交给 spring 的 IOC 容器)
3,传统方式如果 A 依赖 B,那么会在 A 里创建 B 对象,在 spring 里面使用 @Autowired 即可
4,IOC 和 DI 描述的是同一个事情,只是角度不同
- IOC 是站在对象的角度,即 控制反转。程序需要什么对象,就到容器去取
- DI 是站在容器的角度,即 依赖注入。程序需要什么对象,容器就推送给他什么对象
AOP
1,Aspect oriented Programming ⾯向切⾯编程
2,OOP(面向对象) 的延续。面向对象能解决很多的代码重复问题。但是有局限性。比如每个接口需要增加额外的操作(比如接口耗时、日志统计等)
- 在不改变原有业务逻辑的情况下,横切逻辑代码,从根本上解耦,避免重复代码
- 切:代码是竖着执行,执行的是业务逻辑,业务逻辑之外的增强逻辑代码,可以想象成一把刀把业务横着切开了
- 面:横切往往影响很多方法,每个方法都如同一个点,点多了构成一个面
IOC 应用
其实就是创建 bean 对象的一些操作
bean 生命周期
<bean id="transferService" class="com.lagou.service.impl.TransferServiceImpl" scope="singleton" />
- singleton:单例模式,容器创建时创建 bean,每次使用都是这个 bean
- prototype:多例模式,每次使用都创建新的 bean
基于 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">
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl" />
</beans>
方式一:使用无参构造函数(默认)创建对象
使用反射调用无参构造,如果当前类没有无参构造函数会创建失败
<!-- id 必须唯一;class 指定该 bean 的实现类,反射创建的对象 -->
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
<!-- 指定默认值,将调用 setName 方法 -->
<property name="name" value="Rose"></property>
</bean>
方式二:使用静态工厂方法创建对象
<!-- 定义 chinese Bean 由 PersonFactory 工厂的 getPerson(这是个静态方法) 方法创建 -->
<bean id="chinese" class="com.mao.staticFactory.PersonFactory" factory-method="getPerson">
<!-- getPerson 方法传参 -->
<constructor-arg value="chinese"/>
<!-- setMsg 传参 -->
<property name="msg" value="我是中国人"/>
</bean>
<!-- 创建american Bean -->
<bean id="american" class="com.mao.staticFactory.PersonFactory" factory-method="getPerson">
<constructor-arg value="american"/>
<property name="msg" value="我是美国人"></property>
</bean>
public class PersonFactory {
public static Person getPerson(String arg){
if(arg.equalsIgnoreCase("chinese")){
return new Chinese();
} else {
return new American();
}
}
}
分析 chinese bean 的创建
- 将调用 PersonFactory.getPerson 完成实例化
- 根据 constructor-arg = chinese,方法传入了 chinese,所以会返回 new Chinese()
- 根据
<property name="msg" value="我是中国人"/>
会调用 Chinese 的 setMsg("我是中国人") - 得到的 bean:chinese:
方式三:使用普通方法创建对象
方法不是静态的,不用 static 修饰
<!-- 配置工厂Bean,class指定该工厂的实现类,该Bean负责产生其他Bean实例 -->
<bean id="personFactory" class="com.mao.instanceFactory.PersonFactory"/>
<!-- 由实例工厂Bean的getPerson()方法,创建Chinese Bean, -->
<bean id="ch" factory-bean="personFactory" factory-method="getPerson">
<!-- 为该方法传入参数为chinese -->
<constructor-arg value="chinese"/>
</bean>
<!-- 由实例工厂Bean的getPerson()方法,创建American Bean, -->
<bean id="usa" factory-bean="personFactory" factory-method="getPerson">
<constructor-arg value="american" />
</bean>
public class PersonFactory {
// 不是静态方法(其实是不是都没关系,因为不管是否是静态的,都能调用到这个方法)
public Person getPerson(String arg){
if(arg.equalsIgnoreCase("chinese")){
return new Chinese();
} else {
return new American();
}
}
}
基于注解
不再使用 xml 配置文件,使用注解完成 bean 的定义和作用域等属性配置
注解 | 对应 xml 标签 | 注解的地方 | 含义 |
---|---|---|---|
@Configuration | xml配置文件 | 类 | 表名当前类是⼀个配置类 |
@Bean | xml 的 bean 标签 | 方法 | 方法名是 id 属性,return 类型是 class 属性 有个属性 initMethod,初始化后执行,效果和 @PostConstruct 一致 对于初始化后执行的操作还有一种方式:实现 InitializingBean 接口,这种性能要高些,因为不是反射完成的 |
@PostConstruct | bean 标签的 init-method 属性 | 方法 | 配合 @Bean 使用,初始化 bean 后执行 |
@Scope | bean 标签的 scope 属性 | 方法 | 和 @Bean 配合使用,指明作用域,单例还是多例 |
@Lazy | bean 标签的 lazy-init=true | 方法 | 和 @Bean 配合使用,是否懒加载 使用时才实例化 bean,而不是服务启动就实例化所有 bean |
@Value | bean 的 property 属性标签 | 属性 | 实例化该 bean 时,调用 set 方法赋值 1,@Value("#{xxx.xxx}"是把别的 bean 的值进行赋值 2,@Value("${xxx.xxx}") 是把配置文件的值进行赋值(读取配置文件) 3,@Value("xxx") 直接赋值基本类型 |
@ComponentScan | context:component-scan | 类 | 扫描包范围,该路径下的 bean 交给 spring 管理 1,一般使用 @ComponentScans,全局出现一次 @ComponentScans(value = {@ComponentScan(value = "io.mieux.controller"),@ComponentScan(value = "io.mieux.service")}) 2,value 和 basePackages 功能相同 3,basePackageClasses 属性把指定的类添加到容器中 4,includeFilters 添加某些类到容器中,即使这些类没有标注 @Component 注解 5,指定某些类不添加到容器中 |
@PropertySource | context:property-placeholder location="classpath:xxx.properties" | 类 | 引⼊外部属性配置⽂件,利用其数据构建 bean,一般配合@Configuration、 @ConfigurationProperties、@EnableConfigurationProperties 等注解完成 |
@Import | - | 类或注解 | 也是把指定的类添加到容器中,有三种用法,后面详细讲解 |
@Component | - | 类或接口 | 依赖注入 bean,还有 @Service、@Controller、@Repository,必须在 @ComponentScan 指定的路径下 |
循环依赖
两个或以上的对象互引用对方,造成创建 bean 时死循环
public class A{
private B b;
}
public class B{
private A a;
}
- 产生原因
- spring 在实例化 A 的时候,发现其属性引用了 B
- 实例化 B,但是在 B 内部也引用了 A(此时 A 还没有创建好,ApplicationContext.getBean(A)获取不到,于是就会创建 A)
- ...
- spring 解决方案
- 实例化 A 的时候,创建好了,但还没有完成初始化(成员变量赋值),这时把 A 先暴露出去(三级缓存)
- 为 A 初始化,发现成员变量依赖 B,B 还没有创建,于是实例化 B,B 实例化完成
- 为 B 初始化,发现成员变量依赖 A,这时候已经能获取到 A 了,B 初始化完成,这时能拿到 B 了,再完成 A 的初始化
AOP 应用
本质是在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代码、事务控制代码、性能监控代码
说人话就是 一些方法要添加相同的操作功能,不到每个方法里添加相同的代码块,单独写一个方法,在每个方法执行时能执行到新添加的这个方法
术语
- 连接点(Joinpoint):目标方法执行的某个时机,执行前,执行后,抛出异常时等
- 切点(Pointcut):具体方法(定义类也会落实到类下面的所有方法)
- 通知(advice):前置、后置、环绕、异常、返回通知,具体增强的逻辑是啥
- 目标对象(target):被代理的对象
- 代理对象(proxy):代理对象(动态代理,有接口就 JDK ,没有接口就 CGLIB)
- 织入(Weaving):编译期、类装载、动态代理织入三种方式,spring 采用动态代理方式,AspectJ 采用编译期和类装载方式
- 切面(Aspect):是一个类,定义切点、连接点、通知等
基于 xml
基于注解
见以前我写的 demo(https://gitee.com/huanggyaaa/springboot/tree/master/aspect),可以切注解、类、方法、接口,有前置、后置、环绕、返回等通知
这是基于 springboot 的,springboot 的自动加载功能(@EnableAutoConfiguration)会自动加载事务相关的信息,所以可以不用 @EnableTransactionManagement 注解
声明式事务底层
用两个注解即可完成,@EnableTransactionManagement 和 @Transactional,分析下这俩货的底层做了啥操作
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 这里导入了 TransactionManagementConfigurationSelector 类
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
// @Import 注解用法之一就是 类 必须实现 ImportSelector 接口并重写 selectImports 方法
public class TransactionManagementConfigurationSelector extends
// 省略...
// 像容器又添加了两个组件:AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration
protected String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
case PROXY:
return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[]{this.determineTransactionAspectClass()};
default:
return null;
}
}
// 省略...
}
AutoProxyRegistrar组件
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
// 省略 ...
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 省略 ...
if (mode == AdviceMode.PROXY) {
// 这里又注册了一个组件
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
}
}
// 省略 ...
}
}
public abstract class AopConfigUtils {
// 省略 ...
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary(registry, (Object)null);
}
// 省略 ...
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(
// 最终注册了 InfrastructureAdvisorAutoProxyCreator 的 bean
// 这个类继承了 AbstractAutoProxyCreator
InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
// 省略 ...
}
ProxyTransactionManagementConfiguration 组件
@Configuration(proxyBeanMethods = false)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
public ProxyTransactionManagementConfiguration() {
}
@Bean(name = {"org.springframework.transaction.config.internalTransactionAdvisor"})
@Role(2)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
// 事务增强器
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
// 向事务增强器中注⼊ 属性解析器 transactionAttributeSource
advisor.setTransactionAttributeSource(transactionAttributeSource);
// 向事务增强器中注⼊ 事务拦截器 transactionInterceptor
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder((Integer)this.enableTx.getNumber("order"));
}
return advisor;
}
@Bean
@Role(2)
// 属性解析器 transactionAttributeSource,作用之一就是 解析@Transaction注解
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(2)
// 事务拦截器 transactionInterceptor
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
// interceptor.invoke 调用 interceptor.invokeWithinTransaction,完成原有的逻辑和增强的通知执行
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
- 可以看到 @EnableTransactionManagement 导入了两个类:
AutoProxyRegistrar
和ProxyTransactionManagementConfiguration
- AutoProxyRegistrar 组件引入了 InfrastructureAdvisorAutoProxyCreator 类,这个类继承了 AbstractAutoProxyCreator 是一个后置处理器
- ProxyTransactionManagementConfiguration 组件完成 AOP 通知操作