Discuss / Java / 思考题

思考题

Topic source

1. 为什么Spring刻意不初始化Proxy继承的字段?

这个问题,廖老师能解答一些吗,查了半天,都没有什么很好的解释

2 如果一个Bean不允许任何AOP代理,应该怎么做来“保护”自己在运行期不会被代理?

将类设置为 final 的防止 cglib 创建Proxy,并且不继承接口防止 JDK 自带的动态代理

廖雪峰

#2 Created at ... [Delete] [Delete and Lock User]

因为你初始化的时候很可能会用到注入的其他类:

@Component
public class MailService {
    @Value("${smtp.from:xxx}")
    String mailFrom;

    SmtpSender sender;

    @PostConstruct
    public void init() {
        sender = new SmtpSender(mailFrom, ...);
    }

    public void sentMail(String to) {
        ...
    }
}

你看,MailService的字段sender初始化需要依赖其他注入,并且已经初始化了一次,proxy类没法正确初始化sender

主要原因就是spring无法在逻辑上正常初始化proxy的字段,所以干脆不初始化,并通过NPE直接暴露出来

廖雪峰

#3 Created at ... [Delete] [Delete and Lock User]

还有一个原因是如果对字段进行修改,proxy的字段其实根本没改:

@Component
public class MailService {
    String status = "init";

    public void sentMail(String to) {
        this.status = "sent";
    }
}

因为只有原始Bean的方法会对自己的字段进行修改,他无法改proxy的字段

Zavicks

#4 Created at ... [Delete] [Delete and Lock User]

总之还是OOP的思想:proxy类只承诺了要代理目标类的public方法,所以理论上一切的交互都应该被限制在被代理的方法集合里,不应该越过proxy的接口而直接交互。如果有新增的交互需求,都应该被统一到这个“被代理的方法集合”里去。

_

#5 Created at ... [Delete] [Delete and Lock User]

如果原始类的字段不是final的话,proxy访问的是原始类的字段,并可能在运行中修改其值,而代理类的同一个字段无法检测到这些修改,因此会造成不一致。

如果原始类的方法是final的话,从语义上应该是能把值迁移到被代理类上吧?比如这段代码里,如果sender是final字段的话

@Component
public class MailService {
    final SmtpSender sender;

    public void MailService(String to) {
        sender = new SmtpSender(to, ...);
    }
}

那对应的代理类就可以在MailService初始化之后把sender的值拿出来,赋值给自己的sender字段了:

public class MailServiceEnhancerBySpringCGLIB extends MailService {
    final MailService target;

    public void MailServiceEnhancerBySpringCGLIB(MailService mailService) {
        this.target = mailService;
        this.sender = mailService.sender;
        ...
    }
}

由于targetsender都是final的,可以认为target.sender恒等于sender


  • 1

Reply