前言
在前一篇文章中荔枝已经初步了解了Spring并通过一个入门案例了解了Spring的基本操作,接下来荔枝就要梳理Spring中的最为重要的知识点之一——IoC控制反转,控制反转和属性注入均是基于Java中的反射机制来实现的。所以学习这个知识点之前必须要学习Java的反射机制。在这篇文章中荔枝会着重梳理两种获取bean和操作bean的方式,其中基于注解方式来操作bean更为常用。
文章目录
前言
一、概述
1.1 依赖注入
1.2 IOC容器在Spring中的实现
二、基于XML管理bean
2.1 获取bean对象的三种方式
2.2 接口实现类的获取
2.3 注入
2.3.1 Spring中基于setter注入
2.3.2 基于构造器进行注入
2.4 特殊值的注入
2.4.1 字面量赋值
2.4.2 null值注入
2.4.3 xml实体
2.4.4 CTADA节
2.5 特殊类型的注入
2.5.1 对象类型属性注入
2.5.2 数组类型属性注入
2.5.3 集合类型属性注入
2.5.4 P命名空间注入
2.5.5 引用外部文件注入
2.6 bean的作用域
2.7 bean的生命周期
2.7.1 具体的生命周期过程
2.7.2 bean的后置处理器
2.8 基于xml自动装配
三、基于注解管理bean
3.1 通过注解创建bean对象
3.2 @Autowired注入
3.3 @Resource注入
3.4 全注解开发
总结
一、概述
loC是Inversion of Control的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。Spring通过loC容器来管理所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由IoC容器管理的Java对象称为Spring Bean,它与使用关键字new创建的Java对象没有任何区别。loC容器是Spring框架中最重要的核心组件之一,它贯穿了Spring从诞生到成长的整个过程。
容器放bean对象,使用的是map集合。
控制反转的具体实现方式:依赖注入DI
1.1 依赖注入
依赖注入指Spring创建对象的过程中,将对象依赖属性通过配置进行注入,依赖注入常见的实现方式包括两种:set注入和构造注入。
IOC就是一种控制反转的思想,而DI是对IoC容器对bean管理的一种具体实现。Bean管理指的是是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)
1.2 IOC容器在Spring中的实现
Spring提供了IoC容器的两种实现方式:
- BeanFactory接口:该接口是IoC容器的基本实现,是Spring内部使用的接口。一般我们用的都是它的子接口ApplicationContext。
- ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
类型名 | 描述 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的ML格式的配置文件创建IOC容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取ML格式的配置文件创建IOC容器对象 |
ConfigurableApplicationContext | ApplicationContext的子接口,包含一些扩展方法refresh()和close(),让ApplicationContext具有启动、关闭和刷新上下文的能力. |
WebApplicationContext | 专门为Web应用准备,基于Web环境创建IOc容器对象,并将对象引入存入ServletContext域中。 |
二、基于XML管理bean
2.1 获取bean对象的三种方式
IoC中获取bean对象有三种方式:根据xml配置文件中的id来获取、根据类型来获取、根据ID和类型共同获取
在配置文件bean.xml中配置要创建的对象
<bean id="user" class="com.crj.spring6.iocxml.User"></bean>
三种获取bean对象的方式
package com.crj.spring6.iocxml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//获取bean
根据ID获取bean
User user1 = (User)context.getBean("user");
根据类型来获取bean
User user2 = context.getBean(User.class);
根据ID和类型来获取
User user3 = context.getBean("user",User.class);
System.out.println("根据ID获取bean对象user1:"+user1);
System.out.println("根据类型获取bean对象user2:"+user2);
System.out.println("根据类型和ID获取bean对象user2:"+user3);
}
}
需要注意的是:当根据类型获取bean时,要求IoC容器中指定类型的bean有且只能有一个。
2.2 接口实现类的获取
首先应该在bean.xml文件中配置
<!--一个接口实现类的获取过程-->
<bean id="userDaompl" class="com.crj.spring6.iocxml.bean.UserDaompl"></bean>
接口UserDao
package com.crj.spring6.iocxml.bean;
public interface UserDao {
public void run();
}
接口实现类UserDaompl
package com.crj.spring6.iocxml.bean;
public class UserDaompl implements UserDao{
@Override
public void run() {
System.out.println("UserDao is running!!!");
}
}
测试类
package com.crj.spring6.iocxml.bean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserDao {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//根据类型获取接口对应的bean
UserDao userDao = context.getBean(UserDao.class);
System.out.println(userDao);
userDao.run();
}
}
这里context获取bean类型应该选择的是UserDao这个接口类,这样才是接口实现类的获取。
2.3 注入
首先借助一个在原生类中的注入来明确注入的概念:
package com.crj.spring6.iocxml.DI;
public class Book {
private String name;
private String author;
//无参构造
public Book(){
}
//有参构造
public Book(String name,String author){
this.name = name;
this.author = author;
}
//生成set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public static void main(String[] args) {
//setter方法注入
Book book = new Book();
book.setAuthor("CRJ");
book.setName("庄子的智慧");
//基于构造器注入
Book b = new Book("编程珠玑","CRJ");
}
}
这里有一个小tips:IDEA中可以右键并在Generate中选择自动生成的类方法嘿嘿嘿~~~
package com.crj.spring6.iocxml.DI;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testDI {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("di.xml");
Book b = context.getBean("book", Book.class);
System.out.println(b);
}
}
2.3.1 Spring中基于setter注入
首先新建一个配置文件di.xml,直接在配置文件中使用setter方法将对象属性值进行注入。
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--set方法注入-->
<bean id="book" class="com.crj.spring6.iocxml.DI.Book">
<property name="name" value="图书名称"></property>
<property name="author" value="作者"></property>
</bean>
</beans>
测试类
package com.crj.spring6.iocxml.DI;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testDI {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("di.xml");
Book b = context.getBean("book", Book.class);
System.out.println(b);
}
}
2.3.2 基于构造器进行注入
基于构造器进行注入的步骤:
- 创建类,定义属性,生成有参数构造方法
- 在spring中进行配置
<!--构造方法注入-->
<bean id="bookCon" class="com.crj.spring6.iocxml.DI.Book">
<constructor-arg value="图书名称2" name="name"></constructor-arg>
<constructor-arg value="作者2" name="author"></constructor-arg>
</bean>
2.4 特殊值的注入
2.4.1 字面量赋值
使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量
<property name="author" value="crj"></property>
2.4.2 null值注入
<!--空值-->
<property name="name">
<null />
</property>
2.4.3 xml实体
小于号在xml文件中用来定义标签的开始,不能随便使用,这时候就需要使用xml实体来代替。
<property name="others"value="<"></property>
<!--
其中:>表示大于号、<表示小于号
-->
2.4.4 CTADA节
通过CTADA区来将特殊符号放在这个区里面。
<property name="others">
<value><![CDATA[a<b]]></value>
</property>
2.5 特殊类型的注入
2.5.1 对象类型属性注入
首先创建两个类:公司类和员工类,并在员工类中建立与公司类的关系即定义一个Dept类型的属性。在这里注入采用setter方式进行注入,所以要味蕾的所有属性构建set方法。完成后再bean.xml文件中配置注入的内容。
公司类
package com.crj.spring6.iocxml.DITest;
//公司类
public class Dept {
private String dName;
public void info(){
System.out.println("公司名称:"+dName);
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
}
员工类
package com.crj.spring6.iocxml.DITest;
//员工类
public class Emp {
// 表示员工属于某个公司
private Dept dept;
private String name;
private String age;
public void work(){
System.out.println("working>>>>>>");
System.out.println("员工名称"+name+",年龄:"+age);
dept.info();
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
}
引用外部bean赋值
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
引入外部bean
在emp的bean标签中使用proberty来引入dept的bean
-->
<bean id="dept" class="com.crj.spring6.iocxml.DITest.Dept">
<property name="dName" value="A"></property>
</bean>
<bean id="emp" class="com.crj.spring6.iocxml.DITest.Emp">
<property name="name" value="crj"></property>
<property name="age" value="18"></property>
<!--注入对象属性
private Dept dept
-->
<property name="dept" ref="dept"></property>
</bean>
</beans>
内部bean
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
通过内部bean来注入
-->
<bean id="emp2" class="com.crj.spring6.iocxml.DITest.Emp">
<property name="name" value="CRJ"></property>
<property name="age" value="19"></property>
<!--
内部bean方式注入,某个属性类
-->
<property name="dept">
<bean id="dept2" class="com.crj.spring6.iocxml.DITest.Dept">
<property name="dName" value="B"></property>
</bean>
</property>
</bean>
</beans>
级联属性赋值
<bean id="dept3" class="com.crj.spring6.iocxml.DITest.Dept">
<property name="dName" value="C"></property>
</bean>
<bean id="emp3" class="com.crj.spring6.iocxml.DITest.Emp">
<property name="name" value="C1"></property>
<property name="age" value="20"></property>
<property name="dept" ref="dept"></property>
<!--级联赋值-->
<property name="dept.dName" value="D"></property>
</bean>
2.5.2 数组类型属性注入
在员工类中添加数组类型属性
private String[] loves;
对应在xml文件中需要加上配置才可以完成属性注入
<!--数组类型属性-->
<property name="loves">
<array>
<value>eat</value>
<value>sleep</value>
<value>coding</value>
</array>
</property>
2.5.3 集合类型属性注入
对于集合类型的属性注入,比如在上述的公司类中有一个包含员工信息的集合,因此可以在公司类中加入Emp类的类型集合并生成相应的set方法。
package com.crj.spring6.iocxml.DITest;
import java.util.List;
//公司类
public class Dept {
private String dName;
private List<Emp> empList;
public List<Emp> getEmpList() {
return empList;
}
public void setEmpList(List<Emp> empList) {
this.empList = empList;
}
public void info(){
System.out.println("公司名称:"+dName);
for (Emp emp:empList){
System.out.println(emp.getName());
}
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
}
List类型属性集合
这里因为我们在公司类中声明的集合属性是一个Emp类型的,因此我们需要在注入多个Emp类型的对象后,通过<ref>标签中的bean属性来将集合类型数据进行注入。
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dept" class="com.crj.spring6.iocxml.DITest.Dept">
<property name="dName" value="A"></property>
<property name="empList">
<list>
<!-- 因为这里我们在集合中是引入一个对象,所以这里不再使用value标签-->
<ref bean="empOne"></ref>
<ref bean="empTwo"></ref>
</list>
</property>
</bean>
<bean id="empOne" class="com.crj.spring6.iocxml.DITest.Emp">
<property name="name" value="crj"></property>
<property name="age" value="18"></property>
<!--注入对象属性
private Dept dept
-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="empTwo" class="com.crj.spring6.iocxml.DITest.Emp">
<property name="name" value="crj"></property>
<property name="age" value="18"></property>
<!--注入对象属性
private Dept dept
-->
<property name="dept" ref="dept"></property>
</bean>
</beans>
Map类型的属性集合
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="teacher" class="com.crj.spring6.iocxml.DIMap.Teacher">
<property name="tid" value="1"></property>
<property name="tName" value="CRJ"></property>
</bean>
<bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
<property name="name" value="A"></property>
<property name="id" value="S-1"></property>
<property name="teachersMap">
<map>
<entry>
<key>
<value>001</value>
</key>
<ref bean="teacher"></ref>
</entry>
</map>
</property>
</bean>
</beans>
引入集合bean
这种方法是整合map和list集合bean注入的方式,使用util:map或util:bean来进行属性注入。但需要在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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="teacherOne" class="com.crj.spring6.iocxml.DIMap.Teacher">
<property name="tid" value="1"></property>
<property name="tName" value="CRJ"></property>
</bean>
<bean id="teacherTwo" class="com.crj.spring6.iocxml.DIMap.Teacher">
<property name="tid" value="2"></property>
<property name="tName" value="CRJ"></property>
</bean>
<bean id="lession1" class="com.crj.spring6.iocxml.DIMap.Lession">
<property name="lessionName" value="高数"></property>
</bean>
<bean id="lession2" class="com.crj.spring6.iocxml.DIMap.Lession">
<property name="lessionName" value="大物"></property>
</bean>
<bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
<property name="name" value="A"></property>
<property name="id" value="S-1"></property>
<!--注入list和map-->
<property name="lession" ref="lessionList"></property>
<property name="teachersMap" ref="teacherMap"></property>
</bean>
<util:list id="lessionList">
<ref bean="lession1"></ref>
<ref bean="lession2"></ref>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>001</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>002</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</util:map>
</beans>
2.5.4 P命名空间注入
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="studentP" class="com.crj.spring6.iocxml.DIMap.Student"
p:id="5" p:name="crj" p:lession-ref="lessionList" p:teachersMap-ref="teacherMap">
</bean>
<bean id="teacherOne" class="com.crj.spring6.iocxml.DIMap.Teacher">
<property name="tid" value="1"></property>
<property name="tName" value="CRJ"></property>
</bean>
<bean id="teacherTwo" class="com.crj.spring6.iocxml.DIMap.Teacher">
<property name="tid" value="2"></property>
<property name="tName" value="CRJ"></property>
</bean>
<bean id="lession1" class="com.crj.spring6.iocxml.DIMap.Lession">
<property name="lessionName" value="高数"></property>
</bean>
<bean id="lession2" class="com.crj.spring6.iocxml.DIMap.Lession">
<property name="lessionName" value="大物"></property>
</bean>
<bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
<property name="name" value="A"></property>
<property name="id" value="S-1"></property>
<!--注入list和map-->
<property name="lession" ref="lessionList"></property>
<property name="teachersMap" ref="teacherMap"></property>
</bean>
<util:list id="lessionList">
<ref bean="lession1"></ref>
<ref bean="lession2"></ref>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>001</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>002</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</util:map>
</beans>
2.5.5 引用外部文件注入
在实际的开发中,我们一般都是采用通过数据库并引入外部文件依赖来实现属性注入的操作的,具体的步骤如下:
- 在整个项目的配置文件pom.xml中配置数据库环境依赖;
- 创建properties格式的外部属性文件,定义数据信息如:用户名、密码、地址等;
- 创建spring配置文件,引入context命名空间并引入属性文件完成注入
配置数据库环境依赖
<dependencies>
<!--引入数据库依赖-->
<!--MySQL驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
</dependencies>
配置外部属性文件
在resources文件夹中创建一个jdbc.properties的文件,之后在内部配置的bean-jdbc.xml文件中加上context依赖:
<?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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
那么在原生方法中,也就是不采用属性注入的方式来进行数据库外部文件的属性获取是通过一个DruidDataSource()这一个类来实现:
@Test
public void fun1(){
DruidDataSource dataSource = new DruidDataSource();
// //原生的调用数据库的方法
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
}
采用bean注入的方式我们也是需要在配置文件中配置号数据库的bean对象,通过${}来获取外部文件的属性值
<!-- 引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 完成数据库信息注入-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
定义一个测试类来实现:
@Test
public void fun2(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml");
DruidDataSource d = context.getBean(DruidDataSource.class);
System.out.println(d.getUrl());
}
2.6 bean的作用域
在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围:
取值 | 含义 | 创建对象时间 |
---|---|---|
singleton(默认) | 在IOC容器中,这个bean对象始终为单实例 | IOC容器初始化时 |
prototype | 这个bean在IOC容器中有多个实例 | 获取bean时候 |
如果是在WebApplicationContext环境下还会有另外几个作用域:
取值 | 含义 |
---|---|
request | 在一个请求范围有效 |
session | 在一个会话范围有效 |
2.7 bean的生命周期
2.7.1 具体的生命周期过程
- bean对象创建(调用无参构造器)
- 给bean对象设置属性
- bea的后置处理器(初始化之前)
- bean对象初始化(需在配置bean时指定初始化方法)
- bean的后置处理器(初始化之后)
- bean对象就绪可以使用
- bean对象销毁(在bean中配置指定销毁方法)
- IOC容器关闭
<bean class="" id="" scope="singleton" init-method="初始化方法" destroy-method="销毁方法"></bean>
2.7.2 bean的后置处理器
后置处理器需要配继承一个BeanPostProcessor 接口,并在相应的配置文件中需要使用bean配置好。
package com.crj.spring6.iocxml;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean的后置处理器,会在初始化之前执行");
System.out.println("beanName:"+beanName+",bean:"+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean的后置处理器,会在初始化之后执行");
System.out.println("beanName:"+beanName+",bean:"+bean);
return bean;
}
}
2.8 基于xml自动装配
自动装配指的是:根据指定的策略,在OC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。
这里的自动装配指的是自动调用set方法来创建某个类类型的属性,比如有controller、service和dao三层并且每一层都有相应的接口和实现类:
Controller层
package com.crj.spring6.iocxml.auto.controller;
import com.crj.spring6.iocxml.auto.service.UserService;
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void addUser(){
System.out.println("controller方法执行了");
//调用Service中的方法
userService.addUserService();
}
}
Service层
package com.crj.spring6.iocxml.auto.service;
import com.crj.spring6.iocxml.auto.dao.UserDao;
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUserService() {
System.out.println("UserService方法执行了");
userDao.addUserDao();
}
}
Dao层
package com.crj.spring6.iocxml.auto.dao;
public class UserDaoImpl implements UserDao{
@Override
public void addUserDao() {
System.out.println("userDao方法执行");
}
}
测试类
package com.crj.spring6.iocxml.auto;
import com.crj.spring6.iocxml.auto.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--创建三个类的对象-->
<bean id="userController" class="com.crj.spring6.iocxml.auto.controller.UserController" autowire="byType"></bean>
<bean id="userService" class="com.crj.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.crj.spring6.iocxml.auto.dao.UserDaoImpl" ></bean>
</beans>
<!--
autowire还可以取byName属性值
这里需要id值必须跟类中声明的属性名一致userService
-->
三、基于注解管理bean
从Java5开始,Java增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
3.1 通过注解创建bean对象
Spring中通过注解实现自动装配步骤:
- 引入依赖
- 开启组件扫描
- 使用注解定义bean
- 依赖注入
开启组件扫描
基本的依赖引入跟上面是一样滴,但在这里我们需要在bean.xml配置文件中来开启组件扫描功能,这是因为Spring默认是无法使用注解来装配bean的。开启此功能后,Spring会自动从扫描指定的包(base-package属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。
<?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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.crj"></context:component-scan>
<!--另外两种过滤情况-->
<context:component-scan base-package="com.crj.spring6">
<!--
type有两种值:annotation和assignable
annotation:根据注解排除,expression中设置要排除的注解的全类名
assignable:更具类型排除,expression中设置要排除的类型的全类名
-->
<!--排除exclude-filter-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--仅扫描include-filter-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
四种注解
注解 | 说明 |
---|---|
@Component | 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao层))的类标识为Spring中的Bean,其功能与@Component相同。 |
@Service | 该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。 |
其实四种注解的用法和功能是一样的,只不过我们一般为了区分比较常见地就把注解类型分开了。
package com.crj.spring6.bean;
import org.springframework.stereotype.Component;
@Component(value = "user") //等价于<bean id="user" class="...."> 这里的value值可以不设置,默认就是类名首字母小写
public class User {
}
3.2 @Autowired注入
Autowire注解默认是采用类型进行装配的,也就是默认是byType。该注解可以用在构造方法、普通方法、形参、属性和注解上。
属性注入
借助@Autowire注解来修饰类中声明的属性实现调用Service接口实现类。
package com.crj.spring6.bean.Controllor;
import com.crj.spring6.bean.UserService.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
//第一种方式:属性注入
@Autowired
private UserService userService;
public void add(){
System.out.println("controller>>>>>>>>>");
userService.add();
}
}
Service层
package com.crj.spring6.bean.UserService;
import com.crj.spring6.bean.Dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
public void add() {
System.out.println("Service>>>>>>>>>");
userDao.add();
}
}
Set注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
构造方法注入
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
形参上注入
生成有参构造后再形参前加Autowire注入
private UserService userService;
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
注意:只有一个有参构造的时候,无需注解也可以实现注入。
@Qualifier注解根据名称进行匹配,它可以和@Autowire联合进行匹配。这种情况会出现在接口有多个实现类的时候我们需要使用两个注解并根据名称进行注入。
@Autowired
@Qualifier(value = "userDaoImpl") //指定实现类
private UserDao userDao;
3.3 @Resource注入
@Resource注解也能完成属性注入,它与@Autowire注解的区别在于@Resource是JDK中的一部分,属于标准注解,默认根据名称进行装配,未指定name时会使用属性名作为name,再找不到就会自动启用byType装配。需要注意的是,@Resource只能用在属性和setter方法上,并且在版本高于11的时候需要引入依赖。
引入依赖
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
根据名称进行注入
@Resource(name "myUserService")
private UserService userservice;
3.4 全注解开发
所谓的全注解开发其实就是不再使用配置文件pom.xml了,而是采用一个配置类来取代配置文件。
配置类
package com.crj.spring6.bean.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //配置类
@ComponentScan("com.crj.spring6") //开启组件扫描
public class SpringConfig {
}
测试类
不再是new一个ClassPathXmlApplicationContext,而是实例化一个AnnotationConfigApplicationContext来注入配置类。
package com.crj.spring6.bean;
import com.crj.spring6.bean.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class testConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean(User.class);
System.out.println(user);
}
}
总结
以上就是IoC控制反转的主要知识点了,接下来荔枝也会梳理有关AOP面向切面编程的相关内容,希望能加快进度嘿嘿嘿,感觉自己学习梳理得太慢了哈哈,大家一起加油吧~~~
今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~