1 Spring IoC(控制反转)

通过前面的内容,我们已经可以使用 Spring 框架实现对自定义的 Java 对象管理,由 Spring 框架加载对象,实例化对象,放入容器。其实这就是 Spirng 的核心功能之 IoC,那么什么是 IoC 呢?什么又是容器呢?

1.1 什么是 IoC?

百度百科:
Inversion of Control,缩写为 IoC,是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称 DI),还有一种方式叫 “依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

慕课解释:
IoC 是一种设计模式。将实例化对象的控制权,由手动的 new 变成了 Spring 框架通过反射机制实例化。

那为什么使用 IoC 做控制反转,它到底能帮助我们做什么。

假设有这样几个类:

  • UserServlet
  • UserService 接口
  • UserServiceImpl 接口的实现类
  • UserDao

代码如下:

/*
UserServlet  作为控制器 接收浏览器的请求
*/
public class UserServlet extends HttpServletRequest {//用户的业务类 提供逻辑处理 用户相关的方法实现private UserService userService;public void service(HttpServletRequest request,HttpServletResponse response){//手动实例化UserService接口的实现类userService = new UserServiceImpl();List<User> list =  userService.findAll();//省略结果的跳转代码     }
}
/*
用户的业务接口UserService
*/
public interface UserService{public List<User> findAll();
}
/*
UserServiceImpl 作为用户的业务实现类 实现类UserService的接口
*/
public class UserServiceImpl implements UserService{//用户的Daoprivate UserDao userDao;public List<User> findAll(){//手动实例化DaouserDao = new UserDao();return userDao.findAll();}
}

问题分析:
上面的代码有什么问题吗? 按照我们学习过的知识…答案是没有。因为 Dao 只要数据源编写代码正确, 完全可以实现数据的增删改查 ,对吗?

但是我们发现代码耦合性太强,不利于程序的测试

因为 userServlet 依赖于 userService ,而 userService 依赖于 userDao , 那么只要是被依赖的对象,一定要实例化才行。所以我们采取在程序中硬编码,使用 new 关键字对对象做实例化。不利于测试,因为你不能确保所有使用的依赖对象都被成功地初始化了。有的朋友很奇怪,对象实例化有什么问题吗? 如果构造参数不满足要求,或者你的构造进行了逻辑处理,那么就有可能实例化失败;

代码也不利于扩展:

假设一下,我们花了九牛二虎外加一只鸡的力气,整理好了所有的类使用的依赖,确保不会产生问题,那么一旦后续我们的方法进行扩充,改造了构造函数,或者判断逻辑,那么是不是所有手动 new 对象的地方都需要更改? 很明显这就不是一个优雅的设计。

解决方式:
Spring 的 IoC 完美的解决了这一点, 对象的实例化由 Spring 框架加载实现,放到 Spring 的容器中管理,避免了我们手动的 new 对象,有需要用到对象实例依赖,直接向 Spring 容器要即可,而一旦涉及到对象的实例修改,那么 只需更改 Spring 加载实例化对象的地方,程序代码无需改动,降低了耦合,提升了扩展性。

1.2 容器的使用

刚刚解释了 IoC 的作用,是对象的实例化由主动的创建变成了 Spring 的创建,并放入容器管理,那么这个容器是什么?

日常生活中有很多的容器,例如:水桶、茶杯、酒瓶,那么他们都有一个特点,就是装东西。而 Spring 的容器,就是装对象的实例的。

1.3 IoC 容器的体系结构

Spring 的容器有两个:

  • BeanFactory
  • ApplicationContext

它们两个都是接口,那么有什么区别呢?见图如下:

BeanFactory 才是 Spring 容器中的顶层接口。 ApplicationContext 是它的子接口。简而言之,BeanFactory 提供了配置框架和基本功能,并在 ApplicationContext 中增加了更多针对企业的功能。

BeanFactory 和 ApplicationContext 的区别:
创建对象的时间点不一样。ApplicationContext是在读取配置文件时,默认就会创建对象。BeanFactory是什么时候使用,什么时候创建对象。

1.4 IoC 容器实例化的方式

