Spring

1.框架

高度抽取可重用代码的一种设计,具有高度的通用性(半成品软件)

  • 书城: WebUtils. java; BaseServlet; Filter
  • 打包: bookstore jar(工具类),commons-fileupload, commons-IO, commons-dbutils
  • 框架:抽取成一种高度可重用的,比如:事务控制,强大的 servlet,项目中的一些工具。它是多个可重用模块的集合,形成一个某个领域的整体解决方案

2.Spring

容器(可以管理所有的组件/类)框架

核心关注:IOC和AOP

2.1.模块划分

Spring - 图1

Test:单元测试模块

Core Container:核心容器(IOC);黑色表示由哪些jar组成

AOP + Aspects:面向切面编程

Data Access/Integration:JDBC;ORM;TX

  • OX(xml)M

Web:

  • servlet:spring-web
  • web:spring-webmvc
  • portlet:spring-webmvc-portlet(开发web语应用的组件集成)

2.2.IOC(Inversion Of Control)

控制:控制资源的获取方式

  • 主动式:需要资源自己主动创建
  1. UserServlet{
  2. //简单对象
  3. private UserService userService = new UserService();
  4. //复杂对象的创建是比较庞大的工程
  5. }
  • 被动式:资源获取不是自己主动创建,而是交给一个容器创建
  1. UserServlet{
  2. private UserService userService;
  3. pubilc void test(){
  4. userService.addUser();
  5. }
  6. }

反转:从主动的new资源变为被动的接受资源。

2.3.DI(Dependency Injection)

容器能直到哪个组件运行的时候,需要另外一个组件,容器通过反射的形式,将所需对象注入(利用反射给属性赋值)给需求对象

操作细节

@Scope(“prototype”)

  • 多实例的
    • 容器启动之前不会创建对象
    • 在获取的时候创建bean
    • 每次获取都会创建新的bean

@Scope(“single”)

  • 单实例的;默认
    • 在容器启动完成之前就已经创建好对象,保存在容器中
    • 获取任意此对象都是容器启动之前创建的对象(单例)

静态工厂:工厂本身不用创建,通过静态方法调用:

  • 对象 = 工厂类.工厂方法名();

实例工厂:工厂本身需要创建,

  • 工厂类 工厂对象 = new 工厂类();
  • 工厂对象.get对象(..);

@Autowired原理

  1. @Autowired
  2. private UserService userService;

先按照类型去容器中找到对应的组件:

  1. UserService userService = ioc.getBean(UserService.class);
  • 找到一个:找到就赋值
  • 没找到:抛异常
  • 找到多个:

    • 按照变量名作为id继续匹配:

      1. UserService(id = userService)
      2. UserServiceExt(id = UserServiceExt)
      • 匹配上:注入
      • 没有匹配上:抛异常
        解决办法:指定bean的id

        1. @Qualifier("userService")
        • 匹配上:注入
        • 没有匹配上:抛异常

          1. @Autowired(required = false)
        • 匹配不上,不抛出异常,不进行处理

方法上有Autowried:方法上的每个参数都会自动注入值


@Resource和@Autowried

@Resource:扩展性强(J2EE标准),切换容器框架依旧可以使用,而@Autowried不行


泛型依赖注入:注入一个组件时,它的泛型也是参考标准

2.3.1.源码

  1. IOC是一个容器
  2. 容器启动的时候创建所有单例对象
  3. 可以从容器中获取bean
  1. ApplicationContext ioc = new ClassPathXmlApplicationContext();

