Bean 相关的编程
Dependency
Injection(依赖注入)
构造方法注入
首先我们建立一个 Java Bean
User1.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.itheima;
import org.springframework.beans.factory.annotation.Autowired;
public class User1 { private int id; private String name; private String password;
@Autowired(required = false) public User1(int id, String name, String password) { this.id = id; this.name = name; this.password = password; }
public String toString() { return "id=" + id + ",name=" + name + ",password=" + password; } }
|
Java Bean 实际上是一个普通的 Java
类,但其按照一定的约定去写,例如包含 setter/getter
方法,提供无参构造方法等。Java Bean
的主要作用是封装数据,通常用于在不同层之间传递数据
这里的 @Autowired(required = false) 是为了消除 IDEA
的警告提示,表示这个构造方法的参数不需要自动装配,因为我们采用 xml
手动配置
接下来我们在 applicationContext.xml 中配置这个
Bean:
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user1" class="com.itheima.User1"> <constructor-arg name="id" value="1"/> <constructor-arg name="name" value="张三"/> <constructor-arg name="password" value="123"/> </bean> </beans>
|
在这里我们在 <beans> 标签中定义了一个 Bean,id 是
user1,class 是 com.itheima.User1。我们通过
<constructor-arg> 标签来指定构造方法的参数值。其中的
name 和 value
属性分别对应构造方法的参数名称和参数值。
随后我们测试依赖注入的结果
TestUser1.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser1 { public static void main(String[] args) throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-User.xml"); User1 user1 = applicationContext.getBean("user1", User1.class); System.out.println(user1); } }
|
首先我们通过 ApplicationContext 加载
applicationContext-User.xml 配置文件,然后通过
getBean 方法获取配置中的 User1
实例,并打印出来(调用 toString 方法)
setter 方法注入
我们来创建另一个 Bean
User2.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.itheima;
public class User2 { private int id; private String name; private String password;
public void setId(int id) { this.id = id; }
public void setName(String name) { this.name = name; }
public void setPassword(String password) { this.password = password; }
public String toString() { return "id=" + id + ",name=" + name + ",password=" + password; } }
|
getter/setter 可由 IDEA 自动生成,只需要编写成员变量
接下里我们在 applicationContext-User2.xml 中配置这个
Bean:
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user2" class="com.itheima.User2"> <property name="id" value="2"/> <property name="name" value="李四"/> <property name="password" value="456"/> </bean> </beans>
|
在这里我们通过 <property> 标签来指定 setter
方法的参数值。其中的 name 属性对应 setter 方法的名称(去掉
set 前缀,并将首字母小写),value 属性对应参数值
同样的,我们测试依赖注入的结果:
TestUser2.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser2 { public static void main(String[] args) throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-User2.xml"); User2 user2 = applicationContext.getBean("user2", User2.class); System.out.println(user2); } }
|
ApplicationContext Interface
ApplicationContext 建立在 BeanFactory 的基础上,下面是几种常见
Implementation
- ClassPathXmlApplicationContext:从类路径下加载配置文件
- FileSystemXmlApplicationContext:从文件系统加载配置文件
- AnnotationConfigApplicationContext:从 Java 注解配置类加载配置
Bean 的配置与实例化方法
常用 XML 配置 Bean,具体来说通过 <beans> 和
<bean> 标签来配置 Bean。<beans>
标签是 Bean 定义的根元素,<bean> 标签用于定义一个
Bean
<bean> 标签的常用属性:
- id:Bean 的唯一标识符
- name:Bean 的别名,可以有多个别名,使用逗号分隔
- class:Bean 的全限定类名
- scope:Bean 的作用域,常用的有 singleton(单例)和 prototype
<bean> 标签的常用子标签:
<constructor-arg>:用于指定构造方法的参数值
<property>:用于指定 setter 方法的参数值
直接由 Bean 类实例化
参考前文中的实现,不做单独介绍
由静态 Bean 工厂方法实例化
Bean2.java1 2 3 4 5 6 7
| package com.itheima;
public class Bean2 { public Bean2() { System.out.println("这是Bean2"); } }
|
MyBean2Factory.java1 2 3 4 5 6 7 8
| package com.itheima;
public class MyBean2Factory { public static Bean2 createBean() { return new Bean2(); } }
|
applicationContextBean2.xml1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="bean2" class="com.itheima.MyBean2Factory" factory-method="createBean"/> </beans>
|
在这里,通过制定 bean2 的 class 属性为
MyBean2Factory,并指定 factory-method 属性为
createBean,Spring 就会调用 MyBean2Factory 类的 createBean 方法来创建
Bean2 实例
随后通过 ApplicationContext 获取 Bean2 实例:
Bean2Test.java1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Bean2Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationBean2.xml"); System.out.println(applicationContext.getBean("bean2")); } }
|
由实例 Bean 工厂方法实例化
Bean3.java1 2 3 4 5 6 7
| package com.itheima;
public class Bean3 { public Bean3() { System.out.println("这是Bean3"); } }
|
MyBean3Factory.java1 2 3 4 5 6 7 8 9 10 11 12
| package com.itheima;
public class MyBean3Factory { public MyBean3Factory() { System.out.println("bean3工厂实例化中"); }
public Bean3 createBean() { return new Bean3(); } }
|
applicationContextBean3.xml1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="myBean3Factory" class="com.itheima.MyBean3Factory"/>
<bean id="bean3" factory-bean="myBean3Factory" factory-method="createBean"/> </beans>
|
这里 myBean3Factory 本身即是一个 Bean,在
bean3 的配置中通过 factory-bean 属性指定其工厂
Bean 为 myBean3Factory,并通过 factory-method
属性指定使用 myBean3Factory 中的 createBean
方法来创建 Bean3 实例
Bean3Test.java1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Bean3Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationBean3.xml"); System.out.println(applicationContext.getBean("bean3")); } }
|
这里从应用上下文获取 bean3 实例时,Spring 会先实例化
myBean3Factory,然后调用其 createBean
方法来创建 bean3 实例
Bean 的作用域
通过 <bean> 标签的 scope 属性来指定
Bean 的作用域,常用的有:
- singleton(单例):默认值,表示在 Spring 容器中只创建一个 Bean
实例,所有对该 Bean 的请求都会返回同一个实例
- prototype(原型):表示每次请求该 Bean
时都会创建一个新的实例。
- request:表示在一个 HTTP 请求内共享一个 Bean 实例,不同请求之间的
Bean 实例不同(仅适用于 Web 应用)
- session:表示在一个 HTTP 会话内共享一个 Bean 实例,不同会话之间的
Bean 实例不同(仅适用于 Web 应用)
Singleton(单例)作用域
applicationBean1.xml1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bean1" class="com.itheima.Bean1" scope="singleton"/> </beans>
|
由于其默认就是 singleton,所以其实可以省略 scope
属性。这里在 IDEA 中将会给出多余配置的 warning
Bean1.java1 2 3 4 5 6 7
| package com.itheima;
public class Bean1 { public Bean1() { System.out.println("这是Bean1"); } }
|
ScopeTest.java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ScopeTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationBean1.xml"); Bean1 bean1_1 = (Bean1) applicationContext.getBean("bean1"); Bean1 bean1_2 = (Bean1) applicationContext.getBean("bean1"); System.out.print(bean1_1 == bean1_2); } }
|
运行结果将会显示 true,而若将 scope
属性改为 prototype,则运行结果将会显示
false
Bean 的装配
基于 setter/getter
方法的装配方式需要由默认构造函数,而基于构造方法的装配方式则不需要默认构造函数,具体参考前文中
依赖注入部分
基于注解(annotation)的装配
Spring 常用注解:
@Component:用于标注一个类为 Spring
管理的组件,表示该类是一个普通的 Bean
@Controller:用于标注一个类为 Spring
的控制器组件,表示该类是一个控制层的 Bean
@Service:用于标注一个类为 Spring
的服务组件,表示该类是一个业务逻辑层的 Bean
@Repository:用于标注一个类为 Spring
的数据访问组件,表示该类是一个 DAO 层的 Bean
@Scope:用于指定 Bean 的作用域
@Value:用于注入简单类型的属性值
装配相关注解:
@Autowired:用于自动装配
Bean,默认按照类型进行装配
@Resource:用于自动装配 Bean,默认按照名称进行装配
@Qualifier:用于指定装配 Bean 的名称,配合 `@Autowired
@PostConstruct:用于标注一个方法,在 Bean
初始化完成后执行
@PreDestroy:用于标注一个方法,在 Bean 销毁前执行
注解注入属于 AOP,需要导入 spring-aop
依赖,并在配置文件中开启注解扫描和自动装配功能:
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.8.RELEASE</version> </dependency>
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
|
applicationContext.xml1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" 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="com.itheima"/> </beans>
|
定义一个 User 的 Entity (位于
Entity 包内)
User.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.itheima.entity;
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;
@Component("user") @Scope("singleton") public class User { @Value("1") private int id; @Value("张三") private String name; @Value("123") private String password;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String toString() { return "id=" + id + ",name=" + name + ",password=" + password; } }
|
定义 UserDao 和 UserService,分别位于 dao 和
service 包内
UserDao.java1 2 3 4 5
| package com.itheima.dao;
public interface UserDao { public void save(); }
|
UserDaoImpl.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.itheima.dao;
import com.itheima.entity.User; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Repository;
@Repository("userDao") public class UserDaoImpl implements UserDao { public void save() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user); System.out.println("执行UserDaoImpl.save()"); } }
|
Service 的接口与实现:
UserService.java1 2 3 4 5
| package com.itheima.service;
public interface UserService { public void save(); }
|
UserServiceImpl.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.itheima.service;
import com.itheima.dao.UserDao; import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("userService") public class UserServiceImpl implements UserService { @Resource(name = "userDao") private UserDao userDao;
public void save() { this.userDao.save(); System.out.println("执行UserServiceImpl.save()"); } }
|
这里通过 @Resource(name = "userDao") 注解将
UserDaoImpl 注入到 UserServiceImpl
中,name 属性指定要注入的 Bean 的名称,这里是
userDao
Controller 层:
UserController.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.itheima.controller;
import com.itheima.service.UserService; import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller public class UserController { @Resource(name = "userService") private UserService userService;
public void save() { this.userService.save(); System.out.println("执行UserController.save()"); } }
|
编写测试类:
AnnotationTest.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.itheima;
import com.itheima.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); UserController userController = (UserController) applicationContext.getBean("userController"); userController.save(); } }
|
自动装配
通过 @Autowired
注解实现自动装配,默认按照类型进行装配,如果有多个同类型的
Bean,可以配合 @Qualifier 注解指定 Bean
的名称,这里不做详细介绍
Bean 的生命周期
对于 Singleton 作用域的 Bean,Spring
可以管理创建、初始化和销毁的整个生命周期;对于 Prototype 作用域的
Bean,Spring 只负责创建和初始化,销毁由用户自己负责
这里介绍基于注解的监控时间节点方法
Student.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.itheima;
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy;
@Component("student") public class Student { @Value("1") private String id; @Value("张三") private String name;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@PostConstruct public void init() { System.out.println("Bean的初始化完成,调用init()方法"); }
@PreDestroy public void destroy() { System.out.println("Bean销毁前调用destroy()方法"); }
public String toString() { return "id=" + id + ",name=" + name; } }
|
这里的 @PostConstruct 注解标注的方法会在 Bean
初始化完成后执行,而 @PreDestroy 注解标注的方法会在 Bean
销毁前执行
applicationContextStudent.xml1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" 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="com.itheima"/> </beans>
|
StudentTest.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.itheima;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationStudent.xml"); Student student = (Student) applicationContext.getBean("student"); System.out.println(student); AbstractApplicationContext ac = (AbstractApplicationContext) applicationContext; ac.registerShutdownHook(); } }
|