Spring是由工厂创建的对象的AOP,它们是bean

时间:2015-02-24 13:33:45

标签: java spring-aop

我有一段Spring AOP建议,当调用auth(..)方法时它不会触发:

@AfterReturning(pointcut="execution(* auth(..))",
        returning="result")
public void trigger(final Boolean result) {
    System.out.println("I was triggered " + result);
}

这是因为使用Auth方法的Authenticator对象不是Spring bean,而是由一个Spring bean工厂创建的:

    <bean id="passwordUserAuthFact" class="foo.bar.PasswordAuthFactory">
    ...
    </bean>

    <bean id="server" class="foo.bar.Server">
       <property name="authFactory" ref="passwordUserAuthFact"/>
       ...
    </bean>

所以当应用程序执行时:

 session.authenticator = server.getAuthFactory().create();

... session.authenticator是一个未经处理的普通PasswordAuthFactory,AOP不会发生在它上面。

在我看来,应该可以告诉Spring有关工厂的bean,以便它可以将工厂包装在代理中,这样create()返回的对象本身就包含在AOP代理中。

也就是说,Spring会动态地在我的工厂周围创建一个包装器:

public Foo create() {
    Foo instance = target.create();
    return wrapWithAopProxy(instance);
}

但我无法看到现成的方法。

我宁愿不让我的类感知Spring。工厂类本身就是第三方,所以我更倾向于一个我不会改变其来源的解决方案。

是否有共同的模式来实现这一目标?或者更好地解决问题?

1 个答案:

答案 0 :(得分:1)

您可以选择使用程序化方面定义,因为您无法使用源代码。

实现一个新的身份验证工厂装饰器,它包装旧的身份验证工厂。前者主要将身份验证器创建委托给后者,然后用ProxyBean包装返回的对象并注册所需的建议。

Sprig使这些bean松散耦合,authenticator POJO成为非托管bean。在下面的示例中,我的目的只是提供一个如何做到这一点的视图,将实现细节留在您的应用程序中:

  • 这是假的Authenticator

    public class Authenticator
    {
        private String name;
    
        public Authenticator( String name )
        {
            this.name = name;
        }
    
        public void authenticate( Object subject )
        {
            System.out.println( subject + " is being authenticated by: " + name );
        }
    }
    
  • 假设您的AuthFactory界面如下所示:

    public interface AuthFactory 
    {
        Authenticator create();
    }
    
  • 它的遗留实现,提供非托管身份验证的实现如下:

    public class AuthenticationFactory implements AuthFactory
    {
        @Override
        public Authenticator create()
        {
            return new Authenticator("mocked-authenticator");
        }
    }
    
  • 创建一个新的MethodInterceptor(请注意,您可能需要 aopalliance 依赖关系),它封装了您的建议逻辑:

    public class AuthenticationAdvise implements MethodInterceptor
    {
        @Override
        public Object invoke( MethodInvocation methodInvocation ) throws Throwable
        {
            System.out.println("Before invocation..."); // Your advice logic goes here
            return methodInvocation.proceed();
        }
    }
    
  • 创建一个新的身份验证提供程序装饰器,它将是一个 Spring托管bean

    public class AuthenticationFactoryDecorator implements AuthFactory {
    
        private AuthFactory authenticationFactoryDelegate;
    
        private MethodInterceptor interceptor;
    
        public AuthenticationFactoryDecorator( final AuthFactory authenticationFactoryDelegate, final MethodInterceptor interceptor )
        {
            this.authenticationFactoryDelegate = authenticationFactoryDelegate;
            this.interceptor = interceptor;
        }
    
        @Override
        public Authenticator create()
        {
             // Create the needed pointcut
            NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
            pc.addMethodName("authenticate");
            // Get an authenticator from your legacy class
            Authenticator auth = authenticationFactoryDelegate.create();
            // Create a new Proxy wrapping your authFactory with the needed pointcut and advice
            ProxyFactory proxyFactory = new ProxyFactory(auth);
            proxyFactory.addAdvice(interceptor);
            return (Authenticator) proxyFactory.getProxy();
        }
    }
    
  • 将新的AuthFactory bean注册为Spring组件,并将其与您的建议和遗留工厂bean(例如 beans.xml )连接起来:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="legacyAuthFactory" class="some.package.AuthenticationFactory"/>
    
        <bean id="interceptor" class="some.package.AuthenticationFactoryAdvise"/>
    
        <bean id="authFactory" class="some.package.AuthenticationFactoryDecorator">
            <constructor-arg name="authenticationFactoryDelegate" ref="legacyAuthFactory"/>
            <constructor-arg name="interceptor" ref="interceptor"/>
        </bean>
    
    </beans>
    

现在您可以自由地从Spring Application Context中提取 authFactory bean并使用它来实例化新的身份验证器对象:

public class MainAuthentication
{
    public static void main( String[] args )
    {
        ApplicationContext  applicationContext = new ClassPathXmlApplicationContext("classpath:META-INF/beans.xml");
        AuthFactory factory = applicationContext.getBean("authFactory", AuthFactory.class);
        Authenticator authenticator = factory.create();
        authenticator.authenticate(new MockSubject());
    }

    private static class MockSubject
    {
        @Override
        public String toString() {
            return "MockSubject";
        }
    }
}

执行下面的主类,请注意您正在处理一个新的代理身份验证器实例,该实例包含您的建议逻辑,如输出所示:

  

在调用之前......

     

MockSubject正在通过验证!!!