Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如果Spring能够在@Configuration类中成功拦截类内函数调用,为什么它在常规bean中不支持它?_Java_Spring_Proxy_Interception - Fatal编程技术网

Java 如果Spring能够在@Configuration类中成功拦截类内函数调用,为什么它在常规bean中不支持它?

Java 如果Spring能够在@Configuration类中成功拦截类内函数调用,为什么它在常规bean中不支持它?,java,spring,proxy,interception,Java,Spring,Proxy,Interception,我最近注意到Spring在@Configuration类中成功地拦截了类内函数调用,但在普通bean中没有 这样的电话 @Repository public class CustomerDAO { @Transactional(value=TxType.REQUIRED) public void saveCustomer() { // some DB stuff here... saveCustomer2(); } @Trans

我最近注意到Spring在@Configuration类中成功地拦截了类内函数调用,但在普通bean中没有

这样的电话

@Repository
public class CustomerDAO {  
    @Transactional(value=TxType.REQUIRED)
    public void saveCustomer() {
        // some DB stuff here...
        saveCustomer2();
    }
    @Transactional(value=TxType.REQUIRES_NEW)
    public void saveCustomer2() {
        // more DB stuff here
    }
}
无法启动新事务,因为当saveCustomer()的代码在CustomerDAO代理中执行时,saveCustomer2()的代码在未包装的CustomerDAO类中执行,正如我在调试器中看到的“this”,因此Spring没有机会截获对saveCustomer2的调用

但是,在下面的示例中,当transactionManager()调用createDataSource()时,它会被正确拦截并调用代理的createDataSource(),而不是unwrapped类的createDataSource(),在调试器中查看“this”可以证明这一点

@Configuration
public class PersistenceJPAConfig {
    @Bean
    public DriverManagerDataSource createDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        //dataSource.set ... DB stuff here
        return dataSource;
    }

   @Bean 
       public PlatformTransactionManager transactionManager(   ){
           DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(createDataSource());
           return transactionManager;
       }
}
所以我的问题是,为什么Spring可以在第二个示例中正确地拦截类内函数调用,而在第一个示例中却不能。它是否使用不同类型的动态代理

编辑: 从这里的答案和其他来源,我现在了解到: @事务模式使用SpringAOP实现,其中代理模式通过包装/组合用户类来执行。AOP代理足够通用,因此许多方面可以链接在一起,可以是CGLib代理或Java动态代理

在@Configuration类中,Spring还使用CGLib创建一个从user@Configuration类继承的增强类,并使用在调用user/super函数之前做一些额外工作的函数覆盖用户的@Bean函数,例如检查这是否是函数的第一次调用。这个类是代理吗?这取决于定义。您可以说它是一个代理,它使用来自真实对象的继承,而不是使用组合来包装它


总而言之,从这里给出的答案中,我了解到这是两种完全不同的机制。为什么做出这些设计选择是另一个悬而未决的问题。

Spring使用代理进行方法调用,当您使用此选项时。。。它绕过了代理。对于@Bean注释,Spring使用反射来查找它们。

阅读一些Spring源代码。我试着回答

关键是spring如何处理
@配置
@bean
。 在作为BeanFactory后处理器的ConfigurationClassPostProcessor中,它将增强所有ConfigurationClass,并创建一个
增强器作为子类。
此增强程序注册两个回调(BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor)。
您调用
PersistenceJPAConfig
方法将通过回调。在BeanMethodInterceptor中,它将从spring容器中获取bean

可能不太清楚。您可以在
ConfigurationClassEnhancer.java BeanMethodInterceptor
ConfigurationClassPostProcessor.java enhanceConfigurationClasses

这是SpringAOP(动态对象和cglib)的一个限制

如果您将Spring配置为使用AspectJ来处理事务,那么您的代码将正常工作

最简单也是最好的选择是重构代码。例如,一个处理用户的类和一个处理每个用户的类。然后,SpringAOP的默认事务处理将起作用

此外,@Transactional应该在@Repository上,而不是在@Repository上

事务属于服务层。它了解工作单元和用例。如果将多个DAO注入到需要在单个事务中协同工作的服务中,那么这是正确的答案

因此,您需要重新考虑您的事务方法,以便您的方法可以在一个流中重用,该流包括其他几个可滚动的DAO操作

它是否使用不同类型的动态代理

几乎完全正确

让我们找出
@Configuration
类和AOP代理之间的区别,回答以下问题:

  • 为什么自调用的
    @Transactional
    方法没有事务语义,即使Spring能够拦截自调用的方法
  • @Configuration
    与AOP有何关联
  • 为什么自调用的
    @Transactional
    方法没有事务语义? 简短回答:

    这就是AOP的制作方法

    长答案:

  • 声明性事务管理依赖于(对于上的大多数Spring应用程序)
  • Spring框架的声明性事务管理是通过Spring面向方面编程(AOP)实现的

  • 它是基于代理的()
  • SpringAOP是基于代理的

    从同一段落
    SimplePojo.java

    公共类SimplePojo实现Pojo{
    公共图书馆{
    //下一个方法调用是对“this”引用的直接调用
    这个.bar();
    }
    公共空白栏(){
    //一些逻辑。。。
    }
    }
    
    和一个代理它的片段:

    public class Main {
    
        public static void main(String[] args) {
            ProxyFactory factory = new ProxyFactory(new SimplePojo());
            factory.addInterface(Pojo.class);
            factory.addAdvice(new RetryAdvice());
    
            Pojo pojo = (Pojo) factory.getProxy();
            // this is a method call on the proxy!
            pojo.foo();
        }
    }
    
    这里要理解的关键是
    main
    类的
    main(..)
    方法中的客户机代码引用了代理

    这意味着对该对象引用的方法调用是对代理的调用

    因此,代理可以委托给与该特定方法调用相关的所有拦截器(通知)

    但是,一旦调用最终到达目标对象(本例中的
    SimplePojo
    ,引用),它可能对自身进行的任何方法调用,例如
    this.bar()
    this.foo()
    ,都将针对
    this
    引用而不是代理进行调用

    这具有重要意义。这意味着自调用不会产生与方法inv关联的通知
    public interface Interceptor {
      Object invoke(InterceptingFoo interceptingFoo);
    }
    
    public static void main(String[] args) throws Throwable {
      Foo target = new Foo();
      InterceptingFoo interceptingFoo = new InterceptingFoo();
      interceptingFoo.method = Foo.class.getDeclaredMethod("getInt");
      interceptingFoo.target = target;
      interceptingFoo.interceptors.add(new LogInterceptor());
      interceptingFoo.interceptors.add(new InvokeTargetInterceptor());
    
      interceptingFoo.getInt();
      interceptingFoo.getInt();
    }
    
    log. before
    Invoking target
    Target returned 42
    Invoked target
    log. after
    log. before
    Invoking target
    Target returned 42
    Invoked target
    log. after