【问题标题】:Spring AOP: applying properties through the aspectSpring AOP:通过方面应用属性
【发布时间】:2009-01-08 09:15:41
【问题描述】:

这里的目的是处理资源的混淆密码。

我们有一个顾问,它拦截对 setPassword 的调用并解密参数。

我们设置了一个看起来有点像这样的模板:

<bean id="pwAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
   <property name="advice"><bean class="our.advice.bean.class"/></property>
   <property name="mappedName" value="setPassword"/>
</bean>
<bean id="passwordHandlerTemplate" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
   <property name="interceptorNames"><list><value>pwAdvisor</value></list></property>
</bean>

我不清楚使用它的确切语法。最明显的方式是:

<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
   <property name="target">
      <bean class="the.target.class.name">
         <property name="password" value="encrypted garbage"/>
      </bean>
    </property>
 </bean>

但这并不正确,因为密码属性应用于内部 bean,这意味着顾问将不会完成它的工作。

那么,这个呢:

<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
   <property name="target"><bean class="the.target.class.name"/></property>
   <property name="password" value="encrypted garbage"/>
</bean>

不。 Spring 抱怨 ProxyFactoryBean 没有密码属性。当然,事实并非如此。具有密码属性的东西是工厂 bean创建的

比勒?

【问题讨论】:

    标签: spring aop


    【解决方案1】:

    我的第一次努力很糟糕,但我很着急。我道歉。现在我想我知道它应该如何工作了,因为我相信我自己已经实现了你想要的。

    我从一个 Credential 类开始(注意:没有接口):

    
    package aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Credential
    {
       private static final String DEFAULT_USERNAME = "username";
       private static final String DEFAULT_PASSWORD = "password";
    
       private String username;
       private String password;
    
       public static void main(String[] args)
       {
          Credential cred1 = new Credential("foo", "bar");
          System.out.println("created using new: " + cred1);
    
          ApplicationContext context = new ClassPathXmlApplicationContext("classpath:aop-context.xml");
          Credential cred2 = (Credential) context.getBean("credential");
    
          System.out.println("created using app context: " + cred2);
    
          String password = ((args.length > 0) ? args[0] : "baz");
          cred2.setPassword(password);
    
          System.out.println("initialized using setter: " + cred2);      
       }
    
       public Credential()
       {
          this(DEFAULT_USERNAME, DEFAULT_PASSWORD);
       }
    
       public Credential(String username, String password)
       {
          this.setUsername(username);
          this.setPassword(password);
       }
    
       public String getUsername()
       {
          return username;
       }
    
       public void setUsername(String username)
       {
          this.username = username;
       }
    
       public String getPassword()
       {
          return password;
       }
    
       public void setPassword(String password)
       {
          this.password = password;
       }
    
    
       public String toString()
       {
          return new StringBuilder().append("Credential{").append("username='").append(username).append('\'').append(", password='").append(password).append('\'').append('}').toString();
       }
    }
    

    我创建了一个 Decryptor 接口:

    
    package aop;
    
    public interface Decryptor
    {
       String decrypt(String encrypted);
    }
    

    还有一个 DecryptorImpl:

    
    package aop;
    
    public class DecryptorImpl implements Decryptor
    {
       public static final String DEFAULT_DECRYPTED_VALUE = " - not secret anymore";
    
       public String decrypt(String encrypted)
       {
          // Any transform will do; this suffices to demonstrate
          return encrypted + DEFAULT_DECRYPTED_VALUE;
       }
    }
    


    我需要 DecryptorAdvice 来实现 Spring 的 MethodBeforeAdvice:


    
    package aop;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class DecryptionAdvice implements MethodBeforeAdvice
    {
       private Decryptor decryptor;
    
    
       public DecryptionAdvice(Decryptor decryptor)
       {
          this.decryptor = decryptor;
       }
    
       public void before(Method method, Object[] args, Object target) throws Throwable
       {
          String encryptedPassword = (String) args[0];
    
          args[0] = this.decryptor.decrypt(encryptedPassword);
       }
    }
    

    然后我将它们连接到一个 aop-context.xml 中。 (如果你告诉我如何让 XML 显示,我会发布它。)注意 passwordDecryptionAdvisor:它只匹配 setPassword 方法。

    有趣的部分发生在我运行它时。这是我在控制台中看到的:

    
    created using new: Credential{username='foo', password='bar'}
    created using app context: Credential{username='stackoverflow', password='encrypted-password'}
    initialized using setter: Credential{username='stackoverflow', password='baz - not secret anymore'}
    

    这告诉我的是:

    1. 如果我用 new 创建一个对象 不受 Spring 的控制,建议 未应用。
    2. 如果我在 ctor 中调用 setPassword 在应用上下文之前 已初始化,建议未应用。
    3. 如果我在代码中调用 setPassword 在应用程序上下文之后 初始化,建议应用。

    希望对你有帮助。

    【讨论】:

    • 这正是我所看到的,但它引出了一个问题:在应用程序上下文中设置属性时如何应用建议?
    • 如果这是真的,那真是太糟糕了。
    • 也许这不是做你想做的事的方法。这并不意味着它不能完成。
    【解决方案2】:

    我以为您希望 beforeMethod 建议使用传递给 setPassword 方法的加密密码字符串。您想解密它并让建议的班级获得解密版本。

    我也没有在您的代理工厂中看到代理接口设置。 “Spring In Action”说“......使用接口创建代理比代理类更受欢迎......”代理类应该是例外,而不是规则。

    发布您的建议课程。

    【讨论】:

    • 有问题的对象实现了多个接口,并且它们都被Spring自动代理。我可以发布建议课程,但这将是一个红鲱鱼。可以说,如果您在其中放入 println,那么当 在 appctx 中时,它不会被调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-26
    • 2015-02-26
    • 2011-09-20
    • 2013-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多