什么是Bean呢?
- Swoft中Bean是一个类的对象实例
- 每个被Swoft管理的PHP对象称之为Bean
- Swoft提供了一个IoC容器来初始化对象和获取对象,用来解决对象之间的依赖管理。
以前当我们要调用一个类的时候是需要先使用new
关键字对类进行实例化后才能使用,现在有了Bean,当Swoft启动的时候就给实例化并一直存在,当你使用时不再需要再去new
,这样可以节省资源。
以JavaBean为例:JavaBean是一种Java语言编写的可重用组件,为了编写JavaBean类必须是具体的和公共的,并且具有无参构造器。JavaBean通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,并通过
set
和get
方法获取。总所周知,属性名符合这种模式,其它Java类可以通过自省(反射)机制发现和操作这些JavaBean的属性。
什么是IoC容器呢?
Swoft为应用提供了一个完整的IoC容器作为依赖管理方案,是Swoft AOP功能、RPC模块等功能的实现基础。
如果对IoC容器不是非常理解的话,请参见《IoC控制反转》。
IoC容器主要解决了三个问题:
- 避免手工管理对象之间的依赖嵌套
- 对象的依赖关系不再在编译期间确定,提供了运行起改变行为的更多弹性。
- 对象可以不再依赖具体实现,而是依赖抽象的接口或是抽象类实现。
什么是Bean容器呢?
容器是一个巨大的工厂,用来存放和管理Bean生命周期。
Bean定义
Bean有两种定义方式:注解、数组配置
- 通过数组定义
- 通过注解定义:注解定义使用PHP文档注解,在类上做标记,通过解析类注解,实现不同的功能。
例如:中间件的定义中
$ vim /app/Middlewares/ActionTestMiddleware.php
<?php
namespace App\Middlewares;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Swoft\Bean\Annotation\Bean;
use Swoft\Http\Message\Middleware\MiddlewareInterface;
/**
* @Bean()
* @uses ActionTestMiddleware
*/
class ActionTestMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);
return $response->withAddedHeader('Middleware-Action-Test', 'success');
}
}
Bean通过类级别注解@Bean
定义,Bean定义后程序可以直接通过App::getBean()
获取一个Bean实例。
注解使用
@Bean 注解
命名空间:use Swoft\Bean\Annotation\Bean;
源码分析:
$ vim vendor/swoft/framework/src/Bean/Annotation/Bean.php
<?php
namespace Swoft\Bean\Annotation;
/**
* bean注解
*
* @Annotation
* @Target("CLASS")
* @uses Bean
*/
class Bean
{
/**
* bean名称
* @var string
*/
private $name = "";
/**
* bean类型
* @var int
*/
private $scope = Scope::SINGLETON;
/**
* referenced bean, default is null
* @var string
*/
private $ref = "";
/**
* Bean constructor.
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->name = $values['value'];
}
if (isset($values['name'])) {
$this->name = $values['name'];
}
if (isset($values['scope'])) {
$this->scope = $values['scope'];
}
if (isset($values['ref'])) {
$this->ref = $values['ref'];
}
}
/**
* 获取bean名称
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* 获取bean类型
* @return int
*/
public function getScope()
{
return $this->scope;
}
/**
* return name of referenced bean
* @return string
*/
public function getRef(): string
{
return $this->ref;
}
}
打开源码,可以看到Bean构造函数可传入一个关联数组的参数。
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->name = $values['value'];
}
if (isset($values['name'])) {
$this->name = $values['name'];
}
if (isset($values['scope'])) {
$this->scope = $values['scope'];
}
if (isset($values['ref'])) {
$this->ref = $values['ref'];
}
}
构造器参数
name
定义Bean别名,缺省默认类名。scope
注入Bean类型,默认单例Scope::SINGLETON/Scope::PROTOTYPE
。ref
指定引用Bean,用于定义在接口上指定使用哪个接口实现。
使用注意
@Bean
里面只能使用双引号@Bean()
是指注入的Bean名称使用包含命名空间的类名。@Bean("userModel")
和@Bean(name="userModel")
含义是一样的@Bean(name="beanName", scope=Scope::SINGLETON)
默认注入的Bean都是单例,使用scope属性设置其类型。
@Inject
命名空间:use Swoft\Bean\Annotation\Inject;
源码分析:
$ vim vendor/swoft/framework/src/Bean/Annotation/Inject.php
<?php
namespace Swoft\Bean\Annotation;
/**
* inject注解
*
* @Annotation
* @Target({"PROPERTY","METHOD"})
*
* @uses Inject
*/
class Inject
{
/**
* 注入bean名称
* @var string
*/
private $name = "";
/**
* Inject constructor.
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->name = $values['value'];
}
if (isset($values['name'])) {
$this->name = $values['name'];
}
}
/**
* 获取bean名称
* @return string
*/
public function getName(): string
{
return $this->name;
}
}
构造函数:
name
定义属性注入的Bean名称,缺省属性自动为类型名称。
使用注意:
@Inject
格式与@Bean
基本一样,注意通过注解目前不支持构造函数参数注入。@Inject()
默认注入该属性,对应的包含命名空间的类型名Bean。@Inject(name="${logger}")
和@Inject("${logger}")
注入名称为logger
的Bean到属性@Inject(name="${config.user.stelin.steln}")
注入properties
中配置的值,可以层级和直接直接。
使用示例:
/**
* 别名注入.
* @Inject("httpRouter")
* @var \Swoft\Http\Server\Router\HandlerMapping
*/
private $router;
/**
* 别名注入.
* @Inject("application")
* @var Application
*/
private $application;
/**
* 注入逻辑层
* @Inject()
* @var IndexLogic
*/
private $logic;
操作Bean
App::getBean()
提供服务定位器式的依赖管理方式,用于可以通过访问服务定位器获取特定的实例,服务定位器解决了“实例构造,实例间依赖管理,具体实现类选择”的问题,并对用户屏蔽相关细节。
Container->set()
方法是App::getBean()
底层实际创建Bean的方法,原理是通过反射和各种注解提供的信息和方法构造Bean的一个代理对象。
use Swoft\App;
App::getBean("name");
//例如
$logic = App::getBean(UserLogic::class);
use Swoft\Core\ApplicationContext;
ApplicationContext::getBean('name');
use Swoft\Bean\BeanFactory;
// App/ApplicationContext/BeanFactory都可从容器中得到Bean
BeanFactory::getBean('name');
use Swoft\Bean\BeanFactory;
// hasBean 某个bean是否存在
BeanFactory::hasBean("name");