Spring系列开篇 — 核心功能IoC和AOP 知识点总结

6 篇文章 18 订阅
2 篇文章 1 订阅


本文是对Spring的2大核心功能——IoC和AOP 的总结提炼,并增加了【环境profile】和【条件化bean】的内容。篇幅较短,更像是一个大纲,或者思维导图。若有不完善或纰漏的地方,请各位大佬斧正。

主要参考资料为:《Spring实战》(第四版)

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属性时,按照名称beanid)注入;否则,按照类型注入

  • @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.activespring.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个参数:ConditionContextAnnotatedTypedMetadata

通过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的限定符,默认情况下是这个beanid;当然,也可以为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中需要换成andornot

    通知

    • @Before

    • @After:无论目标对象的方法是否执行成功,都会执行通知。

    • @Around

      环绕通知,最强大的通知。其方法形参中必须包含一个ProceedingJoinPoint对象,可以通过这个对象上的getArgs方法获取被拦截的目标对象的方法入参,通过proceed方法执行原目标对象的方法。

    • @AfterReturning:仅在目标对象的方法执行成功后,执行通知。

    • @AfterThrowing:仅在目标对象的方法执行抛出异常时,执行通知。

  • 开启AOP自动代理

    @EnableAspectJAutoProxy 或者 <aop:aspectj-autoproxy/>

事务支持

3大核心接口:PlatformTransactionManagerTransactionDefinitionTransactionStatus

需要

  • 一个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>

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值