上面已经知道 Spring 的容器是通过接口 org.springframework.context.ApplicationContext 表示,并负责实例化,配置和组装 Bean 对象。容器通过读取 xml 文件中的配置信息来获取关于实例化对象,配置属性等命令。

ApplicationContext 只是一个接口,我们通常创建 ClassPathXmlApplicationContext 的实例或者 FileSystemXmlApplicationContext 的实例。前者是从类路径中获取上下文定义文件,后者是从文件系统或 URL 中获取上下文定义文件 。例如:

public class test {public static void main(String[] args) {//ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");UserServiceImpl userService = (UserServiceImpl) context.getBean(UserService.class);userService.saveUser();}
}

代码解析:
第3行注释掉的代码是加载类路径下的配置文件,一般来说 Java 工程放在 src 目录下。我们使用的是 Maven 工程,放在 resources 目录下。

第4行代码是通过加载本地 D 盘目录下的文件来初始化容器, 实例化 bean 对象。

结论
通过上面的两种方式测试,发现都可以成功初始化容器, 获取测试的 bean 对象实例。
也证明了容器的初始化可以创建 ClassPathXmlApplicationContext 也可以创建 FileSystemXmlApplicationContext 的实例。

1.5 IoC 容器的使用实例

首先创建一个User类如下:

public class User {public User(){System.out.println("我被Spring实例化了");}
}

容器的初始化必须先配置 xml 文件,代码如下:

<bean id="user"  class="com.ty.entity.User" ></beans>

加载配置文件:

ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

调用方法:

context.getBean("user")

2 Spring IoC (控制反转)之 xml 配置

2.1 bean 标签中的属性介绍

核心配置文件回顾

<bean id="user" class="com.ty.entity.User" ></bean>

在上面的代码中可以看到,在 bean 标签中有两个属性,一个是 id 一个是 class。那么在 bean 标签中都有哪些属性呢?

属性列表

属性 意义
id 定义的唯一标识
name 同 id 的意义一致
class
factory-bean 工厂对象
factory-method 工厂方法
init-method 初始化执行的方法
destroy-method 销毁执行的方法
scope 对象的作用域
lazy-init 懒加载
autowire 依赖注入
depends-on 依赖于某个实例

疑问导出
上述属性是配置 bean 标签中可以选择的属性,一般来讲我们无需配置所有,那么如何有选择地配置这些属性呢?

2.2 id 和 name 标签的使用

查看官方文档得知 Spring 的容器会给初始化的每个 bean 都定义一个或多个标识符。这些标识符在容器内必须是唯一的。一个 bean 通常只有一个标识符。而 name 和 id 都可以起到标识符的作用。

所以在 XML 配置文件,我们一般使用 id 或者 name 属性,定义 bean 的唯一标识,这样我们才能通过定义好的唯一标识,从 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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" name="user2" class="com.ty.entity.User" ></bean>
</beans>

测试代码如下:

public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");System.out.println(context.getBean("user"));System.out.println(context.getBean("user2"));
}

运行结果:

结果分析:
我们通过 bean 标签中的 id 属性 user, 或者使用 bean 标签中的 name 属性 user2, 都可以得到 Spring 容器中的 user 对象的示例,而且打印的地址是同一个。之前说容器中的实例默认都是单例的,在这里也得到了证明。

1.3 class 属性

bean 标签的定义实质上是创建一个或多个对象的方法。当 xml 文件被解析加载的时候,使用该 bean 定义封装的配置数据来创建(或获取)实际对象,而创建获取的对象是谁呢?就是通过 class 属性中定义的类的全路径来指定。

一般来讲 class 中的类实例化有两种方式:

  • 一种是反射,相当于我们使用的 new 关键字。这种也是我们常用的方式。当然不要忘记提供无参数的构造方法(类中默认有无参构造,但是如果自定义了有参构造,默认的无参不会提供)
  • 一种是工厂模式,需要借助于 factory-beanfactory-method 两个属性,这种方式不常用,了解即可。

1.4 factorybean 和 factorymethod 属性

这两个属性主要用于工厂模式实例化 bean 的时候使用,不是很常见。工厂模式有两种,这里分别做个实例,帮助大家理解。

静态工厂模式实例:

<!--applicationContext的配置bean节点-->
<bean id="user" class="com.wyan.entity.User" factory-method="createUserInstance"/>

