Spring学习(一) ---- ioc与di

ioc&&di

控制反转:IOC——Inversion of Control,把对象的创建权交给容器
翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
依赖注入:DI——Dependency Injection,再创建对象的时候给属性赋值

●谁依赖于谁:应用程序依赖于IoC容器;

●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

●谁注入谁:IoC容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

1、Spring中使用xml配置ioc与di

在Spring中使用ApplicationContext.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

中间填写具体配置

</beans>

1.1 Spring中使用xml配置ioc

1)ioc的基本配置

在Spring中配置ioc使用<bean id="" name="" class=""/>标签进行配置
id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id唯一
name:bean的别名,可定义多个,使用 , ; 空格 都可以进行分隔
class:bean的全限类名(注意:不能使用接口)

2)ioc创建对象的三种方式

1、构造器实例化
无参构造器实例化

 <bean id="cvn" class="com.gxa.bean.CVN">

    </bean>

有参构造器实例化

    <bean id="jet1" class="com.gxa.bean.Jet">
        <constructor-arg name="name" value="歼十五"/>
        <constructor-arg index="1" value="白色"/>
	<!--<constructor-arg type="java.lang.String" value="白色"/>-->
    </bean>

2、静态工厂实例化
先实现静态工厂

public class StaticFactory {

    public static CVN createCVN(){
        return new CVN();
    }
}

再配置xml

<!--    静态工厂实例化-->
    <bean id="cvn" class="com.gxa.factory.StaticFactory" factory-method="createCVN"/>

3、实例化工厂实例化
先实现实例化工厂

public class NoStaticFactory {

    public CVN createCVN(){
        CVN cvn = new CVN();
        cvn.setName("天津号");
        return cvn;
    }
}

再配置xml

<!--    实例化工厂实例化-->
	<!--先实现工厂对象-->
    <bean id="noStaticFactory" class="com.gxa.factory.NoStaticFactory"/>
	<!--再使用工厂对象创建-->
    <bean id="cvn" factory-bean="noStaticFactory" factory-method="createCVN"/>

1.2 Spring中使用xml配置di -- di注入的四种方式

1、setter注入
在对象的<bean></bean>标签中使用property标签进行配置
需要对象中有无参构造器

<!--    无参构造器实例化-->
    <bean id="cvn" class="com.gxa.bean.CVN" scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
<!--        setter注入-->
        <property name="length" value="350"/>
        <property name="name" value="福建号"/>
        <property name="width" value="120"/>
        <property name="jet" ref="jet1"/>
    </bean>

对于引用数据类型的注入统一使用 ref= 来进行标记

2、构造器注入
使用有参构造器进行注入,再标签中使用constructor-arg进行配置
需要对象中有对应的有参构造器

    <bean id="jet1" class="com.gxa.bean.Jet">
	<!--可以使用name、index(构造器中参数的下标位置--默认1开始)、type(参数类型)进行配置-->
        <constructor-arg name="name" value="歼十五"/>
        <constructor-arg index="1" value="白色"/>
	<!--<constructor-arg type="java.lang.String" value="白色"/>-->
    </bean>

3、自动装配(针对引用类型)
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配的方式:按类型(最常用)、按名称、按构造方法、不启用自动装配

<!--不用property标签,在 <bean>标签中添加autowire属性即可 -->
    <bean id="cvn" class="com.gxa.bean.CVN" scope="singleton" init-method="initMethod" destroy-method="destroyMethod" autowire="byType">
<!--        setter注入-->
        <property name="length" value="350"/>
        <property name="name" value="福建号"/>
        <property name="width" value="120"/>
	<!--<property name="jet" ref="jet1"/>-->
    </bean>
	
    <bean id="jet1" class="com.gxa.bean.Jet">
        <constructor-arg name="name" value="歼十五"/>
<!--        也可以通过index下标来指定构造器参数-->
        <constructor-arg type="java.lang.String" value="白色"/>
    </bean>
输出结果:`CVN{name='福建号', length=350, width=120, jet=Jet{name='歼十五', color='白色'}}`
注意事项:
	需要注入属性的类中对应属性的setter方法不能省略
	被注入的对象必须要被Spring的IOC容器管理
	按照类型在Spring的IOC容器中如果找到多个对象,会报 NoUniqueBeanDefinitionException

4、集合注入(了解)
数组的注入:使用array标签嵌套value标签
list集合的注入:使用list标签嵌套value标签
set集合的注入:使用set标签嵌套value标签
map集合的注入:使用map标签嵌套entry标签
proporties集合的注入:使用props标签中嵌套prop标签使用

