Spring IoC以及DI

注意:为演示方便,所有class值都省略了包名

一、IoC(Inverse of Control)

IoC即控制反转,它是指将对象的创建权交给Spring容器管理
在开发中,我们经常遇到类A需要用到类B的场景,传统方式中我们往往在A中通过new显式的生成B实例,这样代码就产生了耦合。使用Spring框架后,我们可以通过配置将类注册到Spring容器中,容器再借助反射等机制为我们提供对象。
请看下面这个例子:

public class Person {

    public void say() {
        System.out.println("Hello Spring!");
    }
    
}

在resources文件夹下配置applicationContext.xml文件:

<bean id="person" class="Person"/>

使用Spring工厂类获得对象:

// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类
UserService userService = (UserService) applicationContext.getBean("person");

userService.say(); // 成功打印Hello Spring!

二、DI(Dependency Injection)

DI即依赖注入,它是指在Spring托管对象的过程中,将该对象所依赖的属性注入进去
请看这个例子:

public class Person {

	// 属性:姓名、工作
    private String name;
    private Job job;

    public Person() {

    }

    public Person(String name, Job job) {
        this.name = name;
        this.job = job;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Job getJob() {
        return job;
    }

    public void setJob(Job job) {
        this.job = job;
    }
    
}
public class Job {

	// 属性:工作名称
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

现在,我们欲创建一组对象:Person{name=‘小明’, job=Job{name=‘研发工程师’}}

1. 通过构造器注入

<bean id="person" class="Person">
	<constructor-arg name="name" value="小明"/>
	<constructor-arg name="job" ref="job"/>
</bean>

<bean id="job" class="Job">
	<property name="name" value="研发工程师"/>
</bean>

2. 通过set方法注入

在resources文件夹下配置applicationContext.xml文件:

<bean id="person" class="Person">
	<property name="name" value="小明"/>
	<property name="job" ref="job"/>
</bean>

<bean id="job" class="Job">
	<property name="name" value="研发工程师"/>
</bean>

3. 通过p命名空间注入

p命名空间是Spring2.5新增的注入方法,在使用前需在applicationContext.xml的头部中增加一行约束:xmlns:p="http://www.springframework.org/schema/p"

<bean id="person" class="Person" p:name="小明" p:job-ref="job"/>

<bean id="job" class="Job" p:name="研发工程师"/>

4. 通过SpEL注入

SpEL(Spring Expression Language)即Spring表达式,编写者可以在表达式中调用其它类的某个方法,并将它的返回值作为参数值注入,也可以直接传入字符串。
为演示方便,新增一个工厂类:

public class JobFactory {

    public static Job createJob() {
        Job job = new Job();
        job.setName("研发工程师");
        return job;
    }

}
<bean id="jobFactory" class="JobFactory"/>

<bean id="person" class="Person">
	<property name="name" value="#{'小明'}"/>
	<property name="job" value="#{jobFactory.createJob()}"/>
</bean>

* 5. 复杂类型的注入

该例是针对数组、集合的属性注入:

public class Data {

    private String[] arr;
    private List<String> list;
    private Set<String> set;
    private Map<Integer, String> map;

    public String[] getArr() {
        return arr;
    }