ClassPathXmlApplicationContex

  1. public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
  2. //*
  3. this(new String[] {configLocation}, true, null);
  4. }

  1. public ClassPathXmlApplicationContext(
  2. String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
  3. throws BeansException {
  4. super(parent);
  5. setConfigLocations(configLocations);
  6. if (refresh) {
  7. //*所有单例bean被实例化
  8. refresh();
  9. }
  10. }

AbstractApplicationContext

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // Prepare this context for refreshing.
  5. prepareRefresh();
  6. // Tell the subclass to refresh the internal bean factory.
  7. //spring解析xml配置文件,将要创建的所有bean的配置信息保存
  8. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  9. // Prepare the bean factory for use in this context.
  10. prepareBeanFactory(beanFactory);
  11. try {
  12. // Allows post-processing of the bean factory in context subclasses.
  13. postProcessBeanFactory(beanFactory);
  14. // Invoke factory processors registered as beans in the context.
  15. invokeBeanFactoryPostProcessors(beanFactory);
  16. // Register bean processors that intercept bean creation.
  17. registerBeanPostProcessors(beanFactory);
  18. // Initialize message source for this context.
  19. //支持国际化功能
  20. initMessageSource();
  21. // Initialize event multicaster for this context.
  22. initApplicationEventMulticaster();
  23. // Initialize other special beans in specific context subclasses.
  24. //留给子类的方法
  25. onRefresh();
  26. // Check for listener beans and register them.
  27. registerListeners();
  28. // Instantiate all remaining (non-lazy-init) singletons.
  29. //*初始化所有单例bean
  30. finishBeanFactoryInitialization(beanFactory);
  31. // Last step: publish corresponding event.
  32. finishRefresh();
  33. }
  34. catch (BeansException ex) {
  35. if (logger.isWarnEnabled()) {
  36. logger.warn("Exception encountered during context initialization - " +
  37. "cancelling refresh attempt: " + ex);
  38. }
  39. // Destroy already created singletons to avoid dangling resources.
  40. destroyBeans();
  41. // Reset 'active' flag.
  42. cancelRefresh(ex);
  43. // Propagate exception to caller.
  44. throw ex;
  45. }
  46. finally {
  47. // Reset common introspection caches in Spring's core, since we
  48. // might not ever need metadata for singleton beans anymore...
  49. resetCommonCaches();
  50. }
  51. }
  52. }

  1. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  2. // Initialize conversion service for this context.
  3. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  4. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  5. beanFactory.setConversionService(
  6. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  7. }
  8. // Register a default embedded value resolver if no bean post-processor
  9. // (such as a PropertyPlaceholderConfigurer bean) registered any before:
  10. // at this point, primarily for resolution in annotation attribute values.
  11. if (!beanFactory.hasEmbeddedValueResolver()) {
  12. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  13. }
  14. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  15. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  16. for (String weaverAwareName : weaverAwareNames) {
  17. getBean(weaverAwareName);
  18. }
  19. // Stop using the temporary ClassLoader for type matching.
  20. beanFactory.setTempClassLoader(null);
  21. // Allow for caching all bean definition metadata, not expecting further changes.
  22. beanFactory.freezeConfiguration();
  23. // Instantiate all remaining (non-lazy-init) singletons.
  24. //*初始化所有单例bean
  25. beanFactory.preInstantiateSingletons();
  26. }

DefaultListableBeanFactory

  1. @Override
  2. public void preInstantiateSingletons() throws BeansException {
  3. if (logger.isTraceEnabled()) {
  4. logger.trace("Pre-instantiating singletons in " + this);
  5. }
  6. // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  7. // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  8. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
  9. // Trigger initialization of all non-lazy singleton beans...
  10. //按顺序创建bean
  11. for (String beanName : beanNames) {
  12. //根据bean的id获取bean的定义信息
  13. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  14. //此bean不是抽象的,是单例的,不是懒加载的
  15. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  16. //实现了FactoryBean接口的bean
  17. if (isFactoryBean(beanName)) {
  18. Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
  19. if (bean instanceof FactoryBean) {
  20. final FactoryBean<?> factory = (FactoryBean<?>) bean;
  21. boolean isEagerInit;
  22. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  23. isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
  24. ((SmartFactoryBean<?>) factory)::isEagerInit,
  25. getAccessControlContext());
  26. }
  27. else {
  28. isEagerInit = (factory instanceof SmartFactoryBean &&
  29. ((SmartFactoryBean<?>) factory).isEagerInit());
  30. }
  31. if (isEagerInit) {
  32. getBean(beanName);
  33. }
  34. }
  35. }
  36. //未实现FactoryBean接口的普通bean
  37. else {
  38. //*创建bean的细节
  39. getBean(beanName);
  40. }
  41. }
  42. }
  43. // Trigger post-initialization callback for all applicable beans...
  44. for (String beanName : beanNames) {
  45. Object singletonInstance = getSingleton(beanName);
  46. if (singletonInstance instanceof SmartInitializingSingleton) {
  47. final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
  48. if (System.getSecurityManager() != null) {
  49. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  50. smartSingleton.afterSingletonsInstantiated();
  51. return null;
  52. }, getAccessControlContext());
  53. }
  54. else {
  55. smartSingleton.afterSingletonsInstantiated();
  56. }
  57. }
  58. }
  59. }