//编写对象
public class CollectionBean {

    private List list;
    private Set set;
    private Map map;
    private Properties properties;
	...
}
编写相应的xml文件注入值
<!--    集合注入-->
    <bean id="collectionBean" class="com.gxa.bean.CollectionBean">
        <property name="list">
            <list>
                <value>aaa</value>
                <value>bbb</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>DASDASD</value>
                <value>DASDAFASD</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="first" value="CCCC"/>
                <entry key="second" value="ddddd"/>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="001">AAAA</prop>
                <prop key="002">BBBB</prop>
            </props>
        </property>
    </bean>

1.3 xml中的其他配置

1)配置bean的初始化方法和销毁方法

1、在注解中配置init-method 和 destroy-method

//创建初始化和销毁方法
//表示bean初始化对应的操作,再类的对象中的属性设置后再执行(即容器创建完成后执行)
public void init(){
	System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
	System.out.println("destory...");
}
<!--使用xml在初始化bean处配置参数-->
    <bean id="jet1" class="com.gxa.bean.Jet" init-method="init()" destroy-method="destroy()">
        <constructor-arg name="name" value="歼十五"/>
<!--        也可以通过index下标来指定构造器参数-->
        <constructor-arg type="java.lang.String" value="白色"/>
    </bean>

2、关于销毁方法的调用
调用销毁方法需要在容器关闭前才调用

close()关闭容器

\\ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
\\使用ApplicationContext没有close()方法,需要将其向下转型为ConfigurableApplicationContext及其子类才能调用

\\创建容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
...
...
\\关闭容器
applicationContext.close();

注册钩子关闭容器
在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器,调用ctx的registerShutdownHook()方法
ctx.registerShutdownHook();

总结:两种关闭容器方法的差异--close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。

3、类实现InitializingBean, DisposableBean接口中的方法实现初始化与销毁
添加两个接口 InitializingBean, DisposableBean并实现接口中的两个方法 afterPropertiesSet和 destroy即可实现配置bean的初始化方法和销毁方法

public class CVN implements InitializingBean, DisposableBean {

...

public void destroy() throws Exception {
	System.out.println("service destroy");
}
//在属性设置后再执行初始化方法
public void afterPropertiesSet() throws Exception {
	System.out.println("service init");
}

...

2)bean的其他设置

1、设置创建对象的scope

  • scope="singleton" --默认单例模式
  • scope="prototype" --多例模式,在每次需要时直接创建,不会把对象存入spring容器中
  • scope="session" -- 在web项目中使用,会把对象放入session域
  • scope="request" -- 在web项目中使用,会把对象放入request域

2、关于scope的思考

  • 为什么bean默认为单例?
    bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
    bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
  • bean在容器中是单例的,会不会产生线程安全问题?
    如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
    如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
  • 哪些bean对象适合交给容器进行管理?
    表现层对象、业务层对象、数据层对象、工具对象
  • 哪些bean对象不适合交给容器进行管理?
    封装实例的域对象,因为会引发线程安全问题,所以不适合。

3)Spring的IOC容器分析

image

ioc容器的顶层接口为BeanFactory,为其他所有的父类
其中最常用的子接口为ApplicationContext,拥有更多的新特性。几乎在所有的场合都使用ApplicationContext接口
不同点:
BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建
ApplicationContext是立即加载,容器加载的时候就会创建bean对象,延迟加载需要在每个对象后配置lazy-init="true"

在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关。

ApplicationContext主要实现类及其作用
image

容器的创建方式:
类路径下的XML配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
文件系统下的XML配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src \\main\\resources\\applicationContext.xml");

4)多个配置文件的加载

1、在创建容器时读取多个配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","beans.xml");
2、在一个配置文件中引入其他配置文件
<!-- 把其他xml合并到当前xml中--> <import resource="beans.xml"/>

5)bean对象的获取方式

方式一:
CVN cvn = (CVN) applicationContext.getBean("cvn");
直接通过name来获取对象,但是需要进行类型转换

方式二:
CVN cvn = applicationContext.getBean("cvn",CVN.class);
这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少。

方式三:
CVN cvn = applicationContext.getBean(CVN.class);
这种方式是1依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个。

2、Spring中使用注解配置ioc与di

2.1 使用注解配置ioc

