spring-解析Scope注解

spring-解析Scope注解java自定义Scope1、实现Scope接口public interface Scope {//从当前的作用域中获取name的对象。如果没有找

欢迎大家来到IT世界,在知识的湖畔探索吧!

java自定义Scope

1、实现Scope接口

public interface Scope { //从当前的作用域中获取name的对象。如果没有找到对象,那么将使用提供的ObjectFactory创建一个新的对象。 Object get(String name, ObjectFactory<?> objectFactory); //从当前的作用域中删除一个命名的对象,并返回这个对象。如果没有找到对象,那么返回null。 Object remove(String name); //于注册一个销毁回调,当命名的对象从作用域中移除时,这个回调将被执行。 void registerDestructionCallback(String name, Runnable callback); //解析当前作用域上下文中的对象。这通常用于解析一些特殊的、基于当前上下文的对象,例如HttpRequest、Session等。 Object resolveContextualObject(String key); //返回当前作用域的唯一标识符。这个ID通常用于日志记录或者用于跟踪在特定作用域中发生的事件。比如sessionId String getConversationId(); } 

欢迎大家来到IT世界,在知识的湖畔探索吧!

我们自定义实现这个接口,同一个线程里边拿到的值是一样的。

欢迎大家来到IT世界,在知识的湖畔探索吧!public class ThreadScope implements Scope { private final ThreadLocal<Map<String, Object>> threadScope = new ThreadLocal<Map<String, Object>>() { @Override protected Map<String, Object> initialValue() { return new HashMap<>(); } }; @Override public Object get(String name, ObjectFactory<?> objectFactory) { Map<String, Object> scope = threadScope.get(); return scope.computeIfAbsent(name, k -> objectFactory.getObject()); } @Override public Object resolveContextualObject(String key) { Map<String, Object> scope = threadScope.get(); return scope.get(key); } @Override public Object remove(String name) { Map<String, Object> scope = threadScope.get(); return scope.remove(name); } @Override public void registerDestructionCallback(String name, Runnable callback) { // For this example, we do not execute any action } @Override public String getConversationId() { return String.valueOf(Thread.currentThread().getId()); } } 

2、注入到spring

@Configuration public class MainConfig implements BeanFactoryPostProcessor { public static Integer num = 1; // 注入容器中 @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.registerScope("threadScope", new ThreadScope()); } @Bean @Scope("threadScope") public UserService userService() { return new UserService("小明" + num++ + "号"); } } 

3、调用

欢迎大家来到IT世界,在知识的湖畔探索吧!public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); new Thread(() -> System.out.println(context.getBean("userService").toString() + "-" + context.getBean("userService").toString())).start(); new Thread(() -> System.out.println(context.getBean("userService").toString())).start(); new Thread(() -> System.out.println(context.getBean("userService").toString())).start(); } 

输出结果如下:

UserService(name=小明1号) UserService(name=小明2号)-UserService(name=小明2号) UserService(name=小明3号) 

同一个线程拿到的值是一样的,不同的线程拿到的值是不一样。

原理

1、注册

beanFactory.registerScope("threadScope", new ThreadScope()); 

最终会调用AbstractBeanFactory#registerScope并把我们传入值作为key-value存储到scopes Map集合中。

public void registerScope(String scopeName, Scope scope) { //SCOPE_SINGLETON是singleton,SCOPE_PROTOTYPE是prototype //禁止我们重写这两个key if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) { throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'"); } Scope previous = this.scopes.put(scopeName, scope); } 

现在我们scopes中有{“threadScope”:threadScope}。

2、调用

context.getBean(“userService”);

去工厂拿bean的时候,是去单例工厂拿的。

/** 单例对象的缓存:从bean名称到bean实例。 */ // 单例池 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 

我们这个不是单例single,而是threadScope,从单例工厂工厂拿不到,会走一下逻辑:

//是否是 single if (mbd.isSingleton()) { ... } //是否是 prototype else if (mbd.isPrototype()) { ... } else { //这里才是我们自定义的 String scopeName = mbd.getScope(); Scope scope = this.scopes.get(scopeName); try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } } 

上边代码解释,我们@Scope(“threadScope”),scope的值是”threadScope”

1、判断是否是”single”,不是。

2、判断是否是”pototype”,也不是。

3、以上都不是,从scopes中取值,取得是上边注册进去的值。

4、调用Scope.get(String name, ObjectFactory<?> objectFactory)并返回。

我们的实现逻辑是:

@Override public Object get(String name, ObjectFactory<?> objectFactory) { Map<String, Object> scope = threadScope.get(); return scope.computeIfAbsent(name, k -> objectFactory.getObject()); } 

从ThreadLoop中获取,如果有直接返回,没有就用ObjectFactory.getObject创建一个,存放到ThreadLoop中并返回。在spring中我们传的是lambda表达式去调用createBean创建一个bean。

整体已理清,总结一下:

实现流程:

1、实现Scope接口。

2、注入spring。实现BeanFactoryPostProcessor重写postProcessBeanFactory方法,在里边调用registerScope(key,Scope)进行注入

源码流程:

1、注入Scope到AbstractBeanFactory.scopes的map中,其中key就是@Scope(“xxx”)里边的xxx,value是我们重写Scope的实体类。

2、spring中获取写有@Scope(“xxx”)的bean的时候,因为xxx不是single所以不会存放到单例池中。

3、用xxx从scopes中获取scope的实现类。

4、调用get(xxx,ObjectFactory)。ObjectFactory是个lambda,传入的createBean方法,也就是spring创建bean的方法。这里具体实现逻辑自己定义,上边例子的实现是,先用xxx去ThreadPool获取这个线程里边的值,判断是否存在,如果不存在调用ObjectFactory的方法,然后存放到ThreadPool中并返回,也就是调用创建bean,存放到ThreadPool中,并返回这个bean。

Signle、prototype和Scope接口的关系

Signle和prototype是spring写死了的值。

spring-解析Scope注解

他们两个不用实现Scope接口,是在spring写死了的逻辑。spring为了扩展,提供了Scope接口,然后注入到scopes Map中,获取bean的时候spring会先去写死了的逻辑中判断singleton和prototype,如果不是的话才会走我们扩展的逻辑。

像我们常见的有:

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) { //request beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); //session beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope()); beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); } 

一会说明request的流程

@Scope注解

@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Scope { @AliasFor("scopeName") String value() default ""; @AliasFor("value") String scopeName() default ""; ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT; } 

value()scopeName():这两个属性是互为别名的,可以互换使用。用于指定bean的作用域。常用的值包括singletonprototyperequestsession等。默认值为空字符串,此时通常会被视为singleton

ScopedProxyMode我们先看个例子。

定义两个bean,一个是单例,一个是原型,我们把原型的bean注入到单例bean中。

@Component @ToString @Data //单例 public class OrderService { @Autowired private UserService userService; } @Component @Scope(value = "prototype") public class UserService { } 

目的是想每次获取OrderService bean的时候userService都是不同的。

输出:

public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(context.getBean("userService")); System.out.println(context.getBean("userService")); System.out.println(context.getBean("orderService")); System.out.println(context.getBean("orderService")); } com.soundCode.scan.service.UserService@3d24753a com.soundCode.scan.service.UserService@59a6e353 OrderService(userService=com.soundCode.scan.service.UserService@7a0ac6e3) OrderService(userService=com.soundCode.scan.service.UserService@7a0ac6e3) 

我们发现单例orderService里边的原型userService每次都是一样的。这就会有问题。

我们可以用@LookUp注解来实现,

@Component @Data public abstract class OrderService { //这个就不要了 //@Autowired //private UserService userService; @Lookup public abstract UserService getUserService(); public String toString(){ return this.getUserService().toString(); } } 

其中spring会为们创建个动态代理,去实现OderService#getUserService()的方法,这样每次调用getUserService方法都会返回不一样的UserService。

在Scope中,我们也可以用ScopedProxyMode.TARGET_CLASS,来告诉spring这个bean需要创建动态代理。

@Component @Scope(value = "prototype",proxyMode=ScopedProxyMode.TARGET_CLASS) public class UserService { } //输出 com.soundCode.scan.service.UserService@735f7ae5 com.soundCode.scan.service.UserService@180bc464 OrderService(userService=com.soundCode.scan.service.UserService@e) OrderService(userService=com.soundCode.scan.service.UserService@2c6a3f77) 

这样每次获取单例orderService的时候,UserService都重新生成。

补充上一篇扫描时候判断@Scope是否要生成代理。

下边分析applyScopedProxyMode都做了什么。

definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 

1、判断@Scope scopedProxyMode是否是ScopedProxyMode.TARGET_CLASS,如果是往下走

2、创建一个RootBeanDefinition proxyDefinition,class是ScopedProxyFactoryBean。targetBeanName是scopedTarget.+xxx

proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName); 

这里属性赋值的时候会给ScopedProxyFactoryBean里边的targetBeanName属性赋值。

3、把原来的beanDefinition用名字scopedTarget.+xxx注入到beanDefinitionMap容器,它依然是原型。

spring-解析Scope注解

scopedTarget.+xxx

4、返回新创建RootBeanDefinition,并注册到beanDefinitionMap容器,name=’xxx’,新的beanDefinition就是单例了也是个工厂。

上边最主要逻辑是ScopedProxyFactoryBean,看名字就知道他是个FactoryBean。

public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean { /** The TargetSource that manages scoping. */ private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource(); @Nullable private String targetBeanName; @Nullable private Object proxy; @Override public void setBeanFactory(BeanFactory beanFactory) { ProxyFactory pf = new ProxyFactory(); pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject)); pf.setTargetSource(this.scopedTargetSource); this.proxy = pf.getProxy(cbf.getBeanClassLoader()); } @Override public Class<?> getObjectType() { if (this.proxy != null) { return this.proxy.getClass(); } Object beanInstance = this.beanFactory.getBean(this.targetBeanName); return beanInstance.getClass(); } @Override public Object getObject() { return this.proxy; } } public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource { //这个方法就是返回工厂去目标名字的bean。 public Object getTarget() throws Exception { return getBeanFactory().getBean(getTargetBeanName()); } } 

在spring容器中会注册ScopedProxyFactoryBean单例,执行Aware时候会执行BeanFactoryAware#setBeanFactory方法并创建代理对象。

代理对象getTarget被SimpleBeanTargetSource类重写,当调用getgetTarget方法的时候就会去工厂取名字为scopedTarget.+xxx的bean。

这里其实有个问题,就是每次调用目标方法的时候都会被代理类拦截并调用getTarget方法,如果是原型的的话每次都会生成一个新的对象。

我们注入到OrderService的userService是个代理是ScopedProxyFactoryBean#getObject返回的Proxy。在真正调用里边的方法时候才会调用getTarget方法创建bean。

public enum ScopedProxyMode { DEFAULT, NO, INTERFACES, TARGET_CLASS } 

其中NO是默认的,如果是DEFAULT默认会转换成NO。TARGET_CLASS是用cglib代理,INTERFACES是基于jdk代理。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/73502.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信