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();
 }
 我们自定义实现这个接口,同一个线程里边拿到的值是一样的。
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、调用
 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写死了的值。
他们两个不用实现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的作用域。常用的值包括singleton、prototype、request、session等。默认值为空字符串,此时通常会被视为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@1324409e)
 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容器,它依然是原型。
scopedTarget.+xxx
 4、返回新创建RootBeanDefinition,并注册到beanDefinitionMap容器,name=‘xxx’,新的beanDefinition就是单例了也是个工厂。
上边最主要逻辑是ScopedProxyFactoryBean,看名字就知道他是个FactoryBean。
public class ScopedProxyFactoryBean extends ProxyConfig
 implements FactoryBean, 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代理。










