Spring中IOC和AOP的核心组成架构详解

 更新时间:2023年08月25日 10:13:42   作者:抠脚的大灰狼  
这篇文章主要介绍了Spring中IOC和AOP的核心组成架构详解,本文是对Spring的2大核心功能——IoC和AOP 的总结提炼,并增加了环境profile和条件化bean的内容,篇幅较短,更像是一个大纲,或者思维导图,需要的朋友可以参考下

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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java版飞机大战实战项目详细步骤

    java版飞机大战实战项目详细步骤

    这篇文章主要为大家详细介绍了java版飞机大战实战项目详细步骤,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • Java import导入及访问控制权限修饰符原理解析

    Java import导入及访问控制权限修饰符原理解析

    这篇文章主要介绍了Java import导入及访问控制权限修饰符过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • java中的i++和++i的区别详解

    java中的i++和++i的区别详解

    这篇文章主要介绍了java中的i++和++i的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Java 中的伪共享详解及解决方案

    Java 中的伪共享详解及解决方案

    这篇文章主要介绍了Java 中的伪共享详解及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • 如何在IDEA运行spark程序(搭建Spark开发环境)

    如何在IDEA运行spark程序(搭建Spark开发环境)

    spark程序可以通过pom.xml的文件配置,添加spark-core依赖,可以直接在IDEA中编写spark程序并运行结果,这篇文章主要介绍了如何在IDEA运行spark程序(搭建Spark开发环境),需要的朋友可以参考下
    2024-02-02
  • Spring中的事务管理如何配置

    Spring中的事务管理如何配置

    这篇文章主要介绍了spring中的事务管理如何配置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 使用监听器对Spring bean id进行唯一校验过程解析

    使用监听器对Spring bean id进行唯一校验过程解析

    这篇文章主要介绍了使用监听器对Spring bean id进行唯一校验过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • BeanUtils.copyProperties在拷贝属性时忽略空值的操作

    BeanUtils.copyProperties在拷贝属性时忽略空值的操作

    这篇文章主要介绍了BeanUtils.copyProperties在拷贝属性时忽略空值的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • java web实现自动登录

    java web实现自动登录

    这篇文章主要为大家详细介绍了java web实现自动登录,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • java获取文件大小的几种方法

    java获取文件大小的几种方法

    这篇文章主要介绍了java获取文件大小的几种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10

最新评论