Spring中IOC和AOP的核心组成架构详解
作者:抠脚的大灰狼
IoC配置
注册bean
XML
<bean> 标签
属性
- id
- class
- init-method
- destroy-method
- scope
子标签
- <constructor-arg>:构造器注入 --> c命名空间注入
- <property>:set方法注入 --> p命名空间注入
注解
- @Component:注解在类上,配合自动扫描(@ComponentScan),完成注册
- @Controller
- @Service
- @Repository
- @Bean:注解在方法上,方法的返回值对象会注册到Spring容器(常用于创建第三方jar包中的类对象)。这些被@Bean标注的方法需要放到Spring配置类当中(被@Configuration标注的类)
其他注解
- @ComponentScan:启用自动扫描,扫描指定包下带有@Component注解的类(对应XML中的<context:component-scan>)
- @Configuration:将某个类标记为配置类
- @Import:组合多个配置类
- @Scope:指定bean的作用范围。与@Component或@Bean组合使用。值是字符串,可以用ConfigurableBeanFactory中的常量,或WebApplicationContext中的常量
- @PostContruct:自定义初始化函数,约等XML的init-method。(也可通过InitializingBean接口实现)
- @PreDestroy:自定义销毁函数,约等于XML的destroy-method。(也可通过DisposableBean接口实现)
装配bean
@Autowired
属于Spring,按类型注入, required 属性默认为 true 。由Spring中的 AutowiredAnnotationBeanPostProcessor 进行处理。
注:带 @Autowired 注解的方法,会被Spring自动调用。
@Resource
属于JavaEE(JSR250),当指定了 name 属性时,按照名称( bean 的 id )注入;否则,按照类型注入
@Inject
属于JavaEE(JSR330),需导入额外的 jar 包,按照类型注入
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
@Value
用来注入基本类型和 String 类型的值。
可以用 ${} ,来获取 properties 文件中的属性。( properties 文件要加载到Spring中)
关于把 properties 文件加载到Spring
- 创建一个 PropertyPlaceholderConfigurer 对象到Spring容器中,设置 location 的值为 properties 文件的路径
可通过 <context:property-placeholder> 标签进行简化
- @PropertySource
用 @PropertySource 结合 @Component 或者 @Configuration
注意: properties 文件中的属性,会被加载到Spring的Environment对象中,多个 properties 中的同名属性,会发生覆盖
高级装配
环境与Profile
@Profile 与 @Configuration 或 @Component , @Bean 等结合使用。
可以控制仅在某一 profile 下激活对应的类。如下面的配置类仅在 dev 环境下生效
@Profile("dev") @Configuration public class DevConfig { @Bean public PetService petService() { return new PetService(); } }
如何指定当前激活的 profile ?
依赖于2个独立属性: spring.profiles.active 和 spring.profiles.default 。
若设置前者,则根据前者来确定;若没设置前者,则根据后者来确定。若2者都未指定,则只会激活那些没有定义在某一 profile 下的类
有多种方式来设置这2个属性
- 作为环境变量(如在maven的profile标签下配置上述2个属性,并在maven打包时用 -P 参数指定maven的profile)
<profiles> <!-- 开发环境 --> <profile> <id>dev</id> <properties> <spring.profiles.active>dev</spring.profiles.active> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <!-- 生产环境 --> <profile> <id>prod</id> <properties> <spring.profiles.active>prod</spring.profiles.active> </properties> </profile> </profiles>
- 作为Web应用的上下文参数(SpringMvc)
<!-- web,xml 中 --> <!-- 省略其他部分 --> <context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param>
作为DispatcherServlet的启动参数(SpringMvc)
- 在 web.xml 中配置 servlet 时,通过 <init-param> 指定
- 集成测试时,在测试类上用 @ActiveProfiles 注解进行指定
- 通过JVM启动参数 -Dspring.profiles.active=dev
条件化bean
在满足某些条件时,才创建某些 bean 。通过 @Conditional 注解和 Condition 接口实现,示例如下
package org.demo.pet.conditional; public class MagicBean { }
package org.demo.pet.conditional; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class MagicCondition implements Condition { /** * 若 matches 方法返回 true, 则表示条件满足, 会创建对应的bean; 若返回 false, 则不会创建对应bean * **/ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 当Spring的Environment中存在名为 magic 的属性, 才认为满足条件 return context.getEnvironment().containsProperty("magic"); } }
package org.demo.pet.conditional; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class ConditionalConfig { @Conditional(MagicCondition.class) @Bean public MagicBean magicBean() { return new MagicBean(); } }
测试
package org.demo.test.condition; import org.demo.pet.conditional.ConditionalConfig; import org.demo.pet.conditional.MagicBean; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = ConditionalConfig.class) public class ConditionTest { @Autowired(required = false) private MagicBean magicBean; @Test public void test() { System.out.println(magicBean == null); } }
由于 magic 属性在Spring中不存在,所以上面的 magicBean == null 表达式值为 true ,如下
若添加 magic 属性,则 MagicBean 会被创建
# magic.properties magic=HarryPotter
package org.demo.pet.conditional; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource("classpath:magic.properties") public class ConditionalConfig { @Conditional(MagicCondition.class) @Bean public MagicBean magicBean() { return new MagicBean(); } }
再次运行测试, magicBean == null 表达式值为 false
Condition 接口中的 matches 方法提供了2个参数: ConditionContext 和 AnnotatedTypedMetadata
通过 ConditionContext
- 借助getRegistry方法,可以检查bean的定义
- 借助getBeanFactory方法,可以检查bean是否存在,检查bean的属性等
- 借助getEnvironment方法,检查环境变量等
- …
通过 AnnotatedTypedMetadata ,可以检查注解的情况
Spring自带的的 @Profile 注解,也是通过 @Conditional 这种方式实现的
处理歧义性
依赖注入时,当同一个类型,有多个候选 bean 时,如何解决?
- 将其中一个 bean 标记为首选
使用 @Primary 注解( @Primary 与 @Component 搭配,或 @Primary 与 @Bean 搭配)(只能有一个首选 bean ,当出现多个首选 bean 时,Spring仍然不知道该注入哪一个)
- 使用限定符
@Qualifier 是主要的限定符使用方式,可以搭配 @Autowired ,或者 @Inject 。
一个 bean 的限定符,默认情况下是这个 bean 的 id ;当然,也可以为 bean 设置自定义的限定符,如
@Component @Qualifier("cold") // 也可以与@Bean注解结合使用 public class IceCream implements Dessert { }
一个 bean 可以有多个限定符,不同的 bean 可以有相同的限定符。当依赖注入时,如果通过一个限定符仍然能匹配多个候选者,那么可以指定更多的限定符,如
@Component @Qualifier("cold") @Qualifier("creamy") public class IceCream implements Dessert { }
@Component @Qualifier("cold") public class Popsicle implements Dessert { }
@Autowired @Qualifier("cold") @Qualifier("creamy") public void setDessert(Dessert dessert) {}
注:Java 8 之前不允许出现重复注解,Java 8以后,当这个注解被标记为 @Repeatable 时,可以重复。
可以创建自定义的限定符注解来解决这个问题,如
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Cold { }
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Creamy { }
AOP配置
术语
- JoinPoint:连接点。共3种:方法级别,字段级别,构造器级别。SpringAop仅支持方法级别,AspectJ支持全部3种。
- PointCut:切入点。连接点的匹配条件。
- Advice:通知。根据执行时机,分为前置通知,后置通知,环绕通知,等。
- Aspect:切面。等于切入点+通知
- Weaving:织入。分为静态织入,动态织入。SpringAop采用动态织入(动态代理),AspectJ采用静态织入。
- Target:目标对象。即被AOP拦截的原始对象。
- Proxy:代理对象。
- Introduction:引介。可对目标对象添加新的方法。
使用 XML
<aop:config> <aop:pointcut id="pointcut" expression="within(org..*.PetService)"/> <!-- 切入点 --> <aop:aspect ref="xmlAspect"> <!-- 切面, 使用id为xmlAspect的对象 --> <aop:before method="log" pointcut-ref="pointcut"/> <!-- 使用切面对象中的log方法, 对切入点应用前置通知 --> </aop:aspect> </aop:config>
注解
- 定义切面
- @Aspect @Component
- 切面中定义切入点和通知
切入点
- @Pointcut :定义一个切入点,在其他地方可引用。
切入点表达式
- execution:匹配方法。格式为[修饰符] 返回值 包名.类名.方法名(形参列表) [异常]
- within:匹配类
- args:匹配参数
- this:匹配代理对象(可指定为接口,会拦截到接口实现类)
- target:匹配目标对象
- bean:匹配Spring容器中的bean
- @annotation:匹配带某一注解的方法
- @within:匹配带某一注解的类(要求注解的RetentionPolicy至少为CLASS级别)
- @target:匹配带某一注解的类(要求注解的RetentionPolicy至少为RUNTIME)
- @args:匹配带某一注解的参数
切入点表达式中可以用通配符和逻辑运算符
通配符
- *:匹配任意数量字符
- ..:匹配多级包名, 或任意参数
- +:匹配指定类及其子类
逻辑运算符: && , || , ! (在XML中需要换成 and , or , not )
通知
- @Before
- @After:无论目标对象的方法是否执行成功,都会执行通知。
- @Around
环绕通知,最强大的通知。其方法形参中必须包含一个 ProceedingJoinPoint 对象,可以通过这个对象上的 getArgs 方法获取被拦截的目标对象的方法入参,通过 proceed 方法执行原目标对象的方法。
- @AfterReturning :仅在目标对象的方法执行成功后,执行通知。
- @AfterThrowing :仅在目标对象的方法执行抛出异常时,执行通知。
- 开启AOP自动代理
@EnableAspectJAutoProxy 或者 <aop:aspectj-autoproxy/>
事务支持
3大核心接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
需要
- 一个PlatformTransactionManager对象(这个对象需要一个DataSource对象)
- 事务的AOP配置
通常使用Mybatis时,采用spring-jdbc包下的DataSourceTransactionManager即可
使用
- 需要导入spring-tx依赖,通常还需要spring-jdbc。
- 当PlatformTransactionManager对象准备就绪后,进行事务的AOP配置即可,如下
XML
- <tx:advice>配置需要进行事务管理的方法。
- <aop:config>配置AOP。其子标签使用<aop:advisor>。
注解
- @Transactional:在需要事务支持的方法上,打上该注解,并配置事务隔离级别,传播行为等
- 开启事务事务管理
@EnableTransactionManagement或者<tx:annotation-driven>
到此这篇关于Spring中核心功能IOC和AOP的详细解读的文章就介绍到这了,更多相关Spring中的IOC和AOP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!