@Component注解替代bean标签中的配置,如果配置了这个注解spring发现当前类有这个注解,
就会用反射创建对象放入spring容器中,key就用注解配置的名字。

@Component("userService")//注解配置
public class UserServiceImpl implements UserService {
    @Override
    public void helloSpring() {
        System.out.println("hello spring!");
    }
}

配置完成后需要配置xml来扫描

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置扫描包 扫描当前包以及当前包的子包-->
    <context:component-scan base-package="com.gxa.service"/>

</beans>

1)ioc中的等效注解

@Componen(重点)替代bean标签的配置
提供了三个等效的注解
@Controller(重点) web层 controller层
@Service(重点) service层
@Repository dao层
@Component 哪一层都不属于用@Component注解

这三个等效注解都是使用了@Component来标记,即@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

2)ioc注解的默认值

1、默认情况
类名首字母小写就是bean的id。例如:UserService类对应的bean的id就是userServcice

@Service //此时注解生成的bean的名字即是userServiceImpl
public class UserServiceImpl implements UserService {
    @Override
    public void helloSpring() {
        System.out.println("hello spring!");
    }
}

所以,一般开发时都不命名

2、使用value属性指定

@Service (value="userService")//此时注解生成的bean的名字即是userService
public class UserServiceImpl implements UserService {
    @Override
    public void helloSpring() {
        System.out.println("hello spring!");
    }
}

3、ioc中的其他注解配置

@Scope("prototype")// prototype 多例 singleton 单例 配置bean的作用范围
@PostConstruct//配置初始化的方法
@PreDestroy//配置销毁的方法

@Service(value = "userService") 
@Scope("prototype")//配置bean的作用范围
public class UserServiceImpl implements UserService {


    @PostConstruct//配置初始化的方法
    public void initMethod(){
        System.out.println("userService被创建了");
    }

    @PreDestroy//配置销毁的方法
    public void destroyMethod(){
        System.out.println("userService被销毁了");
    }

    @Override
    public void helloSpring() {
        System.out.println("hello spring!");
    }
}

2.2 使用注解配置di

1)注入普通属性

使用@Value注解代替property标签
使用注解注入是使用暴力反射注入,可以没有getter、setter方法
但是使用注解配置时必须要有getter、setter方法

@Component
public class FighterPlane {

    //<property name="name" value="歼15"></property>
    @Value("歼15")
    private String name;

    @Value("灰色")
    private String color;

//...省略
}

2)注入引用属性

1、使用Resource

参与自动装配的组件(需要装配别人、被别人装配)全部都必须在IOC容器中。

@Resource是JDK自带的注解,用于自动装配。 javax.annotation;

装配方式:
@Resource默认按照名称自动注入。

@Service("userService")
public class UserServiceImpl implements UserService {

    //<property name="userDao" ref="userDao"/>
    //暴力反射
	//Resource 注解可以使用name与type来进行注入
	//@Resource(type = UserServiceImpl.class)
    @Resource(name = "userDao")
    private UserDao userDao;

    @Override
    public void helloSpring() {
        userDao.addUser();
    }
}

再配置扫描包即可实现

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置扫描包 扫描当前包以及当前包的子包,如果有多个包逗号分割配置-->
    <context:component-scan base-package="com.gxa.service,com.gxa.bean,com.gxa.dao"/>

</beans>

@Resource注解的扫描流程
image

2、使用Autowire

@Autowire注解的扫描流程
image

@Autowire默认是使用type匹配注入

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired//按照类型注入  就去spring容器自动查找有没有实现了UserDao接口的对象,有的话自动注入
    private UserDao userDao;

    @Override
    public void helloSpring() {
        userDao.addUser();
    }

}

和Qualifier配合按照名称注入:

//@Qualifier的使用
//通过@Autowired和@Qualifier的结合使用可以按名称装配。
@Service("userService")
public class UserServiceImpl implements UserService {

    //<property name="userDao" ref="userDao"/>
    @Qualifier("userDao")  //按照名称注入
    @Autowired     //=  @Resource(name="userDao")
    private UserDao userDao;

    @Override
    public void helloSpring() {
        userDao.addUser();
    }

}

有多个bean对象时配置Primary
在类上使用@Primary标签

@Repository
@Primary
public class UserDaoImpl2 implements UserDao {
    @Override
    public void addUser() {

        System.out.println("2号");
    }
}
posted @ 2022-06-24 20:23  夜色哪里都是美  阅读(87)  评论(0)    收藏  举报