spring的ioc容器总结

核心容器

core
beans
context
expression
commons-logging
spring运行时需要依赖一个日志包,否则会报错

源码包:所有类的运行都是从bin文件夹下运行的,这个路径就叫做原始路径(类路径),通过new一个resource folder创建的文件会自动合并到bin文件夹下(类路径里边)
java创建的项目都是从在/bin/, web项目都是在/WEB-INF/classes
要想spring能运行,除了导入几个核心包以外,一定还要到commons-logging包先导包再创建配置文件
spring容器接管了所有标有s标志的组件
实验1:通过IOC容器创建对象,并为属性赋值(重要)

在源码包下创建配置文件applicationConttext.xml
注册一个对象,spring会自动创建这个对象,一个bean标签就代表spring帮助创建了一个对象
id是这个对象的唯一标示,class是要创建对象的全类名

<!-- 使用 -property标签为Person对象的属性赋值
  name为属性名
  value为属性赋值
-->
<bean id="person01" class="com.atguigu.bean.Person">
    <property name="lastName" value="张三"></property>
    <property name="age" value="18"></property>
    <property name="gender" value="女"></property>
    <property name="address" value="陕西西安"></property>
 </bean>

几个细节:
1:ApplicationContext(IOC容器的接口)
new ClassPathXmlApplicationContext(“类路径下的配置文件路径”);

ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

2:给容器注册一个组件,我们也从容器中按照id拿到了这个组件的对象?
组件的创建工作,是容器完成的

3:对象是什么时间创建好的呢?
容器中对象的创建是在容器创建完成的时候就已经创建好了
同一个组件在ioc容器中都是单实例存在的,容器启动完成时都已经创建好了的

4:容器中如果没有注册这个组件,获取组件报异常
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘person03’ available

5:ioc容器在创建这个组件对象的时候,(property)会调动setter方法为javabean的属性赋值

6:javabean的属性名是由什么决定的呢?
getter/setter方法是属性名。set后边那串

所以所有的getter/setter方法都使用自动生成的,不要去进行二次创作

实验2:根据bean的类型从IOC容器中获取bean的实例(重要)
ioc.getBean(Person.class())//通过bean的类型从容器中获取bean的实例

需要注意的是:
1.按照类型获取组件,可以获取到相同的所有实现类,子类等等
2.从ioc容易中用类型获取对象时,同类型的对象只能注册一个,不能出现多个org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type ‘com.atguigu.bean.Person’ available:
expected single matching bean but found 2: person01,person2
如果有多个,但是需要其中一个,这种情况下就可以传入id,类型同时查找组件

实验3:通过构造器为bean的属性赋值(index,type属性)

基本类型直接使用property赋值,自动进行类型转换

  1. 调用有参构造器为对象属性赋值,调用构造器赋值是用constructor-arg属性,调用property是调用set方法来赋值
<!-- public Person(String lastName, Integer age, String gender, String address)  -->
            <bean id="person03" class="com.atguigu.bean.Person">
                        <constructor-arg name="lastName" value="三"></constructor-arg>
                        <constructor-arg name="gender" value="男"></constructor-arg>
                        <constructor-arg name="age" value="20"></constructor-arg>
                        <constructor-arg name="address" value="陕西咸阳"></constructor-arg>
            </bean>
  1. 用有参构造赋值时,不指定name属性的话,value属性会按照参数顺序赋值
    如果有需要调整的,用index属性可以调整位置 ,index为参数指定索引,从0开始
<bean id="person04" class="com.atguigu.bean.Person">
                        <constructor-arg value="小花"></constructor-arg>                   
                        <constructor-arg value="13"></constructor-arg>
                        <constructor-arg value="河南郑州" index='3'></constructor-arg>
                        <constructor-arg value="女" index='2'></constructor-arg>
</bean>

3: 如果有构造器重载的情况下,可以通过type去指定类型

            <!-- public Person(String lastName, Integer age, String gender)  -->
  <bean id="person05" class="com.atguigu.bean.Person">
            <constructor-arg value="小李"></constructor-arg>
            <constructor-arg value="20" type="java.lang.Integer"></constructor-arg>
            <constructor-arg value="男"></constructor-arg>
  </bean>
<!--第二个参数不同,可以指定类型-->
            <!-- public Person(String lastName, String address, String gender) -->
  <bean id="person06" class="com.atguigu.bean.Person">
        <constructor-arg value="小丽"></constructor-arg>
        <constructor-arg value="陕西宝鸡" type="java.lang.String"></constructor-arg>
        <constructor-arg> value="女"></constructor-arg>
 </bean>-->