创建 bean 示例的 Java 工厂类:

public class User {    private static User user = new User();   private User() {}public static User createUserInstance() {return user;}
}

解析:在定义使用静态工厂方法创建的 bean 时,class 属性指定的是被创建的类,包含静态的方法,并使用 factory-method 属性来指定工厂方法本身名称。

普通工厂模式:

<!--spring实例化工厂对象 用于创建java实例 -->
<bean id="beanFactory" class="com.wyan.factory.BeanFactory"></bean>
<!-- 被工厂创建的对象实例 -->
<bean id="user1" factory-bean="beanFactory" factory-method="createUser1"/>

工厂类代码:

public class BeanFactory {private static User1 user1 = new User1();private static User2 user2 = new User2();public User1 createUser1() {return user1;}public User2 createUser2() {return user2;}
}

解析:先创建各个对象实例的工厂对象到容器中,自身的 bean 标签将 class 属性保留为空,并在 factory-bean 属性中指定当前容器中的工厂 Bean 名称,再使用 factory-method 属性设置创建示例的方法名称。

1.5 init-method 和 destroy-method 属性的使用

这两个属性比较好理解 init-method 就是 bean 被初始化后执行的方法,destory-method 就是 bean 被销毁执行的代码。

我们来个测试类:

public class User {public User(){System.out.println("我被spring实例化了");}public void initMethod(){System.out.println("user类实例化时候执行的代码");}public void destoryMethod(){System.out.println("user类实例被销毁时候执行的代码");}
}

配置文件:

<bean id="user" name="user2" class="com.ty.entity.User" init-method="initMethod" destroy-method="destoryMethod" ></bean>

测试代码:

public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}

运行结果:

有个小疑问:销毁语句没打印呢?那是因为并没有调用容器的销毁方法。

改造测试代码如下:

public static void main(String[] args) {AbstractApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");context.close();
}

解析ApplicationContext 没有 close 方法,所以使用它的子类

运行结果:

1.6 其余属性作用

  • scope :指定示例的作用范围;
  • lazy-init :表示是否为懒加载;
  • autowire :指定属性注入方式;
  • depends-on: 表示是否有依赖的 bean 对象。

1.7 构造函数的使用

实例:
改造 User 类:这是一个普通的 Java 类对象,包含两个属性及其 get 和 set 方法,并且提供了空参构造和有参构造,为了测试方便再覆写一个 toString 方法。

public class User {private Integer id;private String name;public User() {}public User(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
}

xml 配置文件方式:

<bean id="user"  class="com.ty.entity.User"  ><constructor-arg name="id" value="1"></constructor-arg><constructor-arg name="name" value="ty"></constructor-arg>
</bean>

测试结果:

其实对于有参构造实例化对象而言,使用一个标签 constructor-arg 即可,表示构造的参数,如果有多个,可以继续添加,这里不多做演示。

1.8 小结

  • 容器内部命名唯一标识可以通过 id 也可以通过 name;
  • 实例化对象有两种方式 反射模式和工厂模式;
  • 如果是反射模式,那么必须配置 class 属性,因为需要用 class 属性中类的全路径来实例化 bean 对象;
  • 如果需要在类实例化初始化参数,可以使用 init 方法也可以使用有参构造。

3 Spring 框架模拟实现

3.1 思路分析:

  • Spring 框架的容器 是一个接口 ApplicationContext 和接口的实现类 ClassPathXmlApplicationContext 来初始化的;
  • 在初始化容器对象的时候需要传递 xml 配置文件的位置;
  • xml 的配置文件中主要是通过 bean 标签对 Java 的类进行描述:类的路径、类的标识、类的构造参数等等;
  • 容器初始化后需要解析 xml 配置文件的各个 bean 标签;
  • 实例化的对象如果有参数或者构造方法,那么也需要给参数赋值;

3.2 自定义容器的接口和实现类

容器接口名称改为 SpringContextXmlSpringContext 区别于框架的接口和实现类。

接口中定义方法 getBean 用于获取容器内的实例,实现类定义有参构造用于接受初始化时候的配置文件路径。

接口代码:

public interface SpringContext {public Object getBean(String beanName);
}

实现类代码:

public class XmlSpringContext  implements SpringContext  {Map<String,Object> map = new HashMap<String,Object>();public XmlSpringContext (String filename){  }public Object getBean(String beanName){        return map.get(beanName);}
}

代码解析:

