Spring6-IoC(learning)
Spring-IoC
A. 控制反转
- IoC(Inversion of Control):控制反转(设计思想)
- Spring通过IoC容器管理所有Java对象的实例化与初始化,控制对象间的依赖关系。将由IoC容器管理的Java对象称为Spring Bean,它与使用new关键字创建的Java对象没有任何区别。IoC容器是Spring框架最重要的核心组件之一。
- 将创建对象的权力交给第三方容器
- 将维护对象间关系的权利交给第三方容器
B. 依赖注入
- 依赖注入(DI , Dependency Injection):实现了控制反转思想,指在Spring对象创建过程中,将对象依赖属性通过配置注入
- 注入方式:
- setter注入
- 构造注入
C. IoC在Spring中的实现
BeanFactory
:IoC的基本实现,是Spring内部使用的接口,不提供给外界使用ApplicationContext
:是BeanFactory
的子接口,面向Spring使用者ApplicationContext
拥有一系列具体的实现类
D. 基于xml管理Bean
1. 使用xml定义和获取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="user" class="org.spring6Demo.iocxml.User"></bean>
</beans>
public static void main(String[] args) {
// 加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 1.根据id获取bean
User user1 = (User) context.getBean("user");
System.out.println("user1 " + user1.getInfo());
// 2.根据类型获取bean
User user2 = context.getBean(User.class);
System.out.println("user2 " + user2.getInfo());
// 3.根据id和类型获取bean
User user3 = context.getBean("user", User.class);
System.out.println("user3 " + user3.getInfo());
}
-
当上述2根据类型获取bean时,要求IoC容器中指定类型的bean只能有一个
- 有超过一个时,会抛出异常
- 应当改用1/3来获取bean
-
接口有一个或多个实现类时(Liscov)
-
一个接口有一个实现类时,可以根据接口类型获取bean(bean应当唯一)
UserDaoImpl
implementsUserDao
<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="userDaoImpl" class="org.spring6Demo.iocxml.bean.UserDaoImpl"></bean> </beans>
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); // 根据接口类型获取bean UserDao userDao = context.getBean(UserDao.class); System.out.println(userDao); // org.spring6Demo.iocxml.bean.UserDaoImpl@14ec4505 }
-
一个接口有多个实现类时,不可以根据接口类型获取bean(bean不唯一)
UserDaoImpl
implementsUserDao
,UserDaoImpl2
implementsUserDao
<!-- 接口有多个实现类--> <bean id="userDaoImpl" class="org.spring6Demo.iocxml.bean.UserDaoImpl"></bean> <bean id="userDaoImpl2" class="org.spring6Demo.iocxml.bean.UserDaoImpl2"></bean>
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.spring6Demo.iocxml.bean.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2
-
根据类型获取bean时,在满足bean唯一性的前提下,当
对象 instanceof 指定类型
为true时就可认定为与类型匹配,可以获取到对象
-
2. 依赖注入
2.1 set注入(setter)
-
创建类,定义属性,设置属性的setter方法
public class Book { private String name; private String author; public Book() {} public void setName(String name) { this.name = name; } public void setAuthor(String author) { this.author = author; } }
-
Spring配置文件
<!--set注入--> <bean id="book1" class="org.spring6Demo.iocxml.di.Book"> <property name="name" value="Head First Java"></property> <property name="author" value="Sierra Bates"></property> </bean>
标签即调用类中已定义的setter方法,name指定属性,value为属性赋值
2.2 构造注入(有参constructor)
-
创建类,定义属性,设置有参构造方法
public class Book { private String name; private String author; public Book(String name, String author) { this.name = name; this.author = author; } }
-
Spring配置文件
<!--构造注入--> <bean id="book2" class="org.spring6Demo.iocxml.di.Book"> <constructor-arg name="name" value="Head First Java"></constructor-arg> <constructor-arg name="author" value="Sierra Bates"></constructor-arg> </bean>
标签即调用类中已定义的有参构造方法
2.3 特殊值处理
-
null
<bean id="null_test" class="org.spring6Demo.iocxml.di.Book"> <property name="name" value="几何原本"></property> <property name="author"> <null></null> </property> </bean>
-
xml实体:采用转义符号(< >)
-
CDATA节:用于表示纯文本数据,xml解析器遇到CDATA则认为是纯文本而不再对这段文本进行xml解析,从而可以直接在CDATA节中书写特殊符号
<bean id="null_test" class="org.spring6Demo.iocxml.di.Book"> <property name="name" value="几何原本"></property> <property name="author"> <value><![CDATA[ <<欧几里得>> ]]></value> </property> </bean>
3. 特殊类型属性注入
3.1 对象类型属性注入
-
引用外部Bean
<!--注入外部bean : 部门Department 员工Employee 向Employee的bean中注入Department的bean 1 分别创建两个类的对象dpm emp 2 在emp的bean中使用property引入dpm的bean--> <bean id="dpm" class="org.spring6Demo.iocxml.objDI.Department"> <property name="name" value="security"></property> </bean> <bean id="emp" class="org.spring6Demo.iocxml.objDI.Employee"> <!--普通属性注入--> <property name="name" value="Leo"></property> <property name="age" value="20"></property> <!--对象类型属性注入--> <property name="dept" ref="dpm"></property> </bean>
@Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean-obj-di.xml"); Employee emp = context.getBean("emp", Employee.class); System.out.println(emp); // Employee{name='Leo', age=20, dept=Department{name='security'}} // 创建了Employee类型bean:emp 同时也为其dept属性创建并赋值了Department类型bean:dpm }
-
内部Bean
<!--注入内部bean--> <bean id="emp2" class="org.spring6Demo.iocxml.objDI.Employee"> <property name="name" value="David"></property> <property name="age" value="21"></property> <!--内部bean--> <property name="dept"> <bean id="dpm2" class="org.spring6Demo.iocxml.objDI.Department"> <property name="name" value="finance"></property> </bean> </property> </bean>
@Test public void test2() { ApplicationContext context = new ClassPathXmlApplicationContext("bean-obj-di.xml"); Employee emp2 = context.getBean("emp2", Employee.class); System.out.println(emp2); // Employee{name='David', age=21, dept=Department{name='finance'}} }
-
级联属性赋值
<bean id="dpm" class="org.spring6Demo.iocxml.objDI.Department"> <property name="name" value="security"></property> </bean> <bean id="emp" class="org.spring6Demo.iocxml.objDI.Employee"> <property name="name" value="Leo"></property> <property name="age" value="20"></property> <property name="dept" ref="dpm"></property> <!--级联赋值--> <property name="dept.name" value="test"></property> </bean> <!-- Employee{name='Leo', age=20, dept=Department{name='test'}} -->
3.2 数组类型属性注入
<!--注入数组类型属性-->
<bean id="emp3" class="org.spring6Demo.iocxml.objDI.Employee">
<property name="name" value="Tom"></property>
<property name="age" value="22"></property>
<property name="dept">
<bean id="dpm3" class="org.spring6Demo.iocxml.objDI.Department">
<property name="name" value="HR"></property>
</bean>
</property>
<!--数组类型属性赋值-->
<property name="loves">
<array>
<value>eating</value>
<value>sleeping</value>
<value>coding</value>
</array>
</property>
</bean>
<!-- Employee{name='Tom', age=22, dept=Department{name='HR'}, loves=[eating, sleeping, coding]} -->
3.3 List类型属性注入
<!-- List类型属性注入 : 向Department的List<Employee>属性注入值 -->
<bean id="emp4-1" class="org.spring6Demo.iocxml.objDI.Employee">
<property name="name" value="Judy"></property>
<property name="age" value="21"></property>
</bean>
<bean id="emp4-2" class="org.spring6Demo.iocxml.objDI.Employee">
<property name="name" value="Jerry"></property>
<property name="age" value="23"></property>
</bean>
<bean id="dpm4" class="org.spring6Demo.iocxml.objDI.Department">
<property name="name" value="security"></property>
<!--List类型属性赋值-->
<property name="employees">
<list>
<ref bean="emp4-1"></ref>
<ref bean="emp4-2"></ref>
</list>
</property>
</bean>
<!-- Department{name='security', employees=[Employee{name='Judy', age=21, dept=null, loves=null}, Employee{name='Jerry', age=23, dept=null, loves=null}]} -->
3.4 Map类型属性注入
<bean id="teacher1" class="org.spring6Demo.iocxml.mapDI.Teacher">
<property name="tid" value="001"></property>
<property name="tName" value="刘钦"></property>
</bean>
<bean id="teacher2" class="org.spring6Demo.iocxml.mapDI.Teacher">
<property name="tid" value="002"></property>
<property name="tName" value="刘嘉"></property>
</bean>
<bean id="student1" class="org.spring6Demo.iocxml.mapDI.Student">
<property name="sid" value="201250001"></property>
<property name="sName" value="Tommy"></property>
<!--Map类型属性赋值-->
<property name="teacherMap">
<map>
<entry>
<key>
<value>001</value>
</key>
<!--定义的Map为<String,Teacher>类型,因此value部分应使用ref-->
<ref bean="teacher1"></ref>
</entry>
<entry>
<key>
<value>002</value>
</key>
<ref bean="teacher2"></ref>
</entry>
</map>
</property>
</bean>
<!--Student{sid='201250001', sName='Tommy', teacherMap={001=Teacher{tid='001', tName='刘钦'}, 002=Teacher{tid='002', tName='刘嘉'}}}-->
3.5 引用集合类型的bean注入
- 在命名空间约束中,应加入与util相关的部分才能使用util标签
<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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--Student bean-->
<bean id="student" class="org.spring6Demo.iocxml.mapDI.Student">
<property name="sid" value="201250002"></property>
<property name="sName" value="zy"></property>
<property name="courses" ref="courseList"></property>
<property name="teacherMap" ref="teacherMap"></property>
</bean>
<!--util:list courses-->
<util:list id="courseList">
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</util:list>
<!--util:map teacherMap-->
<util:map id="teacherMap">
<entry>
<key>
<value>001</value>
</key>
<ref bean="teacher1"></ref>
</entry>
<entry>
<key>
<value>002</value>
</key>
<ref bean="teacher2"></ref>
</entry>
</util:map>
<!--Teacher bean-->
<bean id="teacher1" class="org.spring6Demo.iocxml.mapDI.Teacher">
<property name="tid" value="001"></property>
<property name="tName" value="刘钦"></property>
</bean>
<bean id="teacher2" class="org.spring6Demo.iocxml.mapDI.Teacher">
<property name="tid" value="002"></property>
<property name="tName" value="刘嘉"></property>
</bean>
<!--Course bean-->
<bean id="course1" class="org.spring6Demo.iocxml.mapDI.Course">
<property name="cName" value="OS"></property>
</bean>
<bean id="course2" class="org.spring6Demo.iocxml.mapDI.Course">
<property name="cName" value="SE"></property>
</bean>
</beans>
<!--Student{sid='201250002', sName='zy', teacherMap={001=Teacher{tid='001', tName='刘钦'}, 002=Teacher{tid='002', tName='刘嘉'}}, courses=[Course{cName='OS'}, Course{cName='SE'}]}-->
4. 引入外部属性文件(以mysql数据库连接为例)
-
当外部配置发生改变时,只需修改外部属性文件,而无需对代码直接进行修改
-
数据库表准备
mysql> select * from bill; +----+------+--------+---------+----------+ | id | type | amount | balance | info | +----+------+--------+---------+----------+ | 1 | 1 | 9000 | 9000 | 初始收入 | +----+------+--------+---------+----------+
-
引入相关依赖(pom.xml)
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency>
-
外部属性文件(jdbc.properties)
jdbc.user=root jdbc.password=************* jdbc.url=jdbc:mysql://localhost:3306/mysql_demo1?serverTimezone=UTC jdbc.driver=com.mysql.cj.jdbc.Driver
-
创建spring配置文件,引入context命名空间,引入外部属性文件,使用表达式完成注入(
${...}
)<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:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--完成配置信息注入--> <bean id="dataSource" 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> </beans>
-
连接测试
public class JDBCTest { @Test public void test() throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml"); DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class); testDatabase(dataSource); } private void testDatabase(DruidDataSource dataSource) throws SQLException { Connection connection = dataSource.getConnection(); String sqlQuery = "SELECT * FROM bill;"; PreparedStatement preparedStatement = connection.prepareStatement(sqlQuery); ResultSet resultSet = preparedStatement.executeQuery(); System.out.println("id\tamount\tinfo"); while (resultSet.next()) { int id = resultSet.getInt("id"); int amount = resultSet.getInt("amount"); String info = resultSet.getString("info"); System.out.println(id + "\t" + amount + "\t" + info); } resultSet.close(); preparedStatement.close(); connection.close(); } }
输出:
id amount info
1 9000 初始收入
5. bean作用域scope
Spring中可以通过配置bean标签的scope属性,来指定bean的作用域范围
取值 | 含义 | 创建对象的时机 |
---|---|---|
singleton(默认) | 此bean对象在IoC容器中始终为单例 | IoC容器初始化时 |
prototype | 此bean对象在IoC容器中有多个实例 | 获取bean时 |
<!--单实例-->
<bean id="user1" class="org.spring6Demo.iocxml.bean_scope.User" scope="singleton">
<property name="user_name" value="zy"></property>
</bean>
<!--多实例-->
<bean id="user2" class="org.spring6Demo.iocxml.bean_scope.User" scope="prototype">
<property name="user_name" value="zyx"></property>
</bean>
public class BeanScopeTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml");
User user_singleton1 = context.getBean("user1", User.class);
System.out.println(user_singleton1); // org.spring6Demo.iocxml.bean_scope.User@65987993
User user_singleton2 = context.getBean("user1", User.class);
System.out.println(user_singleton2); // org.spring6Demo.iocxml.bean_scope.User@65987993
}
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml");
User user_prototype1 = context.getBean("user2", User.class);
System.out.println(user_prototype1); // org.spring6Demo.iocxml.bean_scope.User@4d1bf319
User user_prototype2 = context.getBean("user2", User.class);
System.out.println(user_prototype2); // org.spring6Demo.iocxml.bean_scope.User@6f53b8a
}
}
6. bean生命周期
- bean对象创建
- 给bean对象设置相关属性
- bean后置处理器
BeanPostProcessor
(初始化前) - bean对象初始化(调用指定初始化方法
init-method=
) - bean后置处理器
BeanPostProcessor
(初始化后) - bean对象完成创建
- bean对象销毁(配置指定销毁方法
destroy-method=
) - IoC容器关闭
7. FactoryBean
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是其getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
package org.spring6Demo.iocxml.factoryBean;
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
<bean id="user" class="org.spring6Demo.iocxml.factoryBean.MyFactoryBean"></bean>
public class TestFactory {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-factorybean.xml");
User user = (User) context.getBean("user");
System.out.println(user); // org.spring6Demo.iocxml.factoryBean.User@18271936
}
}
8. 自动装配
根据指定策略(bean标签的autowire属性),在IoC容器中匹配某一个bean,自动为匹配的bean中所依赖的类类型或接口类型属性赋值,完成自动装配
-
byType
:根据类型匹配IoC容器中某个兼容类型的bean并为属性自动赋值- 若IoC容器中没有兼容类型可以用于赋值,则该属性不装配,值为null
- 若IoC容器中有多个兼容类型可以用于赋值,则抛出异常
NoUniqueBeanDefinitionException
- 若IoC容器有多个此兼容类型的bean,则会抛出异常。此时不能使用autowire属性进行自动装配,而要在xml文件中使用
<qualifier value="xxx">
指定要注入的bean,或采用注解。
-
byName
:将自动装配的属性的属性名作为bean的id在IoC容器中匹配相应的bean,进行赋值
<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="org.spring6Demo.iocxml.autoConfig.controller.UserController" autowire="byType">
</bean>
<bean id="userService" class="org.spring6Demo.iocxml.autoConfig.service.UserServiceImpl" autowire="byName">
</bean>
<bean id="userDao" class="org.spring6Demo.iocxml.autoConfig.dao.UserDaoImpl">
</bean>
</beans>
public class UserController {
private UserService service;
public void setService(UserService service) {
this.service = service;
}
public void addUser() {
System.out.println("UserController::addUser()");
this.service.addUserService();
}
}
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUserService() {
System.out.println("UserServiceImpl::addUserService()");
this.userDao.addUserDao();
}
}
public class UserDaoImpl implements UserDao{
@Override
public void addUserDao() {
System.out.println("UserDaoImpl::addUserDao()");
}
}
public class AutoConfigTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.addUser();
}
}
输出:
UserController::addUser()
UserServiceImpl::addUserService()
UserDaoImpl::addUserDao()
E. 基于注解管理Bean
注解(Annotation)是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。我们可以使用注解来实现自动装配,简化Spring的xml配置。
1. 开启组件扫描
Spring默认不使用注解装配Bean,因此我们需要在Spring的xml配置中,通过context.component-scan
元素开启Spring Beans的自动扫描功能。开启此功能后,Spring会默认自动从扫描指定的包(base-package
属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。
<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="org.spring6Demo"></context:component-scan>
</beans>
-
在
<context:component-scan>
标签中,可以通过<context:exclude-filter>
指定组件扫描排除规则,通过<context:include-filter>
指定在原有组件扫描规则上追加的规则 -
type
属性值:- annotation:根据注解排除,expression中设置要排除的注解的全类名
- assignable:根据类型排除,expression中设置要排除的类型的全类名
-
要设置include-filter仅扫描指定组件,必须设置
use-default-filters
属性为false以关闭默认扫描规则(默认扫描规则为扫描指定包下所有类)
2. 使用注解定义bean
以下注解可以直接标注在Java类上,将他们定义为Spring Bean
注解 | 说明 |
---|---|
@Component | 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao层)的类标识为Spring中的Bean,其功能与@Component相同。 |
@Service | 该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC 的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。 |
@Component(value = "user") // <bean id="user" ... > (value缺省为类名首字母小写)
public class User {
}
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean(User.class);
System.out.println(user); // org.spring6Demo.bean.User@5a37d3ed
}
3. 依赖注入:@Autowired
单独使用@Autowired注解时,默认根据类型装配(byType)
-
该注解可以标注于:构造方法上、方法上、形参上、属性上、注解上
-
在注入时,默认要求被注入的bean必须存在,否则报错(默认required=true)
3.1 属性注入
将@Autowired注解直接标注在属性上,此时会自动根据类型找到相应对象并完成注入,无需像xml管理bean时一样需要声明setter。此种注入方法基于 setter 方法的隐式调用,Spring 会为带有 @Autowired注解的属性生成一个 setter 方法(如果还没有定义的话),并在创建 bean 时调用这个 setter 方法来完成注入。
@Controller
public class UserController {
// 属性注入
@Autowired
private UserService userService;
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void add() {
System.out.println("UserServiceImpl.add()");
userDao.add();
}
}
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("UserDaoImpl.add()");
}
}
public class AutoUserTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.add();
}
// 输出:
// UserController.add()
// UserServiceImpl.add()
// UserDaoImpl.add()
}
3.2 set注入
可以将@Autowired注解标注在属性的setter上,此时会自动进行注入
@Controller
public class UserController {
private UserService userService;
// set注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
3.3 构造注入
通过在构造方法上使用@Autowired注解来实现构造方法的自动装配。当使用@Autowired注解标注构造方法时,Spring容器会在初始化bean时自动调用带有@Autowired注解的构造方法,并将所需的依赖项注入到构造方法的参数中。
@Controller
public class UserController {
private UserService userService;
// 构造方法注入
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
当类中有多个构造方法时,只有一个构造方法可以被标记为@Autowired。如果有多个构造方法都使用了@Autowired注解,Spring容器将抛出异常,此时需要使用@Qualifier注解来指定要使用的构造方法(3.6)。
3.4 形参上注入
@Controller
public class UserController {
private UserService userService;
// 形参上注入
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
3.5 单一构造函数,无注解
当只有一个有参数构造函数时,@Autowired注解可以省略
3.6 @Autowired与@Qualifier 联合注解
当Spring容器中存在多个相同类型的Bean时,自动装配可能会产生歧义;或即使只有一个Bean符合依赖注入的要求,开发者也可能希望更精确地控制Bean的注入。此时需要使用@Qualifier注解明确指定要注入的Bean (的名称 value
)。
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("UserDaoImpl.add()");
}
}
@Repository
public class UserRedisDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("UserRedisDaoImpl.add()");
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier(value = "userRedisDaoImpl")
private UserDao userDao;
@Override
public void add() {
System.out.println("UserServiceImpl.add()");
userDao.add();
}
}
4. 依赖注入:@Resource
@Resource注解也可以完成属性注入,其与@Autowired注解区别为:
-
@Resource注解是JDK扩展包中的,属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)而@Autowired注解是Spring框架自己的。
-
@Resource注解默认根据名称装配byName,未指定name时使用属性名作为name,通过name找不到的话会自动启动通过类型byType装配。@Autowired注解默认根据类型装配byType,如果想根据名称装配则需要配合@Qualifier注解一起使用。
-
@Resource注解用在属性上、setter方法上。@Autowired注解用在属性上、setter方法上、 构造方法上、构造方法参数上。
4.1 引入依赖
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
4.2 根据name注入
@Controller("resourceUserController")
public class UserController {
@Resource(name = "userService1")
private UserService userService;
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
@Service("userService1")
public class UserServiceImpl implements UserService {
@Resource(name = "userDao1")
private UserDao userDao;
@Override
public void add() {
System.out.println("UserServiceImpl.add()");
userDao.add();
}
}
@Repository("userDao1")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("UserDaoImpl.add()");
}
}
4.3 name未知时注入
已知name时会直接根据name寻找匹配的bean,未知时则按类型寻找进行装配
@Controller("resourceUserController")
public class UserController {
@Resource
private UserService userService;
public void add() {
System.out.println("UserController.add()");
userService.add();
}
}
5. 全注解开发
不再使用Spring配置文件(bean.xml),而是使用配置类代替配置文件
-
配置类
@Configuration @ComponentScan("org.spring6Demo.resource") // <context:component-scan base-package="org.spring6Demo.resource"></context:component-scan> public class SpringConfig { }
-
测试类
public class ResourceConfigUserTest { @Test public void test(){ ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // UserController controller = context.getBean(UserController.class); controller.add(); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性