2015-02-24 56 views
0

我有一块弹簧AOP通知的未触发时在auth(..)方法被调用创建的对象:Spring AOP的由工厂,是豆

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

这是因为身份验证对象与该验证方法是不是一个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豆类是工厂,以便它可以将工厂包装在代理中,使得由create()返回的对象本身被包装在AOP代理中。

也就是说,春天会动态地创建在我厂的包装是这样的:

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

,但我看不到一个现成的办法做到这一点。

我不想让我的课程能够感知春天。工厂类本身就是第三方,所以我宁愿选择不改变源代码的解决方案。

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

+0

hope [this](http://stackoverflow.com/questions/2470014/how-to-create-an-aspect-on-class-that-is-not-a-bean-using-spring- aop)有助于 – 2015-02-25 06:26:23

+0

有点 - 它说“你不能用代理来做到这一点,使用加载时编织”。将看看是否有人说不同。 – slim 2015-02-25 14:26:49

回答

1

你主要的替代,将是去与编程方面的定义,因为你不能与源播放。

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

小枝使这些豆被松散耦合和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(); 
    } 
    
  • 这是传统的实现中,一个提供非管理AUT hentication如下:

    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(); 
        } 
    } 
    
  • 创建一个新的身份验证提供装饰,这将是一个春季管理的豆

    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应用上下文的authFactory豆腐,并用它来实例化新的认证对象:

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正在验证中!

+0

感谢您的回答。你花了很多时间和精力。如果在接受之前有任何进一步的评论,将会花费几天的时间。 – slim 2015-02-26 17:38:15

+0

欢迎您随时提出建议,如果有的话:) – tmarwen 2015-02-26 17:55:02