Spring学习——IOC控制反转、DI依赖注入(下)

一、bean的高级配置

1.1 配置信息的继承

指定parent属性为要重用的bean的id值,不写的属性就沿用,也可以重写定义属性

<!-- abstract="true":这个bean的配置是一个抽象的,不能获取他的实例,只能被别人用来继承 -->
<bean id="person08" class="com.zb.bean.Person" abstract="true">
	<property name="lastName" value="张三"></property>
	<property name="age" value="18"></property>
	<property name="gender" value=""></property>
	<property name="email" value="[email protected]"></property>
</bean>

<!--parent:指定当前bean的配置信息继承于哪个,继承后可以省略公共属性值的配置  -->
<bean id="person09" parent="person08">
	<property name="lastName" value="李四"></property>
</bean>

还可以指定属性abstract=“true”,这样的bean只能被用来继承信息,不能获取实例。否则会报异常 BeanIsAbstractException

<bean id="person08" class="com.zb.bean.Person" abstract="true">
	<property name="lastName" value="张三"></property>
	<property name="age" value="18"></property>
	<property name="gender" value=""></property>
	<property name="email" value="[email protected]"></property>
</bean>

1.2 bean之间的依赖

多个bean的默认创建顺序,是按照配置顺序创建的

<bean id="person" class="com.zb.bean.Person"></bean>
<bean id="car" class="com.zb.bean.Car"></bean>
<bean id="book" class="com.zb.bean.Book"></bean>

有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Person对象的时候必须创建Car和Book对象

<!-- 创建Person对象的时候必须创建Car和Book对象 -->
<bean id="person" class="com.zb.bean.Person" depends-on="book,car"></bean>
<bean id="car" class="com.zb.bean.Car"></bean>
<bean id="book" class="com.zb.bean.Book"></bean>

1.3 bean的作用域scope

可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的

<bean id="" class="" scope="作用域"/>

singleton: 单例模式,是默认模式。在容器启动完成之前就已经创建好对象保存在容器中了。
prototype :多实例的。容器启动不会去创建实例bean,每次调用getBean()的时候会产生一个新的对象。
request:在web环境下,同一次请求创建一个bean实例(没用)
session:在web环境下,同一次会话创建一个bean实例(没用)

1.4 bean的生命周期

在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法

public class Book {
    
    
	private String bookName;
	private String author;
	public void myInit(){
    
    
		System.out.println("这是图书的初始化方法");
	}
	public void myDestory(){
    
    
		System.out.println("这是图书的销毁方法。。。");
	}
}

//在配置文件中设置
<bean id="book01" class="com.zb.bean.Book" destroy-method="myDestory" init-method="myInit"></bean>

IOC容器中注册的bean的生命周期:

单实例bean:容器启动的时候就会创建好,容器关闭也会销毁创建的bean
	(容器启动)构造器 —> 初始化方法 —> (容器关闭)销毁方法
	
多实例bean:获取的时候才去创建
	获取bean(构造器--->初始化方法)--->容器关闭不会调用bean的销毁方法

1.4.1 bean的后置处理器

bean后置处理器允许在调用初始化方法前后对bean进行额外的处理

定义一个类实现org.springframework.beans.factory.config.BeanPostProcessor接口,在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
postProcessBeforeInitialization(Object, String)、postProcessAfterInitialization(Object, String)。需要注册这个实现类