4:通过p名称空间为bean赋值,在xml中名称空间是用来防止标签重复的

<book>
    <b:name>西游记</b:name>
    <price>19.8</price>
    <author>
           <a:name>余承恩</a:name>
            <gender>男</gender>
    </author>
</book>
实验4:正确的为各种属性赋值
复杂的赋值都是在property标签体内进行的 ,property中的value的值都是基本类型-内部bean是不能直接get到的,只能内部使用
测试使用null值,引用类型赋值(引用其他bean,引用内部bean)

ref代表引用外部的一个值 car == ioc.getBean(“car01"),这就是外部bean
在property中再添加property,内部创建了一个bean,就=new Car(),再重新赋值,这就是内部bean

<bean id="person01" class="com.atguigu.bean.Person">
             <property name="lastName" >
                        <null/>
             </property>
             <property name="car" ref="car01">
                                    <property name="carName" value="宝马"></property>
                                    <property name="price" value="120000"></property>
                        </bean>
             </property>
 </bean>

集合类型赋值(List,Map,Properties)

<bean id="book01" class="com.atguigu.bean.Book">
            <property name="bookName" value="红楼梦"></property>
            <property name="author" value="曹雪芹"></property>
</bean>

<bean id="person02" class="com.atguigu.bean.Person">
<!-- 如何为list赋值呢 -->
            <property name="books">
            <!--一个list标签就相当于new ArrayList<book>()  -->
                        <list>
                                    <!-- 给list添加每一个元素 -->
                                    <bean id="bookInner" class="com.atguigu.bean.Book" 
p:bookName="西游记" p:author="余承恩"/>
                                    <ref bean="book01"/>
                        </list>
            </property>
            <property name="maps">
                        <map>
                                    <entry key="key01" value="李四" />
                                    <entry key="key02" value="18"></entry>
                                    <entry key="key03" value-ref="book01"></entry>
                                    <entry key="ke04">
                                                <bean class="com.atguigu.bean.Car">
                                                            <property name="carName" value="现代"></property>
                                                            <property name="color" value="yellow"></property>
                                                </bean>
                                    </entry>
                        </map>
            </property>
            <property name="properties">
            <!-- properties=new Properties()。所有的k-v都是string -->
                        <props>
                        <!-- k-v都是string,value直接写在标签体中 -->
                                    <prop key="username">root</prop>
                                    <prop key="password">123456</prop>
                        </props>
            </property>
</bean>

util名称空间创建集合类型的bean
级联属性赋值

实验6:通过继承实现bean配置信息的重用

通过parent属性指定继承的模板,详细请看实验7

实验7:通过abstract属性创建一个模板bean

通过abstract="true"指定一个模板bean,设置这个属性之后,不能获取他的实例,只能作为继承的模板使用

            <bean id="person07" class="com.atguigu.bean.Person" abstract="true">
                        <property name="lastName" value="零零七"></property>
                        <property name="age" value="18"></property>
                        <property name="gender" value="男"></property>
                        <property name="address" value="china"></property>
            </bean>
            
            <!-- 通过parent属性指定继承的模板 -->
            <bean id="person08" class="com.atguigu.bean.Person" parent="person07">
                        <property name="lastName" value="零零八"></property>
            </bean>
实验8:bean之间的依赖(决定bean在容器中的创建顺序)

通过index,type等属性设定,具体请看实验4

实验5: 配置通过静态工厂方法创建的bean,实例工厂方法创建的bean,FactoryBean
bean的默认创建模式就是框架利用反射new出来的bean实例
工厂模式:工厂帮我们创建对象,我们自己不用关心创建细节 ,有个专门帮我们创建对象的类,这个类就是工厂
AirPlane ap = AirPlaneFactory.getAirPlane(String jzName);       

1:静态工厂:工厂本身不用创建对象,都是通过工厂的静态方法直接掉用
工厂类.getAirPlane(String jzName);
class:指定静态工厂全类名
factory-method:指定工厂方法
constructor-arg:可以为工厂方法传参

<bean id="airPlane02" class="com.atguigu.factory.AirPlaneStaticFactory" 
            factory-method="getAirPlane">
            <!-- 可以为方法指定参数 -->
                        <constructor-arg value="王五"></constructor-arg>
            </bean>

2:实例工厂:工厂本身需要创建对象,用工厂对象get方法
工厂类 工厂对象 = new 工厂类();
工厂对象.getAirPlane(“张三”);
factory-bean:指定使用哪个工厂实例
factory-method:指定使用哪个工厂方法

<bean id="airPlaneInstanceFactory" class="com.atguigu.factory.AirPlaneInstanceFactory">
</bean>
<bean id="airPlane03" class="com.atguigu.bean.AirPlane" factory-bean="airPlaneInstanceFactory" factory-method="getAirPlane">
            <constructor-arg value="赵六"></constructor-arg>
</bean>

3:FactoryBean(是spring规定的一个接口):
只要是实现了这个factorybean的接口,spring就认为他是一个工厂,ioc容器启动的时候, 并不会直接创建实例;factorybean而是获取的时候才创建对象。

实验9:测试bean的作用域,分别创建单实例和多实例的bean
实验10:创建带有生命周期的bean

生命周期:bean的创建到销毁;
ioc容器中注册的bean:
1)、 容器启动的时候就会创建好,容器关闭也会销毁创建的bean
2)、多实例bean,获取的时候才创建
init-method:为对象指定初始化方法
destory-method:为对象指定销毁方法
我们可以为bean自定义一些生命周期方法,spring创建或者销毁的时候就会调用指定的方法

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

