Spring重要注解介绍以及原理

组件注册

@Configuration

告诉Spring这是一个配置类

@ComponentScan

扫描、只要标注了@Controller、@Service、@Repository,@Component

在这里插入图片描述
在这里插入图片描述

@Bean、@Lazy、@Scope、@Repeatable

@Repeatable:表示可重复注解

@Bean向IOC容器中注入组件

在这里插入图片描述

@Conditional

// 类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才会生效
// 可以在方法和类上定义

在这里插入图片描述
在这里插入图片描述

@Import

// 注意:调用是的id是全类名

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用Spring提供的FactoryBean(工厂Bean) 注册组件

在这里插入图片描述
在这里插入图片描述

生命周期

/
 * bean的声明周期:
 * 		bean创建---初始化----销毁的过程
 * 	容器管理bean的生命周期;
 * 	我们可一个自定义初始化和销毁方法;容器在bean进行当前生命周期的时候来调用我们自定义的初始化和销毁方法
 *
 *	构造(对象创建)
 *		单实例:在容器启动的时候创建对象
 *		多实例:在每次获取的时候创建对象
 *	初始化:
 *			对象创建完成,并赋值好,调用初始化方法。。。
 *	销毁:
 *			单实例:容器关闭的时候销毁
 *			多实例:容器不会管理这个bean;容器不会调用销毁方法
 *
 * 	1)、指定初始化和销毁方法;
 * 			指定init-method 和 destroy-method
 * 			 通过@Bean指定init-method和destory-method;
 *
 *	2)、通过让Bean实现InitializingBean(定义初始化逻辑)
 *				DisposableBean(定义销毁逻辑)
 *	3)、可以使用JSR250;
 * 			@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化
 * 			@PreDestroy: 在容器销毁之前通知我们进行清理工作
 *	4)BeanPostProcessor(接口):bean的后置处理器;
 *		在bean初始化前后进行一些处理工作;
 *		postProcessBeforeInitialization: 在初始化之前工作
 *		postProcessAfterInitialization: 在初始化之后工作
 *
 */
1)、指定初始化和销毁方法;
*        指定init-method 和 destroy-method
*         通过@Bean指定init-method和destory-method;

在这里插入图片描述

2)、通过让Bean实现InitializingBean(定义初始化逻辑)
          DisposableBean(定义销毁逻辑)

在这里插入图片描述
在这里插入图片描述

3)、可以使用JSR250;
        @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化
        @PreDestroy: 在容器销毁之前通知我们进行清理工作

在这里插入图片描述
在这里插入图片描述

4)BeanPostProcessor(接口):bean的后置处理器;
*     在bean初始化前后进行一些处理工作;
*     postProcessBeforeInitialization: 在初始化之前工作
*     postProcessAfterInitialization: 在初始化之后工作

在这里插入图片描述
在这里插入图片描述

BeanPostProcessor原理
/*   遍历得到容器中所有的BeanPostProcessor:挨个执行beforeInitialization,
*     一旦返回null,跳出for循环,BeanPostProcessor.postProcessorsBeforeInitialialization
*
*  BeanPostProcessor原理
*  populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
*  initializeBean(初始化){
*  applyBeanPostProcessorsBeforeInitialialization(wrappedBean,beanName);
*  invokeInitMethods(beanName,wrappedBean, mbd);执行自定义初始化
*  applyBeanPostProcessorsAfterInitialication(wrappedBean,beanName);
*  }
*/

属性赋值有关的注解

@Value

// 使用@Value赋值
// 1.基本数值
// 2.可以写SpEL;#{}
// 3.可以写${};取出配置文件中的值(在运行环境变量里面的值)
//
public class Person{
    @Value("张三")
	private String name;
	@Value("#{23-33}")
	private Integer age;
	@Value("${person.nickName}")
	private String nickName;
    ....
}

在这里插入图片描述

第三个方法需要添加@PropertySource


@PropertySource("classpath:person.properties")
@Configuration
public class MainConfigOfPropertyValue {

   @Bean
   public Person person(){
      return new Person();
   }
}

// 因为加载在环境变量中也可以这样获取
@Test
public void test(){
    // 1.创建ioc容器
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(MainConfigOfPropertyValue.class);

    ConfigurableEnvironment environment = context.getEnvironment();
    String property = environment.getProperty("person.nickName");
}

自动装配

@AutoWired、@Qualifier、@Primary

