我有一段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。工厂类本身就是第三方,所以我更倾向于一个我不会改变其来源的解决方案。
是否有共同的模式来实现这一目标?或者更好地解决问题?
答案 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正在通过验证!!!