Spring的IOC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)是Spring框架的核心概念,用于实现松耦合和可维护的应用程序。
Spring在实现IOC时运用了一些常见的设计模式,如工厂模式、单例模式和依赖注入模式等。这些设计模式帮助Spring实现了对象的创建、组装和管理,提供了灵活、可扩展的编程模型。
<bean id="userBean" class="org.example.bean.UserBean"></bean>
在Spring配置文件中,使用bean
标签,添加相应的属性,实现对象的创建
id
:对象的唯一表示,一般为类名并首字母小写class
:创建对象所在类的全类名name
:和id作用相同,只不过name属性可以添加特殊符号如/
,id属性不可以创建对象的时候,默认执行无参的构造
实际上就是Spring在创建对象后,通过调用相应的Setter完成属性的注入
1、bean类中,创建属性,以及对应的Setter
public class Book {
private String bookName;
public void setBookName(String bookName) {
this.bookName = bookName;
}
}
2、在applicationContext.xml
配置文件中,先配置对象的创建,再配置属性
<bean id="book" class="org.example.bean.Book">
<!-- 使用property标签,完成属性注入
name:表示需要注入的属性
value:表示注入属性的值,如果注入的是对象,那么使用ref
-->
<property name="bookName" value="西游记"></property>
</bean>
1、创建类,并提供有参构造
public class Person {
private String personName;
public Person() {
}
public Person(String personName) {
this.personName = personName;
}
}
2、在Spring配置文件中进行配置
<bean id="person" class="org.example.bean.Person">
<!-- 使用constructor-arg标签,完成使用有参构造属性注入
name:需要注入的属性,可以换成index,按照在有参构造中的属性的索引注入
value:注入属性的值,如果注入的是对象,那么使用ref
-->
<constructor-arg name="personName" value="lucy"></constructor-arg>
</bean>
实际上是对于Setter注入的一种简化,底层使用的还是Setter
1、配置文件添加P命名空间xmlns:p="http://www.springframework.org/schema/p"
<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">
</beans>
2、在bean标签中,进行操作
<bean id="book" class="org.example.bean.Book" p:bookName="西游记"></bean>
在Spring的XML配置文件中,<property>
标签用于定义Bean的属性注入。通过<property>
标签,可以将值或引用注入到Bean的属性中。
<property name="bookName">
<null/>
</property>
例如特殊符号<
,可以使用如下两种方式注入值
方式一,使用转义字符
<property name="bookName" value="<lucy>"></property>
方式二,使用CDATA
<property name="bookName">
<value>
<![CADATA[<lucy>]]>
</value>
</property>
只需要将相关的对象,添加在配置文件中,由Spring创建,然后通过ref
属性指定beanId,进行相应的属性(对象)注入,需要在调用类中,添加被调用类对象为属性,并且提供setter,例如,UserService中,需要调用UserDao
public class UserDao {
public void add(){
System.out.println("open in UserDao -- add");
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void showAdd(){
userDao.add();
}
}
<bean id="userDao" class="org.example.dao.UserDao"></bean>
<bean id="userService" class="org.example.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
例如有Student、School两个实体类,Student实体类中有School属性,那么可以通过内部bean的方式进行注入,也可以通过外部bean,内部bean方式如下
public class School {
private String schoolName;
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
public class Student {
private String studentName;
private School school;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
}
<bean id="student" class="org.example.bean.Student">
<property name="studentName" value="lucy"></property>
<property name="school">
<bean id="school" class="org.example.bean.School">
<property name="schoolName" value="清华大学"></property>
</bean>
</property>
</bean>
用于自动装配Bean的依赖项。它指定了如何解析Bean的依赖关系,常见值如下
no
(默认值):不自动装配依赖项。需要手动通过<property>
或<constructor-arg>
标签显式地注入依赖项。byName
:按照属性名称自动装配依赖项。Spring会查找与属性名称相同的Bean,并将其注入到相应的属性中。byType
:按照属性类型自动装配依赖项。Spring会查找与属性类型兼容的Bean,并将其注入到相应的属性中。如果存在多个兼容的Bean,将抛出异常。constructor
:通过构造函数自动装配依赖项。Spring会根据构造函数的参数类型查找与之兼容的Bean,并自动将其注入到构造函数中。default
:会根据情况自动确定使用哪种自动装配方式。public class Family {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Family{" +
"user=" + user +
'}';
}
}
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="top.ygang.springdemo.User">
<property name="name" value="grady"/>
</bean>
<!-- 创建bean,并使用autowire注入属性 -->
<bean id="family" class="top.ygang.springdemo.Family" autowire="byType"/>
</beans>
使用bean
标签的scope
属性进行指定Bean的作用域,控制Bean的生命周期和可见性。
singleton
:默认值,单例模式,表示在整个应用程序中只创建一个Bean实例。prototype
:每次请求时都创建一个新的Bean实例,每次在获得才会被创建,每次创建都是新的对象。request
:在每个HTTP请求中创建一个新的Bean实例(仅适用于Web应用)。session
:在每个HTTP会话中创建一个新的Bean实例(仅适用于Web应用)。<bean scope="singleton" ... />
使用bean
标签的lazy-init
属性指定Bean的延迟初始化。当设置为true
时,Bean将在第一次使用时才被初始化,而不是在应用程序启动时立即初始化。
<bean lazy-init="true" ... />
使用bean
标签的depends-on
属性指定Bean依赖的其他Bean的名称。它确保在当前Bean实例化之前,指定的Bean已经被实例化。
<bean depends-on="userRepository" ... />
注解 | 描述 |
---|---|
@Component |
通常用于普通Java类身上,表示这是一个需要被Spring容器进行管理的组件 |
@Controller |
用于描述表现(控制)层controller的类,这个类需要被Spring容器进行管理,效果等同于@Component |
@Service |
用于描述业务层service的类,这个类需要被Spring容器进行管理,效果等同于@Component |
@Repository |
用于描述持久层mapper(dao)的类,这个类需要被Spring容器进行管理,效果等同于@Component |
@Configuration |
声明配置类,效果等同于@Component |
@scope |
声明该类的作用范围,例如@Scope("prototype") |
@Bean |
在配置类的方法上添加注解,方法返回的对象将被注册为bean |
@Value |
注入外部配置文件的值,例如@Value("${property.name}") |
@Qualifier |
指定具体的依赖对象,和@Autowired 一起使用,通过指定bean的名称或标识符来精确注入依赖对象,例如:@Autowired @Qualifier("myBean") |
@PostConstruct |
在需要执行初始化操作的方法上添加注解,在构造函数执行之后执行初始化操作 |
@PreDestroy |
在需要执行清理操作的方法上添加注解,在bean销毁之前执行清理操作 |
@Autowired |
自动装配,由Spring框架提供,按照类型进行自动装配,找不到会抛出异常,可以设置属性required=false 找不到也不会抛异常,更具有Spring的特性和功能,例如支持@Qualifier 注解进行更精确的依赖注入,可以应用于类的构造方法、字段、Setter方法和任意方法上 |
@Resource |
自动装配,由Java EE提供,先按照名称进行装配,找不到再按类型,可以应用于类的字段、Setter方法和任意方法上,但是不支持构造方法注入 |
以上注解要使用的前提条件,必须在XML配置文件中开启注解扫描,指定要扫描的基础包,可以使用逗号分隔多个包名
<context:component-scan base-package="top.ygang.controller,top.ygang.service" />
标签指定了要扫描的基础包为top.ygang
。Spring将在该包及其子包下扫描所有被注解标记的组件,并将其注册到容器中。
过滤条件,可以使用<context:include-filter>
和<context:exclude-filter>
来设置扫描的过滤条件
<context:component-scan base-package="top.ygang">
<!-- 包含所有以org.springframework.stereotype.Service注解标记的类 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<!-- 排除所有以Impl结尾的类 -->
<context:exclude-filter type="regex" expression=".*Impl$"/>
</context:component-scan>
<context:annotation-config />
用于启用基于注解的配置和自动装配的支持,让Spring能够自动解析和处理@Autowired
、@Resource
等注解,从而实现依赖注入(2.5后的版本没必要加了)
在Spring2.5版本之前,还用来开启声明周期注解的使用@PostConstruct
和@PreDestroy
,从Spring 2.5版本开始,这些生命周期注解的支持被默认启用,无需额外的配置。
注意:使用@Value
注解时,通常需要将其应用于被Spring管理的组件(例如使用@Component
、@Service
、@Repository
等注解标识的类)中,以便Spring能够扫描到该组件,并在实例化过程中处理@Value
注解。
用于将值注入到类的字段、方法参数或构造函数参数中。它可以用于注入简单类型的值,如字符串、数字,也可以用于注入复杂类型,如对象、集合等
@Value("grady")
private String name;
// 使用Autowired注解可以在Spring容器创建当前类实例的时候自动调用,并且注入参数
@Autowired
public void setName(@Value("${name}")String name) {
this.name = name;
}
也可以从配置文件中读取,Spring内置了解析.properties
、.yaml\yml
的文件解析器,用来解析配置文件,需要在Spring配置文件中开启属性文件解析器,多个配置文件可以使用逗号,
进行分隔,也可以使用通配符*
来指定匹配,例如*.yml
<context:property-placeholder location="1.properties,2.yml" />
my.name=grady
my:
name: grady
@Value("${my.name}")
private String name;
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new
进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。
在Spring框架中,每个Bean都有其生命周期,即它在容器中的创建、初始化和销毁过程中经历的一系列阶段。Spring框架中的Bean生命周期涉及多个阶段,包括实例化、依赖注入、初始化、使用和销毁。下面是Spring Bean的生命周期的详细解释:
BeanNameAware
、BeanFactoryAware
、ApplicationContextAware
等),Spring容器会调用相应的回调方法,使Bean能够获取与Spring容器相关的信息。BeanPostProcessor
接口实现类的postProcessBeforeInitialization()
方法,可以在Bean初始化之前对Bean进行一些定制和处理@PostConstruct
注解标记的方法。InitializingBean
接口,并实现其中的afterPropertiesSet()
方法。BeanPostProcessor
接口实现类的postProcessAfterInitialization()
方法,可以在Bean初始化之后对Bean进行一些定制和处理@PreDestroy
注解标记的方法。DisposableBean
接口,并实现其中的destroy()
方法。Spring配置文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 https://www.springframework.org/schema/context/spring-context.xsd"
>
<!-- 开启注解扫描 -->
<context:component-scan base-package="top.ygang"/>
<!-- 开启注解支持 -->
<context:annotation-config/>
</beans>
Bean
@Component
public class User implements ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public User(){
System.out.println("Bean实例化:constructor");
}
public String getName() {
return name;
}
@Autowired
public void setName(@Value("grady") String name) {
System.out.println("Bean依赖注入:@Autowired");
this.name = name;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("Aware调用:setApplicationContext");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean初始化:afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
System.out.println("Bean销毁:destroy");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
BeanPostProcessor
@Configuration
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前回调:postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后回调:postProcessAfterInitialization");
return bean;
}
}
main方法
public static void main(String[] args) {
System.out.println("容器启动:ClassPathXmlApplicationContext");
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User bean = applicationContext.getBean(User.class);
System.out.println("使用Bean");
System.out.println(bean);
System.out.println("容器关闭:ConfigurableApplicationContext.close()");
applicationContext.close();
}
输出结果
容器启动:ClassPathXmlApplicationContext
Bean实例化:constructor
Bean依赖注入:@Autowired
Aware调用:setApplicationContext
初始化前回调:postProcessBeforeInitialization
Bean初始化:afterPropertiesSet
初始化后回调:postProcessAfterInitialization
使用Bean
User{name='grady'}
容器关闭:ConfigurableApplicationContext.close()
Bean销毁:destroy