第8讲 - Scope

发布于:2022-10-29 ⋅ 阅读:(799) ⋅ 点赞:(0)

Scope的类型有哪些

  • singleton:在Spring的IoC容器中只存在一个对象实例,所有该对象的引用都共享这个实例。Spring 容器只会创建该bean定义的唯一实例,这个实例会被保存到缓存中,并且对该bean的所有后续请求和引用都将返回该缓存中的对象实例,一般情况下,无状态的bean使用该scope。
  • prototype:每次对该bean的请求都会创建一个新的实例,一般情况下,有状态的bean使用该scope。
  • request:每次http请求将会有各自的bean实例,类似于prototype。
  • session:在一个http session中,一个bean定义对应一个bean实例。
  • global session【application】:在一个全局的http session中,一个bean定义对应一个bean实例。典型情况下,仅在使用portlet context的时候有效。

singleton和prototype我们接触得比较多,今天主要聊一聊其它三个作用域

创建三个不同作用域的请求对象

@Scope("request")
@Component
public class BeanForRequest {

    @PreDestroy
    public void destroy() {
        System.out.println("request destroy...");
    }

}

@Scope("session")
@Component
public class BeanForSession {

    @PreDestroy
    public void destroy() {
        System.out.println("session destroy...");
    }

}

@Scope("application")
@Component
public class BeanForApplication {

    @PreDestroy
    public void destroy() {
        System.out.println("application 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 str = "<ul>" +
                "<li>" + "request scope:" + beanForRequest + "</li>" +
                "<li>" + "session scope:" + beanForSession + "</li>" +
                "<li>" + "application scope:" + beanForApplication + "</li>" +
                "</ul>";
        return str;
    }

}

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

自定义Controller后启动程序

在同一浏览器连续请求两次结果如下:

 

可以发现,只有request域请求对象有变化

在不同浏览器查看:

同样可以发现,request和session域都有变化,而application始终不变。 

特殊说明:老师使用的是JDK17,所以会有访问权限的异常,博主使用的是JDK8所以没出现异常。

异常原因:JDK >= 9 反射调用 jdk 中的方法会有权限检查

解决方案:运行时添加参数 --add-opens java.base/java.lang=ALL-UNNAMED

Scope失效情况分析

单例对象注入其它作用域的对象会有问题,也就是为什么在Controller中要添加@Lazy注解,下面一起来看看原因究竟是什么?

创建Scope为prototype的F1类,在Scope为Singleton的E类中注入F1

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

@Component
public class E {

    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }

}
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(SubApplication.class);
        E e = context.getBean(E.class);
        System.out.println(e.getF1().getClass());
        System.out.println(e.getF1());
        System.out.println(e.getF1());
        System.out.println(e.getF1());
    }

连续获取F1对象,结果如下:

class com.shuttle.spring5.sub.F1
com.shuttle.spring5.sub.F1@3febb011
com.shuttle.spring5.sub.F1@3febb011
com.shuttle.spring5.sub.F1@3febb011 

发现F1对象并不是按照我们预期的那样,每次获取都生成一个新的对象。

原因如下【截取自老师课件】

 添加@Lazy后程序运行结果如下:

class com.shuttle.spring5.sub.F1$$EnhancerBySpringCGLIB$$9953ecf4
com.shuttle.spring5.sub.F1@293a5bf6
com.shuttle.spring5.sub.F1@1283bb96
com.shuttle.spring5.sub.F1@f6efaab

可以发现,F1的Class类型变为了代理类,并且每次调用它生成了新对象 。

第二种解决方式:

创建Scope为prototype的F2类

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

E类中注入F2,不添加@Lazy注解

    @Autowired
    private F2 f2;

    public F2 getF2() {
        return f2;
    }

class com.shuttle.spring5.sub.F2$$EnhancerBySpringCGLIB$$fba119f1
com.shuttle.spring5.sub.F2@2a4fb17b
com.shuttle.spring5.sub.F2@5c6648b0
com.shuttle.spring5.sub.F2@6f1de4c7

也同样达到了上述的效果。

第三种解决方式:

使用ObjectFactory

E类中注入F3对象工厂

    @Autowired
    private ObjectFactory<F3> f3;

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

class com.shuttle.spring5.sub.F3
com.shuttle.spring5.sub.F3@47eaca72
com.shuttle.spring5.sub.F3@55182842
com.shuttle.spring5.sub.F3@235834f2

第四种解决方式:

注入ApplicationContext

E类中添加如下代码:

    @Autowired
    private ApplicationContext context;

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

程序运行结果如下:

class com.shuttle.spring5.sub.F4
com.shuttle.spring5.sub.F4@71d44a3
com.shuttle.spring5.sub.F4@7b98f307
com.shuttle.spring5.sub.F4@4802796d 

与前两种方式不同,注入对象工厂和ApplicationContext方式并不是使用代理来实现。 解决方式虽然不同,但理论上殊途同归,都是推迟其它scope bean 的获取。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到