@Primary    // 自动装配时,当有多个相同的类,默认使用首先的bean
@Repository // id名字默认首字母小写
public class BookDao {
}
/**
 * 自动装配;
 *        Spring利用依赖注入(DI),完成对容IOC容器中,各个组件的依赖关系赋值;
 * 1)、 自动注入;
 *           1)、默认优先按照类型去找容器对应的组件:applicationContext.getBean(BookDao.class);
 *           2)、 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
 *                                applicationContext.getBean("bookDao");
 *           3)、@Qualifier("booDao"):使用@Qualifier指定需要装配的组件id,而不是使用属性名
 *           4)、自动装配默认一定要属性赋值好,没有找到就会报错;
 *                可以使用@Autowired(required = false) 就不会报错,默认是true
 *           5)、@Primary:让Spring进行自动装配的时候,默认使用首先的bean
 *                 也可以继续使用@Qualifier指定需要装配bean的名字
 *        BookService{
 *           @Qualifier("booDao")
 *           @Autowired
 *           BookDao bookDao;
 *        }
 *
 */

@Resource(JSR250)、@Inject(JSR330)[java规范的注解]

/**
*
*  2)、Spring还支持使用@Resource(JSR250)和Inject(JSR330)[java规范的注解]
*     @Resource:
*           可以和@Autowired一样实现自动装配功能;默认按照组件名称进行装配的
*           没有能支持@Primary功能
*           没有能支持@Autowired(required = false)
*     BookService{
*          @Resource(name = "bookDao2")
*        private BookDao bookDao;
*     }
*     @Inject:
*           需要导入javax.inject的包,和Autowired的功能一样
*           只不过里面没有属性(没有required=false的功能)
*      BookService{
*          @Inject
*        private BookDao bookDao;
*     }
*
* @Autowired:Spring定义的;@Resource、@Inject都是Java规范
*
*/

@Autowired放的位置

/**
*
* 3)、@Autowired(可以放的位置):构造器,参数,方法,属性;都是从容器中获取参数组件的值
*
*     1)、标注在方法位置:@Bean+ 方法参数:参数从容器中获取;默认不写@Autowired效果是一样的,都能自动装配
*           标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;
*           方法使用的参数,自定义类型的值从ioc容器中获取
*        @Autowired
*        public void setCar(Car car){
*          this.car = car;
*        }
*     2)、标注在构造器上:
*        如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置还是自动从容器中获取
*        默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,在进行初始化赋值操作
*        @Component
*        public class Boss{
*           private Car car;
*           // 启动的时候就会调用有参构造器(构造器要用的组件,都是从容器中获取)
*           @Autowired
*           public Boss(Car car){
*               this.car = car
*           }
*        }
*
*     3)、标注在参数上:
*        public Boss(@Autowired Car car){
*               this.car = car
*        }
*
*
*        @Bean // @Bean标注在方法创建对象的时候,方法参数从容器中获取
*        public Color color(Car car){
*          Color color = new Color();
*          color.setColor(car);
*          return color;
*        }
*/

自己定义的组件使用spring底层的组件(Aware)

/**
*
*  4)、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
*        自定义组件实现xxxAware;在创建对象的时候,会调用接口(Aware)规定的方法注入相关组件;
*        把Spring底层一些组件注入到自定义的bean中;
*		 xxxAware:功能使用xxxProcessor来处理的;
*			例: ApplicationContextAware ==> ApplicationContextAwareProcessor;
*/
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

   private ApplicationContext ioc;
   // ApplicationContext 为传入的ioc容器
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      ioc = applicationContext;
   }


   // name :当前bean的名字
   public void setBeanName(String name) {

   }

   // StringValueResolver值解析器
   public void setEmbeddedValueResolver(StringValueResolver resolver) {
      String resolverStringValue = resolver.resolveStringValue("你好${os.name}");

   }
}

@Profile

/**
 * Profile:
 *     Spring为我们提供的可以根据当前环境,动态激活和切换一系列组件的功能;
 *
 *  开发环境、测试环境、生产环境;
 *  数据源:(/A)(/B)(/C);希望不同环境使用不同的数据源
 *  @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
 *    	写@Profile("default")可以使其默认添加
 *  1)、加了环境表示的bean,只有这个环境被激活的时候才能注册到容器中
 *  2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
 *  3)、没有标注环境表示的bean在,任何环境下都是加载的;
 */