AbstractBeanFactory

  1. @Override
  2. public Object getBean(String name) throws BeansException {
  3. //*
  4. return doGetBean(name, null, null, false);
  5. }

  1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
  2. @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  3. final String beanName = transformedBeanName(name);
  4. Object bean;
  5. // Eagerly check singleton cache for manually registered singletons.
  6. //先从已注册的所有单例bean中看是否存在此bean
  7. Object sharedInstance = getSingleton(beanName);
  8. if (sharedInstance != null && args == null) {
  9. if (logger.isTraceEnabled()) {
  10. if (isSingletonCurrentlyInCreation(beanName)) {
  11. logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
  12. "' that is not fully initialized yet - a consequence of a circular reference");
  13. }
  14. else {
  15. logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
  16. }
  17. }
  18. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  19. }
  20. else {
  21. // Fail if we're already creating this bean instance:
  22. // We're assumably within a circular reference.
  23. if (isPrototypeCurrentlyInCreation(beanName)) {
  24. throw new BeanCurrentlyInCreationException(beanName);
  25. }
  26. // Check if bean definition exists in this factory.
  27. BeanFactory parentBeanFactory = getParentBeanFactory();
  28. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
  29. // Not found -> check parent.
  30. String nameToLookup = originalBeanName(name);
  31. if (parentBeanFactory instanceof AbstractBeanFactory) {
  32. return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
  33. nameToLookup, requiredType, args, typeCheckOnly);
  34. }
  35. else if (args != null) {
  36. // Delegation to parent with explicit args.
  37. return (T) parentBeanFactory.getBean(nameToLookup, args);
  38. }
  39. else if (requiredType != null) {
  40. // No args -> delegate to standard getBean method.
  41. return parentBeanFactory.getBean(nameToLookup, requiredType);
  42. }
  43. else {
  44. return (T) parentBeanFactory.getBean(nameToLookup);
  45. }
  46. }
  47. if (!typeCheckOnly) {
  48. markBeanAsCreated(beanName);
  49. }
  50. try {
  51. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  52. checkMergedBeanDefinition(mbd, beanName, args);
  53. // Guarantee initialization of beans that the current bean depends on.
  54. //拿到当前bean创建之前需要的提前创建的bean。depends-on属性,若有就循环创建
  55. String[] dependsOn = mbd.getDependsOn();
  56. if (dependsOn != null) {
  57. for (String dep : dependsOn) {
  58. if (isDependent(beanName, dep)) {
  59. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  60. "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
  61. }
  62. registerDependentBean(dep, beanName);
  63. try {
  64. getBean(dep);
  65. }
  66. catch (NoSuchBeanDefinitionException ex) {
  67. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  68. "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
  69. }
  70. }
  71. }
  72. // Create bean instance.
  73. //创建bean实例
  74. if (mbd.isSingleton()) {
  75. //*
  76. sharedInstance = getSingleton(beanName, () -> {
  77. try {
  78. return createBean(beanName, mbd, args);
  79. }
  80. catch (BeansException ex) {
  81. // Explicitly remove instance from singleton cache: It might have been put there
  82. // eagerly by the creation process, to allow for circular reference resolution.
  83. // Also remove any beans that received a temporary reference to the bean.
  84. destroySingleton(beanName);
  85. throw ex;
  86. }
  87. });
  88. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  89. }
  90. else if (mbd.isPrototype()) {
  91. // It's a prototype -> create a new instance.
  92. Object prototypeInstance = null;
  93. try {
  94. beforePrototypeCreation(beanName);
  95. prototypeInstance = createBean(beanName, mbd, args);
  96. }
  97. finally {
  98. afterPrototypeCreation(beanName);
  99. }
  100. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  101. }
  102. else {
  103. String scopeName = mbd.getScope();
  104. final Scope scope = this.scopes.get(scopeName);
  105. if (scope == null) {
  106. throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
  107. }
  108. try {
  109. Object scopedInstance = scope.get(beanName, () -> {
  110. beforePrototypeCreation(beanName);
  111. try {
  112. return createBean(beanName, mbd, args);
  113. }
  114. finally {
  115. afterPrototypeCreation(beanName);
  116. }
  117. });
  118. bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
  119. }
  120. catch (IllegalStateException ex) {
  121. throw new BeanCreationException(beanName,
  122. "Scope '" + scopeName + "' is not active for the current thread; consider " +
  123. "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
  124. ex);
  125. }
  126. }
  127. }
  128. catch (BeansException ex) {
  129. cleanupAfterBeanCreationFailure(beanName);
  130. throw ex;
  131. }
  132. }
  133. // Check if required type matches the type of the actual bean instance.
  134. if (requiredType != null && !requiredType.isInstance(bean)) {
  135. try {
  136. T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
  137. if (convertedBean == null) {
  138. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  139. }
  140. return convertedBean;
  141. }
  142. catch (TypeMismatchException ex) {
  143. if (logger.isTraceEnabled()) {
  144. logger.trace("Failed to convert bean '" + name + "' to required type '" +
  145. ClassUtils.getQualifiedName(requiredType) + "'", ex);
  146. }
  147. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  148. }
  149. }
  150. return (T) bean;
  151. }

