Spring阅读笔记:Spring Core
Spring Core
1. Spring 总体了解
1.1 Spring作用:简化Java开发
1.1.1 基于POJO的轻量级实现最小侵入性编程
一个类使用Spring注解,但未实现或继承任何Spring规范的API,仍保留POJO模式。代码整洁
1.1.2 通过依赖注入实现松耦合
-
紧耦合
- 实现:在对象中具体实现或初始化了另外的依赖对象
- 极大地限制了对象的能力
- 不便于测试
-
松耦合
- 实现:构造器注入依赖对象
- 在对象毫不知情的情况下,使用不同的依赖对象(前提是依赖对象实现了同一个注入的接口)
-
具体使用
- 创建POJO
- 在XML或者使用Configuration注解,装载Bean,注入依赖
- 在主程序中加载Application Context,获取Bean,调用方法
1.1.3 面向切面编程实现高内聚
-
实现:使公共服务模块化,并以声明的方式将他们应用到组件中
-
好处:使核心应用只关注自身业务,不必关心其他服务的实现细节
-
具体使用
- 创建POJO
- 在XML或者使用Configuration注解,装载Bean,注入依赖
- 在XML或者使用Configuration注解,定义切点(绑定组件),声明前置或后置通知(绑定方法)
1.1.4 通过模板封装消除样板化代码
1.2 Spring组件:Bean
1.2.1 BeanFactory(不推荐)
1.2.2 Application Context
- Annotation Config Application Context
- Annotation Config Web Application Context
- Class Path Xml Application Context
- File System Xml Application Context
- Xml Web Application Context
1.2.3 Bean的生命周期
- Spring对Bean进行实例化
- Spring将值或者Bean的引用注入Bean对应的属性中
- 如果Bean实现了BeanNameAware接口,Spring将Bean的id传给setBeanName()
- 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory(),传入BeanFactory容器实例对象
- 如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext(),传入Bean所在的应用上下文
- 如果Bean实现了BeanPostProcessor接口,Spring将调用postProcessBeforeInitialization()
- 如果Bean实现了InitializingBean接口,Spring将调用afterPropertiesSet()
- 如果Bean使用了init-method,也会被调用
- 如果Bean实现了BeanPostProcessor接口,Spring将调用postProcessAfterInitialization()
- 此时,Bean已经准备就绪,一直存在于应用上下文中,直到该应用上下文被销毁
- 如果Bean实现了Dispos ableBean接口,Spring将调用destroy()
- 如果Bean使用了destroy-method,也会被调用
2. 面向切面
2.1 AOP基础概念
2.1.1 AOP作用
在一个地方定义通用功能,通过声明方式定义这个功能要在何时何处做何事,而无需修改受影响的类
2.1.2 AOP术语
- 通知:定义切面是什么,何时使用
- 连接点:可以是调用方法时、抛出异常时、修改字段时
- 切点:定义连接点的范围,即在何处
- 切面:通知+切点
- 引入:向已有的类添加新属性和方法
- 织入:把切面应用到目标对象并创建新的代理对象的过程
- 编译期织入(AspectJ的织入编译器)
- 类加载期织入(ClassLoader)
- 运行时织入(SpringAop)
2.1.3 Spring的AOP
- Spring通知通过Java编写
- Spring在运行时通知对象
- Spring只支持方法级别的连接点
2.2 通过切点来选择连接点
2.2.1 切点编写
- execution(* packageName.className.method(..))
- 限制条件:arg()、this()、target、within()
- 连接:and、or、not、&、|、!
2.2.2 在切点中选择Bean
bean()
2.3 创建切面的两种方式
2.3.1 使用注解创建切面
@Aspect定义切面
- @After
- @Before
- @AfterThrowing
- @AfterReturning
- @Around
- 声明的方法必须有ProceedingJoinPoint参数,且需要在try中合适位置调用jp.proceed(),代表被通知方法调用
- @Pointcut声明频繁使用的切点表达式,定义切点时直接使用被@Pointcut声明的方法即可
开启AspectJ自动代理
- 在JavaConfig配置类上添加@EnableAspectJ***
- 在XML中添加<aop:aspectj-*** />
处理通知中的参数
切点表达式中使用arg(),@Pointcut声明方法带参数,再在合适时机调用
引入新功能
@DeclareParents(value, defaultImpl)声明要引入的接口
-
value:指定哪种类型的Bean要引入该接口
-
defaultImpl:指定实现引入接口功能的类
2.3.2 使用XML创建切面
3. 装配Bean
3.1 自动化装配
创建接口,创建实现该接口且实现@Component的Bean类
- Bean的id可以在@Component中声明
- 在构造器或Setter方法上使用@Autowired,为Bean注入依赖Bean
在JavaConfig或XML中开启组件扫描配置
- 在@ComponentScan中声明basePackages数组,里面填充包名
- 在@ComponentScan中声明basePackageClasses数组,里面填充类名,自动扫描类所在的包
3.2 JavaConfig装配
- 使用@Configuration创建配置类
- 在JavaConfig中使用@Bean创建方法,返回值是Bean对象(通过@Bean中name参数设置id)
- 为Bean注入依赖:使用参数为被依赖的Bean对象创建方法的方法,创建Bean对象
- 被依赖的Bean对象的创建方法并未真的调用,因为该方法也是@Bean。保证多次调用返回的是同一个Bean对象
- 创建依赖Bean的方法也可以传入被依赖的Bean对象,此对象可以有三种方式装配(自动、Java Config、XML)
3.3 XML装配
3.3.1 XML配置规范
3.3.2 声明Bean
3.3.3 构造器注入
-
依赖Bean注入
-
字面量注入
-
集合注入
-
注入方式:、c名称空间
3.3.4 Setter注入
- 依赖Bean注入
- 字面量注入
- 集合注入
- 注入方式:、p名称空间
4. 高级装配
4.1 Profile适应不同环境
目的
在不同环境中某个Bean会有所不同,我们需要选择最合适的配置
做法
使用Bean Profile,在运行时根据环境决定该创建哪些Bean和不创建哪些Bean
具体实现
Java Config
- 在@Configuration配置类上使用@Profile(),表示这个配置类中的所有Bean都在该Profile激活时创建
- 在配置类中的@Bean创建方法上使用@Profile(),表示单独这个Bean在Profile激活时创建
XML
配置<beans>
元素中的profile属性,也可重复使用配置多个profile
激活Profile
-
spring.profiles.active
-
spring.profiles.default
-
激活方式
- 作为DispatcherServlet的初始化参数
- 作为Web应用的上下文参数
- 作为JNDI条目
- 作为环境变量
- 作为JVM的系统属性
- 在集成测试类上,使用@ActiveProfiles注解
4.2 Conditional条件化装载
需求
- 希望一个或多个Bean只有在应用的类路径下包含特定的库时才创建
- 希望某个Bean只有当另外某个特定的Bean也声明了之后才会创建
- 希望只有某个特定的环境变量设置后才创建
实现
- 在Java Config配置类的@Bean创建方法上,使用@Conditional注解,注解中传入实现了Condition接口的类
- 创建Condition接口的实现类,实现matches方法
详解matches方法的参数
ConditionContext接口
- 通过getRegistry()返回的BeanDefinitationRegistry检查Bean定义
- getBeanFactory()
- getEnviroment()
- getResourceLoader()
- getClassLoader()
AnnotatedTypeMetadata接口
- isAnnotated(),判断带@Bean的方法是不是还有其他特定注解
- getAnnotationAttributes(),检查@Bean方法上其他注解的属性
4.3 处理自动装配的歧义性
标识首选
- 在@Component类上使用@Primary设为首选
- 在Java Config中@Bean方法上使用@Primary
- 在XML中将
<bean>
的primary属性设为true
限定范围
使用Bean自动生成的id限定
在@Autowired的Setter方法上使用@Qualifier限定注入Bean的id
- 导致被依赖的Bean的id和依赖的Bean紧耦合,修改重构时无法自动匹配
创建自定义限定符(面向特性)
- 在@Component类上添加@Qualifier限定范围
- 在@Autowired注入依赖方法上添加@Qualified注入限定范围内的Bean
- 使用Java Config也可以将@Bean和@Qualifed一起用
创建自定义限定符注解(面向特性)
- 使用@Target()、@Retention()、@Qualifier自定义注解
- 在@Component类上添加多个自定义注解
4.4 Bean的作用域
单例Singleton
整个应用中只创建一个(默认)
原型Prototype
- 功能:每次注入或者通过Spring应用上下文获取时,都会创建一个新的Bean
- 组件扫描配置
- 在@Component类上添加@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- Java Config配置
- 在@Bean方法上添加@Scope(ConfigurableBeanFactory.SCOPE_PRO TOTYPE)
- XML 配置
- 在
<bean>
中设置scope属性值为prototype
- 在
会话Session
- 功能:在Web应用中,每个会话创建一个Bean
- 在@Component类上使用@Scope,设置value、proxyMode
- 在XML中将
<bean>
的scope属性设为session,增加<aop:scoped-proxy>
- 默认是创建目标类的代理
- 可以将proxy-target-class设为false,创建基于接口的代理
请求Request
- 功能:在Web应用中,每次请求创建一个Bean
- 用法与会话类似
4.5 运行时值注入
目的
将具体值提取到配置文件中,运行时加载,避免硬编码
Spring中的Environment
-
与属性相关方法:getProperty()、getRequiredProperty()、getPropertyAsClass()
-
与Profile状态相关方法:getActiveProfiles()、getDefaultProfiles()、accpetsProfiles()
注入方式
-
属性占位符
- 组件扫描自动装配
- 在@Component类的构造器中使用@Value(“${name}”)获取配置文件中的值
- 为使用占位符,需配置一个Bean是PropertySourcesPlaceholderConfigurer
- 组件扫描自动装配
-
Spring表达式语言(SpEL)