Spring中创建Bean的几种方式
Spring框架下,Bean的创建和装配非常的灵活,提供了三种主要的方式,并且相互见可以互相看见,也就是你可以随意地采用你喜欢且合适的方式创建Bean,而不用担心他们之间的兼容问题。
一、使用XML显示配置Bean
在Spring框架最初的是,XML是最主要的配置方式。在XML中创建Bean需要使用<bean>元素,例如
<bean class= "man.BigMan" />
这是一个最简单的XML方式的Bean声明,仅指定了该Bean属于的类,那么它的名称是什么样的呢?这里有个自动命名的原则,即全限定类名来命名,如该bean的名称为“man.BigMan#0”,其中“#0”是一个计数,从0开始。
那我们可不可以指定bean的名称呢?当然是可以的,只需借助id属性即可,如:
<bean id= "bigMan" class= "man.BigMan" />
一般我们可以对那些需要引用的Bean指定名称,而不需要引用的,则采用自动命名即可。
二、自动创建Bean
Spring中Bean地自动创建和装配是通过Java的注解机制来实现的。
比如创建一个Bean,可以在类上添加一个@Component,来告诉Spring该类是一个组件类,这样Spring就会为其创建Bean。与之类似的还有@Service和@Constroller,其功能目前与@Component相同,不过通常@Service用在业务层,@Constroller用在控制层。
packet man;
import org.springframework.stereotypr.Component;
@Component(bigMan) ---> bigMan为自动创建Bean的名称。
public class BigMan{
private String name = "wang";
}
三、在Java中显示配置Bean
通过Java配置Bean,也是通过注解来实现的,首先要通过@Configuration注解声明一个配置类,该类中应该包含在spring中创建Bean的所有细节,如下:
packet man;
import org.springframework.context.annotation.Configuration;
public class BigManConfig{
}
创建配置类后,还需要创建一个方法,并为其添加一个@Bean的注解,来声明bean,如下:
@Bean(name="wang")
public BigMan bigMan(){
return BigMan();
}
该例子中,通过name属性为bean指定了名称,当没有指定name的时候,spring会为其自动分配名称为@Bean注解修改的方法的名称。
Spring中的bean
spring中通过xml配置管理bean
Spring容器中的bean具备不同的scope,最开始只有singleton和prototype,但是在2.0之后,又引入了三种类型:request、session和global session,不过这三种类型只能在Web应用中使用。
在定义bean的时候,可以通过指定<bean>的singleton或者scope属性来指定相应对象的scope,例如:
<bean id="testMock" class="org.test.javadu.TestMock" scope="prototype"/>
或者
<bean id="testMock" class="org.test.javadu.TestMock" singleton="false"/>
1. singleton
配置中的bean定义可以看作是一个模板,容器会根据这个模板来构造对象。bean定义中的scope语义会决定:容器将根据这个模板构造多少对象实例,又该让这个对象实例存活多久。标记为拥有singleton scope的对象定义,在Spring的IoC容器中只存在一个对象实例,所有该对象的引用都共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。
下图是Spring参考文档中给出的singleton的bean的实例化和注入语义示意图,或许更能形象得说明问题。
singleton scope
需要注意的是,不要将Spring中的singleton bean的概念和GoF中提出的Singleton模式混淆,二者的语义并不相同:Spring中的singleton scope是指在每个容器中只有一个bean的实例对象;GoF模式中的Singleton指的是在同一个classloader中只有某个Singleton类的一个实例对象。
Spring中的bean默认是singleton的,下面这两种写法的效果相同:
<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
2. prototype
针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的实例对象给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继的声明周期的管理工作,例如该对象的销毁。也就是说,容器每次返回给请求方一个新的实例对象后,就任由这个对象“自生自灭”了。
对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己专有的一个对象实例。通常,声明为prototype的对象都是一些有状态的,比如保存每个顾客信息的对象。
从Spring参考文档下的这幅图片,可以再次了解prototype scope的bean定义,在实例化对象和注入依赖的时候,它的具体语义是什么样子。
prototype scope
用以下两个ban定义的效果是一样的:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>
实践教训
今天写这个主题的原因是:在最近的项目中由于对于singleton类型的对象没有理解透彻,导致每日定时任务的统计数据出错。
具体描述如下:每天我需要跑一个定时任务,该任务使用多线程方式去执行,每个线程都需要往一个统一的结果集中写数据,基本的代码结构如下:
public class StatisticsTaskManager {
private static final Logger logger = LoggerFactory.getLogger(OdsManager.class);
private ConcurrentHashMap<Long, StatisticsBean> resultMap = new ConcurrentHashMap<>();
/*对外暴露的统计接口*/
public List<StatisticsBean> getOdsStatisticDataFromUtc() {
ExecutorService pool = Executors.newFixedThreadPool(80);
for (int dbIndex = 0; dbIndex < Constants.DB_NUM; dbIndex++) {
for (int tableIndex = 0; tableIndex < Constants.TABLE_NUM; tableIndex++) {
DealUidRunnable thread = new DealUidRunnable(dbIndex, tableIndex);
pool.execute(thread);
}
}
pool.shutdown();
try {
pool.awaitTermination(6, TimeUnit.HOURS);
} catch (InterruptedException e) {
logger.error("error:", e);
}
logger.info("finished! num={}", resultMap.size());
//resultMap.clear();
return convertMapToList();
}
/*具体的任务执行过程*/
这里会修改resultMap
}
在上述代码片段中,每个线程会判断resultMap中是否有key存在,如果存在则更新对应的bean,如果不存在则新建一个bean。这就要求在每个任务开始执行前,这个resultMap是空的,但是我没有意识到这个resultMap仅仅随着StatisticsTaskManager对象的生成而仅仅初始化一次,后续会作为singleton对象存在于容器中。
修改也非常简单,就是在当天的定时任务执行完之后,调用resultMap.clear()
将结果map中的数据清除即可。
一.spring mvc的特点
- M 代表 模型(Model)
模型是什么呢? 模型就是数据,就是 dao,bean - V 代表 视图(View)
视图是什么呢? 就是网页, JSP,用来展示模型中的数据 - C 代表 控制器(controller)
控制器是什么? 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。
1.他是基于组件技术的,全部是应用对象,无论是控制器或视图,还是业务对象的类都是java组件,并且和spring提供的其他基础结构紧密集成.
2.不依赖于servlet Api
3.可以使用任意一种视图技术,不仅仅是jsp.
4.支持各种请求资源的映射策略.
5.是易于扩展的.
二.spring mvc的工作流程
1、前端控制器DispatcherServlet(不需要程序员开发)
作用接收请求,响应结果,相当于转发器,中央处理器。
有了DispatcherServlet减少了其它组件之间的耦合度。
2、处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的url查找Handler
3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
4、处理器Handler(需要程序员开发, 类似代码中写的Controller)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
返回ModelAndView
5、视图解析器View resolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
返回View
6、视图View(需要程序员开发, jsp)
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)
填充模型数据
三.spring mvc与strtus的区别
1.spring mvc的入口是一个servlet(即前端控制器),而strtus的入口是一个filter过滤器
2.spring mvc是基于方法开发的(即一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或者多例模式(建议单例),strtus是基于类开发的,传递参数通过类的属性,所以只能设计程多例.
Spring MVC 的架构
为解决持久层中一直未处理好的数据库事务的编程,又为了迎合 NoSQL 的强势崛起,Spring MVC 给出了方案:
传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。在 Service 下可以通过 Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL ,这样就能够满足异军突起的 NoSQL 的使用了,它可以大大提高互联网系统的性能。
- 特点:
结构松散,几乎可以在 Spring MVC 中使用各类视图
松耦合,各个模块分离
与 Spring 无缝集成