public class Book {

            private String bookName;
            private String author;

            public void myInit(){
                        System.out.println("spring02中的book初始话方法");
            }
            
           public void myDestory(){
                        System.out.println("spring02的book销毁");
            }
            ...
}

实验11:测试bean的后置处理器

spring有一个接口,后置处理器 可以在bean的初始化前后调用方法
需要实现BeaPostProcessor接口,重写两个两个方法,return的bean就是后续需要的那个
后置处理器处理多实例和单实例的区别:多实例没有对象的销毁,但是单实例有

<bean id="myBeanProcessImpl" 
class="com.atguigu.bean.MyBeanProessImpl"></bean>

public class MyBeanProessImpl implements BeanPostProcessor {

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) 
throws BeansException {
                        System.out.println(beanName+"初始化后"+bean);
                        return null;
            }

            @Override
            public Object postProcessBeforeInitialization(Object bean, String 
beanName) throws BeansException {
                        System.out.println(beanName+"初始化前"+bean);
                        return bean;
           }
}

总结:
容器中先去创建对象,配置了初始化方法的话,去调用初始化方法,如果容器中有配置后置处理器,则在初始化方法前调用初始化前方法,在初始化后调用初始化后方法容器关闭
单实例的话对象销毁,多实例没有销毁

 bean的生命周期
 单例:(容器启动)构造器---->初始化方法---->(容器关闭)销毁
多例 :获取的时候才创建对象,容器关闭不会销毁对象
获取bean)构造器---->初始化方法---->容器关闭不会调用bean的销毁方法,什么时候用完,自己销毁
加了后置处理器:
单例 : (容器启动)构造器--postProcessBeforeInitialization-->初始化方法--postProcessAfterInitialization-->(容器关闭)销毁
多例:获取bean)构造器--postProcessBeforeInitialization-->初始化方法--postProcessAfterInitialization-->容器关闭不会调用bean的销毁方法,什么时候用完,自己销毁 
实验12:引用外部属性文件(spring管理数据库连接池)

一个项目中,数据库连接池作为单实例是最好的,一个项目一个连接池,连接池里边有很多连接,连接时直接从连接池中获取
根据这个特性,我们可以将连接池交给spring去管理,(管理连接池)因为spring本身默认的就是单实例 要使用连接池,需要加入C3P0的jar包和数据库驱动的jar包
为了代码已维护,经常把数据库的信息抽取到配置文件,(dbconfig.properties),

要引用配置文件,需要借助名称空间context
location指定引用的文件位置,classpath固定写法,表示类路径下的一个资源
用${key} 动态取出配置文件中key所对应的值
动态取值注意事项,不要在value中出现空格之类的,会直接拼到取出的结果中,就找不到资源了
username是spring的关键字,所以文件中不应该出现username,
防止冲突,一般会给配置文件加上标识,例如dbconfig的配置文件 一般用jdbc.username这种
${username}取得是系统当前的用户名,所以在数据库中user这样取就会替代数据库用户名的root,所以就报错了

<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>
 <!--dbconfig.properties-->
 jdbc.username=root
jdbc.password=root
jdbc.jdbcUrl=jdbc:mysql://<u>localhost</u>:3306/test
jdbc.driverClass=com.mysql.jdbc.Driver
实验13:基于xml的自动装配

自动装配只针对于对象,基本数据类型不能自动装配

<bean id="car" class="com.atguigu.bean.Car">
            <property name="carName" value="BMW"></property>
            <property name="color" value="白色"></property>
