一.前言
如果说spring是一个生产商品的工厂,那么bean就是里面的商品.对商品的装配是整个流程的重要部分,那么我们从下面几个方面对bean有个更深层的理解
二.bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例.默认为singleton
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
三.bean的生命周期
bean的初始化与销毁的方法:
针对某个类使用
方法一.自行定义方法
<bean id="beanLifeCycle" class="com.eduask.chp.test.BeanLifeCycle" init-method="start" destroy-method="end"/>
在这里配置了两个方法,然后你在所在的类书写两个方法,如下所示
public class BeanLifeCycle {
public void start() {
System.out.println("bean start");
}
public void end() {
System.out.println("bean end");
}
}
方法二.实现两个接口,自动生成方法
public class BeanLifeCycle implements InitializingBean,DisposableBean{
public void destroy() throws Exception {
System.out.println("初始化");
}
public void afterPropertiesSet() throws Exception {
System.out.println("销毁");
}
}
xml配置这里就直接写这个
<bean id="beanLifeCycle" class="com.eduask.chp.test.BeanLifeCycle" />
配置全局默认
public class BeanLifeCycle {
public void defaultInit() {
System.out.println("默认方法 初始化");
}
public void defaultDestroy() {
System.out.println("默认方法 销毁");
}
}
四.spring awer接口
容器管理的Bean一般不需要了解容器的状态和直接使用容器,但是在某些情况下,是需要在Bean中直接对IOC容器进行操作的,这时候,就需要在Bean中设定对容器的感知。Spring IOC容器也提供了该功能,它是通过特定的Aware接口来完成的。aware接口有以下这些:
BeanNameAware,可以在Bean中得到它在IOC容器中的Bean的实例的名字。
BeanFactoryAware,可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务。
ApplicationContextAware,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用上下文的服务。
MessageSourceAware,在Bean中可以得到消息源。
ApplicationEventPublisherAware,在bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件。
ResourceLoaderAware,在Bean中可以得到ResourceLoader,从而在bean中使用ResourceLoader加载外部对应的Resource资源。
重点来讲下ApplicationContextAware 与BeanNameAware
当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。
其实之前在测试类也可以通过这种形式获取这个对象
ApplicationContext app=new ClassPathXmlApplicationContext("spring-bean.xml")
现在通过实现这个接口来获取对象 ,为了测试这两个是不是同一个对象,输出两个的hashcode值
public class AppContent implements ApplicationContextAware,BeanNameAware{
private static ApplicationContext appContext;
private String beanName;
public void setBeanName(String name) {
this.beanName=name;
System.out.println(name);
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.appContext=applicationContext;
System.out.println(appContext.getBean(this.beanName).hashCode());
}
}
单元测试
public class Junit {
static ApplicationContext app;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
app=new ClassPathXmlApplicationContext("spring-bean.xml");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
((AbstractApplicationContext) app).destroy();
}
@Test
public void test() {
System.out.println(app.getBean("appContent").hashCode());;
}
}
最后测试结果为两个都是同一个对象
五.bean的自动装配
上一篇写到spring注入,业务层跟dao层是通过这种途径的
<bean id="testService" class="com.eduask.chp.test.TestServiceImp">
<property name="testDao" ref="testDao"></property>
</bean>
<bean id="testDao" class="com.eduask.chp.test.TestDaoImp"/>
但是如果使用自动装配的话通过byName形式
在beans里加入这句话 default-autowire="byName",那么它就自动将两者绑定在一起了
constructor 是在要装配的对象调用另个对象的构造方法才能使用
六.spring Resource
为了获得文件资源的一些信息
Application 是继承ResourceLoader
示例:创建一个db.txt
测试代码
public class AppContent implements ApplicationContextAware{
private static ApplicationContext appContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.appContext=applicationContext;
Resource rs=appContext.getResource("classpath:db.txt");//这里面的地址,还可以写网址,写法:url:http://网址
try {
System.out.println(rs.getFilename()+"\n"+rs.contentLength());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
七.bean的定义及注解形式
针对下面的图上的内容进行详细讲解
1.类的自动检测与bean注册
<context:annotation-config>
该标签隐式的向Spring容器注册了下面四个BeanPostProcessor:
AutowiredAnnotationBeanPostProcessor @AutoWired
CommondAnnotationBeanPostProcessor @Resource、@PostConstruct、@Predestory等注解
PersistenceAnnotationBeanPostProcessor @PersistenceContext
RequiredAnnotationBeanPostProcessor. @Required
这四个是为了我们之后使用相应的注解准备的
<context:component-scan base-package=" ">
这个<context:component-scan>除了具有<context:annotation-config>的功能之外,<context:component-scan>还可以在指定的package下扫描以及注册javabean 。
所以工作开发中一般使用后者
2.bean的定义及其作用域
例子
首先建立一个BeanTest的测试类
package com.eduask.chp.bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope//作用域
@Component("bean")//自定义类的名字
public class BeanTest {
public void show() {
System.out.println("BeanTest:=="+this.hashCode());
}
}
然后在xml包扫描
context:component-scan base-package="com.eduask.chp.bean"></context:component-scan>
接着单元测试
@Test
public void test() {
BeanTest bean=(BeanTest) app.getBean("bean");//这里的bean是@Component("bean"),如果不定义那么就是类名的首字母小写
bean.show();
BeanTest bean1=(BeanTest) app.getBean("bean");
bean1.show();
进行两次创建bean对象,是为了测试scope的作用域,scope的默认是单例,也就是只创建一个容器,所以测试结果的hashcode是一样的,但是如果将scope后改为@Scope("prototype"),那么每次getBean就会创建一个新的实例
3.@Required注解
Spring 配置文件中 dependency-check 依赖检查的灵活性不够,并不能满足我们所有的需求
Spring还提供一种更加灵活的检查方式
@Required注解检查 但他只检查属性是否已经设置而不会测试属性是否非空
这个注解只能设置在set方法之上
4.@Autowired注解
也就是自动注入绑定,项目一般分dao数据库操作层,service业务层,mvc控制层,autowired可以方便的将三者联系在一起
实例:
dao层:
@Repository
public interface ApplyDao {
@Insert("insert into tb_invitejob values(null,#{name},#{sex},#{age},#{born},#{job},#{specialty},"
+ "#{experience},#{teachSchool},#{afterSchool},#{tel},#{address},#{createtime},#{content},#{isstock})")
public void addApply(Apply apply);//添加应聘信息
}
service层实现类:
@Service
public class ApplyServiceImp implements ApplyService {
@Autowired
private ApplyDao applyDao;
@Override
public void addApply(Apply apply) {
applyDao.addApply(apply);//添加应聘者信息
}
}
controller层
@Controller
@RequestMapping("/view")
public class ApplyController {
@Autowired
private ApplyService applyService;
@RequestMapping("/inviteJob.do")
public String addApply(Apply apply) {
applyService.addApply(apply);
return "main";
}
}
你会发现都有一个@Autowired,这个注解在包扫描的时候已经可以使用了
以上是注入到单个接口或者类中,它还能将类或接口注入到list和map中
@Autowired
private List<Service> list;//将service接口注入到list中
你可以在实现Service接口的类加@order()设置先后
@Autowired
private Map<String, Object>;//将service接口注入到map中
遍历的是继承这个接口的类
结合@Qualifier注解
由于有时候自动绑定的接口有多个实现类,这时候就要用@Qualfier()指定相应的接口
@Component
public class Invoker {
@Autowired
@Qualifier("oneService")//注意:开头首字母要小写
private Service service;
public void show() {
System.out.println(service.getClass().getName());
}
}
5.基于java的容器注解
完整实例:
store接口
public interface Store {
}
它的实现类StringStore
public class StringStore implements Store{
public void init() {
System.out.println("开始初始化");
}
public void destroy() {
System.out.println("开始销毁");
}
}
它的配置类StoreConfig
//配置bean
@Configuration
public class StoreConfig {
@Bean(name="store",initMethod="init",destroyMethod="destroy")//这里面是配置bean的id,初始化方法与销毁放方法
public Store getStringStore() {
return new StringStore();
}
}
注意:如果没指定name,那么它的bean id是方法名,而不是类名
如何读取数据库配置文件的内容?
JDBC.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/empmanager
jdbc.username=root
jdbc.password=root
xml形式配置:
<!--配置数据源 -->
<context:property-placeholder location="classpath:JDBC.properties"/>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}" />
</bean>
这里需要c3p0的所需jar包
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
6.基于泛型的自动装配
接口Store<T>
两个实现类StringStore,IntStore
7.@Resource注解
@Resource注解,@Autowired,@inject的区别:
@Autowire默认按照类型装配,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果我们想使用按照名称装配,可以结合@Qualifier注解一起使用;
@Resource默认按照名称装配,当找不到与名称匹配的bean才会按照类型装配,可以通过name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象.
JSR-330的@Inject 为了统一各种依赖注入框架的编程模型,JCP最近发布了java依赖注入规范,JCP将其称为JSR-330,@Inject注解是JSR-330的核心部件。该注解几乎可以完全替代Spring的@Autowired注解。所以除了使用Spring特定的@Autowired注解,我们可以选择@Inject。 和@Autowired一个,@Inject可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。因此@Inject注解注入的依赖关系必须存在,否则报异常。 与@Autowired一样,@Inject有自己限定的方法,即处理限定歧义性的依赖, 与@Named("xxName")一起用,可以指定具体的哪一个
注:实际上,@Named注解就是一个使用@Qualifier注解所标注的注解
注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖的对象时候,会回退到按照类型装配,但一旦指定了name属性,就只能按照名称装配了
8.关于初始化与销毁的注解
之前上面xml配置的时候已经说明了初始化与销毁,注解形式如下:
@Service
public class JrsServiceImp implements JrsService{
@Resource
private JrsDao jrsDao;
public void saveService() {
jrsDao.saveDao();
System.out.println("数据已保存");
}
@PostConstruct
public void init() {
System.out.println("初始化");
}
@PreDestroy
public void destroy() {
System.out.println("销毁");
}
}