分析企业网站建设流程做返利网站能赚钱么
- 作者: 五速梦信息网
- 时间: 2026年03月21日 11:18
当前位置: 首页 > news >正文
分析企业网站建设流程,做返利网站能赚钱么,网页设计和网站开发哪个好,蒙古网站后缀你好#xff0c;这里是codetrend专栏“Spring6全攻略”。 在 Spring 框架中#xff0c;Bean 的作用域#xff08;Scope#xff09;定义了 Bean 实例在容器中如何创建、管理和销毁的策略。 Spring 提供了多种 Bean 作用域#xff0c;每种作用域都有其特定的生命周期和适用…你好这里是codetrend专栏“Spring6全攻略”。 在 Spring 框架中Bean 的作用域Scope定义了 Bean 实例在容器中如何创建、管理和销毁的策略。 Spring 提供了多种 Bean 作用域每种作用域都有其特定的生命周期和适用场景。 先试试不同的 Bean Scope 下面通过一个简单的 Spring MVC Controller 示例来感受下 Bean 的作用域。 例子代码是这样的 import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.web.context.WebApplicationContext;import java.util.UUID;Configuration public class AppConfig {BeanScope(ConfigurableBeanFactory.SCOPE_SINGLETON)public SingletonBean singletonBean() {return new SingletonBean();}BeanScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public PrototypeBean prototypeBean() {return new PrototypeBean();}BeanScope(WebApplicationContext.SCOPE_SESSION)public SessionBean sessionBean() {return new SessionBean();} }class SingletonBean {private int count 0;public void increment() {count;}public int getCount() {return count;} }class PrototypeBean {private String id;public PrototypeBean() {this.id UUID.randomUUID().toString();}public String getId() {return id;} }class SessionBean {private String id;public SessionBean() {this.id UUID.randomUUID().toString();}public String getId() {return id;} }controller 代码 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;RestController public class ScopeController {Autowiredprivate SingletonBean singletonBean;Autowiredprivate ApplicationContext context;GetMapping(/singleton)public String singletonCount() {singletonBean.increment();return Singleton Count: singletonBean.getCount();}GetMapping(/prototype)public String prototypeGet() {PrototypeBean prototypeBean context.getBean(PrototypeBean.class);return Prototype ID: prototypeBean.getId();}GetMapping(/session)public String sessionGet() {SessionBean prototypeBean context.getBean(SessionBean.class);return Session ID: prototypeBean.getId();} }Singleton单例的属性持续增加也就是说访问到的SingletonBean每次都是同一个对象。访问/singleton接口的返回是这样的 1 2 3Prototype原型的属性每次都是不一样的也就是说明id每次都是调用构造器新创建的。访问/prototype接口的返回是这样的 Prototype ID: 3ea5af10-ddce-4a89-ad3c-3f07a764f179 Prototype ID: 7e6e9fe8-c0dc-423e-b282-96b7f8087dac Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49Session会话的属性同一窗口是一样的开启无痕窗口或者其他浏览器就不一样。访问/session接口的返回是这样的 Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49 Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49
开启新的窗口后
Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0可以直接把样例代码复制到例子里面验证测试。这样我们就对BeanScope作用域有个直观的感受。
自定义一个 Bean Scope
接下来通过实现一个自定义作用域来感受下Bean的作用域原理。
在 Spring 框架中除了预定义的几种作用域如 singleton、prototype 等外用户还可以自定义作用域以满足特定的业务需求。
自定义作用域允许控制 Bean 的创建、缓存和销毁逻辑以适应特定的场景如基于特定条件的实例化策略、自定义生命周期管理等。
自定义步骤
定义作用域接口首先需要实现org.springframework.beans.factory.config.Scope接口该接口定义了 Bean 作用域的基本行为。实现逻辑在自定义的 Scope 接口实现中需要覆盖get、remove和registerDestructionCallback方法分别用于获取 Bean 实例、移除 Bean 实例以及注册销毁回调。注册作用域在 Spring 配置中注册的自定义作用域使其可被容器识别和使用。使用自定义作用域在 Bean 定义中通过Scope注解指定使用自定义的作用域名称。
自定义作用域实现
首先自定义作用域实现也就是实现接口org.springframework.beans.factory.config.Scope。
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.UUID;public class CustomScope implements Scope {public final static String CUSTOM_SCOPE_NAME custom;private final MapString, Object scopedObjects new ConcurrentHashMap();private final MapString, Runnable destructionCallbacks new ConcurrentHashMap();Overridepublic Object get(String name, ObjectFactory? objectFactory) {Object scopedObject scopedObjects.get(name);if (scopedObject null) {scopedObject objectFactory.getObject();scopedObjects.put(name, scopedObject);}return scopedObject;}Overridepublic Object remove(String name) {scopedObjects.remove(name);Runnable callback destructionCallbacks.remove(name);if (callback ! null) {callback.run();}return null;}Overridepublic void registerDestructionCallback(String name, Runnable callback) {destructionCallbacks.put(name, callback);}Overridepublic Object resolveContextualObject(String key) {// 可以根据需要实现上下文对象解析逻辑return null;}Overridepublic String getConversationId() {// 返回一个唯一的标识用于区分作用域上下文return UUID.randomUUID().toString();}
}可以看到Scope接口其实是对Bean的全生命周期进行管理包括获取get、缓存和销毁remove和销毁回调等逻辑。这也是作用域的核心原理。
Spring6怎么实现的Scope
这里以org.springframework.web.context.request.RequestScope 为例子来理解Spring6怎么实现BeanScope的。
得益于Spring框架的抽象和封装这个类的实现代码并没有多少。 RequestScope extends AbstractRequestAttributesScope 核心实现在这个类 AbstractRequestAttributesScope。 get获取对象方法其中对象的存储放在了ThreadLocal中也就是RequestContextHolder这个类的核心。
/*** 根据名称获取对象如果当前请求属性中没有该对象则使用对象工厂创建一个对象并将其设置到请求属性中* 然后再次获取该对象以便进行隐式会话属性更新。作为额外的好处我们还允许在获取属性级别进行潜在的装饰。* 如果再次获取到的对象不为空预期情况则只使用该对象。如果它同时消失了我们则返回本地创建的实例。/
public Object get(String name, ObjectFactory? objectFactory) {// 获取当前请求的属性RequestAttributes attributes RequestContextHolder.currentRequestAttributes();// 根据名称和作用域获取对象Object scopedObject attributes.getAttribute(name, getScope());if (scopedObject null) {// 使用对象工厂创建对象scopedObject objectFactory.getObject();// 将创建的对象设置到请求属性中attributes.setAttribute(name, scopedObject, getScope());// 再次获取对象进行隐式会话属性更新// 并允许进行潜在的装饰Object retrievedObject attributes.getAttribute(name, getScope());if (retrievedObject! null) {// 只使用再次获取到的对象如果仍然存在这是预期情况// 如果它同时消失了我们则返回本地创建的实例scopedObject retrievedObject;}}// 返回获取到的对象return scopedObject;
}remove 方法也是差不多的。借助工具类RequestContextHolder将缓存在ThreadLocal中的对象移除。
/** 移除指定名称的对象如果当前请求属性中存在该对象则将其从请求属性中移除并返回该对象否则返回 null/
public Object remove(String name) {// 获取当前请求的属性RequestAttributes attributes RequestContextHolder.currentRequestAttributes();// 根据名称和作用域获取对象Object scopedObject attributes.getAttribute(name, getScope());if (scopedObject! null) {// 将该对象从请求属性中移除attributes.removeAttribute(name, getScope());// 返回移除的对象return scopedObject;} else {// 返回 nullreturn null;}
}注册自定义作用域
注册作用域需要通过BeanFactory的registerScope方法进行注册。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;Component
public class ScopeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.registerScope(CustomScope.CUSTOM_SCOPE_NAME, new CustomScope());}
}验证自定义作用域效果
将Bean注册到Spring容器中并使用自定义作用域。
public class MyScopeBean {private String id;public MyScopeBean() {this.id UUID.randomUUID().toString();}public String getId() {return id;}
}import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;Configuration
public class AppScopeConfig {BeanScope(CustomScope.CUSTOM_SCOPE_NAME)public MyScopeBean myBean() {return new MyScopeBean();}
}新建一个Controller访问/customScope接口返回自定义作用域的Bean实例。
RestController
public class CustomScopeController {Autowiredprivate ApplicationContext context;GetMapping(/customScope)public String customScope() {MyScopeBean prototypeBean context.getBean(MyScopeBean.class);return Prototype ID: prototypeBean.getId();}
}访问的结果输出如下
Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0因为对象全局缓存到了一个MapscopedObjects所以可以看到这个自定义作用域效果和单例模式基本一致的。
Bean Scope 的分类
Scope描述singleton(Default) 将单个 bean 定义作用域限定为 Spring IoC 容器中的单个对象实例。prototype将单个 bean 定义作用域限定为任意数量的对象实例。request将单个 bean 定义作用域限定为单个 HTTP 请求的生命周期。也就是说每个 HTTP 请求都有自己的一个基于单个 bean 定义创建的 bean 实例。仅在 Web-aware Spring ApplicationContext 上下文中有效。session将单个 bean 定义作用域限定为 HTTP Session 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。application将单个 bean 定义作用域限定为 ServletContext 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。websocket将单个 bean 定义作用域限定为 WebSocket 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。
其中singleton、prototype是比较常用的数据。
Bean Scope 的使用
可以通过在Spring的配置文件如XML配置文件或Java注解中指定Scope注解或bean元素的scope属性来定义Bean的Scope。
其中Scope注解可以是自定义的值或者如下常量
ConfigurableBeanFactory.SCOPE_PROTOTYPEConfigurableBeanFactory.SCOPE_SINGLETONorg.springframework.web.context.WebApplicationContext.SCOPE_REQUESTorg.springframework.web.context.WebApplicationContext.SCOPE_SESSION
其中ConfigurableBeanFactory.SCOPE_PROTOTYPE是默认值。
例如
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;Component
Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MyPrototypeBean {// Bean内容
}或者使用XML配置
bean idmyBean classcom.example.MyBean scopeprototype!– Bean的其他配置 –
/bean选择合适的Bean Scope取决于应用程序的需求。
为什么设计 Bean Scope
Spring 框架设计 Bean 作用域Scope的原因主要是为了提供灵活性和资源管理能力以适应不同应用场景的需求。
不同的 Bean 作用域会影响 Bean 的生命周期、创建方式和在容器中的共享程度从而影响应用的性能、内存占用和并发处理能力。
以下是 Spring 提供 Bean 作用域设计背后的主要原因
资源优化通过作用域设计Spring 能够根据业务场景高效管理 Bean 的创建与销毁。例如单例Singleton模式可以减少频繁创建实例的开销原型Prototype模式则确保每次请求都得到新的实例避免了共享状态问题。并发处理对于 Web 应用特定作用域如请求Request和会话Session使得每个用户请求或会话都有独立的 Bean 实例解决了并发用户数据隔离的问题提高了应用的线程安全。生命周期管理不同的作用域允许开发者控制 Bean 的生命周期比如通过自定义作用域实现复杂的生命周期管理逻辑。Spring 容器在 Bean 的创建、初始化、销毁等关键时刻调用生命周期回调方法增加了灵活性。可测试性通过作用域的设计特别是原型模式可以更容易地创建独立的测试环境因为每次测试都能得到全新的实例减少了测试间状态干扰。扩展性Spring 允许开发者自定义作用域为特定的业务需求或架构设计提供定制化的 Bean 管理方式增强了框架的扩展性和适应性。内存管理合理使用作用域可以减少内存消耗例如原型模式避免了单例 Bean 累积大量状态导致的内存泄漏风险而请求作用域则确保请求结束后自动清理资源。
单例 bean 里面注入了原型 bean
当单例 Bean 中注入原型PrototypeBean 时会出现一个问题
单例 Bean 在整个应用生命周期中只创建一次。而原型 Bean 本应每次请求时创建新实例。但直接注入到单例 Bean 中时实际上只会注入一次原型 Bean 的实例。后续对该原型 Bean 的使用都将复用首次注入的同一个实例这可能并不符合预期。
以下demo可以复现这种情况。
SpringBean的配置
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;Configuration
public class FaultAppConfig {BeanScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public PrototypeInjectBean prototypeInjectBean() {return new PrototypeInjectBean();}
}单例SpringBean:
import java.util.UUID;public class PrototypeInjectBean {private String id;public PrototypeInjectBean() {this.id UUID.randomUUID().toString();}public String getId() {return id;}
}测试代码如下
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;RestController
Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PrototypeFaultController {final private PrototypeInjectBean prototypeInjectBean;protected PrototypeFaultController(PrototypeInjectBean prototypeInjectBean) {this.prototypeInjectBean prototypeInjectBean;}/** 原型作用域失效每次返回同一个id* return/GetMapping(/prototypeDemo1)public String prototypeDemo1() {return Prototype ID: prototypeInjectBean.getId();}}在不重启应用或者垃圾回收的情况下访问接口 /prototypeDemo1 原型 Bean 的id值始终是相同的。
Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97
Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97
Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97那这种常用的使用场景遇到了该怎么解决呢别急Spring早已经给出了几种解决办法。
通过完善上面的测试代码给出3中解决方法。
修改完善后的代码如下 import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;RestController
Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public abstract class PrototypeFaultController {Autowiredprivate ApplicationContext context;Autowiredprivate ObjectProviderPrototypeInjectBean prototypeBeanProvider;final private PrototypeInjectBean prototypeInjectBean;protected PrototypeFaultController(PrototypeInjectBean prototypeInjectBean) {this.prototypeInjectBean prototypeInjectBean;}/** 原型作用域失效每次返回同一个id* return/GetMapping(/prototypeDemo1)public String prototypeDemo1() {return Prototype ID: prototypeInjectBean.getId();}/** 使用实例工厂方法注入获取原型Bean每次返回不同id* return/GetMapping(/prototypeDemo2)public String prototypeDemo2() {PrototypeInjectBean prototypeBean context.getBean(PrototypeInjectBean.class);return Prototype ID: prototypeBean.getId();}/** Spring 提供了ObjectProvider接口继承自Provider接口它允许延迟查找和实例化 Bean非常适合在单例 Bean 中按需获取原型 Bean 的新实例。* return/GetMapping(/prototypeDemo4)public String prototypeDemo4() {return Prototype ID: prototypeBeanProvider.getObject().getId();}/** 使用Lookup注解获取原型Bean每次返回不同id* return*/GetMapping(/prototypeDemo5)public String prototypeDemo5() {return Prototype ID: getPrototypeBean().getId();}Lookuppublic abstract PrototypeInjectBean getPrototypeBean();}解决办法1 Spring 提供了ObjectProvider接口继承自Provider接口它允许延迟查找和实例化 Bean非常适合在单例 Bean 中按需获取原型 Bean 的新实例。
通过访问接口/prototypeDemo4可以发现每次返回的id值是不同的。
解决办法2 可以通过定义一个工厂方法来创建原型 Bean 的实例然后在单例 Bean 中注入这个工厂方法每次需要时调用工厂方法获取新实例。
通过访问接口/prototypeDemo2可以发现每次返回的id值是不同的。
解决办法3 通过Lookup注解Lookup注解是Spring框架中的一个特殊注解用于在Spring容器中查找另一个Bean并将其注入到当前Bean中。注意使用Lookup注解的方法必须是抽象的abstract。
通过访问接口/prototypeDemo5可以发现每次返回的id值是不同的。
关于作者
来自一线全栈程序员nine的探索与实践持续迭代中。
欢迎关注或者点个小红心~
- 上一篇: 分析竞争对手的网站搜狐新闻手机网
- 下一篇: 分析网站推广和优化的原因怎么做网上卖货
相关文章
-
分析竞争对手的网站搜狐新闻手机网
分析竞争对手的网站搜狐新闻手机网
- 技术栈
- 2026年03月21日
-
分类信息网站做淘客小白如何自己做网站
分类信息网站做淘客小白如何自己做网站
- 技术栈
- 2026年03月21日
-
分类信息网站手机版做网站推广的
分类信息网站手机版做网站推广的
- 技术栈
- 2026年03月21日
-
分析网站推广和优化的原因怎么做网上卖货
分析网站推广和优化的原因怎么做网上卖货
- 技术栈
- 2026年03月21日
-
分享类网站源码官方网站建设与维护好处
分享类网站源码官方网站建设与维护好处
- 技术栈
- 2026年03月21日
-
分享站wordpress主题南宁网站制作开发公司
分享站wordpress主题南宁网站制作开发公司
- 技术栈
- 2026年03月21日