在这里插入图片描述

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
   @Value("${jdbc.root}")
   private String user ;

   private StringValueResolver stringValueResolver;

   private String driverClass;

   public void setEmbeddedValueResolver(StringValueResolver resolver) {
      this.stringValueResolver = resolver;
      driverClass = stringValueResolver.resolveStringValue("${jdbc.driver}");
   }

   @Profile("test")
   @Bean("testDataSource")
   public DataSource dataSourceTest(@Value("${jdbc.password}") String passWord) throws PropertyVetoException {
      ComboPooledDataSource source = new ComboPooledDataSource();
      source.setUser(user);
      source.setPassword(passWord);
      source.setJdbcUrl("jdbc:mysql://localhost:3306/tx?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimezone=UTC");
      source.setDriverClass(driverClass);
      return source;
   }

   @Profile("dev")
   @Bean("devDataSource")
   public DataSource dataSourceDev(@Value("${jdbc.password}") String passWord) throws PropertyVetoException {
      ComboPooledDataSource source = new ComboPooledDataSource();
      source.setUser(user);
      source.setPassword(passWord);
      source.setJdbcUrl("jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimezone=UTC");
      source.setDriverClass(driverClass);
      return source;
   }

   @Profile("prod")
   @Bean("prodDataSource")
   public DataSource dataSourceProd(@Value("${jdbc.password}") String passWord) throws PropertyVetoException {
      ComboPooledDataSource source = new ComboPooledDataSource();
      source.setUser(user);
      source.setPassword(passWord);
      source.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimezone=UTC");
      source.setDriverClass(driverClass);
      return source;
   }
}

测试:

// 1.使用命令行动态参数:在虚拟机参数位置加载-Dspring-profiles.active=test
// 2.代码的方式激活某种环境;
@Test
public void test01(){
   // 1.创建ioc容器
   AnnotationConfigApplicationContext context =
         new AnnotationConfigApplicationContext();
   // 1.创建一个applicationContext
   // 2.设置一个需要激活的环境
   context.getEnvironment().setActiveProfiles("test","dev");
   // 3.注册主配置类
   context.register(MainConfigOfProfile.class);
   // 4.启动刷新容器
   context.refresh();

   String[] type = context.getBeanNamesForType(DataSource.class);
   for(String name:type){
      System.out.println(name);
   }
}

AOP

@Aspect、@EnableAspectJAutoProxy

三步:

  •  1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
    
  •  2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
    
  •  3)、开启基于注解的aop模式;@EnableAspectJAutoProxy
    

在这里插入图片描述

配置类
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
   // 业务逻辑类加入到容器中
   @Bean
   public MathCalculator calculator(){
      return new MathCalculator();
   }

   // 切面类加入到容器中
   public LogAspects logAspects(){
      return new LogAspects();
   }
}
被切入的类
public class MathCalculator {

   public int div(int i, int j){
       System.out.println("MathCalculator.....div....");
      return i/j;
   }
}
切面类

注意:JoinPoint joinPoint一定要在参数表的第一个位,不然会报错

/**
 * @Aspect:告诉Spring当前类是一个切面类
 */
@Aspect
public class LogAspects {

	// 抽取公共的切入点表达式
	// 1、本类引用 方法名加括号就行
	// 2、 其他的切面引用 就是方法的全类名加方法名
	@Pointcut(value = "execution(* com.atguigu.aop.MathCalculator.*(..))")
	public void pointcut(){
	}
    
	@Before("pointcut()")
	public void logStrat(JoinPoint joinPoint){
		Object[] args = joinPoint.getArgs();
		System.out.println(joinPoint.getSignature().getName()+"运行。。。参数列表是{"+ Arrays.asList(args)+"}");
	}

	@After("pointcut()")
	public void logEnd(JoinPoint joinPoint){
		System.out.println(joinPoint.getSignature().getName()+"结束。。。");
	}

	@AfterReturning(value = "pointcut()",returning = "result")
	public void logReturn(Object result){
		System.out.println("除法正常返回。。。运行结果:"+result);
	}

	@AfterThrowing(value = "pointcut()",throwing = "exception")
	public void logException(Exception exception){
		System.out.println("除法异常。。。异常信息:" + exception);
	}

}
结果

在这里插入图片描述

AOP原理