/**
 * 1)、编写后置处理器的实现类
 * 2)、将后置处理器注册在配置文件中(感觉类似于javaweb过滤器)
 * @author zb
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor{
    
    

	/**
	 * 初始化之前调用
	 * Object bean, 传递过来的,将要初始化的bean
	 * String beanName:bean在xml中配置的id
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
    
    
		System.out.println("postProcessBeforeInitialization...【"+beanName+"】bean将要调用初始化方法了....这个bean是这样:【"+bean+"】");
		//返回传入的bean
		return bean;
	}
	
	/**
	 * postProcessAfterInitialization:
	 * 		初始化方法之后调用
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
    
    
		System.out.println("postProcessAfterInitialization...【"+beanName+"】bean初始化方法调用完了...AfterInitialization..");
		//初始化之后返回的bean;返回的是什么,容器中保存的就是什么
		return bean;
	}
}

//在配置文件中注册:
<bean id="beanPostProcessor" class="com.zb.bean.MyBeanPostProcessor">
</bean>

<bean id="book01" class="com.zb.bean.Book" destroy-method="myDestory" init-method="myInit">
</bean>

执行结果:

这是图书的构造方法
postProcessBeforeInitialization...【book01】bean将要调用初始化方法了....这个bean是这样:【Book [bookName=null, author=null]】
这是图书的初始化方法
postProcessAfterInitialization...【book01】bean初始化方法调用完了...AfterInitialization..

总结:添加bean后置处理器后bean的生命周期

[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
[4]调用bean的初始化方法
[5]将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
[6]bean可以使用了
[7]当容器关闭时调用bean的销毁方法

1.5 引用外部属性文件(重点)

当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。

Spring管理连接池(以配置C3P0的数据库连接池为零)

  1. 直接配置

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    	<property name="user" value="root"></property>
    	<property name="password" value="root"></property>
    	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
    	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>
    
  2. 创建properties属性文件(重点)

    1. 首先新建一个数据库连接池的配置文件db.properties:

      jdbc.username=root
      jdbc.password=root
      jdbc.jdbcUrl=jdbc:mysql://localhost:3306/test
      jdbc.driverClass=com.mysql.jdbc.Driver
      
    2. 引入context命名空间:
      在这里插入图片描述

    3. 使用context:property-placeholder location=" … "标签导入数据库配置文件db.properties,就可以用$取出对应的属性了:

      
      <context:property-placeholder location="classpath:dbconfig.properties"/>
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      	<property name="user" value="${jdbc.username}"></property>
      	<property name="password" value="${jdbc.password}"></property>
      	<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
      	<property name="driverClass" value="${jdbc.driverClass}"></property>
      </bean>
      

1.6 基于XML的自动装配(不推荐)

  • 设置<bean/>元素的: autowire属性

  • autowire属性: 让Spring按照一定的规则方式自己去找合适的对象,并完成DI注入操作
    在这里插入图片描述
    注意:

    • 如果按照byName自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名
    • 如果安装byType注入,如果Spring容器中同一个类型有多个实例,报 bean不是唯一类型错误;

在这里插入图片描述

二、SpEL表达式(了解)

好的博客:spring之SpEL表达式

  • 简介:
    SpEL表达式语言是一种表达式语言,是一种可以与一个基于spring的应用程序中的运行时对象交互的东西。有点类似于ognl表达式。总得来说SpEL表达式是一种简化开发的表达式,通过使用表达式来简化开发,减少一些逻辑、配置的编写。

  • 基本语法:
    SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。

代码示例:

	<bean id="car01" class="com.zb.bean.Car">
		<property name="carName" value="宝马"></property>
	</bean>
	
	<bean id="person04" class="com.zb.bean.Person">
		<!-- 进行运算符操作 -->
		<property name="salary" value="#{20*12}"></property>
		<!-- 引用其他bean的属性值作为自己某个属性的值-->
		<property name="lastName" value="#{car01.carName}"></property>
		<!-- 引用其他的bean -->
		<property name="car" value="#{car01}"></property>
		<!-- 
		      调用静态方法: UUID.randomUUID().toString();
		      #{T(全类名).静态方法名(1,2)}
		-->
		<!-- 调用静态方法 -->
		<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(1,3)}"></property>
		<!--   调用非静态方法;  对象.方法名  -->
		<property name="gender" value="#{car01.getCarName()}"></property>
	</bean>

三、通过注解配置bean(重点)

相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。

3.1 使用注解标识组件

  1. 通过给bean上添加注解,可以快速的将bean加入到IOC容器中。创建Dao、Service、Controller层所需要用到的注解

    普通组件:@Component
    标识一个受Spring IOC容器管理的组件
    持久化层组件:@Respository
    标识一个受Spring IOC容器管理的Dao持久化层组件
    业务逻辑层组件:@Service
    标识一个受Spring IOC容器管理的Service业务逻辑层组件
    表述层控制器组件:@Controller
    标识一个受Spring IOC容器管理的表述层控制器组件

  • 给bean上添加注解之后,还需要告诉Spring,自动扫描加了注解的组件:依赖context名称空间,还需要有AOP包的依赖。

      <context:component-scan base-package="com.zb"/>。
    
  • 组件的id默认是类名首字母小写

    组件的id。默认为_组件类名首字母小写
    @Controller("bookServlethh")(value="bookServlethh")
    public class BookServlet{
          
          }
    
  • 组件的作用域—默认为单例模式
    可通过@Scope修改作用域模式

    @Scope(value="prototype")//修改成多例模式
    @Repository
    public class BookDao {
          
          
    

总结:使用注解将组件快速的加入到容器中需要几步:

1)给需要添加的组件上标四个注解的任何一个
2)告诉Spring自动扫描加了注解的组件;依赖context名称空间
3)一定要导入aop包,才支持加注解模式

3.2 context扫描组件的配置

  • 指定要扫描的包

    <!-- 
    	context:component-scan:自动组件扫描 
     	base-package:指定扫描的基础包
     -->
    <context:component-scan base-package="com.zb" ></context:component-scan>
    
  • 使用context:exclude-filter指定扫描包时不包含的类

    <!-- 
    	使用context:exclude-filter指定扫描包时不包含的类
    	annotation:按照注解进行排除,expression属性中指定要排除的注解的全类名
    	assignable:排除某个具体的类,expression属性中指定要排除的类的全类名
    	aspectj:后来aspectj表达式(了解)
    	custom:自定义一个TypeFilter;自己写代码决定哪些使用(了解)
    	regex:使用正则表达式(了解)
     -->
    <context:component-scan base-package="com.zb" >
    	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    	<context:exclude-filter type="assignable" expression="com.zb.dao.BookDao"></context:exclude-filter>
    </context:component-scan>
    
  • 使用context:include-filter指定扫描包时要包含的类,只扫描进入哪些组件

    <!-- 
    	使用context:include-filter指定扫描包时要包含的类,只扫描进入哪些组件,默认都是全部扫描进来
    	use-default-filters需要设置为false,关闭默认
     -->
    <context:component-scan base-package="com.zb" use-default-filters="false">
    	<!-- 指定只扫描哪些组件 -->
    	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>
    

3.3 Autowired自动装配

直接在成员上添加@Autowired完成自动装配。

@Service
public class BookService {
    
    
    //使用@Autowired完成成员BookDao的自动装配
    @Autowired
    private BookDao bookDao;public void read() {
    
    
        this.bookDao.readBook();
    }
}

Autowired的执行流程:

  1. 首先按照类型去容器中找对应的组件,如果找到一个就赋值,找不到就抛异常;

  2. 如果有多个类型匹配时,会使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功,找不到就报错。

  3. 结合注解@Qualifer,指定一个id:在自动按照类型注入的基础之上,再按照指定的bean的id去查找

  4. @Autowired标注的属性如果找不到就会报错,可以指定required属性,找不到就自动装配null

    @Controller
    public class BookServlet {
          
          
    	/**
    	 * 按照类型找,找到一个就赋值,找到多个按照变量名作为id继续匹配
    	 * @Qualifier:指定一个名作为id,让spring别使用变量名作为id,如果变量名和该id都可以匹配到,则按照该id匹配
    	 * 找不到的话:@Autowired(required=false),找不到赋值为null
    	 */
    	@Qualifier("bookServiceExt")
    	@Autowired(required=false)
    	private BookService bookService1;
    }
    

