美文网首页
Spring基础(持续更新)

Spring基础(持续更新)

作者: 笔记本一号 | 来源:发表于2020-06-06 09:33 被阅读0次

先介绍下面经常使用的beanDefinition,这个类装载了bean的信息,比如名字,是否单例,作用范围,bean的类型,是否延迟加载等诸多信息

@Configuration

spring配置注解

注册一个bean

@Bean

  @Bean
    public FaBean gatFabean(){
        System.out.println("gatFabean:我被创建了");
        return new FaBean();
    }

@Scope和@Lazy

指定bean以单例还是多例,单例:singleton 多例:prototype ,默认单例
在单例模式下,spring默认使用的是饿汉式加载,而多例模式用的是懒加载,也就是在容器创建的过程中就会把容器中组件实例创建出来,可以使用@Lazy变为懒加载,就是创建容器时不加载实例,在调用过程中才创建实例,懒加载只针对单例模式

  • @Scope
@Configuration
@ComponentScan(value = "com.example.springLearn.newlearn")
public class SpringConfig {
//设置多例模式
    @Scope("prototype")
    @Bean
    public User getuser(){
        return new User(1,"james");
    }
}
  @org.junit.Test
    public void test2(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user=(User) ac.getBean("getuser");
        User user2=(User) ac.getBean("getuser");
        System.out.println(user==user2?true:false);
    }

去掉 @Scope("prototype")后

 //@Scope("prototype")
    @Bean
    public User getuser(){
        return new User(1,"james");
    }
  • @Lazy
    默认模式下创建容器,并且不调用bean,只是创建spring容器,控制台有了输出,说明spring创建的同时调用了getuser()去创建了user实例
 @Bean
    public User getuser(){
        System.out.println("我被创建了");
        return new User(1,"james");
    }

 @org.junit.Test
    public void test2(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
      
    }

使用懒加载,创建容器时,控制台没有输出,调用组件才有输出

    @Bean
    @Lazy
    public User getuser(){
        System.out.println("我被创建了");
        return new User(1,"james");
    }

@ComponentScan 包扫描

1、excludeFilters 排除掉指定的类、includeFilters只包含指定的类(只包含需设置useDefaultFilters = false)
  • FilterType.ANNOTATIONilterType.ANNOTATION
    指定扫描的注解
//排除扫描到的Controller注解
@Configuration
@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes = {Controller.class})
},value = "com.example.springLearn.newlearn")
public class SpringConfig {

}

测试

 @org.junit.Test
    public void test1(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
//获取容器中的组件
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
  • FilterType.ASSIGNABLE_TYPE
    指定扫描的类型
@Configuration
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {ServiceTest.class})
},value = "com.example.springLearn.newlearn")
public class SpringConfig {

}

测试结果

  • FilterType.CUSTOM
    自定义过滤器