*   AOP原理:【看给容器中注册了什么组件这个组件什么时候工作,这个组件的功能是什么】
*     1@EnableAspectJAutoProxy是什么?
*        @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
*           利用AspectJAutoProxyRegistrar自定义给容器中注册bean
*           internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
*        给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;BeanDefinetion
*     2AnnotationAwareAspectJAutoProxyCreator:
*         AnnotationAwareAspectJAutoProxyCreator
*           ->AnnotationAwareAspectJAutoProxyCreator // 针对AspectJ自动代理创建器
*              ->AbstractAdvisorAutoProxyCreator // 通知顾问自动代理创建器
*                 ->AbstractAutoProxyCreator // 抽象的自动代理创建器
*                    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
*                 关注后置处理器(在bean初始化完成前后做的事情)、自动装配BeanFactory
*     AbstractAutoProxyCreator.setBeanFactory()
*     AbstractAutoProxyCreator.有后置处理器的逻辑;(postProcessAfterInitialization)
*
*     AbstractAdvisorAutoProxyCreator.setBeanFactory()->initBeanFactory()
*     AbstractAdvisorAutoProxyCreator
*
*     AspectJAwareAdvisorAutoProxyCreator
*
*     AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
*
*     流程:
*        1)、传入配置类:创建ioc容器
*        2)、注册配置类、调用refresh()刷新容器;
*        3)registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建
*           1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
*           2)、给容器中加别的BeanPostProcessore
*           3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
*           4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
*           5)、注册没实现优先级接口的BeanPostProcessor;
*           6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
*              创建internalAutoProxyCreator的BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator*              1)、创建Bean的实例
*              2)、populateBean;给bean的各种属性赋值
*              3)、initializeBean:初始化bean;
*                    1)invokeAwareMethods():处理Aware接口的方法回调
*                    2)applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization();
*                    3)invokeInitMethods();执行自定义的初始化方法
*                    4)applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessBeforeInitialization();
*              4)BeanPostProcessors(AnnotationAwareAspectJAutoProxyCreator)创建成功;---aspectJAdvisorsBuilder
*           7)、把BeanPostProcessor注册到BeanFactory;
*                 beanFactory.addBeanPostProcessor(postProcessor);
*  ===========以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程===========
*              AnnotationAwareAspectJAutoProxyCreator==>InstantiationAwareBeanPostProcessor的后置处理器
*        4)finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
*           1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
*              getBean->doGetBean()->getSingleton()->
*           2)、创建bean
*AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()*              1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
*                 只要创建好的Bean都会被缓存起来
*              2)createBean();创建bean;
*                  AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前,先尝试返回bean的实例
*BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
*InstantiationAwareBeanPostProcessor是在创建Bean实例之前,先尝试用后置处理器返回的对象】
*                 1)resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
*                       希望后置处理器在此能返回一个代理对象如果能返回一个代理对象就使用,如果不能就继续
*                    1)、后置处理器先尝试返回对象;
*                       bean = applyBeanPostProcessorsBeforeInstantiation();
*                           拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
*                           就执行postProcessBeforeInstantiation
*                       if(bean != null){
*                          bean = applyBeanPostProcessorsAfterInstantiation(bean, beanName)
*                       }
*
*                 2)doCreateBean(beanName, mbdToUse, args); 真正的去创建一个bean实例;3.6流程一样
*
*  AnnotationAwareAspectJAutoProxyCreatorInstantiationAwareBeanPostProcessor】的作用;
*  1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
*     关心MathCalculatorLogAsepect的创建(两个自己创建的类一个切面类,一个被切入的类)
*     1)、判断当前bean是否在adviseBeans中(保存了所有需要增强bean)
*     2)、判断当前bean是否是基础类型的AdvicePointcutAdvisorAopInfrastructureBean,
*        或者判断是否是切面(@Aspect)
*     3)、是否需要跳过
*           1)、获取候选的的增强器(切面里面的通知方法)List<Advisor> candidateAdvisors】
*              每一个封装的通知方法的增强器是InstantiationModeAwarePointcutAdvisor;
*              判断每一个增强器是否是AspectJPointcutAdvisor类型的;返回true
*           2)、永远返回false
*  2)、创建对象
*  postProcessAfterInstantiation;
*     return wrapIfNecessary(bean, beanName, cacheKey);// 包装如果需要的情况下
*     1)、获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
*        1、找到候选的所有的增强器(找到哪些通知方法是需要切入当前bean方法的)
*        2、获取到能在bean使用等待增强器
*        3、给增强器排序
*     2)、保存当前bean在adviseBeans中;
*     3)、如果当前bean需要增强,创建当前bean的代理对象
*        1)、获取所有增强器(通知方法)
*        2)、保存到proxyFactory
*        3)、创建代理对象:Spring自动决定
*            JdkDynamicAopProxy(config);jdk的动态代理代理
*            ObjenesisCglibAopProxy(config);cglib的动态代理
*     4)、给容器中返回当前组件使用cglib增强了的代理对象;
*     5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
*  3)、目标方法执行
*     容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx)
*     1)CglibAopProxy.intercept();拦截目标方法的执行
*     2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
*        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
*        1)List<Object> interceptorList保存所有拦截器; 长度5
*           一个默认的ExpoesInvocationInterceptor 和四个增强器;
*        2)、遍历所有的增强器,将其转为Interceptor;
*           registry.getInterceptors(advisor);
*        3)、将增强器转为List<MethodInterceptor>;
*           如果是MethodInterceptor,直接加入到集合中;
*           如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
*           转换完成返回MethodInterceptor数组;
*
*     3)、如果没有拦截器链,直接执行目标方法;
*        拦截器链(每一个通知方法又被包装方法拦截器,利用MethodInterceptor机制)
*     4)、如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation对象,
*        并调用Object retVal = mi.proceed()
*     5)、拦截器链的触发过程;
*        1)、如果没有拦截器执行执行的目标方法,或者拦截器的索引和拦截器数组-1大小一样(也就是执行到了最后一个拦截器)执行目标方法;
*        2)、链式获取每一个拦截器,拦截器执行invoke方法,每个拦截器等待下一个拦截器执行完返回以后再来执行;
*            拦截器链的机制,保证通知方法与目标方法的执行顺序
*