DefaultSingletonBeanRegistry

  1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(beanName, "Bean name must not be null");
  3. synchronized (this.singletonObjects) {
  4. //先从
  5. /** Cache of singleton objects: bean name to bean instance. */
  6. //private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  7. //拿取bean
  8. Object singletonObject = this.singletonObjects.get(beanName);
  9. if (singletonObject == null) {
  10. if (this.singletonsCurrentlyInDestruction) {
  11. throw new BeanCreationNotAllowedException(beanName,
  12. "Singleton bean creation not allowed while singletons of this factory are in destruction " +
  13. "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
  14. }
  15. if (logger.isDebugEnabled()) {
  16. logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
  17. }
  18. beforeSingletonCreation(beanName);
  19. boolean newSingleton = false;
  20. boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  21. if (recordSuppressedExceptions) {
  22. this.suppressedExceptions = new LinkedHashSet<>();
  23. }
  24. try {
  25. //※利用反射创建bean
  26. singletonObject = singletonFactory.getObject();
  27. newSingleton = true;
  28. }
  29. catch (IllegalStateException ex) {
  30. // Has the singleton object implicitly appeared in the meantime ->
  31. // if yes, proceed with it since the exception indicates that state.
  32. singletonObject = this.singletonObjects.get(beanName);
  33. if (singletonObject == null) {
  34. throw ex;
  35. }
  36. }
  37. catch (BeanCreationException ex) {
  38. if (recordSuppressedExceptions) {
  39. for (Exception suppressedException : this.suppressedExceptions) {
  40. ex.addRelatedCause(suppressedException);
  41. }
  42. }
  43. throw ex;
  44. }
  45. finally {
  46. if (recordSuppressedExceptions) {
  47. this.suppressedExceptions = null;
  48. }
  49. afterSingletonCreation(beanName);
  50. }
  51. if (newSingleton) {
  52. //添加创建的bean
  53. addSingleton(beanName, singletonObject);
  54. }
  55. }
  56. return singletonObject;
  57. }
  58. }

创建好的对象最终会保存在DefaultSingletonBeanRegistry类的singletonObjects中

IOC容器之一:singletonObjects:保存单例bean

Spring - 图2


BeanFactory和ApplicationContext的区别

  • ApplicationContext是BeanFactory的子接口;
  • BeanFactory:bean工厂接口,负责创建bean实例;
    • 最底层的接口
  • ApplicationContext:容器接口,负责容器功能的实现;
    • 留给人使用的ioc接口
    • 可以基于BeanFactory创建好的对象,完成强大的容器功能

spring中最大的设计模式就是工厂模式

2.4.动态代理:

场景:计算器运行计算方法的时候进行日志记录

加日志记录:

  1. 直接编写在方法内部;不推荐,修改维护麻烦
    • 日志记录:系统的辅助功能;
    • 业务逻辑:(核心功能)
    • 耦合
  2. 希望的是
    • 业务逻辑:(核心功能)
    • 日志模块:在核心功能运行期间,自己动态的加上
    • 可以使用动态代理来将日志代码动态的在目标方法执行前后先进行执行;
  1. package com.jjj.proxy;
  2. import com.jjj.calculator.service.Calculator;
  3. import org.apache.commons.logging.Log;
  4. import org.apache.commons.logging.LogFactory;
  5. import java.lang.reflect.Proxy;
  6. import java.util.Arrays;
  7. /**
  8. * @author <a href="jinyu52370@163.com">JJJ</a>
  9. * @date 2020/5/8 9:30
  10. */
  11. public class CalculatorProxy {
  12. private static Log log = LogFactory.getLog(CalculatorProxy.class);
  13. public static Calculator getProxy(Calculator calculator){
  14. return (Calculator) Proxy.newProxyInstance(
  15. calculator.getClass().getClassLoader(),
  16. calculator.getClass().getInterfaces(),
  17. (proxy, method, args) -> {
  18. log.info("The method "
  19. + method.getName()
  20. + "() begins with "
  21. + Arrays.toString(args));
  22. Object result = null;
  23. try {
  24. result = method.invoke(calculator, args);
  25. } catch (Exception e) {
  26. log.info("The method "
  27. + method.getName()
  28. + "() throw exception: "
  29. + e.getCause());
  30. }finally {
  31. log.info("The method "
  32. + method.getName()
  33. + "() ends with "
  34. + result);
  35. }
  36. return result;
  37. });
  38. }
  39. }