public class MyFilter implements TypeFilter {
    /**
     * @param metadataReader :读取正在扫描的信息
     * @param metadataReaderFactory 获取到其他任何类信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类路径
        Resource resource = metadataReader.getResource();
        System.out.println(resource.getURI());
        System.out.println(classMetadata.getClassName());
//组件中包含Service的都会放行
        if (classMetadata.getClassName().contains("Service")){
          return true;
        }
        return false;
    }
}
@Configuration
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilter.class})
},value = "com.example.springLearn.newlearn")
public class SpringConfig {

}

@Conditional

按照条件进行装配,放类上时需要满足条件才可以进行装配类下的注册配置,放在方法上时需要满足条件才可以装配方法下的注册配置,springboot大量使用这个注解进行条件装配

   @Bean
    @Lazy
    public User getuser(){
        System.out.println("getuser:我被创建了");
        return new User(1,"james");
    }
    @Conditional(ConditionConstom.class)//按照条件进行注册 springboot大量使用的注解
    @Bean
    public User getuser2(){
        System.out.println("getuser2:我被创建了");
        return new User(1,"kobe");
    }
public class ConditionConstom implements Condition {
    /**
     *
     * @param conditionContext:判断上下文环境
     * @param annotatedTypeMetadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        //获取ioc的创建工厂
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //判断容器中是否注册了getuser这个bean
       //注册了getuser就手动注册一个conditionDog的bean
        if (registry.containsBeanDefinition("getuser")){
            Class<Dog> dogClass = Dog.class;
            Field[] declaredFields = dogClass.getDeclaredFields();
            MutablePropertyValues mv=new MutablePropertyValues();
            for (Field declaredField : declaredFields) {
                if (declaredField.getName().equals("name")){
                    mv.add(declaredField.getName(),"conditionDogname");
                }
                if (declaredField.getName().equals("id")){
                    mv.add(declaredField.getName(),1);
                }
            }
            BeanDefinition beanDefinition=new RootBeanDefinition(Dog.class,null,mv);
            //手动注册一个conditionDog的bean
            registry.registerBeanDefinition("conditionDog",beanDefinition);
            Dog dogbean = (Dog) beanFactory.getBean("conditionDog");
            System.out.println("我注册了conditionDog,");
            System.out.println(dogbean.toString());
            return true;
        }
        return false;
    }
}

@Import

快速注册一个bean

@Configuration
@ComponentScan(value = "com.example.springLearn.newlearn")
@Import({Cat.class})
public class SpringConfig {
       ...............

测试

 @org.junit.Test
    public void test1(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

我们看到返回的bean的名字是类的全路径

同时可以通过自定义类进行注册

@Configuration
@ComponentScan(value = "com.example.springLearn.newlearn")
@Import({Cat.class, MySelector.class})
public class SpringConfig {
       ...............

自定义

public class MySelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.example.springLearn.newlearn.bean.Hive","com.example.springLearn.newlearn.bean.Spark"};
    }
}

FactoryBean类注入

通过实现FactoryBean类可以向容器注册组件,

public class FaBean implements FactoryBean<Php> {
    //注册的bean实例
    @Override
    public Php getObject() throws Exception {
        return new Php(1,"php");
    }
    //注册的bean的类型
    @Override
    public Class<?> getObjectType() {
        return Php.class;
    }
    //定义是是否为单例,true是,false否
    @Override
    public boolean isSingleton() {
        return true;
    }
}
 @Bean
    public FaBean gatFabean(){
        System.out.println("gatFabean:我被创建了");
        return new FaBean();
    }

BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar

通过实现BeanDefinitionRegistryPostProcessor 的postProcessBeanDefinitionRegistry方法注册一个bean

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(Hadoop.class);
        registry.registerBeanDefinition("hadoop",rootBeanDefinition);
        System.out.println("MyBeanDefinitionRegistryPostProcessor:postProcessBeanDefinitionRegistry===============");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor:postProcessBeanFactory===============");

    }
}

通过实现ImportBeanDefinitionRegistrar 的registerBeanDefinitions方法注册一个bean

@Component
public class MyImportBeanDefinitionRegistry implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(Hadoop.class);
        registry.registerBeanDefinition("hadoop2",rootBeanDefinition);
        System.out.println("MyBeanDefinitionRegistryPostProcessor:postProcessBeanDefinitionRegistry===============");
    }
}

生命周期

@Bean

使用@Bean指定的生命周期

 @Bean(initMethod = "init",destroyMethod = "die")
    public Dog dog(){
        return new Dog();
    }
@Data
public class Scala {
    private int id;
    private String name;

    public Scala() {
        System.out.println("scala的空参构造函数执行..........");
    }

    public void init() {
        System.out.println("init........");
    }
    public void die() {
        System.out.println("destory........");
    }
}

先执行空参构造,然后容器销毁时在调用die()

@PostConstruct和@PreDestroy

注入容器的组件中的方法使用了@PostConstruct后将在容器初始化组件后被调用,注入容器的组件中的方法使用了@PreDestroy后将在容器销毁前被调

@Data
@Component
public class Html {
    private Integer id;
    private String name;

    public Html(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Html() {
        System.out.println("html调用了构造方法");
    }
    //@PostConstruct在容器的组件初始化后使用
    @PostConstruct
    public void post(){
        System.out.println("Html调用了@PostConstruct的方法");
    }
    //@PreDestroy在容器的组件被摧毁前被调用
    @PreDestroy
    public void destroy(){
        System.out.println("Html调用了@PreDestroy的方法");
    }
}

BeanPostProcessor 后置处理器

内置的postProcessBeforeInitialization方法是在容器的bean开始初始化后调用,postProcessAfterInitialization方法在容器的bean初始后完成后调用

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"开始初始化了.......");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"初始化完成了.......");
        return bean;
    }
}

可以看到bean的后置处理器的postProcessBeforeInitialization方法是在bean的初始化开始后执行的并且是比@PostConstruct提前执行,这是postProcessBeforeInitialization和@PostConstruct的区别,postProcessAfterInitialization是在bean执行完初始后执行的

文件加载和成员变量设值

@PropertySource加载配置文件

@Value设值成员变量的值:

@Value("${rabbitmq.id}")将从配置文件中设值,@Value("#{5673-1}")利用EL表达式进行计算设值, @Value("我的virtualHost")常量设值
加载classpath下的配置文件

@Configuration
@PropertySource("classpath:/myApplication.properties")
@ComponentScan(value = "com.example.springLearn.newlearn")
public class RabbitMqConfig {
    @Value("${rabbitmq.id}")
    private String ip;
    @Value("#{5673-1}")
    private int port;
    @Value("${rabbitmq.username}")
    private String username;
    @Value("${rabbitmq.password}")
    private String password;
    @Value("我的virtualHost")
    private String virtualHost;
    @Bean
    public RabbitMq gatRabbbitMq(){
        try {
            RabbitMq rabbitMq=new RabbitMq(ip,port,username,password,virtualHost);
            return rabbitMq;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
rabbitmq.id=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtualHost=testhost
 @org.junit.Test
    public void test4(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(RabbitMqConfig.class);
        RabbitMq bean = ac.getBean(RabbitMq.class);
        System.out.println(bean.toString());
        ac.close();
    }

自动装配

spring的规范:

@Autowired 按照类型自动注入

@Qualifier("xxxx") 强行按照指定的名称自动注入

@Primary 将标注@Primary的方法的bean变为默认注入

优先级@Autowired <@Primary<@Qualifier("xxxx")
默认按照类型去容器找相应的组件,如果找到就赋值,如果存在多个类型的组件,就选择组件名称是申明等待赋值的引用名称的那个组件注入,如果使用了@Qualifier可以强行指定使用指定名称的那个类型或者使用@Primary优先首选注入,如果申明的@Service,@Component,@Controller没有指明bean的名称,则按照类名称的首字母小写注入

@Autowired(require=false)还可以设置属性,表明能在容器中找到这个组件就赋值,找不到就算了,也不会报错
@Service
@Data
public class Kafka {
    private String name="kafka";
   public void say(){
        System.out.println("我是"+name);
    }
}

 @Bean("kafka2")
    public Kafka gatKafka(){
        Kafka kafka = new Kafka();
        kafka.setName("kafka2");
        return kafka;
    }

@Controller
public class TestController {
    @Autowired
    private Kafka kafka;
    public void test(){
        kafka.say();
    }
}
@Controller
public class TestController {
    @Autowired
    private Kafka kafka2;
    public void test(){
        kafka2.say();
    }
}
@Controller
public class TestController {
    @Qualifier("kafka")
    @Autowired
    private Kafka kafka2;
    public void test(){
        kafka2.say();
    }
}
    @Primary
    @Bean("kafka2")
    public Kafka gatKafka(){
        Kafka kafka = new Kafka();
        kafka.setName("kafka2");
        return kafka;
    }
@Controller
public class TestController {
    //@Qualifier("kafka")
    @Autowired
    private Kafka kafka;
    public void test(){
        kafka.say();
    }
}

java的规范:

@Resource 默认按照引用名称注入,@Primary和@Qualifier将不起效果,可以通过@Resource(name="xxxx")指定注入哪个组件,这个的功能比较少,还是别花里胡哨的了,乖乖用@Autowired把

XXXXXXAware方法:实现了XXXXXXAware方法后可以在创建对象时,对象初始化前会调用接口规定的方法注入组件

@Component
public class TestService implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
        System.out.println("setApplicationContext被调用了");
    }

    public String test(){
        return applicationContext.getEnvironment().getProperty("key1");
    }
}

Spring的后置处理器

BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor是BeanFactory的后置处理器,是在BeanFactory标准初始化之后才会被调用后置处理方法,也就是在BeanFactory保存了所有bean的定义后,才会调用,注意此时只是保存bean的定义信息,例如名字,但是还没有真正的创建对象。BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的BeanFactoryPostProcessor 检测开始之前注册其他 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的BeanFactoryPostProcessor,因为此时所有常规的BeanFactoryPostProcessor 都还没开始被处理,BeanDefinitionRegistryPostProcessor 就已经开始先后执行postProcessBeanDefinitionRegistry和postProcessBeanFactory
同时BeanDefinitionRegistryPostProcessor 是BeanDefinitionRegistry的后置处理器,而BeanDefinitionRegistry是BeanFactory信息保存中心,BeanFactory加载bean的定义信息需要通过BeanDefinitionRegistry获取

现在我们定义两个后置处理器看看调用的前后效果

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor:postProcessBeanDefinitionRegistry===============");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor:postProcessBeanFactory===============");

    }
}
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Autowired
    private MySql mySql;
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("开始执行我们的BeanFactoryPostProcessor=============");
        //此时的mySql没有被创建,调用beanFactory.getBean才会被创建
        if (mySql==null){
            System.out.println("BeanFactory现在只是保存了bean消息,没有创建对象");
        }else {
            System.out.println("BeanFactory现在即保存了bean消息,也创建了对象");
        }

        Object mySql2 = beanFactory.getBean("mySql");
        if (mySql2==null){
            System.out.println("调用beanFactory.getBean才会被创建后,BeanFactory现在只是保存了bean消息,没有创建对象");
        }else {
            BeanDefinition mysql = beanFactory.getBeanDefinition("mySql");
//为bean设置属性
            MutablePropertyValues propertyValues = mysql.getPropertyValues();
            propertyValues.add("name","我是后置处理器的mysql");
            System.out.println("调用beanFactory.getBean才会被创建后,BeanFactory现在即保存了bean消息,也创建了对象");
        }
    }
}

结论是:可以看到BeanDefinitionRegistryPostProcessor比BeanFactoryPostProcessor调用要早,它们都是在spring容器还没有实例化bean前就调用了,但是spring容器已经将所有的bean的申明信息加载到了容器中,这个叫做spring的标准化加载,通过beanFactory.getBean方法就能进行实例化了。还要我们发现继承了Condition方法的类执行matches方法还要比这两个后置处理器要早,并且其调用时间也是在spring的标准化加载之前

Spring的监听器

ApplicationListener

ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。ApplicationListener里面只有一个onApplicationEvent方法。如果在ApplicationContext部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,调用ApplicationContext.publishEvent()方法,这个bean得到通知。

我们自定义一个ApplicationListener看看它的调用时机

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("事件开始:"+applicationEvent+"=================================");
    }
}

结论:我们可以看到,ApplicationListener是默认是在spring容器初始化完成后和容器销毁后进行触发

我们可以自己发布事件去触发在ApplicationContext部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,调用ApplicationContext.publishEvent()方法,这个bean得到通知

自定义发布事件

public class MyApplicationEven extends ApplicationEvent {
    public MyApplicationEven(Object source) {
        super(source);
        if (source instanceof String)
            System.out.println("开始触发事件:"+source);
    }
}

发布一个事件

  @org.junit.Test
    public void test22(){
        AnnotationConfigApplicationContext ac = 
new AnnotationConfigApplicationContext(SpringConfig.class);
        //发布自定义的事件
       ac.publishEvent(new MyApplicationEven("我发布的事件"));
       ac.close();
    }
@Component
public class RefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        if (applicationContext.getParent()==null) {
            System.out.println("ContextRefreshedEvent容器刷新方法的监听器==========");
        }
    }
}

AOP

1、execution(* *(..)):表示匹配所有方法
2、execution(public * com. savage.service.UserService.*(..)):表示匹配com.savage.server.UserService中所有的公有方法
3、execution(* com.savage.server..*.*(..)):表示匹配com.savage.server包及其子包下的所有方法

@Aspect
public class AspectLogs {
    @Pointcut("execution(public * com.example.springLearn.service.AopService.*(..))")
    public void pointCut(){

    }
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("Aop的前置方法-------------------------------");
        System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
        System.out.println("目标方法所属类的简单类名:" +joinPoint.getSignature().getDeclaringType().getSimpleName());
        System.out.println("目标方法所属类的类名:"+ joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("目标方法声明类型:"+ Modifier.toString(joinPoint.getSignature().getModifiers()));
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i+1) + "个参数为:" + args[i]);
        }
        System.out.println("被代理的对象:" + joinPoint.getTarget());
        System.out.println("代理对象自己:" + joinPoint.getThis());
    }
    @After("pointCut()")
    public void after(){
        System.out.println("Aop的后置方法-------------------------------");
    }
     @Around("pointCut()")
    public void around(JoinPoint joinPoint) throws Throwable {
        System.out.println("AOP的环绕方法...................");
        System.out.println("开始增强方法的执行...................");
        //增强方法的执行
        ((ProceedingJoinPoint) joinPoint).proceed();
        System.out.println("增强方法执行结束-------------------------------");
    }
    @AfterReturning("pointCut()")
    public void returnAop(){
        System.out.println("Aop的正常返回方法-------------------------------");

    }
    @AfterThrowing("pointCut()")
    public void exceptionAop(){
        System.out.println("Aop的异常调用方法-------------------------------");

    }
}

被代理对象

public class AopService {
    public Integer testAop() {
        Random random = new Random();
        int i = random.nextInt(5000);
        System.out.println(i);
        return i;
    }
}
@EnableAspectJAutoProxy
@Configuration
@Import({AopService.class, AspectLogs.class})
public class AopConfig {
    @Test
    public void test1(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        AopService aopService = applicationContext.getBean(AopService.class);
        aopService.testAop();

    }
}

JDBC事务

如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中

 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

在springboot中开启

@SpringBootApplication
@EnableTransactionManagement
public class LearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(LearnApplication.class, args);
    }
}

实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。

使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。
事务的传播行为:

PROPAGATION_REQUIRED:表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务(默认)
PROPAGATION_SUPPORTS:表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY:表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW:表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED:表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER:表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

我们分析默认的PROPAGATION_REQUIRED,也是最常用的
例如:

@Transactional
methodA{
    ……
    methodB();
    ……
}
@Transactional
methodB{
   ……
}

单独调用methodB方法:Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。

main{ 
    metodB(); 
}

相当于

Main{ 
    Connection con=null; 
    try{ 
        con = getConnection(); 
        con.setAutoCommit(false); 
        //方法调用
        methodB(); 
        //提交事务
        con.commit(); 
    } Catch(RuntimeException ex) { 
        //回滚事务
        con.rollback();   
    } finally { 
        //释放资源
        closeCon(); 
    } 
}

调用methodA:调用MethodA时,环境中没有事务,所以开启一个新的事务.当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务。

main{ 
    metodA(); 
} 

相当于:

main{ 
    Connection con = null; 
    try{ 
        con = getConnection(); 
        methodA(); 
        con.commit(); 
    } catch(RuntimeException ex) { 
        con.rollback(); 
    } finally {    
        closeCon(); 
    }  
}

参考:https://blog.csdn.net/mocas_wang/article/details/109012583
事务不生效的场景:https://blog.csdn.net/f641385712/article/details/80445933

相关文章

网友评论

      本文标题:Spring基础(持续更新)

      本文链接:https://www.haomeiwen.com/subject/oqybahtx.html