更新时间 更新内容
2019-04-30 IOC原理简要代码实现

IoC控制反转

控制(对象的生命周期)反转(对象的控制权给IoC容器)(Inversion of Control),即创建被调用者的实例不是由调用者完成,而是由 Spring 容器完成,并注入调用者。
当应用了 IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。即,不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
传统意义上创建对象的方式,客户端类如果需要用到User和UserDetail,需要通过new 来构建,这种方式使代码之间的耦合度非常高
image.png

IOC原理简要代码实现

当某个Java对象需要调用另外一个Java对象(被调用者被依赖对象),在传统开发模式下,调用者采用new被调用对象的方式创建对象,导致调用者与被调用者耦合度高,后期维护升级维护成本高。
在Spring框架中,被调用对象不再有调用者创建,而是有Spring容器创建
对象之间的依赖关系由spring使用依赖注入实现
ioc的核心思想在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理。
1.资源的集中管理,实现了资源的可配置和易管理
2降低了使用资源的双方的依赖程度,也是所谓的耦合度。

helloword工程

maven配置

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-context</artifactId>
  5. <version>5.0.2.RELEASE</version>
  6. </dependency>
  7. </dependencies>

创建接口

  1. package com.bx.service;
  2. public interface UserService {
  3. public void sayHello();
  4. }

创建接口实现

  1. package com.bx.service.impl;
  2. import com.bx.service.UserService;
  3. public class UserServiceImpl implements UserService {
  4. public void sayHello() {
  5. System.out.println("Hello Spring");
  6. }
  7. }

创建spring配置文件,在 src/main/resources 目录下创建 spring-context.xml 配置文件,将类的实例化交给 Spring 容器管理(IoC)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="userService" class="com.bx.service.impl.UserServiceImpl"></bean>
  6. </beans>

<bean/>用于定义一个实例对象。一个实例对应一个Bean 元素。
id:该属性是 Bean 实例的唯一标识,通过 id 属性访问 Bean,Bean与Bean间的依赖关系也是通过 id 属性关联的。
class:指定该 Bean 所属的类,注意这里只能是类,不能是接口。
测试spring

  1. public static void main(String[] args) {
  2. // 获取 Spring 容器
  3. ApplicationContext ac = new ClassPathXmlApplicationContext("spring-context.xml");
  4. // 从 Spring 容器中获取对象
  5. UserService us = (UserService) ac.getBean("userService");
  6. us.sayHello();
  7. }
  8. }

spring的简单容器实现原理

  1. import java.io.InputStream;
  2. import java.util.Enumeration;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Properties;
  6. public class BeanFactory {
  7. private static Properties properties;
  8. private static Map<String,Object> beans;
  9. static {
  10. try {
  11. properties = new Properties();
  12. InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
  13. properties.load(in);
  14. beans = new HashMap<String, Object>();
  15. Enumeration<Object> keys= properties.keys();
  16. while (keys.hasMoreElements()){
  17. String key = keys.nextElement().toString();
  18. String beanPath = properties.getProperty(key);
  19. Object value = Class.forName(beanPath).newInstance();
  20. beans.put(key,value);
  21. }
  22. }catch (Exception e){
  23. e.printStackTrace();
  24. }
  25. }
  26. public static Object getBean(String beanName){
  27. return beans.get(beanName);
  28. }
  29. }

ApplicationContext

构建核心容器时,创建对象的采取的策略是采用立即加载的方式,意味着读取配置文件后会通过反射的方式创建配置文件中的bean对象
Spring——IoC&DI - 图2
ClassPathXmlApplicationContext
加载类路径下的配置文件,要求配置文件必须在类路径下,否则无法加载。

  1. new ClassPathXmlApplicationContext("applicationContext.xml");

FileSystemXmlApplicationContext
加载磁盘任意路径下的配置文件 注意需要有文件的访问权限
AnnotationConfigApplicationContext
读取注解创建容器的