</bean>
<bean id="car01" class="com.atguigu.bean.Car">
            <property name="carName" value="奔驰"></property>
            <property name="color" value="黑色"></property>
</bean>   

<!--ref手动赋值,根据ref的值去找对应的id的对象,找不到就报错 -->
<bean id="person" class="com.atguigu.bean.Person">
            <property name="car" ref="car"></property>
</bean>

 <!--自动装配-->
<bean id="person01" class="com.atguigu.bean.Person" autowire="constructor">

</bean>









通过属性自动装配,autowire的属性去实现自动装配–>
1)autowire=“default”/no:不去自动装配
private Car car;
2)autowire=“byName”:根据属性名(car)当id找对应的bean去赋值
找不到:赋值null
找到唯一一个:自动赋值
找到多个:不会有多个的情况,因为容器中bean的id不能重复
3)autowire=“byType”:根据属性名的类型在容器中找对应的bean
找不到:赋值null
找到唯一一个:自动赋值
找到多个:报错
4)autowire=“constructor”:根据构造器在容器中找

查找原理:(如果只有一个有参构造器,但是有没有通过构造器找到对应的bean,就会报错,除过这种情况就有了就装配,没有就null)
先按照有参构造器去匹配,找不到:null,找到了:装配成功,有多个有参构造器就赋值null
按照类型去找,找不到:null,找到一个:赋值,找到多个:再通过id去查找:找不到:null
通过id不可能找到多个

实验14:【SpEL测试】

在SpEl中使用字面量
value="#{18}"
引用其他bean
value="#{car01}">
引用其他的bean的某个属性
value="#{book01.bookName}"
调用静态方法
#{T(全类名).静态方法名(arg1,arg2,…)}
value="#{T(java.util.UUID).randomUUID().toString()}"
调用非静态方法
#{对象.方法名} value="#{book01.getBookName()}"
使用运算符
value="#{10000/13}"

<bean id="person02" class="com.atguigu.bean.Person">
    <!-- 在SpEl中使用字面量 -->
    <property name="age" value="#{18}"></property>
    <!-- #{} 使用运算符 ,可以使用任何运算符 -->
    <property name="salary" value="#{10000*13}"></property>
    <!-- #{} 引用其他bean -->
    <property name="car" value="#{car01}"></property>
    <!-- #{}  引用其他的bean的某个属性-->
    <property name="lastName" value="#{book01.bookName}"></property>
    <!-- #{对象.方法名}   调用非静态方法 -->
    <property name="gender" value="#{book01.getBookName()}"></property>
    <!-- #{T(全类名).静态方法名(arg1,arg2,..)}  调用静态方法 -->
    <property name="address" value="#{T(java.util.UUID).randomUUID().toString()}"></property> 
</bean>
实验14:使用注解创建bean(@controller,@service,@component @reponsitory)

通过注解的方式创建dao,service.controller的bean
通过给bean上添加注解,快速的把该bean加入到容器中来
spring有四个创建bean的注解

类上添加任何一个注解都可以把该类添加到spring容器中
@controller:控制器,推荐给控制器层添加该注解
@service: 业务逻辑层
@reponsitory: dao
@component:不属于上边三种情况的类,比如webUtils。
注解可以随意加,spring底层不回去看你是属于哪个层面的,这个只是为了我们方便阅读
使用注解加入到容器的顺序
1):先快速的把注解加入到对应的类中
2):在applicationContext中配置组建扫描,这个需要去依赖context名称空间
3):一定要倒入aop包,才能支持加注解模式
context:component-scan:容器自动扫描
base-package:指定容器都需要扫那些bean,基础包和基础包下的所有文件都可扫描到

<context:component-scan 
base-package="com.atguigu"></context:component-scan>

使用xml效率比较高,但是xml也是有好处的
使用xml配置有一个好处就是不是自己的类jar包中的也可以填加到容器中,注解的方式就不可以去添加jar包的了

实验15:通过注解分别创建dao,service,controller

给类上直接添加对应的注解就可以了

实验16:使用context:include-filter指定要扫描包时要包含的类

使用context:include-filter 指定扫描的时候只扫描哪些组件,默认都是全部扫描
要想只扫描指定的,那就需要禁用默认的扫描模式use-default-filters=“false”

<context:component-scan base-package="com.atguigu" use-default-filters="false">
            <context:include-filter type="annotation" 
expression="org.springframework.stereotype.Service"/>
</context:component-scan> 
实验17:使用context:exclude-filter指定要扫描包时不包含的类

