Skip to main content

@Scopeโ€‹

The Scope of Bean is a way to define when Bean will be created and how it is managed by container.

Clawject provides @Scope decorator to define a scope and have 3 built-in scopes: singleton and transient, default value is singleton.

tip

If you wish to implement your own scope, you can do it by implementing Scope interface. Read more about it in Creating Scope section.

Singleton scope (default)โ€‹

When you define a bean with the singleton scope, the container creates a single instance of that bean, all requests for that bean will return the same object, which is cached. Any modifications to the object will be reflected in all references to the bean. This scope is the default value if no other scope is specified.

class Foo {
name = 'foo';
}

class Bar {
constructor(public foo: Foo) {}

setName(): void {
this.foo.name = 'bar';
}
}

class Baz {
constructor(public foo: Foo) {}
}

@ClawjectApplication
class Application {
foo = Bean(Foo);

bar = Bean(Bar);
baz = Bean(Baz);

@PostConstruct
postConstruct(
bar: Bar,
baz: Baz,
) {
bar.setName();

console.log(bar.foo === baz.foo); // <- prints "true"

console.log(bar.foo.property); // <- prints "bar"
console.log(baz.foo.property); // <- prints "bar"
}
}

Transient scopeโ€‹

A Bean with the transient scope will return a different instance every time it is requested from the container.

warning

Methods decorated with @PreDestroy will not be called for beans with transient scope.

class Foo {
name = 'foo';
}

class Bar {
constructor(public foo: Foo) {}

setName(): void {
this.foo.name = 'bar';
}
}

class Baz {
constructor(public foo: Foo) {}
}

@ClawjectApplication
class Application {
@Scope("transient")
foo = Bean(Foo);

bar = Bean(Bar);
baz = Bean(Baz);

@PostConstruct
postConstruct(
bar: Bar,
baz: Baz,
) {
bar.setName();

console.log(bar.foo === baz.foo); // <- prints "false"

console.log(bar.foo.property); // <- prints "bar"
console.log(baz.foo.property); // <- prints "foo"
}
}

Scope on @Configuration classโ€‹

When you put @Scope decorator over the @Configuration or @ClawjectApplication class, it indicates that all the beans that are declared in context should have scope defined in decorator.

Let's have a look here:

class Foo {
constructor() {
console.log('Foo has been created');
}
}

@ClawjectApplication
@Scope('transient')
class Application {
foo = Bean(Foo);

@PostConstruct
postConstruct(
foo1: Foo, // <- logs "Foo has been created"
foo2: Foo, // <- logs "Foo has been created",
) {}
}

As we see, foo bean will be created twice, because it's requested 2 times and it inherits transient scope from context.

Scope on Beansโ€‹

We can also put @Scope decorator over the bean declaration.

class Foo {
@PostConstruct
postConstruct() {
console.log('Foo bean has been created');
}
}

@ClawjectApplication
class Application {
@Scope('transient') foo = Bean(Foo);

@PostConstruct
postConstruct(
foo1: Foo, // <- logs "Foo bean has been created"
foo2: Foo, // <- logs "Foo bean has been created",
) {}
}

As you can see - we've defined scope on foo bean, and foo was instantiated two times.

Combining @Scope on @Configuration and Beanโ€‹

We can also combine @Scope decorator @Configuration and Bean. If Bean is decorated with @Scope, it overrides the @Scope on @Configuration level for this bean, if not - value from @Configuration is used.

class Foo {
@PostConstruct
postConstruct() {
console.log('Foo bean has been created');
}
}
class Bar {
@PostConstruct
postConstruct() {
console.log('Bar bean has been created');
}
}

@ClawjectApplication
@Scope('transient')
class Application {
foo = Bean(Foo);
@Scope('singleton') bar = Bean(Bar);

@PostConstruct
postConstruct(
foo1: Foo, // <- logs "Foo bean has been created"
foo2: Foo, // <- logs "Foo bean has been created",
bar1: Bar, // <- logs "Bar bean has been created"
bar2: Bar, // <- logs nothing, because Bar is a singleton
) {}
}