控制反转#
约 1700 个字 53 行代码 3 张图片 预计阅读时间 36 分钟
不使用Spring存在的问题#
- 使用
new
创建对象时,用到了具体的
实现类。当业务需求反复变化,实现类变更为其它类。需要反复修改创建对象处的代码。不满足开闭原则
。 - 使用
new
创建对象。依赖了具体实现类,不符合依赖倒置原则
。
Spring概述#
Spring框架
是 Java 平台的一个开源的全栈(full-stack)应用程序框架和控制反转容器实现,一般被直接称为 Spring。该框架的一些核心功能理论上可用于任何 Java 应用,但 Spring 还为基于Java企业版平台构建的 Web 应用提供了大量的拓展支持。Spring 没有直接实现任何的编程模型,但它已经在 Java 社区中广为流行,基本上完全代替了企业级JavaBeans(EJB)模型
。
Spring框架以 Apache License 2.0 开源许可协议的形式发布,该框架最初由 Rod Johnson
以及 Juergen Hoeller 等人开发。
版本说明#
Warning
从Spring Framework 6.0
开始,Spring需要Java 17+
Spring版本 | Jakarta EE版本 | JDK版本 |
---|---|---|
5.3.x | Java EE 7-8 (javax namespace) | 8-21 |
6.0.x | 9-10 | 17-21 |
6.1.x | 9-10 | 17-23 |
6.2.x | 9-11 | 17-25(预计) |
ICO入门案例#
- 引入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.luguosong</groupId>
<artifactId>spring-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.8</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.8</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
</project>
spring-context
依赖内部会引用其它核心模块:
- 创建User对象:
package com.luguosong.ioc;
import java.util.Arrays;
import java.util.Map;
/**
* @author luguosong
*/
public class User {
private String name;
private Integer age;
private String[] hobby;
private Map<String, String> additionalInfo;
public User() {
}
public User(Integer age, String name) {
this.age = age;
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public void setAdditionalInfo(Map<String, String> additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return "User{" +
"additionalInfo=" + additionalInfo +
", name='" + name + '\'' +
", age=" + age +
", hobby=" + Arrays.toString(hobby) +
'}';
}
}
- 编写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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--id:唯一标识-->
<!--class:类的全限定类名-->
<bean id="userBean" class="com.luguosong.ioc.User"/>
</beans>
- 编写测试类:
package com.luguosong.ioc.hello;
import com.luguosong.ioc.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class SpringHello {
public static void main(String[] args) {
/*
* 启动Spring容器
* 解析xml文件
* 实例化所有Bean对象,放入Spring容器中
* */
ApplicationContext context = new ClassPathXmlApplicationContext("spring_config_hello.xml");
/*
* 根据bean的id获取对应bean的实例
* */
User userBean = context.getBean("userBean", User.class);
System.out.println(userBean); //User{age=null, name='null'}
}
}
IOC原理分析#
初始化阶段
:Spring读取配置文件中的bean标签,根据其中的class属性
(类的全限定类名),默认通过反射
调用类的无参构造创建对象,并将对象实例跟id属性
绑定封装成Map集合。
使用阶段
:Spring根据id名称
可以获取到对应的对象实例
。
配置日志#
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
</dependency>
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 配置appender -->
<Appenders>
<!-- 配置consoleAppender -->
<Console name="consoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
</Console>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="debug">
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
</configuration>
bean的实例化方式#
构造方法#
直接配置文件中配置类的全路径,Spring会调用构造方法
创建对象。
简单工厂#
编写简单工厂:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
/**
* 通过简单工厂创建bean
*
* @author luguosong
*/
public class SimpleFactory {
//定义静态方法用于创建对象
public static User getUser() {
return new User(12,"李四");
}
}
编写配置文件,配置简单工厂类
:
<?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="user" class="com.luguosong.ioc.create_bean.SimpleFactory" factory-method="getUser"/>
</beans>
通过简单工厂创建对象:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_simple_factory.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}
}
工厂方法#
实现FactoryBean<T>
接口的对象本身就是用于创建单个对象的工厂
(工厂方法模式)。如果一个bean实现了这个接口,它将作为一个工厂
来暴露对象,而不是直接作为一个bean实例暴露自身。
Warning
实现此接口的bean不能用作普通bean。一个FactoryBean以bean的风格定义,但对于bean引用暴露的对象(通过getObject())总是它创建的对象。
容器只负责管理FactoryBean实例的生命周期,而不负责FactoryBean创建的对象的生命周期。因此,暴露的bean对象上的销毁方法(如java.io.Closeable.close())不会自动调用。相反,FactoryBean应该实现DisposableBean接口
并将任何此类关闭调用委托给底层对象。
通过配置文件实现#
编写工厂对象:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
/**
* @author luguosong
*/
public class FactoryMethod2 {
public User getUser() {
return new User(12, "lsg");
}
}
编写配置文件:
<?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="userFactory" class="com.luguosong.ioc.create_bean.FactoryMethod2"/>
<!--通过工厂方法创建对象-->
<bean id="user" factory-bean="userFactory" factory-method="getUser"/>
</beans>
测试:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class FactoryMethod2Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_factory_method2.xml");
//通过工厂方法创建对象
User user = context.getBean("user", User.class);
System.out.println(user);
}
}
直接继承FactoryBean接口#
编写工厂类,继承FactoryBean<T>
接口:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
import org.springframework.beans.factory.FactoryBean;
/**
* @author luguosong
*/
public class FactoryMethod1 implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User(12, "lsg");
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
编写配置文件:
<?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="userFactory" class="com.luguosong.ioc.create_bean.FactoryMethod1"/>
</beans>
测试:
package com.luguosong.ioc.create_bean;
import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class FactoryMethod1Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_factory_method1.xml");
//通过工厂方法创建对象
User user = context.getBean("userFactory", User.class);
System.out.println(user);
}
}
依赖注入#
set注入#
<property>标签
通过set方法
进行依赖注入。
可以使用
p命名空间
简化set注入
设置配置文件:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.luguosong.ioc.User">
<!--注入基本数据类型-->
<property name="name" value="张三"/>
<property name="age" value="18"/>
<!--注入数组-->
<property name="hobby">
<array>
<value>篮球</value>
<value>足球</value>
</array>
</property>
<!--注入Map对象-->
<property name="additionalInfo">
<map>
<entry key="性别" value="男"/>
<entry key="职业" value="IT"/>
</map>
</property>
</bean>
<bean id="userService" class="com.luguosong.ioc.UserService">
<!--注入对象-->
<property name="user" ref="user"/>
</bean>
<!--使用p命名空间进行注入-->
<bean id="user2" class="com.luguosong.ioc.User" p:name="李四" p:age="20"/>
</beans>
package com.luguosong.ioc.dependency_injection;
import com.luguosong.ioc.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 使用set方法进行依赖注入
*
* @author luguosong
*/
public class IocSetterTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc_set.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.test();
}
}
Property标签的name属性
name属性
对应set方法方法名去除set,并且首字母小写。
构造方法注入#
<constructor-arg>标签
通过构造方法
进行依赖注入。
- 默认通过参数的类型推断指定参数(❗这种方式构造函数中的参数类型不能重复)
- 通过
index属性
指定构造函数参数位置设置参数 - 通过
name属性
指定构造函数参数名称设置参数
可以使用
c命名空间
简化构造方法注入
设置配置文件:
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.luguosong.ioc.User">
<!--指定构造函数第一个参数-->
<constructor-arg index="0" value="12"/>
<!--指定构造函数第二个参数-->
<constructor-arg index="1" value="李四"/>
</bean>
<bean id="userService" class="com.luguosong.ioc.UserService">
<!--注入对象-->
<constructor-arg index="0" ref="user"/>
</bean>
<bean id="userService2" class="com.luguosong.ioc.UserService">
<!--使用参数名称name指定参数-->
<constructor-arg name="user" ref="user"/>
</bean>
<bean id="userService3" class="com.luguosong.ioc.UserService">
<!--根据类型推断指定参数-->
<constructor-arg ref="user"/>
</bean>
<!--基于c命名空间注入-->
<bean id="user2" class="com.luguosong.ioc.User" c:_0="12" c:_1="李四"/>
</beans>
测试:
package com.luguosong.ioc.dependency_injection;
import com.luguosong.ioc.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 使用构造方法进行依赖注入
*
* @author luguosong
*/
public class IocConstructorTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc_constructor.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.test();
}
}
util命名空间#
util命名空间针对与集合的复用场景。
<?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/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">
<!--util命名空间可以被复用-->
<util:map id="utilMap">
<entry key="性别" value="男"/>
<entry key="职业" value="IT"/>
</util:map>
<bean id="user1" class="com.luguosong.ioc.User">
<property name="additionalInfo" ref="utilMap"/>
</bean>
<bean id="user2" class="com.luguosong.ioc.User">
<property name="additionalInfo" ref="utilMap"/>
</bean>
</beans>
自动注入#
依赖可以根据名字
或类型
自动注入。
基于的还是set方法
。
<?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="com.luguosong.ioc.User">
<property name="name" value="张三"/>
</bean>
<!--根据set注入的 方法名 去找对应 id 的bean标签-->
<bean id="userService1" class="com.luguosong.ioc.UserService" autowire="byName"/>
<!--根据类型去寻找对应class的bean-->
<!--❗根据类型匹配,同一种类型只能有一个bean,否则会报错-->
<bean id="userService2" class="com.luguosong.ioc.UserService" autowire="byType"/>
</beans>
加载外部配置文件#
url=jdbc:mysql://localhost:3306/test
user=root
password=12345678
driver=com.mysql.jdbc.Driver
<?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/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="jdbc.properties"/>
<!--从配置文件中取值-->
<bean id="jdbc" class="com.luguosong.ioc.dependency_injection.Jdbc">
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="driver" value="${driver}"/>
</bean>
</beans>
bean作用域#
单例(singleton)
:将单个bean定义范围限定为每个Spring IoC容器的单个对象实例。原型(prototype)
:将单个bean定义范围限定为任意数量的对象实例。
仅在支持web的Spring ApplicationContext上下文中有效:
请求(request)
:将单个bean定义范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,该实例是基于单个bean定义创建的。会话(session)
:将单个bean定义范围限定为HTTP会话的生命周期。应用程序(application)
:将单个bean定义范围限定为ServletContext的生命周期。WebSocket(websocket)
:将单个bean定义范围限定为WebSocket的生命周期。
<?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">
<!--singleton:默认值,表示整个Spring容器中只有一个Bean实例,无论多少次请求,都会返回同一个实例。-->
<bean id="user1" class="com.luguosong.ioc.User" scope="singleton"/>
<!--prototype:每次请求都会创建一个新的Bean实例。-->
<bean id="user2" class="com.luguosong.ioc.User" scope="prototype"/>
<!--request:每个HTTP请求都会创建一个新的Bean实例。-->
<!--适用于Web应用,需要包含spring-webmvc依赖。-->
<bean id="user3" class="com.luguosong.ioc.User" scope="request"/>
<!--session:每个HTTP会话都会创建一个新的Bean实例。-->
<!--适用于Web应用,需要包含spring-webmvc依赖。-->
<bean id="user4" class="com.luguosong.ioc.User" scope="session"/>
</beans>
bean生命周期#
实例化Bean
调用setter方法属性赋值
实现Aware相关接口,执行对应方法
:BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口初始化Bean之前执行
:自定义处理器,实现BeanPostProcessor接口,重写postProcessBeforeInitialization方法实现InitializingBean接口执行对应方法
初始化Bean
:配置文件bean配置init-method属性初始化Bean之后执行
:自定义处理器,实现BeanPostProcessor接口,重写postProcessAfterInitialization方法使用Bean
实现DisposableBean接口,执行对应方法
销毁Bean
:配置文件bean配置destroy-method属性
Warning
Spring容器只对作用域scope="singleton"
的bean进行完整的生命周期管理。
package com.luguosong.ioc.life_cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
/**
* @author luguosong
*/
public class Dog implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware , InitializingBean,DisposableBean {
private String name;
public Dog() {
System.out.println("实例化Bean");
}
public void setName(String name) {
System.out.println("调用setter方法属性赋值");
this.name = name;
}
@Override
public void setBeanName(String name) {
System.out.println("实现BeanNameAware接口,setBeanName方法执行");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("实现BeanClassLoaderAware接口,setBeanClassLoader方法执行");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("实现BeanFactoryAware接口,setBeanFactory方法执行");
}
public void initBean() {
System.out.println("初始化Bean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("实现InitializingBean接口,afterPropertiesSet方法执行");
}
@Override
public void destroy() throws Exception {
System.out.println("实现DisposableBean接口,destroy方法执行");
}
public void destroyBean() {
System.out.println("销毁Bean");
}
}
配置文件,指定init-method
和destroy-method
:
<?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 class="com.luguosong.ioc.life_cycle.MyBeanPostProcessor"/>
<!--需要手动指定初始化方法和销毁方法,对应方法才会生效-->
<bean id="dog" class="com.luguosong.ioc.life_cycle.Dog" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="wangcai"/>
</bean>
</beans>
配置处理器,其方法在初始化前后执行:
package com.luguosong.ioc.life_cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author luguosong
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后执行");
return bean;
}
}
测试:
package com.luguosong.ioc.life_cycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class LifeCycleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_life_cycle.xml");
context.getBean("dog", Dog.class);
context.close();
}
}
将自己new的对象添加到Bean#
package com.luguosong.ioc.other;
import com.luguosong.ioc.User;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
/**
* @author luguosong
*/
public class MyObjectToApplication {
public static void main(String[] args) {
User user = new User();
//打印地址
System.out.println(Integer.toHexString(System.identityHashCode(user))); //5b480cf9
//将对象添加到容器中
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("user",user);
//从容器中获取Bean
User user1 = factory.getBean("user",User.class);
System.out.println(Integer.toHexString(System.identityHashCode(user1))); //5b480cf9
}
}
注解开发#
入门示例#
通过注解的方式引入依赖:
package com.luguosong.ioc.annotation.hello;
import org.springframework.stereotype.Component;
/**
* 如果不指定value,则默认使用类名首字母小写作为id名
* @author luguosong
*/
@Component("user")
public class User {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
Note
如果不设置@Component
注解的value属性,Spring会默认使用类名首字母小写作为bean的id
配置文件配置包扫描:
<?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/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">
<!--Spring扫描指定包中的类-->
<context:component-scan base-package="com.luguosong.ioc.annotation.hello"/>
</beans>
测试:
package com.luguosong.ioc.annotation.hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author luguosong
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc_annotation_hello.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}
}
@Component别名#
一下别名功能与@Component
相同,只是为了区分不同使用场景:
@Controller
:表示层bean@Service
:业务逻辑层bean@Repository
:持久化层bean
依赖注入#
使用注解方式就行依赖注入可以分为以下几种方式:
@Value
:注入基本数据类型@Autowired
:根据类型注入(接口只能有一个实现类)@Autowired+@Qualifier
:根据bean名称注入@Resource
:根据bean名称注入
Warning
如果@Resource
根据名称没有找到对应的类,则会根据类型
注入。
此时也会要求接口只有唯一的实现类。
要使用@Resource
注解需要依赖以下包:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>3.0.0</version>
</dependency>
package com.luguosong.ioc.annotation.dependency_injection;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author luguosong
*/
@Component
public class User {
//注入基本数据类型
@Value("张三")
private String name;
/*
* 根据类型注入
*
* ⭐接口只能有一个实现类
* */
@Autowired
private Hobby hobby;
/*
* 根据名字注入(也就是bean的id)
*
* 这种情况允许接口有多个实现类
* */
@Autowired
@Qualifier("bike")
private Vehicle vehicle1;
/*
* 根据名字注入
* */
@Resource(name = "car")
private Vehicle vehicle2;
/*
* @Resource省略name属性
* 会根据属性名寻找对应名字的实现类
* */
@Resource
private Vehicle car;
public void play(){
hobby.play();
}
public void driver(){
vehicle1.drive();
vehicle2.drive();
car.drive();
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
注解配置文件#
可以使用@Configuration
注解替代xml方式的配置文件。做到全注解无xml
开发。
package com.luguosong.ioc.annotation.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 通过@ComponentScan注解配置包扫描
*
* @author luguosong
*/
@Configuration
@ComponentScan({"com.luguosong.ioc.annotation.config"})
public class SpringConfig {
}
编写测试类,将ClassPathXmlApplicationContext
更改为AnnotationConfigApplicationContext
,从配置类
读取配置:
package com.luguosong.ioc.annotation.config;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author luguosong
*/
public class Test {
public static void main(String[] args) {
// 根据配置类创建Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user);
}
}