IOC之abstract抽象Bean
实验19:通过abstract属性创建一个模板bean
abstract="true"属性可以设置一个bean对象的配置只是配置信息,而不能被初始化。给其他bean做继承之用。
修改applicationContext.xml配置中的内容
<!-- 先定义一个base的 person对象 --> <bean id="base" class="com.xypuxing.pojo.Person" p:name="basePerson"p:age="18"p:id="12312"abstract="true"/>
<!-- 然后在另一个bean中。使用parent继承所有的 -->
<bean id="person20" class="com.xypuxing.pojo.Person" parent="base"p:id="20"/>
常见错误:abstract="true"的bean不能被创建
IOC之组件创建顺序
实验20:bean之间的依赖
创建三个Bean
publicclass A {
public A() {
System.out.println("创建A");
}
}
publicclass B {
public B() {
System.out.println("创建B");
}
}
publicclass C {
public C() {
System.out.println("创建C");
}
}
applicationContext.xml中的配置
<beanid="a"class="com.xypuxing.pojo.A"/>
<beanid="b"class="com.xypuxing.pojo.B"/>
<beanid="c"class="com.xypuxing.pojo.C"/>
执行ApplicationContextapplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");代码
执行结果,会发现。顺序情况下。Bean的创建顺序是他们在applicationContext.xml中从上到下的配置顺序。
运行结果:
我们可以在bean中添加depends-on来修改Bean的依赖。从而修改他们创建的顺序。
<!-- depends-on 表示依赖顺序,就是说,在创建a之前,要先有b和c。所以使得bean的创建顺序是先创建b和c之后再创建a -->
<beanid="a"class="com.xypuxing.pojo.A"depends-on="b,c"/>
<beanid="b"class="com.xypuxing.pojo.B"/>
<bean id="c" class="com.xypuxing.pojo.C" />
这个时候,再执行上面的测试,bean的创建顺序会发生变化
IOC之Bean的单例和多例(重点)
实验21:测试bean的作用域,分别创建单实例和多实例的bean★
前面我们讲到的bean对象都是单例,且在ApplicationContext创建的时候,就会被初始化实例。
其实我们可以通过修改Bean标签中的scope=""属性来修改对象的单例多例(生命范围)
scope的值可以有
singleton 单例 在applicationContext对象创建的时候都创建好。
prototype 多例
request 一次请求,一个对象实例(保存到Request域对象中)
session 一个Session,一个对象实例(保存到Session域对象中)
修改4.22实例中的三个bean配置
<!--
scope 属性,修改对象的存活范围
singleton表示全局只有一个 对象实例,并且在ApplicationContext对象创建的时候创建好。
prototype表示每次调用getBean的时候创建。
request表示一次请求,就会创建一个对象实例。
session表示一个会话,就会创建一个对象实例。
-->
<bean id="a" class="com.xypuxing.pojo.A" scope="singleton"/>
<bean id="b" class="com.xypuxing.pojo.B" scope="singleton"/>
<bean id="c" class="com.xypuxing.pojo.C" scope="prototype"/>
测试代码
@Test
publicvoid test21() {
ApplicationContext applicationContext = newClassPathXmlApplicationContext(
"applicationContext.xml");
System.out.println("application创建好之后*************");
System.out.println( applicationContext.getBean("c") );
System.out.println( applicationContext.getBean("c") );
}
运行的结果
基于xml配置文件的自动注入
自动装配是根据autowire配置的值决定有,byName根据名称装配。byType根据类型装配。constructor根据构造方法装配
先创建Person类和Car类
publicclass Car {
private String name;
publicclass Person {
private Car car;
public Person(Car car) {
this.car = car;
}
在applicationContext.xml中配置如下:
<bean id="car1" class="com.xypuxing.pojo.Car" p:name="这是Car实例1"/>
<bean id="car" class="com.xypuxing.pojo.Car" p:name="这是Car实例"/>
<!--
autowire自动注入(自动装配)
default 默认表示不装配
no 也表示不装配
byName 会自动的根据 属性名 找到 id跟属性名相同的 bean对象注入
byType 会自动的根据 属性的类型找到 同类型的bean对象注入。但是如果有多个就会报错。
constructor 会自动的先根据类型来查找。如果只有一个类型匹配就注入。
如果有多个类型匹配。就再次按照id来查找。如果匹配就注入。
-->
<bean id="person" class="com.xypuxing.pojo.Person" autowire="constructor"/>
测试的代码
@Test
publicvoid test22() {
ApplicationContext ctx= newClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person)ctx.getBean("person");
System.out.println( person );
}
运行结果
测试byName或byType可自行修改autowire的值进行测试。
如果在测试byType的时候,有多个相同的类型。就会报如下错误:
IOC之Bean的生命周期
实验22:创建带有生命周期方法的bean
给B类添加初始化方法和销毁方法
publicclass B {
public B() {
System.out.println("创建B");
}
publicvoid initB() {
System.out.println("初始化 B 对象……");
}
publicvoid destroyB() {
System.out.println("销毁 B 对象……");
}
}
applicationContext.xml中的配置
<!--
init-method="initB"表示当对象实例化之后,设置完属性值之后。就调用initB初始化方法
destroy-method="destroyB"表示当applicationContext关闭的时候就调用 destroyB 销毁方法
-->
<bean id="b" class="com.xypuxing.pojo.B" scope="singleton"init-method="initB"destroy-method="destroyB"/>
测试的代码
@Test
publicvoid test21() {
ConfigurableApplicationContextapplicationContext = newClassPathXmlApplicationContext(
"applicationContext.xml");
System.out.println("application创建好之后*************");
applicationContext.close();
}
Bean的后置处理器BeanPostProcessor
实验23:测试bean的后置处理器
编写一个类去实现BeanPostProcessor接口
publicclass MyBeanPostProcessor implements BeanPostProcessor {
/**
* 这是初始化之前执行
*/
@Override
public ObjectpostProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("在对象初始化之前……执行 ===== bean的名称是:" + beanName);
return bean;
}
/**
* 这是初始化之后执行
*/
@Override
public Object postProcessAfterInitialization(Objectbean, String beanName)
throws BeansException {
System.out.println("在对象初始化之后……执行 ===== bean的名称是:" + beanName);
return bean;
}
}
B类的代码
publicclass B {
public B() {
System.out.println("创建B");
}
publicvoid initB() {
System.out.println("初始化 B 对象……");
}
publicvoid destroyB() {
System.out.println("销毁 B 对象……");
}
}
新创建一个applicationContext.xml配置文件。
<!-- 配置一个b对象,并配置初始化方法和销毁方法 -->
<bean id="b" class="com.xypuxing.pojo.B" init-method="initB"destroy-method="destroyB"/>
<!-- 配置后置处理器对象 -->
<bean class="com.xypuxing.pojo.factory.MyBeanPostProcessor">
执行代码
@Test
publicvoid test22() {
ApplicationContextapplicationContext = newClassPathXmlApplicationContext(
"applicationContext.xml");
B b =(B) applicationContext.getBean("b");
System.out.println(b);
}
执行结果
Spring配置管理数据库连接池对象(重点)
首先在工程中导入jar包
在applicationContext.xml中配置数据库连接池对象
<!-- 配置C3p0数据库连接池 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root" />
<property name="password" value="root" />
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/xypuxing"/>
</bean>
测试获取数据库连接代码
@Test
publicvoid testDataSource() throws Exception {
ApplicationContextapplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");
DataSourcedataSource = (DataSource) applicationContext.getBean("c3p0DataSource");
System.out.println(dataSource.getConnection());
}
运行结果
Spring引入单独的jdbc.properties配置文件(重点)
实验24:引用外部属性配置文件★
在src目录下创建一个jdbc.properties属性配置文件
user=root
password=root
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/xypuxing
注意:一定要记得修改你自己的数据库连接参数
在applicationContext.xml配置文件中使用org.springframework.beans.factory.config.PropertyPlaceholderConfigurer进行加载配置
<!-- 实际情况,记得根据自己的情况==修改jdbc.properties中的属性信息。 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"p:location="classpath:jdbc.properties"/>
<!-- 配置C3p0数据库连接池 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close">
<property name="user" value="${user}" />
<property name="password" value="${password}" />
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${url}" />
</bean>
测试的代码
@Test
publicvoid testDataSource() throws Exception {
ApplicationContext applicationContext= newClassPathXmlApplicationContext(
"applicationContext.xml");
DataSourcedataSource = (DataSource) applicationContext.getBean("c3p0DataSource");
System.out.println(dataSource.getConnection());
}
测试结果
使用context名称空间加载jdbc.properties配置文件(重点)
在新的Spring版本中,都优先使用Context名称空间加载属性配置文件
修改applicationContext.xml中的名称空间,添加context名称空间
修改原来的jdbc.properties配置文件,添加jdbc前缀
jdbc.username=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xypuxing
修改applicationContext.xml配置文件中的代码
<!-- 使用context名称空间,加载jdbc.properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3p0数据库连接池 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close">
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}" />
</bean>
6、Spring EL表达式(了解内容)
创建java实体Bean对象
publicclass Person {
privateintid;
private String name;
private String phone;
privatedoublesalary;
private Car car;
publicclass Car {
private String name;
private String carNo;
实验26:[SpEL测试I]在SpEL中使用字面量
使用格式:#{数值} #{“字符串” || ‘字符串’}
实验27:[SpEL测试II]在SpEL中引用其他bean
使用格式:#{bean的id}
实验28:[SpEL测试III]在SpEL中引用其他bean的某个属性值
使用格式: #{bean.属性名}
实验29:[SpEL测试IV]在SpEL中调用非静态方法
使用格式: #{bean.方法名(参数)}
实验30:[SpEL测试V]在SpEL中调用静态方法
使用格式:#{T(全名类).方法名(参数)}
实验31:[SpEL测试VI]在SpEL中使用运算符
使用格式:#{表达式}
在applicationContext.xml配置文件中的内容:
<bean id="car" class="com.xypuxing.pojo.Car">
<property name="name" value="宝马"></property>
<property name="carNo" value="京A8888"></property>
</bean>
<bean id="person" class="com.xypuxing.pojo.Person">
<!-- 实验26:[SpEL测试I]在SpEL中使用字面量 -->
<property name="id" value="#{88}" />
<!-- <propertyname="name" value="#{'这是Spring EL注入的值'}" /> -->
<!-- 实验29:[SpEL测试IV]在SpEL中调用非静态方法
<propertyname="name" value="#{person.getUUID()}" />
-->
<!-- 实验30:[SpEL测试V]在SpEL中调用静态方法-->
<property name="name" value="#{T(com.xypuxing.pojo.Person).getUUID2()}"/>
<!-- 实验28:[SpEL测试III]在SpEL中引用其他bean的某个属性值 -->
<property name="phone" value="#{car.carNo}" />
<!-- 实验31:[SpEL测试VI]在SpEL中使用运算符 -->
<property name="salary" value="#{88 * 100}" />
<!-- 实验27:[SpEL测试II]在SpEL中引用其他bean -->
<property name="car" value="#{car}" />
</bean>
测试的代码
@Test
publicvoid test22() {
ApplicationContext ctx= newClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person)ctx.getBean("person");
System.out.println( person );
}
运行结果
注解配置Dao、Service、Controller组件
实验32:通过注解分别创建Dao、Service、Controller★
Spring配置bean的常用注解有
@Controller 专门用来配置web层的 Controller
@Service 专门用来配置Service层的 Service
@Repository 专门用来配置Dao层 Dao
@Component 可以用来配置任意层 但一般如果明确是Dao、Service、Controller都会使用对应层的注解来进行注释
而且以上注解只是配置了bean对象,Spring底层不会去检查这个注解的bean是否是真正的Dao,或Service或Controller。
默认的注解功能只是注册一个bean。bean的id就是首字母小写的类名。scope是单例。
在配置的bean上使用注解@Scope("singleton")表示单例
在配置的bean上使用注解@Scope("prototype")表示多例
如果要修改bean的默认id值。可以在注解中加入一个字符串参数。使用如下:
使用的时候applicationContext.getBean(“id”); 即可以通过id查对象
在bean上使用注解,还需要使用一个aop的jar包。而且还需要使用Context名称空间进行扫描包
注解使用回顾:
1、使用@Service、@Controller、@Repository 在需要配置的bean上使用。
2、需要导入aop的jar包。
3、需要在applicationContext.xml配置文件中添加context的包扫描<context:component-scan base-package="com.xypuxing"/>
添加类和注解
@Repository
publicclass BookDao {
}
//@Service中加参数。是Bean的id名。
@Scope("prototype") // 表示多例
@Service("bs")
publicclass BookService {
}
@Controller
publicclass BookServlet {
}
给applicationContext.xml添加context名称空间
内容如下:
<!-- context:component-scan 是组件扫描
base-package是基础包,表示搜索指定包所有的类,以及子包下所有的类
resource-pattern资源的搜索规则
dao/*.class 表示搜索dao子包下的所有的类
*/*.class 表示搜索任意子包下的所有类
*/*Service.class 表示搜索任意子包下的某某Service.class
<context:component-scanbase-package="com.xypuxing"resource-pattern="*/*Service.class"/>
-->
<context:component-scan base-package="com.xypuxing" />
测试的代码如下:
@Test
publicvoid test1() {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService =(BookService) applicationContext.getBean("bs");
BookService bookService2 =(BookService) applicationContext.getBean("bs");
System.out.println(bookService ==bookService2);
}
测试代码如下:
指定扫描包时的过滤内容(了解内容)
实验33:使用context:include-filter指定扫描包时要包含的类
实验34:使用context:exclude-filter指定扫描包时不包含的类
<context:include-filter /> 设置包含的内容
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,
<context:exclude-filter /> 设置排除的内容
类别 | 示例 | 说明 |
annotation | com.xypuxing.XxxAnnotation | 过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤 |
assignable | com.xypuxing.BaseXxx | 过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 |
aspectj | com.xypuxing.*Service+ | 所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 |
regex | com\.xypuxing\.anno\.* | 所有com.xypuxing.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 |
custom | com.xypuxing.XxxTypeFilter | 使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口 |
applicationContext.xml 中配置的内容如下
<!--use-default-filters="false" 设置取消默认包含规则 -->
<context:component-scan base-package="com.xypuxing" use-default-filters="false">
<!-- context:include-filter 设置包含的内容-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<!-- context:exclude-filter 设置排除的内容-->
<context:exclude-filter type="assignable" expression="com.xypuxing.service.BookService"/>
</context:component-scan>
以上配置会包含所有@Service注解的类。排除com.xypuxing.service.BookService类
使用注解@Autowired自动装配
实验35:使用@Autowired注解实现根据类型实现自动装配★
@Autowired 注解会自动的根据标注的对象类型在Spring容器中查找相对应的类。如果找到,就自动装配。
使用@Autowired注解,不需要get/set方法
使用注解的几个类
@Repository
publicclass BookDao {
}
@Service
publicclass BookService {
@Autowired
private BookDao bookDao;
@Override
public String toString(){
return"BookService[bookDao=" + bookDao + "]";
}
}
@Controller
publicclass BookServlet {
@Autowired
private BookService bookService;
@Override
public String toString(){
return"BookServlet[bookService=" + bookService + "]";
}
}
applicationContext.xml中的配置
<context:component-scan base-package="com.xypuxing"/>
测试代码
@Test
publicvoid test3() {
ApplicationContext applicationContext= newClassPathXmlApplicationContext("applicationContext.xml");
System.out.println( applicationContext.getBean("bookServlet") );
}
运行的结果:
多个同类型的bean如何自动装配
实验36:如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配★
// @Autowired会自动的根据类型进行装配。
如果找到一个就直接装配,如果找不到,就报错
如果找到多个。就根据变量名和bean对象的id进行匹配。匹配上的进行装配。没有匹配上,就报错。
添加一个BookDaoExt类
@Repository
publicclass BookDaoExt extends BookDao{
}
@Repository
publicclass BookDao {
}
@Service
publicclass BookService {
// @Autowired会自动的根据类型进行装配。
// 如果找到一个就直接装配,如果找不到,就报错
// 如果找到多个。就根据变量名和bean对象的id进行匹配。匹配上的进行装配。没有匹配上,就报错。
@Autowired
private BookDao bookDaoExt;
@Override
public String toString(){
return"BookService[bookDao=" + bookDaoExt + "]";
}
}
测试的代码:
@Test
publicvoid test3() {
ApplicationContext applicationContext= newClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(applicationContext.getBean("bookServlet") );
}
运行的结果
使用@Qualifier装配指定id的bean对象
实验37:如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id★
修改两个BookDao的id名称
@Repository("bookDao1")
publicclass BookDao {
}
@Repository("bookDao2")
publicclass BookDaoExt extends BookDao {
}
@Service
publicclass BookService {
// @Autowired会自动的根据类型进行装配。
// 如果找到一个就直接装配,如果找不到,就报错
// 如果找到多个。就根据变量名和bean对象的id进行匹配。匹配上的进行装配。没有匹配上,就报错。
@Autowired
//根据指定id进行查找装配
@Qualifier("bookDao2")
private BookDao bookDaoExt;
}
applicationContext.xml中扫描的配置不变
测试的代码也不代。自行修改进行测试。
@Autowired注解的required属性作用
实验39:@Autowired注解的required属性指定某个属性允许不被设置
@Autowired(required=false)
//根据指定id进行查找装配
@Qualifier("bookDao2")
private BookDao bookDaoExt;
在@Autowired注解中,有一个required属性。这个属性的默认值是true。表示必须装配上值。如果没有装配成功就会报错。
如果我们把required属性的值设置为false。表示不是必须装配。也就是说。
当Spring按照类型查找没有装配成功。按照id查找也没有装配成功。则bookDaoExt的值为null。
@Autowired和@Qualifier在方法上的使用。
实验38:在方法的形参位置使用@Qualifier注解
准备需要Spring容器管理的bean
@Repository
publicclass BookDao {
}
@Repository
publicclass BookDaoExt extends BookDao {
}
@Service
publicclass BookService {
// @Autowired会自动的根据类型进行装配。
// 如果找到一个就直接装配,如果找不到,就报错
// 如果找到多个。就根据变量名和bean对象的id进行匹配。匹配上的进行装配。没有匹配上,就报错。
@Autowired(required = false)
// 根据指定id进行查找装配
@Qualifier("bookDao211")
private BookDao bookDaoExt;
public BookService() {
}
@Autowired
public BookService(BookDaobookDaoExt) {
System.out.println("这是构造 方法的注入---" + bookDaoExt);
}
@Autowired(required = false)
publicvoid setAAA(@Qualifier("bookDao") BookDaobookDaoExt) {
System.out.println("这是set方法的注入----" + bookDaoExt);
}
@Override
public String toString(){
return"BookService[bookDao=" + bookDaoExt + "]";
}
}
BookService类中注解说明
测试代码:
@Test
publicvoid test3() {
ApplicationContext applicationContext= newClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(applicationContext.getBean("bookServlet") );
}
泛型注入(了解内容)
实验40:测试泛型依赖注入★
准备好需要用的包
Spring核心jar包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
Spring的扫描需要的jar包
spring-aop-4.0.0.RELEASE.jar
日记相关jar包
commons-logging-1.1.3.jar
log4j-1.2.17.jar
Spring的测试jar测试
spring-test-4.0.0.RELEASE.jar 新的测试需要用到
创建需要的类
在applicationContext.xml配置文件中添加包的扫描
<context:component-scan base-package="com.xypuxing"/>
测试的代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
publicclass SpringTest {
@Autowired
BookService bookService;
@Autowired
UserService userService;
@Test
publicvoid test1() {
bookService.save();
System.out.println("==============");
userService.save();
}
}
泛型注入原理的分析图: