Spring 由 bean 工厂创建的对象上的 AOP
Spring AOP on objects created by factories which are beans
我有一条 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);
}
但我看不到现成的方法。
我不想让我的 classes Spring-aware。工厂 class 本身是第三方的,所以我更喜欢不更改其来源的解决方案。
是否有实现此目的的通用模式?或者更好的解决问题的方法?
您的主要替代方案是使用编程方面定义,因为您无法使用源代码。
实施一个新的身份验证工厂装饰器来包装您的旧身份验证工厂。前者主要将验证器创建委托给后者,然后用 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 应用程序上下文中提取 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";
}
}
}
在 main class 下执行,请注意,您正在处理一个新的代理身份验证器实例,该实例包含在您的建议逻辑中,如输出所示:
Before invocation...
MockSubject is being authenticated!!!
我有一条 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);
}
但我看不到现成的方法。
我不想让我的 classes Spring-aware。工厂 class 本身是第三方的,所以我更喜欢不更改其来源的解决方案。
是否有实现此目的的通用模式?或者更好的解决问题的方法?
您的主要替代方案是使用编程方面定义,因为您无法使用源代码。
实施一个新的身份验证工厂装饰器来包装您的旧身份验证工厂。前者主要将验证器创建委托给后者,然后用 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 应用程序上下文中提取 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";
}
}
}
在 main class 下执行,请注意,您正在处理一个新的代理身份验证器实例,该实例包含在您的建议逻辑中,如输出所示:
Before invocation...
MockSubject is being authenticated!!!