    public void setArr(String[] arr) {
        this.arr = arr;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Set<String> getSet() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public Map<Integer, String> getMap() {
        return map;
    }

    public void setMap(Map<Integer, String> map) {
        this.map = map;
    }
    
}

配置applicationContext.xml文件:

<bean id="data" class="Data">
	<property name="arr">
    	<list>
        	<value>arr1</value>
            <value>arr2</value>
            <value>arr3</value>
       	</list>
    </property>
    <property name="list">
    	<list>
        	<value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
   	</property>
    <property name="set">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>
    <property name="map">
        <map>
            <entry key="1" value="map1"/>
            <entry key="2" value="map2"/>
            <entry key="3" value="map3"/>
        </map>
  	</property>
</bean>

最终Data对象中的信息:Data{arr=[arr1, arr2, arr3], list=[list1, list2, list3], set=[set1, set2, set3], map={1=map1, 2=map2, 3=map3}}


三、Bean的作用域

默认情况下,Spring托管对象都是以单例模式(singleton)创建的

// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = (UserService) applicationContext.getBean("userService");

System.out.println(userService1 == userService2); // 输出true

我们可以在applicationContext.xml中进行修改配置,来实现多例模式(prototype)

<bean id="userService" class="UserServiceImpl" scope="prototype"/>

再次运行上述代码,将打印false,说明userService1和userService2是两个不同的对象。


四、Bean的生命周期

该图片转载自网络
在这里插入图片描述
生命周期中的BeanPostProcessor阶段主要用于Spring AOP
我们可以手工配置Spring创建和销毁对象时所要执行的init-method和destroy-method,请看示例:

public class Person {

    public Person() {
        System.out.println("===构造方法===");
    }

    public void init() {
        System.out.println("===初始化方法===");
    }

    public void destroy() {
        System.out.println("===销毁方法===");
    }
}

在resources文件夹下配置applicationContext.xml文件:

<bean id="person" class="Person" init-method="init" destroy-method="destroy"/>

五、注解方式的实现

1. IoC

注释 针对场合
@Conponent 传统组件,可用于任何Bean
@Repository 适用于持久层
@Service 适用于业务层
@Controller 适用于表现层

示例:
①首先在配置文件中开启注解扫描

<context:component-scan base-package="完整包名"/>

②然后对需要托管的类增加注解

@Component("person")
public class Person {

    public void say() {
        System.out.println("Hello Spring!");
    }

}

③最后在具体场景中通过Spring工厂类获取实例

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) applicationContext.getBean("person");

person.say(); // 成功打印Hello Spring!

2. DI

注解 特点
@Value 注入字符串或其它的值
@Autowired 注入类(只根据类型匹配)
@Autowired + @Qualifier 注入类(根据类型以及名字进行匹配)
@Resource 注入类(根据类型以及名字进行匹配)

示例:

@Service("userService")
public class UserService {

    @Resource(name = "userDao")
    private UserDao userDao;

    public void save() {
        userDao.save();
    }

}
@Repository("userDao")
public class UserDao {

    public void save() {
        System.out.println("=====UserDao保存=====");
    }

}
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");

userService.save(); // 成功打印=====UserDao保存=====

3. 作用域

注解 含义
@Scope 同scope,默认sington,可选prototype
@Component("person")
@Scope("prototype")
public class Person {
    
}

4. 生命周期

注解 含义
@PostConstruct init-method
@PreDestroy destroy-method

示例:

@Component("person")
public class Person {

    @PostConstruct
    public void init() {
        System.out.println("======初始化======");
    }

    @PreDestroy
    public void destory() {
        System.out.println("======销毁======");
    }

}

六、XML + 注解混合

XML文件清晰集中,便于查看和管理,而注解方式使用便捷高效,在实际开发场景中,两者可以混合运用,利用XML管理Bean,利用注解实现属性注入
①首先在配置文件中开启属性注入的注解
(本配置仅开启属性注入的部分,而例五中的设置则开启了全部)

<context:annotation-config/>

②然后在类的属性上进行注入

public class Person {

    @Value("小明")
    private String name;

    @Value("18")
    private String age;

    @Value("研发工程师")
    private String job;

}

③接着在配置文件中进行Person类的配置

<bean id="person" class="com.imooc.ioc.Person"/>

最终构建了一组对象:Person{name=‘小明’, age=‘18’, job=‘研发工程师’}





附:要使用Spring框架,需要引入下列4个jar包
spring-core、spring-context、spring-beans、spring-expression

发布了48 篇原创文章 · 获赞 4 · 访问量 6149

猜你喜欢

转载自blog.csdn.net/Knightletter/article/details/103883495