缺点:

  1. 写起来难
  2. 被代理对象必须实现接口(代理对象和被代理对象的唯一关联就是实现了同一个接口)

2.5.AOP

OOP:(Object Oriented Programming)面向对象编程

AOP:(Aspect Oriented Programming)面向切面编程

  • 基于OOP基础之上新的编程思想
  • 指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式

Spring - 图3

通知方法的执行顺序:

  1. try{
  2. @Before
  3. {
  4. @Around(Before)
  5. try{
  6. method.invoke(obj, args);
  7. @Around(Returning)
  8. } catch(e){
  9. @Around(Throwing)
  10. } finally{
  11. @Around(After)
  12. }
  13. }
  14. @AfterReturning
  15. } catch(e){
  16. @AfterThrowing
  17. } finally{
  18. @After()
  19. }

正常情况

异常情况

两个切面同时工作时,环绕只影响当前切面

切入点表达式写法:

固定格式:execution(访问权限符 返回值类型 方法全签名(参数表))
通配符:



    1. 匹配一个或多个字符:abc*z
    2. 匹配任意一个参数:abc(int, *)
    3. 匹配一层路径:com.pack.*.abc
    4. 访问权限符不能写,不写为匹配所有:但也只能匹配public,即public可写可不写
  • ..
    1. 匹配任意个数和类型的参数:abc(..)
    2. 匹配任意层数的路径:com.pack..abc

最模糊:execution( .*(..))
最精确:execution(public Double CalculatorImpl.add(Double, Double)))

2.6.声明式事务

2.6.1.编程式事务

  1. TransactionFilter{
  2. try{
  3. //获取连接
  4. //设置非自动提交
  5. chain.doFileter();
  6. //提交
  7. } catch(Exception e){
  8. //回滚
  9. } finally{
  10. //关闭连接释放资源
  11. }
  12. }

2.6.2.声明式事务(基于AOP的环绕通知)

快速为某个方法添加事务

  1. 配置事务管理器
  2. 开启基于注解的事务控制模式
  3. 给事务方法加注解

事务细节

  1. isolation
  2. Isolation //事务的隔离级别
  3. propagation
  4. Propagation //事务的传播行为
  5. 异常分类
  6. 运行时/非检查异常:可以不处理;
  7. 默认回滚
  8. 编译时/检查异常:try-catch throws
  9. 默认不回滚
  10. noRollbackFor
  11. Class[] //可以让某些默认回滚的异常不回滚
  12. noRollbackForclassName
  13. String[] //String全类名
  14. rollbackFor
  15. Class[] //可以让某些默认不回滚的异常回滚
  16. rollbackForclassName
  17. String[] //String全类名
  18. readonly
  19. boolean //设置事务为只读事务
  20. true //加快查询速度,无需进行commit等事务操作
  21. timeout
  22. int //事务超出指定执行时长(s)后自动终止并回滚

隔离级别

读未提交:脏读

Spring - 图4

读已提交:不可重复读

Spring - 图5

可重复读(快照读)

Spring - 图6

传播行为

REQUIRED:当前事务和大事务共用一个事务

  • 当前事务的属性继承于大事务,比如timeout
  • 将之前事务的connection传递给这个方法使用

REQUIRES_NEW:当前事务新开事务,若已经有事务,则将其挂起

  • 使用新的connection

本类事务方法之间调用的是同一个事务

  1. MulServiceProxy.mulTx(){
  2. userServiceProxy.transfer();
  3. userServiceProxy.updateMoneyByName();
  4. }
  5. //本类方法的嵌套调用就只是一个事务
  6. UserServiceProxy.mulTx(){
  7. transfer();
  8. updateMoneyByName();
  9. }