  • map 用于存储实例化的 bean 对象 ;
  • 有参构造方法逻辑暂时为空,下面会做实现,加载文件实例化对象在方法内部;
  • getBean 的方法用于通过 key 获取 map 中存储的实例。

3.3 测试对象的实例化

为了测试对象的实例化,我们自定义 UserServiceUserServiceImpl 作为测试的接口对象和实现类。

UserService代码如下:

public interface UserService {public void deleteById(Integer id);
}

UserServiceImpl代码如下:

public class UserServiceImpl implements UserService {//持久层的dao属性private UserDao userDao;public UserDao getUserDao() {return userDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;}//实现接口的方法public void deleteById(Integer id) {System.out.println("删除的方法执行");}
}

自定义模拟框架的xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans><bean name="userDao" class="com.ty.dao.UserDaoImpl"></bean><bean name="userService" class="com.ty.service.UserServiceImpl"><property name="userDao" ref="userDao"></property></bean>
</beans>

代码解析:userDao 的 bean 需要实例化 是因为 service 用到了它的引用,定义在 property中。

编写测试类加载文件测试:

public class TestSpring {@Testpublic void test() {//初始化容器(读取配置文件 构建工厂)SpringContext context =new XmlSpringContext("applicationContext.xml");UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");userService.deleteById(1);System.out.println(userService.getUserDao());}
}

代码解析:这里只是测试能否获取对象调用方法,如果控制台打印证明案例成功。

3.4 容器对象的实现类构造函数具体代码

思路分析