Setter 注入

  1. public class User {
  2. public class User {
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. @Override
  18. public String toString() {
  19. return "User{" +
  20. "name='" + name + '\'' +
  21. ", age='" + age + '\'' +
  22. '}';
  23. }
  24. }

applicationContext.xml增加user bean

  1. <bean id="user" class="cn.java.bean.User">
  2. <property name="name" value="小王"></property>
  3. <property name="age"><value>20</value></property>
  4. </bean>

Bean实例化

1 默认构造器实例化
配置id和class属性之后,且没有其他属性和标签是,采用的就是默认的构造函数创建bean对象,注意如果类中没有默认构造函数,对象无法创建
2 实例工厂方法

  1. public class InstanceFactory {
  2. public AccountServiceImpl getAccountService(){
  3. return new AccountServiceImpl();
  4. }
  5. }
  1. <bean id="instanceFactory" class="com.jdbc.factory.InstanceFactory"></bean>
  2. <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

3 静态工厂方法
使用工厂中的静态方法创建对象(使用某个类的中静态方法创建对象,并存入spirng容器)

  1. public class InstanceFactory {
  2. public static AccountServiceImpl getAccountService(){
  3. return new AccountServiceImpl();
  4. }
  5. }
  1. <bean id="accountService" class="com.jdbc.factory.InstanceFactory" factory-method="getAccountService"></bean>

构造器注入

User.java增加

  1. public User(String name, int age) {
  2. this.name = name;
  3. this.age = age;
  4. }

applicationContext.xml修改user bean

  1. <bean id="user" class="cn.java.bean.User">
  2. <constructor-arg><value>小明</value></constructor-arg>
  3. <constructor-arg><value>26</value></constructor-arg>
  4. </bean>
  1. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. User user = context.getBean("user", User.class);
  3. System.out.println(user);

引用其他bean

创建UserInfo.java

  1. public class UserInfo {
  2. private User user;
  3. public User getUser() {
  4. return user;
  5. }
  6. public void setUser(User user) {
  7. this.user = user;
  8. }
  9. @Override
  10. public String toString() {
  11. return "UserInfo{" +
  12. "user=" + user +
  13. '}';
  14. }
  15. }

applicationContext.xml修改userInfo bean

  1. <bean id="userInfo" class="cn.java.bean.UserInfo">
  2. <property name="user" ref="user"></property>
  3. </bean>

自动装配

安装名称自动装配

  1. <bean id="userInfo" class="cn.java.bean.UserInfo" autowire="byName"></bean>

安装属性类型自动装配

  1. <bean id="userInfo" class="cn.java.bean.UserInfo" autowire="byType"></bean>

作用范围scope

标签scope指定bean的作用范围,取值参数:
singleton:单例模式,默认是单例模式创建bean 对象
prototype :多例模式
request:作用于web应用的请求范围
session:作用于web应用的回话范围
global-session:作用于集群环境的全局会话范围,如果是在单机实例则等于session

  1. <bean id="userInfo" class="cn.java.bean.UserInfo" ></bean>
  2. <bean id="userInfo" class="cn.java.bean.UserInfo" scope="singleton"></bean>

生命周期

单例对象 多例对象
创建 当容器创建时对象创建 使用对象时才创建
销毁 容器销毁 Java的回收机制

依赖注入(Dependency Injecttion),

当前类需要使用其他类的对象,由spring 为我们提供,我们只需要在配置文件中说明,依赖关系的维护称作依赖注入。
能注入的类型
1 基本类型和String
2 其他bean类型(在配置文件中或者注解配置的bean)
3 复杂类型/集合类型

XML注入的方式

使用构造函数(Constructor injection)
使用到标签是constructor-arg其中主要的属性
type 用于指定要注入的数据的数据类型,该类型也是构造函数中某个或者某些参数的类型
index 指定注入的数据给构造函数中指定的索引位置的参数赋值,索引的位置从0开始
name 用于指定给构造函数中指定名称的参数赋值
value 用于给基本类型和String类型的数据
ref 用于指定其他的bean类型数据,它指的是spring的IOC核心容器配置中出现过的bean对象

  1. private String name;
  2. private String age;
  3. private Date birthday;
  4. public AccountServiceImpl(String name, String age, Date birthday) {
  5. this.name = name;
  6. this.age = age;
  7. this.birthday = birthday;
  8. }
  1. <bean id="accountService" class="com.spring.service.impl.AccountServiceImpl">
  2. <constructor-arg name="name" value="test"></constructor-arg>
  3. <constructor-arg name="age" value="18"></constructor-arg>
  4. <constructor-arg name="birthday" ref="now"></constructor-arg>
  5. </bean>
  6. <bean id="now" class="java.util.Date"></bean>

设置注入(Setter Injection)
property 标签

  1. <bean id="accountServiceSet" class="com.spring.service.impl.AccountServiceImpl">
  2. <property name="name" value="test"></property>
  3. <property name="age" value="20"></property>
  4. <property name="birthday" ref="now"></property>
  5. </bean>
  6. <bean id="now" class="java.util.Date"></bean>

集合类型
list 结构注入标签 array list set
map 结构注入标签 map props

  1. <bean id="accountServiceList"
  2. class="com.spring.service.impl.AccountServiceImpl">
  3. <property name="myStrs">
  4. <array>
  5. <value>a</value>
  6. <value>b</value>
  7. <value>c</value>
  8. </array>
  9. </property>
  10. <property name="myList">
  11. <list>
  12. <value>e</value>
  13. <value>f</value>
  14. <value>g</value>
  15. </list>
  16. </property>
  17. <property name="mySet">
  18. <set>
  19. <value>h</value>
  20. <value>i</value>
  21. <value>j</value>
  22. </set>
  23. </property>
  24. <property name="myMap">
  25. <map>
  26. <entry key="k1" value="v1"></entry>
  27. <entry key="k2">
  28. <value>v2</value>
  29. </entry>
  30. </map>
  31. </property>
  32. <property name="myProps">
  33. <props>
  34. <prop key="p1">v1</prop>
  35. <prop key="p2">v2</prop>
  36. </props>
  37. </property>
  38. </bean>

注解

context:component-scan 需要扫描的包路径

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <context:component-scan base-package="com.spring"></context:component-scan>

Component

把当前的类对象存入spring 容器中 其中value 用于指定bean的id,当我们不写value时,它的默认值是当前类名,且首字母小写。

Controller

同Component功能更一致,一般用在表现层

Service

同Component功能更一致,一般用在业务层

  1. @Service("accountService")
  2. public class AccountServiceImpl implements IAccountService {
  3. public void saveAccount() {
  4. System.out.println("业务层数据存储");
  5. }
  6. }

Repository

同Component功能更一致,一般用在持久层

  1. @Repository("accountDao")
  2. public class AccountDaoImpl implements IAccountDao {
  3. public void saveAccount() {
  4. System.out.println("持久层保存数据");
  5. }
  6. }

autowired

自动按照类型注入。只要容器中唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。出现位置 可以是变量 也可以是方法上。在使用自动注解是 set方法就不是必须的了。

  1. @Service("accountService")
  2. public class AccountServiceImpl implements IAccountService {
  3. @Autowired
  4. private IAccountDao accountDao;
  5. public void saveAccount() {
  6. accountDao.saveAccount();
  7. }
  8. }

Qualifier

在按照类中注入的基础上在按照名称注入。在给类成员注入时不能单独使用,需要和autowired一起使用,但是给方法参数注入时可以。
属性
value 用于指定注入的bean的id
######Resource
作用:直接按照bean的id注入。它可以独立使用
属性
name:用于指定bean的id

  1. @Service("accountService")
  2. public class AccountServiceImpl implements IAccountService {
  3. @Resource(name = "accountDao")
  4. private IAccountDao accountDao;
  5. public void saveAccount() {
  6. accountDao.saveAccount();
  7. }
  8. }
@value
主要作用在基本类型和String类型的数据
属性:value 用于指定数据的值,它可以使用spring的SpEL($(表达式))
######@Scope
作用和在bean标签中使用scope属性实现的功能更是一样的,都是指定bean的作用范围,属性是value 指定范围的取值 singleton prototype
######@PostConstruct
用于指定初始化的方法
######@PreDestroy
用于指定销毁方法