注解加在方法上

/**
 * 在方法上@Autowired,ioc容器加载时就会执行该方法,同时也会为形参自动装配
 * 
 */
@Autowired(required=false)
public void hahaha(Person person,@Qualifier("bookServiceExt")BookService bookService1){
    
    
	System.out.println("Spring运行了这个方法"+person+bookService1);
}

@Autowired、@Resource、@Inject都可以作为注入注解

@Autowired:最强大;Spring的注解
	①扩展性差,依赖Spring容器框架
	
@Resource:j2ee;java的标准【jdk标准】
	①扩展性强:切换成另一个容器框架,其还会可以使用
【@Inject:在EJB环境下使用()】

四、Spring的单元测试(了解)

 1、导入:Spring单元测试包。spring-test-4.0.0.RELEASE.jar
 2、@ContextConfiguration(locations = ""),指定Spring的配置文件位置
 3、@RunWith(),指定用哪种驱动进行单元测试,默认junit
          @RunWith(SpringJUnit4ClassRunner.class)
          使用Spring的单元测试模块来执行@Test注解的测试方法;
          之前@Test由Junit执行
 好处:
     不需要ioc.getBean()获取组件;直接AutoWired组件,Spring自动装配
/**
 * 使用Spring的单元测试:
 * 1、@ContextConfiguration(locations="")指定Spring配置文件的位置
 * 2、@RunWith(),指定用哪种驱动进行单元测试
 */
@ContextConfiguration(locations="classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {
    
    

	ApplicationContext ioc = null;
	@Autowired
	BookService bookService;
	@Autowired
	BookServiceExt bookServiceExt;
	@Test
	public void test() {
    
    
		System.out.println(bookService);
		System.out.println(bookServiceExt);
	}
}

五、泛型的依赖注入

BaseDao

public abstract class BaseDao<T> {
    
    
	public abstract void save();
}

BookDao

@Repository
public class BookDao extends BaseDao<Book> {
    
    
	@Override
	public void save() {
    
    
		System.out.println("向数据库中保存了book");
	}
}

UserDao

@Repository
public class UserDao extends BaseDao<User> {
    
    
	@Override
	public void save() {
    
    
		System.out.println("向数据库中保存了User");
	}
}

BaseService

public class BaseService<T> {
    
    
	
	@Autowired
	private BaseDao<T> baseDao;
	
	public void save(){
    
    
		System.out.println("自动注入的dao:"+baseDao);
		baseDao.save();
	}
}

BookService

@Service
public class BookService extends BaseService<Book> {
    
    
}

UserService

@Service
public class UserService extends BaseService<User>{
    
    
}

测试:

public class IOCTest {
    
    
	@Test
	public void test01(){
    
    
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
		BookService bookService = ioc.getBean(BookService.class);
		UserService userService = ioc.getBean(UserService.class);
		bookService.save();//向数据库中保存了book
		userService.save();//向数据库中保存了User
		
		System.out.println(bookService.getClass().getGenericSuperclass());//com.zb.service.BaseService<com.zb.bean.Book>
	}
}

分析执行结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44630656/article/details/114791058