  • 读取初始化时候传递的文件路径;
  • 通过 SAXReader 解析 xml 文件的节点得到 beans 节点下对应多个 bean 节点集合;
  • 每一个 bean 表示一个对象,都需要被初始化,所以需要循环遍历集合;
  • 在循环遍历的过程中获取 id 属性和 class 属性,id 属性作为存入 map 的 key,class 属性用于反射实例化对象,并存储 map 的 value;
  • 继续解析子节点,如果有参数,反射获取 method 执行参数赋值。

完整代码:

public XmlSpringContext(String filename){        // xml文件的解析器SAXReader  sr = new SAXReader();try {//构建一个直接通向我们配置文件路径 的输入流InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filename);//文档模型对象Document doc = sr.read(inputStream);//获取根标签Element root = doc.getRootElement();//获取当前根标签的子标签List<Element>  beans = root.elements("bean");for(Element bean:beans){         String key = bean.attributeValue("name");String value = bean.attributeValue("class");Class<?> myclass = Class.forName(value);//当前对象Object obj = myclass.newInstance();map.put(key, obj);              List<Element> elements = bean.elements("property");if(elements.size()>0){for(Element pro: elements){String av = pro.attributeValue("name");//dao--->setDao//方法名String methodName="set"+(av.charAt(0)+"").toUpperCase()+av.substring(1,av.length());                        //方法参数String refvalue = pro.attributeValue("ref");Object refobj = map.get(refvalue);                        //根据方法名称获取方法对象MethodMethod method = myclass.getMethod(methodName,refobj.getClass().getInterfaces()[0]);                    method.invoke(obj, refobj);}}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

运行结果:

3.5 小结

本章节模拟了 Spirng 加载文件的过程和实例化对象的过程,当然这个过程只是模拟 Spring 的框架的思路,而并不是真正的 Spring 框架源码,实际源码远比这个要复杂的多。

4 Spring IoC(控制反转)之注解配置

4.1 前言

前面学习了 Spring IoC 的 xml 配置实现,整理了 xml 方式的实现步骤,并且模拟了 Spring 的容器如何加载解析 xml 配置文件,那么我们发现一个现象:

对于 Spring 的 bean 管理而言,如果全部通过 xml 文件实现的话,配置文件的内容未免过于臃肿。因为每个类的实例化都需要一个 bean 标签。而在大型工程中存在几百上千个类,这时Spring 的 xml 文件的维护成本就会太高。

疑问导出:
Spring 能否有更方便的方式实现 IoC 呢?Spring 提出了两种 IoC 实现方式,一种是基于配置文件,一种是基于注解形式。

4.2 步骤介绍

回顾 Spring IoC 的 xml 实现步骤:

  • 使用 new 关键字对 ClassPathXmlApplicationContext 做初始化;
  • 在初始化容器对象的构造传入 xml 配置文件的位置 ;
  • 在配置文件中通过 bean 标签可以对类进行描述:类的路径、类的标识、类的构造参数等等。

注解实现 IoC 的思路分析:

  • Spring 容器一样需要初始化;
  • 一样需要传入 xml 配置文件 ----- 需要描述清楚 需要被实例化的类都有哪些;
  • xml 文件中不需要使用 bean 标签描述被实例化的类 ------ 使用注解实现 IoC 管理目的就是为了简化 bean 标签的配置。

核心思想:
开发人员无需使用 XML 来描述 bean ,而是将配置移入 Java 的类本身,通过 Spring 支持的组件扫描来实现。

4.3 工程示例:

创建工程:略。

导入依赖:

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version> 5.3.17</version></dependency>
</dependencies>

项目代码:
为了测试,在工程内部创建 UserDao 的接口和 UserDao 的实现类 UserDaoImpl。

UserDao 代码如下:

public interface UserDao {public void saveUser();
}

UserDaoImpl 的实现类代码如下:

@Repository
public class UserDaoImpl implements  UserDao {public void saveUser() {System.out.println("执行dao的保存方法");}
}

注意事项: 由于我们是基于注解的方式实现对 bean 的管理,所以在实现类上面需要添加一个注解 @Repository,此注解的作用是为了 Spring 的容器启动后自动检测这些被注解的类并注册相应的 bean 实例到容器中。

Spring 的核心配置文件:

<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.wyan.dao"></context:component-scan></beans>

可以看出跟 xml 的配置文件有很大的区别:

context-component-scan 标签:Spring 框架自定义的 xml 标签,通过 base-package 属性指明需要被自动扫描实例化的类所在位置。此案例中, com.ty.dao 下的类是需要扫描自动注入容器的。

Tips:不是在 com.ty.dao 下的所有类都会自动注入到容器,而是要搭配注解:比如@Repository

测试类运行结果:

代码解析:
测试类其实跟 xml 的方式一模一样,我们本次测试的目的一样也是通过 Spring 容器管理注册的 bean 对象,只不过对象的实例化方式换成了注解,控制台成功输出了测试语句,说明案例搭建完成。

4.4 小结

  • Spring 容器初始化一样需要 xml 文件,目前是 xml 文件搭配注解管理 bean 并不是纯注解开发;
  • Spring 的 xml 配置文件中使用 context:component-scan 标签指定注册 bean 的类所在目录位置;
  • 自定义编写的 Java 类,如果需要被自动扫描注入容器,必须搭配注解。

5 Spring IoC(控制反转)之常用注解

注解配置和 xml 配置实现的功能都是一样的,只不过实现的方式不同,那么也就是说,xml 文件可以实现的,通过注解都可以完全办得到。比如实例化对象,设置属性,设置作用范围,生命周期的方法执行等等…

5.1 注解分类

按功能划分:

  • 创建对象: 对应的就是在 xml 文件中配置的一个 bean 标签,可以定义 id、name、class 等属性;
  • 注入数据: 对应的就是在 bean 标签下,使用 property 标签给类中的依赖属性赋值;
  • 作用范围: 对应的就是设置 bean 标签的 scope 属性,不设置默认也是单例;
  • 生命周期: 对应的就是设置 bean 标签的 init-method 和 destroy-method 方法。

5.2 创建对象的注解

我们都知道,JAVAEE 体系结构,一般开发分为三个层级:

  • 表现层: 主要作用为处理数据生成静态的页面响应给浏览器展示 ;
  • 业务层: 主要作用为业务逻辑代码编写,数据的获取,数据的封装返回等等操作都在这里;
  • 持久层: 主要作用为跟数据库打交道,对于数据的持久化操作等。

创建不同层级对象的注解:

  • @Component :一般用于通用组件的类上使用的注解;
  • @Service : 一般用于业务层类上使用的注解;
  • @Controller : 一般用于控制层类上使用的注解;
  • @Repository :一般用于持久层类上使用的注解。

官网解释:

  • Spring 提供进一步典型化注解:@Component@Service,和 @Dao
  • @Component 是任何 Spring 托管组件的通用构造型。
  • @Repository@Service@Controller@Component 针对更特定用例的专业化(分别在持久性,服务和表示层)。

慕课解释:
@Component 注解是 Spring 框架中通用的一个注解,用于组件扫描实例化对象使用, 其余的三个注解 @Controller@Service@Repository 都是 @Component 注解的衍生注解,作用跟 @Componet 注解的作用一致。

那么意义在于, 三个注解,对应的是三个开发层级 ,一般来讲我们将 @Controller 作为表现层的使用,@Service 作为业务层的注解,@Repository 作为持久层使用的注解。下面通过案例进行演示。

5.3 创建对象示例

分别创建三个层级对应的代码:

表现层的UserController 代码:

@Controller
public class UserController {public void saveUser(){System.out.println("这是controller的执行保存..");}
}

业务层的UserService 和实现类代码:

public interface UserService {public void saveUser();
}@Service
public class UserServiceImpl implements  UserService {public void saveUser() {System.out.println("执行service中的保存逻辑");}
}

项目结构如下:

类虽然看起来很多,实际没有业务逻辑代码,只不过在各个层级使用了三个注解来注入到容器,目的是测试当 Spring 的配置文件加载扫描后,是否可以从容器中获取三种注解(@Controller @Service @Repository)注入的 bean 对象。

Tips: Spring 的配置文件 context:component-scan 标签的扫描层级 需要包含三个包路径,本案例的配置如下:

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

测试类与测试结果:

结论:
三个注解都可以将对象注入到 Spring 的容器,那么开发时按照规范分层开发,使用对应的注解。但它并不是必须这么做,你使用任意一种都可以,只不过,代码的可读性会差。
所以,我们一般表现层使用 @controller ,业务层使用 @service, 持久层使用 @Repository

至于 @Component ,如果有其余的类,不属于三个层级,可以采用 @Component 作为通用组件扫描注入容器。

5.4 注解注入规则

我们知道,Spring 这个容器本质是个 map 集合来存储实例化后的对象。既然是个 map 集合,就应该对应的有 key 和 value。value 肯定是实例化后的 bean ,那么 key 是什么呢?

注入规则:

  • 四种注解都支持 value 的属性作为自定义的 bean id ;
  • 如果 value 属性没有指定,那么默认以类的简单名称(类名首字母小写)作为 bean 对象的 id。

所以当我们使用没有自定义 id 的注解时,可以通过每个类的首字母小写来获取对象实例,那么如果有了自定义的 id,上述代码是否继续可用呢?

自定义 id 获取实例:
改造类上面的注解,设置自定的 id,更改的注解如下:

@Controll("uc")
@Service("us")
@Repository("ud")

测试结果:

结果分析:
为了区分测试结果,在测试代码中只修改了 dao 的获取方式,将 id 改成了 ud 。service 和 controller 并没有修改。

从控制台打印可以看到,只有 dao对象可以成功获取,service 和 controller 都失败了,因为我们已经使用了自定义的 id,所以容器中没有默认的以类名作为 id 的 bean 对象实例。

ps:以上内容来自对慕课教程的学习与总结

Spring核心功能之控制反转(IOC)相关推荐

  1. 浅析Spring——控制反转IoC

    目录 1. IoC理论推导 2. 什么是IoC? 3. 引入DI 4. IoC容器 5. 注入对象的四种方法 1.基于接口 2.基于setter 3.基于构造函数 4.基于注解 6. 两种IoC实现方 ...

  2. 控制反转IOC与依赖注入DI

    为什么80%的码农都做不了架构师?>>>    1. IoC理论的背景 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最 ...

  3. 控制反转(IoC)与依赖注入(DI)详解

    文章目录 什么是控制反转(IoC) 控制反转(IoC)有什么作用 控制反转(IoC)是怎么分类的 依赖注入 接口注入 Setter方法注入 构造器注入 依赖查找 上下文依赖查找(Contextuali ...

  4. 控制反转IOC、依赖注入DI的详细说明与举例

    文章目录 引入 IOC介绍 IOC的实现 通过构造函数注入依赖 通过 setter 设值方法注入依赖 依赖注入容器 IOC优缺点 优点 缺点 阅读时忽略语言差异,参考了很多其他博主内容,参考博文在最后 ...

  5. 26(2)Spring 核心功能演示 + 面试题

    Spring 核心功能演示 + 面试题 Spring Framework 简称 Spring,是 Java 开发中最常用的框架,地位仅次于 Java API,就连近几年比较流行的微服务框架 Sprin ...

  6. 依赖倒置(DIP),控制反转(IoC)与依赖注入(DI)

    DIP,IoC与DI概念解析 依赖倒置 DIP(Dependency Inversion Principle) DIP的两大原则: 1.高层模块不应该依赖于低层模块,二者都应该依赖于抽象. 2.抽象不 ...

  7. 自己实现spring核心功能 一

    聊聊spring spring对于java开发者来说,是最熟悉不过的框架了,我们日常开发中每天都在使用它.它有着各种各样的好处,简单易用,得心应手... ... 我们一说到spring就会讲到ioc ...

  8. Spring依赖注入和控制反转

    文章目录 1.依赖注入 1.1.依赖注入和控制反转的概念 1.2.依赖注入的实现方式 1.3.控制反转的具体实现 1.4.依赖注入的具体实现 1.5.依赖注入和控制反转总结 1.依赖注入 1.1.依赖 ...

  9. 控制反转 java_控制反转( Ioc)快速入门

    2.1 什么是控制反转(IOC:Inverse of Control) IOC反转控制,实际上就是将对象的创建权交给了Spring,程序员无需自己手动实例化对象. 可以看出来工厂的作用就是用来解耦合的 ...

  10. PHP依赖注入(DI)和控制反转(IoC)详解

    这篇文章主要介绍了PHP依赖注入(DI)和控制反转(IoC)的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程 ...

最新文章

  1. Python生物信息学③提取差异基因
  2. javaweb学习总结(三十一)——国际化(i18n)
  3. HUST1024 dance party(最大流)
  4. Visual Studio解决freopen等函数报错函数安全问题
  5. 从RCNN到Fast RCNN,再到Faster RCNN的奥秘
  6. reactjs中的事件处理
  7. 如何免费注册Coursera课程
  8. pytorch中的gather函数_Pytorch中Emdedding函数的解释及使用方法
  9. (转)SpringMVC学习(七)——Controller类的方法返回值
  10. 【Unity开源项目精选】Unity引擎源码的C#部分
  11. Android Dialog和PopWindow的使用
  12. 2013级C++第2周(春)项目——结构体应用大体验
  13. Vue.js 第5章 webpack配置
  14. android客户端功能,人人网Android版手机客户端功能详解
  15. kaggle之识别谷歌街景图片中的字母
  16. linux系统浏览器无声音,在Deepin 20系统中外接显示器切换后浏览器没有声音的解决经历...
  17. Square:从今天开始抛弃Fragment吧!
  18. 中国科学技术大学2021年秋季学期可编程逻辑器件原理及应用试题回忆版
  19. 保研经验帖(二)夏令营经验分享
  20. 7-5 表格输出(c语言)

热门文章

  1. 最近抖音上虚拟元宇宙项目-猜歌名,代码解析
  2. tp5restful API的搭建(2)
  3. matlab 0x000007b,windows应用程序无法正常启动(0x000007b)怎么办_windows应用程序无法正常启动(0x000007b)解决办法_飞翔教程...
  4. arcgis导出的图片无效_img影像在arcgis中生成JPG图片空白求解
  5. Linux+bridge做透明网桥
  6. 美国大学计算机软件专业排名,美国大学计算机专业排名
  7. 开发者必备的网站。javascript手册,css手册
  8. linux 无法切换为root权限不够,Linux入门教程:Ubuntu16.04解决无法切换root权限的问题,...
  9. 【期末复习】现代管理科学基础
  10. 技术博客对找工作有帮助吗?