在这里插入图片描述

总结:

*   总结:
*     1)@EnableAspectJAutoProxy  开启AOP功能
*     2)@EnableAspectJAutoProxy  会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
*     3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
*     4)、容器的创建流程:
*        1)registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
*        2)finishBeanFactoryInitialization()初始化剩下的单实例bean
*           1)、创建业务逻辑组件和切面组件
*           2)AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
*           3)、组件创建完之后,判断组件是否需要增强
*              是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib)
*     5)、执行目标方法:
*        1)代理对象执行目标方法
*        2)CglibAopProxy.intercept();
*           1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
*           2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
*           3)、效果:
*                 正常执行:前置通知-》目标方法-》后置通知-》返回通知
*                 出现异常:前置通知-》目标方法-》后置通知-》异常通知

声明式事务

/**
 * 声明式事务:
 *
 *     环境搭建:
 *     1、导入相关依赖
 *        数据源、数据库驱动、Springjdbc模块
 * 2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
 *
 *     3、给方法上标注@Transactional 表示当前方法是一个事务方法
 *     4、@EnableTransactionManagement开启基于注解的事务管理功能;
 *        @EnabelXXX
 *     5、配置事务管理器(PlatformTransactionManager)来控制事务
 */

配置类:

@EnableTransactionManagement
@ComponentScan(value = "com.atguigu.tx")
@Configuration
public class TxConfig {

   @Bean
   public DataSource dataSource() throws PropertyVetoException {
      ComboPooledDataSource dataSource = new ComboPooledDataSource();
      dataSource.setUser("root");
      dataSource.setPassword("13696944468");
      dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/tx?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimezone=UTC");
      dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
      return dataSource;
   }

   @Bean
   public JdbcTemplate jdbcTemplate(DataSource dataSource) throws Exception {
      // Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
      JdbcTemplate jdbcTemplate = jdbcTemplate(dataSource());
      return jdbcTemplate;
   }

   // 注册事务管理器在容器中
   @Bean
   public PlatformTransactionManager transactionManager() throws PropertyVetoException {
      return new DataSourceTransactionManager(dataSource());
   }

}

@Transactional

方法出现异常,所有操作都回滚

@Transactional
public void insert(){
   userDao.insert();
   System.out.println("插入完成。。。");
   int i = 10/ 0;
}

原理:

*  原理:
*  1)@EnableTransactionManagement
*           利用TransactionManagementConfigurationSelector给容器中导入组件
*           导入两个组件
*           AutoProxyRegistrar*           ProxyTransactionManagementConfiguration
*  2)AutoProxyRegistrar:
*        给容器中注册一个InfrastructureAdvisorAutoProxyCreator组件
*        InfrastructureAdvisorAutoProxyCreator;
*        利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
*  3)ProxyTransactionManagementConfiguration做了什么?
*        1、给容器中注册事务增强器;
*           1)、事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
*           2)、事务拦截器:
*                 TransactionInterceptor;保存了事务属性信息,事务管理器;
*                 他是一个MethodInterceptor;
*                 在目标方法执行的时候;
*                    执行拦截器链;
*                    事务拦截器:
*                       1)、先获取事务相关的属性
*                       2)、在获取PlatformTransactionManager,如果事先没有添加指定任何  transactionManagement
*                          最终会从容器中会按照类型获取一个PlatformTransactionManager;
*                       3)、执行目标方法
*                          如果异常,获取到事务管理器,利用事务管理器回滚操作;
*                          如果正常,利用事务管理器,提交事务
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值