![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e6feaeb0f4456c88e4faf564bd1d2cd4.png)
前一篇 博客之后,HelloWorld这个信仰已经可以完成了。IOC容器是Spring的重要组成,下面我们来详细了解下在spring IOC(Inversion of Control)容器(也可以说依赖注入DI(Dependency Injection)容器)里配置Bean。
☞机制理解:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,传统方式通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为 控制反转IOC。创建被调用者,实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为 依赖注入DI。
值得注意的是:
spring IOC容器只有实例化之后,才可以读取Bean配置创建Bean实例(即对象),以供程序获取/注入该Bean实例并使用
//1.实例化Spring的IOC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("day1hello.xml");
//2.从IOC容器中获取Bean实例
Hello hello = (Hello) ctx.getBean("hello");
//Hello hello = ctx.getBean(Hello.class); //若容器中有多个同类的Bean则该方式会报错
//3.使用bean
hello.hello();
//4.关闭容器
ctx.close();
1. Bean配置
通过全类名反射(得提供一个空参构造器)
开始之前先说一下IDEA复制粘贴全类名的快捷键:ctrl+shift+alt+c/v
1.1 xml方式
day1hello.xml
<!-- 配置一个 bean -->
<bean id="hello" class="xu.day1.hello.Hello">
<!-- 为属性赋值 -->
<!-- 通过属性注入: 利用空参构造器创建实例,再通过setter方法注入属性值 故这里需提供一个空参构造器 -->
<property name="name" value="zhangfei"></property>
</bean>
<!-- 通过构造器注入属性值 -->
<bean id="car2" class="xu.day1.hello.Car">
<!-- 要求: 在 Bean 中必须有对应的构造器.-->
<constructor-arg>
<value><![CDATA[<ATRZA>]]></value>
</constructor-arg>
<constructor-arg value="130" index=1></constructor-arg>
<constructor-arg value="3000" type="double"></constructor-arg>
</bean>
<!--<bean id="user2" class="xu.day1.hello.User">-->
<!--<property name="name" value="Rose"></property>-->
<!--<property name="car">-->
<!-- 内部 bean, 类似于匿名内部类对象. 不能被外部的 bean 来引用, 也没有必要设置 id 属性 -->
<!--<bean class="xu.day1.hello.Car">-->
<!--<constructor-arg value="baoma" index="0"></constructor-arg>-->
<!--<constructor-arg value="130" index="1"></constructor-arg>-->
<!--<constructor-arg value="3000" type="double"></constructor-arg>-->
<!--</bean>-->
<!--</property>-->
<!--</bean>-->
<!-- 声明集合类型的 bean -->
<util:list id="cars">
<ref bean="car1"></ref>
<ref bean="car2"></ref>
</util:list>
<bean id="user3" class="xu.day1.hello.User">
<property name="name" value="Tom"></property>
<!-- 引用外部声明的 list,通过 ref 属性值指定当前属性指向哪一个 bean! -->
<property name="cars" ref="cars"></property>
</bean>
<!--简洁的p命名空间配置-->
<bean id="user4" class="xu.day1.hello.User" p:name="xu" p:cars-ref="cars"></bean>
<!--使用 parent 来完成继承 -->
<bean id="user5" parent="user4" init-method="initMethod" destroy-method="destroyMethod"
p:name="li">
</bean>
<!--使用depents-on来指定前置依赖的Bean:前置依赖的bean会在本bean实例化之前创建好 -->
<!-- 配置 bean 后置处理器: 不需要配置 id 属性, IOC 容器会识别到他是一个 bean 后置处理器, 并调用其方法 -->
<bean class="xu.day1.hello.auto.myBeanPostProcessor"></bean>
<!-- 导入外部的资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- scope 属性来指定 bean 的作用域-->
<!--
prototype: 原型的. 每次调用 getBean 方法都会返回一个新的 bean. 且在第一次调用 getBean 方法时才创建实例
singleton: 单例的. 每次调用 getBean 方法都会返回同一个 bean. 且在 IOC 容器初始化时即创建 bean 的实例. 默认值
-->
<bean id="user6" class="xu.day1.hello.User" p:name="xu" p:cars-ref="cars" scope="prototype"></bean>
①②③④⑤⑥×✔✘☞☜√
☞注意:
- id:Bean的名称 ①在IOC容器里必须是唯一的②若id没有指定,将默认为类名首字母小写作为Bean 的名字
- 构造器注入时,为唯一匹配构造器的参数,可通过参数的index(从0开始),type分别限定(只能用其一)
- 若value中含特殊字符,可通过 <![CDATA[]]包裹
- 抽象bean:abstract属性为true的bean,这样的bean不能被IOC容器实例化,只用来继承配置。若某个bean的class属性没有指定,则该bean必须是抽象bean
<bean id="user9" p:name="zhangFei" p:cars-ref="cars" abstract="true"></bean>
- 生命周期:
默认单例下:IOC容器实例化后便自动执行Bean实例化(即调用构造器)-》将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization-》指定类中自定义的init-method-》将Bean实例传递给Bean后置处理器的postProcessAfterInitialization-》该Bean可以供程序获取了-》当容器关闭,调用类中自定义的destroy-method
若scope="prototype"则在第一次调用 getBean 方法时才创建实例(即调用构造器)
//后置处理器。BeanPostProcessor的实现类
public class myBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
if (s.equals("user5")) { //bean实例较多,故通过equals匹配指定的Bean名称即id,然后再进行对应操作
System.out.println("postProcessBeforeInit: "+s);
User user = (User) o;
user.setName("张");
}
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
if (s.equals("user5")) {
System.out.println(o+" :"+s);
}
return o;
}
}
其余的java类就不赘述,这里就暂且给出类成员结构
1.2 注解方式
机制理解:在base-package寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类注册为bean纳入进Spring容器中管理。
<!-- 配置自动扫描的包--> <!--可通过resource-pattern指定需要扫描的资源 这里的路径是相对于base-package-->
<context:component-scan base-package="xu.day1.hello.auto.annotation" resource-pattern="/*.class">
<!-- 子节点排除指定表达式的组件-->
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> -->
</context:component-scan>
☞注意:①②③④
①排除or包含指定的表达式组件。包含需将use-default-filters=‘false’,而排除不用默认为true。注意type="annotation"为目标是否采取指定组件注解进行过滤,type="assinable"目标是继承或扩展某个特定类进行过滤
xu.day1.hello.auto.annotation下的java类
@Component
public class Bike {
...
}
@Repository("userDao") //通过value指定组件名称/bean名称userDao 默认是类名首字母小写
public class UserDao implements BaseDao<Customer> {
...
}
@Service("userService")
public class UserSerivce implements BaseService<Customer> {
@Autowired //默认根据类型匹配自动装配注入:将IOC容器里对应类型的Bean实例给该变量
//@Qualifier("Bean名称") //属性装配指定Bean,适用于多个同类型bean的自动装配
private Bike bike;
@Autowired
private UserDao userDao;
...
}
public interface BaseDao<T> {
...
}
public interface BaseService<T> {
...
}
public class Customer {
...
}
☞注意:
- 四大注解组件:@Repository:持久层组件如实现DAO、@Service:服务层组件如装配注入DAO、@Constroller:表现控制层组件如装配注入服务、@Component:基本注解组件,将普通类Bean实例化至IOC容器,想必调用的是空参构造器
- 若是类的属性含@Autowired自动装配,则该类的对象一定得是获取Bean实例的方式,否则new出来该自动装配属性为null
- @Autowired遇到多个同类型的Bean,则需@Qualifier(“Bean名称”)
- 若某一属性允许不被设置,则可@Autowired(required=false)
1.3 工厂方法注入(不推荐):可以通过实现FactoryBean,也可以自己创建工厂,然后配置Bean。由于不常用,这里就不赘述了