1.通过BeanPostProcessor来自定义bean
(1) BeanPostProcessor用于在容器完成了对bean的实例化,配置及初始化后来实现一些自定义逻辑,它是用于操纵由容器创建的每个bean实例的,即在容器实例化了一个bean后以及该bean的初始化回调(如InitializingBean.afterPropertiesSet()等)被执行之前,会将这个bean交由BeanPostProcessor来进行处理。通过BeanPostProcessor,我们可以对bean实例进行任何操作,包括忽略掉后续的初始化回调等,BeanPostProcessor通常用来检查回调接口,或用来生成某个bean的代理对象,因此Spring AOP的实现基础就是BeanPostProcessor,如下是简单使用BeanPostProcessor的一个例子
//让ExampleA实现3个初始化回调
public class ExampleA implements InitializingBean {
private String name;
public ExampleA() {
System.out.println("ExampleA的构造方法被调用");
System.out.println("----------------------------------------");
}
//这个方法只用于IOC的属性注入
public void setName(String name) {
System.out.println("IOC对ExampleA的name属性进行注入,值为:" + name);
System.out.println("----------------------------------------");
this.name = name;
}
//这个方法用于我们自己手动注入
public void setNameInOtherWay(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "ExampleA{" +
"name='" + name + '\'' +
'}';
}
@PostConstruct
public void postConstruct() {
System.out.println("正在执行初始化回调PostConstruct,此时的ExampleA为:" + this);
this.setNameInOtherWay("zzz2");
System.out.println("执行完毕,此时的ExampleA为:" + this);
System.out.println("----------------------------------------");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("正在执行初始化回调InitializingBean.afterPropertiesSet,此时的ExampleA为:" + this);
this.setNameInOtherWay("zzz3");
System.out.println("执行完毕,此时的ExampleA为:" + this);
System.out.println("----------------------------------------");
}
public void init() {
System.out.println("正在执行初始化回调init-method,此时的ExampleA为:" + this);
this.setNameInOtherWay("zzz4");
System.out.println("执行完毕,此时的ExampleA为:" + this);
System.out.println("----------------------------------------");
}
}
//实现BeanPostProcessor,自定义后置处理器来操纵bean实例(注意:需要把我们的自定义处理器注入到容器中),它主要提供了2个方法
public class Processor implements BeanPostProcessor {
/**
* 该方法作用于bean实例创建配置好后,初始化回调执行前,来自定义一些逻辑
* @param bean 实例化并配置好后的bean
* @param beanName bean的名称
* @return 自定义操作完成后的bean
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("正在执行postProcessBeforeInitialization,此时的ExampleA为:" + bean);
((ExampleA) bean).setNameInOtherWay("zzz1");
System.out.println("执行完毕,此时的ExampleA为:" + bean);
System.out.println("----------------------------------------");
return bean;
}
/**
* 该方法作用于初始化回调执行后
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("正在执行postProcessAfterInitialization,此时的ExampleA为:" + bean);
((ExampleA) bean).setNameInOtherWay("zzz5");
System.out.println("执行完毕,此时的ExampleA为:" + bean);
System.out.println("----------------------------------------");
return bean;
}
}
<!-- xml配置文件 -->
<beans ....>
<!-- 开启注解扫描,否则@PostConstruct注解不生效 -->
<context:annotation-config></context:annotation-config>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA" init-method="init">
<property name="name" value="zzz"></property>
</bean>
<!-- 注意仅仅实现接口是不行的,我们必须把自定义后置处理器注册成bean,它才会生效 -->
<bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>
//启动容器后,打印结果为
ExampleA的构造方法被调用
----------------------------------------
IOC对ExampleA的name属性进行注入,值为:zzz
----------------------------------------
正在执行postProcessBeforeInitialization,此时的ExampleA为:ExampleA{name='zzz'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz1'}
----------------------------------------
正在执行初始化回调PostConstruct,此时的ExampleA为:ExampleA{name='zzz1'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz2'}
----------------------------------------
正在执行初始化回调InitializingBean.afterPropertiesSet,此时的ExampleA为:ExampleA{name='zzz2'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz3'}
----------------------------------------
正在执行初始化回调init-method,此时的ExampleA为:ExampleA{name='zzz3'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz4'}
----------------------------------------
正在执行postProcessAfterInitialization,此时的ExampleA为:ExampleA{name='zzz4'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz5'}
----------------------------------------
综上可见,添加了BeanPostProcessor后,bean的初始化流程为:执行bean的构造函数 -> IOC进行属性注入 -> BeanPostProcessor.postProcessBeforeInitialization -> 三大初始化回调 -> BeanPostProcessor.postProcessAfterInitialization
(2) 我们可以向容器中注入多个自定义BeanPostProcessor,并通过实现Ordered接口来控制这些BeanPostProcessor的执行顺序,如下所示
public class ExampleA { }
//让该自定义后置处理器实现Ordered接口,指定它在所有自定义后置处理器中的执行顺序
public class Processor0 implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Order值为0的postProcessBeforeInitialization执行...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Order值为0的postProcessAfterInitialization执行...");
return bean;
}
@Override
public int getOrder() {
return 0;
}
}
public class Processor1 implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Order值为1的postProcessBeforeInitialization执行...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Order值为1的postProcessAfterInitialization执行...");
return bean;
}
@Override
public int getOrder() {
return 1;
}
}
<!-- xml配置文件 -->
<beans ....>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<bean id="processor0" class="cn.example.spring.boke.Processor0"></bean>
<bean id="processor1" class="cn.example.spring.boke.Processor1"></bean>
</beans>
//启动容器,输出如下
Order值为0的postProcessBeforeInitialization执行...
Order值为1的postProcessBeforeInitialization执行...
Order值为0的postProcessAfterInitialization执行...
Order值为1的postProcessAfterInitialization执行...
由上可见,getOrder返回值越小,自定义的后置处理器就越先执行,同时也可以发现,post-processor bean全部作用于普通bean,即一个post-processor bean不会作用于容器中的另一个post-processor bean
(3) IOC容器会自动检测容器中的post-processor bean并提前实例化,以作用于容器中的其他bean,当我们向一个容器中注入了一个BeanPostProcessor,那么该BeanPostProcessor仅对该容器中的bean进行后置处理,例如,即父容器中的BeanPostProcessor不会作用于子容器中的bean
(4) BeanPostProcessor作用于bean实例化并配置好了之后,换句话说,在BeanPostProcessor起作用时,bean实例已经存在了,因此,如果我们想要修改bean的配置元数据(即BeanDefinition,此时的bean还未被创建),则需要实现BeanFactoryPostProcessor接口,它与BeanPostProcessor类似,只不过作用时机不同
(5) 可通过容器ConfigurableBeanFactory中的addBeanPostProcessor方法编程式的向容器注册一个BeanPostProcessor实例,但需要注意,通过该方法注入的BeanPostProcessor实例不会遵守Ordered接口,即如果是编程式的向容器中注册BeanPostProcessor实例,那么其注册的顺序就是后置处理器实际执行的顺序,如下所示
<!-- 其余不变,修改xml文件如下 -->
<beans ...>
<!-- 注意这里设置了懒加载属性,因为我们是要编程式的添加BeanPostProcessor,故而要先创建容器,再调用容器的addBeanPostProcessor方法,但singleton bean会随容器创建而被创建,此时addBeanPostProcessor方法还未被执行,因此BeanPostProcessor就无法作用于这些提前创建bean了,所以我们设置为懒加载或不用singleton作用域 -->
<bean id="exampleA" class="com.lb.spring5.ExampleA" lazy-init="true"></bean>
</beans>
//测试,先注册Processor1,再注册Processor0
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBeanFactory().addBeanPostProcessor(new Processor1());
ctx.getBeanFactory().addBeanPostProcessor(new Processor0());
ctx.getBean(ExampleA.class);
//打印结果,可以发现先注册的BeanPostProcessor先执行
Order值为1的postProcessBeforeInitialization执行...
Order值为0的postProcessBeforeInitialization执行...
Order值为1的postProcessAfterInitialization执行...
Order值为0的postProcessAfterInitialization执行...
(6) BeanPostProcessor与自动代理
在Spring官方文档中,提到了这么一句话:Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference are eligible for auto-proxying and, thus, do not have aspects woven into them. 这句话的大意就是AOP是基于BeanPostProcessor实现的,因此我们无法对BeanPostProcessor实例及其依赖的bean进行自动代理,织入通知,如下例所示
//定义一个切面MyAspect,对下面的BeanPostProcessor实例进行织入
public class MyAspect {
// 前置通知
public void before(){
System.out.println("before...");
}
}
//定义一个Bean后置处理器Processor,在该处理器中我们定义了一个方法aop,作为切入点,进行织入
public class Processor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public int getOrder() {
return 0;
}
//切入点
public void aop() {
System.out.println("Processor aop...");
}
}
<!-- xml配置文件 -->
<beans ....>
<!-- 切面 -->
<bean id="myAspect" class="cn.example.spring.boke.MyAspect"></bean>
<!-- aop配置,对指定方法进行切入 -->
<aop:config>
<aop:aspect id="advice" ref="myAspect">
<!-- 声明前置通知 -->
<aop:before method="before" pointcut="execution(public * cn.example.spring.boke.*.aop(..))"></aop:before>
</aop:aspect>
</aop:config>
<bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>
//测试
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBean(Processor.class).aop();
//控制台输出如下,可见前置通知before方法并未执行,此时aop失效
Processor aop...
//然后我们将上面的Processor类变更一下,使它成为一个普通的bean,如下
public class Processor {
public void aop() {
System.out.println("Processor aop...");
}
}
//此时,再启动容器,控制台打印如下,可见,如果是一个普通bean的话,aop是生效的
before...
Processor aop...
对BeanPostProcessor所依赖的bean,它的aop不会生效吗? 答案是不一定,有些情况下会生效,有些情况下不会生效,得结合实际情况具体分析,最好详看这一方面的源码,例子如下
//定义一个普通的bean exampleA,用于测试aop织入
public class ExampleA {
public void aop() {
System.out.println("ExampleA aop...");
}
}
//对于上面的例子,我们把Processor修改一下,让它依赖一个bean exampleA
public class Processor implements BeanPostProcessor,Ordered {
private ExampleA exampleA;
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public int getOrder() {
return 0;
}
}
<!-- xml配置文件 -->
<beans ....>
<!-- 切面保持不变 -->
<bean id="myAspect" class="cn.example.spring.boke.MyAspect"></bean>
<aop:config>
<aop:aspect id="advice" ref="myAspect">
<aop:before method="before" pointcut="execution(public * cn.example.spring.boke.*.aop(..))"></aop:before>
</aop:aspect>
</aop:config>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<!-- 通过自动装配机制,向容器中注入依赖项 -->
<bean id="processor" class="cn.example.spring.boke.Processor" autowire="byType"></bean>
</beans>
//测试
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBean(ExampleA.class).aop();
//打印结果如下,aop前置通知未能进行织入成功
ExampleA aop...
//此时,Spring也会给出一条提醒:Bean 'exampleA' of type [cn.example.spring.boke.ExampleA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying), 提示我们的Bean exampleA可能未被所有的BeanPostProcessor处理
//对于上面的Processor类,我们移除掉Ordered接口,只实现BeanPostProcessor接口,其他属性和方法保持不变
public class Processor implements BeanPostProcessor {
private ExampleA exampleA;
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
//这时,再次启动容器,观察控制台打印,虽然这次Spring也给出了一条关于exampleA可能未被所有的BeanPostProcessor处理的提醒,但可以发现aop前置通知成功执行,说明对BeanPostProcessor实例所依赖的bean,是可以进行自动代理的
before...
ExampleA aop...
总结一下: Spring官方文档,只是笼统的说明了一下:无法对BeanPostProcessor实例及其依赖的bean进行自动代理,织入通知. 然而实际上bean是否会被执行自动代理,与BeanPostProcessor何时被Spring处理有着很大联系,至于具体的机制,详见源码
2.通过BeanFactoryPostProcessor来自定义bean的配置元数据
(1) BeanFactoryPostProcessor用于读取容器中bean的配置元数据,并在这些bean被实例化之前来修改它们的配置元数据(此时bean还未被创建),同上面所提及的BeanPostProcessor,BeanFactoryPostProcessor也仅对它所属容器中的bean进行处理,同时我们也可以向容器中注入多个BeanFactoryPostProcessor实例并通过实现Ordered接口来规定它们的执行顺序,如下所示
//实现BeanFactoryPostProcessor接口,该接口提供了一个方法,用于获取配置BeanDefinition
public class Processor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//获取到exampleA的BeanDefinition,并为其name属性赋予值exampleAAA
configurableListableBeanFactory.getBeanDefinition("exampleA").getPropertyValues().addPropertyValue("name", "exampleAAA");
}
}
//普通的bean
public class ExampleA {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
<!-- xml配置文件 -->
<beans ....>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<!-- BeanFactoryPostProcessor实例也需要注入到容器中 -->
<bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>
//启动容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
System.out.println(ctx.getBean(ExampleA.class).getName());
//打印日志如下,可见BeanFactoryPostProcessor实例成功执行,向我们的bean中注入了属性值
exampleAAA
将上面的Processor类稍微变更一下,其它保持不变
public class Processor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//增添了一个getBean方法,获取一下容器中的bean exampleA
configurableListableBeanFactory.getBean("exampleA");
configurableListableBeanFactory.getBeanDefinition("exampleA").getPropertyValues().addPropertyValue("name", "exampleAAA");
}
}
//之后,我们重新启动容器,控制台打印如下,可见,此时属性注入失效,这是因为我们使用了getBean方法,导致容器过早的实例化了bean exampleA,从而违反了容器标准的生命周期,绕过了后置处理器
null
3.通过FactoryBean来自定义bean的实例化逻辑
(1) FactoryBean,即工厂Bean,使用了工厂模式,一般用于生产初始化过程很复杂的bean,从而避免编写冗杂的xml配置文件,如下例所示
//一个普通的bean
public class ExampleA {}
//实现FactoryBean接口,作为ExampleA的工厂,用于生成ExampleA实例,该接口主要提供了3个方法如下所示
public class ExampleAFactory implements FactoryBean {
/**
* 返回该工厂所生产的bean实例
*/
@Override
public Object getObject() throws Exception {
return new ExampleA();
}
/**
* 返回该工厂所生产的bean实例的类型
*/
@Override
public Class<?> getObjectType() {
return ExampleA.class;
}
/**
* 返回的bean实例是否为单例
*/
@Override
public boolean isSingleton() {
return true;
}
}
<!-- xml配置文件 -->
<beans ....>
<!-- 将工厂Bean注入进容器中 -->
<bean id="exampleAFactory" class="cn.example.spring.boke.ExampleAFactory"></bean>
</beans>
//启动容器,获取这个工厂Bean
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
System.out.println(ctx.getBean("exampleAFactory"));
//打印结果如下,我们可以发现,我们向容器中注入的是工厂Bean ExampleAFactory实例,结果getBean方法取到的不是这个工厂Bean,而是它所生产的ExampleA
cn.example.spring.boke.ExampleA@1a968a59
//当然,我们也可以获取到这个工厂Bean,而非它所生产的实例,如下,只需在它的name前加一个&符号即可
System.out.println(ctx.getBean("&exampleAFactory"));
//打印结果如下,可见这次我们获取到的是工厂Bean ExampleAFactory本身
cn.example.spring.boke.ExampleAFactory@1a968a59
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。
matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
这个问题有两个部分。在RubyProgrammingLanguage一书中,有一个使用模块扩展字符串对象和类的示例(第8.1.1节)。第一个问题。为什么如果您使用新方法扩展类,然后创建该类的对象/实例,则无法访问该方法?irb(main):001:0>moduleGreeter;defciao;"Ciao!";end;end=>nilirb(main):002:0>String.extend(Greeter)=>Stringirb(main):003:0>String.ciao=>"Ciao!"irb(main):004:0>x="foobar"=>"foobar"irb(main):