Spring源码第八讲 Scope

1.request, session, application 作用域

@SpringBootApplication
public class A08copy {
    public static void main(String[] args) {
        SpringApplication.run(A08copy.class, args);

    }
}

@Scope("application")
@Component
public class BeanForApplication {
    private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);

    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }
}
@Scope("request")
@Component
public class BeanForRequest {
    private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);

    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }

}
@Scope("session")
@Component
public class BeanForSession {
    private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);

    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }
}
@RestController
public class MyController {

    @Lazy
    @Autowired
    private BeanForRequest beanForRequest;

    @Lazy
    @Autowired
    private BeanForSession beanForSession;

    @Lazy
    @Autowired
    private BeanForApplication beanForApplication;

    @GetMapping(value = "/test", produces = "text/html")
    public String test(HttpServletRequest request, HttpSession session) {
        ServletContext sc = request.getServletContext();
        String sb = "<ul>" +
                    "<li>" + "request scope:" + beanForRequest + "</li>" +
                    "<li>" + "session scope:" + beanForSession + "</li>" +
                    "<li>" + "application scope:" + beanForApplication + "</li>" +
                    "</ul>";
        return sb;
    }

}

 注:打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED即可。

同一浏览器刷新 第一个变了后面两个没变因为每次刷新是不同的请求。而不同的浏览器

Request和session不同但aplicition一样。

总结:在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope

1 singleton,容器启动时创建(未设置延迟),容器关闭时销毁
2 prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
3 request,每次请求用到此 bean 时创建,请求结束时销毁
4 session,每个会话用到此 bean 时创建,会话结束时销毁
5 application,web 容器用到此 bean 时创建,容器停止时销毁

有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃。


2. scope 的四种解决方法

@ComponentScan("com.itheima.a09")
public class A09 {

    private static final Logger log = LoggerFactory.getLogger(A09.class);

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(A09.class);

        E e = context.getBean(E.class);
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());



        context.close();
        
    }
}
@Component
public class E {
    
    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }

}
@Scope("prototype")
@Component
public class F1 {
}

虽然F1是多例,我们尝试获取e的F1变量但运行输出

可见三个F1相同。
这是因为对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F 。
解决方案  1.仍然使用 @Lazy 生成代理
                2.代理对象虽然还是同一个,但当每次**使用代理对象的任意方法**时,由代理创建新的 f 对象(原视频又建了F2进行演示,比较简单就不写了)

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}加上这个注释就就好了proxyMode = ScopedProxyMode.TARGET_CLASS,实际也是生成代理对象。

第三种方法 ObjectFactory

@Component
public class E {

    @Autowired
    @Lazy
    private F1 f1;

    @Autowired
    private ObjectFactory<F3> f3;

    public F1 getF1() {
        return f1;
    }

    public F3 getF3() {
        return f3.getObject();
    }

}
@Scope("prototype")
@Component
public class F3 {
}
@ComponentScan("com.itheima.a09")
public class A09 {

    private static final Logger log = LoggerFactory.getLogger(A09.class);

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(A09.class);

        E e = context.getBean(E.class);
        log.debug("{}", e.getF1().getClass());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());

        log.debug("{}", e.getF3());
        log.debug("{}", e.getF3());



        context.close();

    }
}

可见两个F3是不同对象

 第四种 ApplicationContext

@Component
public class E {

    @Autowired
    @Lazy
    private F1 f1;

    @Autowired
    private ObjectFactory<F3> f3;
    @Autowired
    private ApplicationContext context;

    public F1 getF1() {
        return f1;
    }

    public F3 getF3() {
        return f3.getObject();
    }

    public F4 getF4() {
        return context.getBean(F4.class);
    }

}

A09中运行

log.debug("{}", e.getF4());
log.debug("{}", e.getF4());输出

 总结

  1. 单例注入其它 scope 的四种解决方法

    • @Lazy

    • @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)

    • ObjectFactory

    • ApplicationContext

  2. 解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值