使用context:exclude-filter 指定扫描的时候可以排除一些不要的组件
type :通过什么样的方式去扫描
type=“annotation”:通过注解去扫描,标注了指定注解的组件不要,expression=“注解的全类名”
type=“assignable” :通过具体的类去排除,expression=“类的全类名”
type=“aspectj” :aspectj表达式
type=“custom” :自定义一个TypeFilter,自己写代码决定哪些需要
type=“regex” :正则表达式

<context:component-scan base-package="com.atguigu">
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
         <context:exclude-filter type="assignable" expression="com.atguigu.service.BookService"/>
  </context:component-scan>
实验18:使用@Autowired注解实现根据类型自动装配(DI(依赖注入) )

@Autowired ,@resource , @Inject 都是自动装配的意思
@Autowired:最强大,但是是spring的东西,只能在spring容器中运行
@resource:扩展性比较强,是java标准的东西,可以再spring以为的容器中运行
@Inject:EJB环境下用的

@Autowired:Spring会自动的为这个属性赋值,
一定是去容器中找这个组件,所以加这个注解的组件一定要是在spring中注册过的才行

方法上加@Autowired,
这个方法在bean创建的时候就自动运行,
并且其中的参数会自动装配

加注解后,我们获取对象时就以类名首字母小写作为id去查询
使用注解加入到容器的组件和使用配置加入的组件行为默认都是一样的
1)id都是默认的类名首字母小写
@Repository(“bookDaohaha”)
可以通过括号里给参数修改bean的id,ioc.getBean(“bookDaohaha”);
2)组件的作用域都是默认的单例模式
@Scope(value=“prototype”)
通过scope中value值为prototype设置成多实例

@Autowired原理:
@Autowired
private BookService bookService;
1):先按照类型去容器中找,就相当于 ioc.getBean(BookService.class)
a:找不到,抛异常(如果项目中有这个组件,那就看看这个组件有没有在容器中注册)
b:找到一个,自动装配
c:找到多个?装配上?
找到多个的情况下,再通过变量名作为id去容器中查找
(BookService bookService,BookService bookServiceExt)
c.1:找到一个自动装配
c.2:找不到:抛异常(这块用id去找的,但是id并不能匹配上)
鉴于这种情况,我们可以给类指定一个名称去匹配,
@Qualifier(bookService)指定用这个作为id去查找,不要使用变量名去匹配了,直接用指定的
c.2.1:找到:自动装配上
c.2.1:找不到:报错
由此可发现,@Autowired默认是一定要装配的,找到就自动装配,找不到就抛异常
在指定了@Qualifier(bookService)的情况下,可以给@Autowired加上属性required默认为true,改为false
@Autowired(required=false)这种的话就是找到了就自动装配,找不到就装配null
但是装配的组件为null的情况下,调用方法还是会出现空指针,(所以本人不太明白要这个什么场景下用)

实验19:如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,自动装配

在用类型找到多个的情况下,再通过变量名作为id去容器中查找
(BookService bookService,BookService bookServiceExt)
1:找到一个自动装配
2:找不到:抛异常(这块用id去找的,但是id并不能匹配上)

实验20:如果根据成员变量名作为id还是查找不到bean,可以使用@Qualifier注解明确指定目标bean的id

@Qualifier:指定一个名作为id去查找,让Spring不要以变量名称去找了

@Qualifier("bookService")
@Resource
BookService bookServiceExt2;
实验21:在方法的形参位置使用@Qualifire注解

在方法参数上用@Qualifier(“bookServiceExt”),给参数指定id去查找装配

@Autowired
public void <u>hahaha</u>(@Qualifier("bookServiceExt")BookService bookService , BookDao bookDao){
            System.out.println("Spring运行了这个方法");
}
实验22:@Autowired注解的required属性指定某个属性允许不被设置

@Autowired(required=false)这种的话就是找到了就自动装配,找不到就装配null

使用spring的单元测试

使用spring的单元测试必须步骤

  • 1)导包,导入spring-test的单元测试的jar包

  • 2)@ContextConfiguration(locations=“classpath:applicationContext.xml”):
    用这个指定spring的配置文件的位置

  • 3)@RunWith()
    指定使用哪种驱动进行单元测试,默认就是junit
    @RunWith(SpringJUnit4ClassRunner.class):使用spring的单元测试模块来执行@Test注解的测试方法

使用spring的单元测试,就可以使用spring提供的自动注入,可以不用ioc.getBean()这种方式去获取了

@ContextConfiguration(locations="classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class IOCTest {

            ApplicationContext ioc = null;

            @Autowired
            BookServlet bookServlet;

            @Test
            public void test04(){
                        System.out.println(bookServlet);
            }

实验23:测试泛型依赖注入*****
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值