Spring 是一个包含众多工具方法的 IoC 容器。
Spring 作为一个 IoC 容器最核心的功能是:存入对象,取出对象。
创建一个 Maven 项目
添加依赖,spring-context、spring-beans
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.2.3.RELEASEversion>
dependency>
dependencies>
public class App {
public static void main(String[] args) {
}
}
Bean: 对象,严格来说,Bean 指的是被多次使用的对象。
public class User {
public void sayHi() {
System.out.println("你好!");
}
}
在 resources 里面创建一个 Spring 的配置文件。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="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">
<bean id="user" class="com.demo.User">bean>
beans>
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
获取 Spring 上下文对象的两种方法:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
相同点:
都是容器管理对象,都可以获取 Spring 上下文对象。这两种获取 Spring 上下文对象的效果是一样的。
ApplicationContext 和 BeanFactory 的区别:
ApplicationContext 属于 BeanFactory 的 子类。Application 不仅继承了 BeanFactory 的功能,它还拥有更多的功能,例如对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持。
BeanFactory 是懒加载,需要用到 Bean 对象时,才去加载并初始化需要的那个 Bean 对象。
ApplicationContext 是在创建 Spring 上下文对象时,一次性加载并初始化所有的 Bean 对象。
ApplicationContext 创建 Spring 上下文时,会比较慢,但是后续获取 Bean 时,会比较快。
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 从 Spring 中,获取 Bean 对象
User user = (User) context.getBean("user"); // 依赖查找
// 使用 Bean 对象
user.sayHi();
}
}
getBean 的用法:
User user = (User) context.getBean("user");
User user = context.getBean(User.class);
但是,如果 Spring 中注入了多个类型相同的 Bean 时,使用 类型 获取 Bean,会报错。
User user = context.getBean("user", User.class);
resources 包下新建配置文件 spring-config.xml,在 spring-config.xml 中配置添加配置,需要配置存储对象的扫描包路径,配置的包路径下的类,并且添加了注解,才能被识别并存储到 Spring 中。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="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">
<content:component-scan base-package="com.demo">content:component-scan>
beans>
有两种注解类型可以实现:
@Controller // 使用类注解将对象存储到 Spring 中
public class User {
public void sayHi() {
System.out.println("User: 你好!");
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
User user = context.getBean("user", User.class);
// 3. 使用 Bean
user.sayHi();
}
}
@Controller(value = "userinfo")
public class User {
public void sayHi() {
System.out.println("User: 你好!");
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
User user = context.getBean("userinfo", User.class);
// 3. 使用 Bean
user.sayHi();
}
}
添加类注解时,指定了 Bean 的名称,那么只能用这个名称获取这个 Bean。
其他几个类注解的用法一样,达到的效果也一样。
为什么要有这么的多类注解?
这么多类注解的作用是标识,让我们看到一个类注解,就能知道这个类的用途:
项目分层和调用关系如下:
通常我们创建的类都是以大驼峰的方式命名的,然后我们通过类名的首字母小写就可以获取到 Bean 了。
@Controller
public class User {
public void sayHi() {
System.out.println("User: 你好!");
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
User user = context.getBean("user", User.class);
// 3. 使用 Bean
user.sayHi();
}
}
@Controller
public class admin {
public void sayHi() {
System.out.println("admin: 你好!");
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
admin admin = context.getBean("admin", admin.class);
// 3. 使用 Bean
admin.sayHi();
}
}
@Controller
public class UController {
public void sayHi() {
System.out.println("UController: 你好!");
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
UController uController = context.getBean("UController", UController.class);
// 3. 使用 Bean
uController.sayHi();
}
}
为什么 Bean 的命名规则是这样呢?
查看源码,找到 Spring框架 中的 AnnotationBeanNameGenerator 类,里面有个 buildDefaultBeanName 这个方法。
这里调用的是 JDK Introspector 中的 decapitalize 方法,源码如下:
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果长度大于1,第一个字符和第二个字符为大写,那么返回原类名
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
// 否则,将首字符小写,并返回
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
可以看到 Spring 中 Bean 的默认命名是使用了 JDK 中的方法。
方法注解 @Bean 是在方法上使用的注解,@Bean 是将方法的返回值的对象存储到 IoC 容器中。
public class User {
public String username;
public String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
@Component // 方法注解必须配合类注解使用
public class Users {
@Bean // Bean 名称默认是方法名
public User getUser() {
User user = new User();
user.username = "张三";
user.password = "123";
return user;
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
User user = context.getBean("getUser", User.class);
// 3. 使用 Bean
System.out.println(user);
}
}
@Component // 方法注解必须配合类注解使用
public class Users {
// 指定 Bean 名称
// @Bean({"user", "User"})
// @Bean(value = "user")
@Bean(name = {"user", "User"})
public User getUser() {
User user = new User();
user.username = "张三";
user.password = "123";
return user;
}
}
可以使用注解来更简单的获取 Bean 对象,获取 Bean 对象叫做对象装配,也叫对象注入。
对象装配的三种实现方式:
使用 @Autowired 可以实现这三种对象装配的方式。
代码演示,按照实际项目开发的分层结构,将 Service 类注入到 Controller 中。
public class User {
public String username;
public String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
@Service
public class UserService {
public User getUser(String username) {
User user = new User();
user.username = username;
return user;
}
}
@Controller
public class UserController {
// 属性注入
@Autowired
private UserService userService;
public User getUser(String username) {
return userService.getUser(username);
}
}
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 通过 Spring 上下文对象, 获取 Bean 对象
UserController userController = context.getBean("userController", UserController.class);
// 3. 使用 Bean
System.out.println(userController.getUser("李四"));
}
}
属性注入的关键代码:
@Controller
public class UserController {
// 属性注入
@Autowired
private UserService userService;
public User getUser(String username) {
return userService.getUser(username);
}
}
@Controller
public class UserController {
private UserService userService;
// 构造方法注入
// @Autowired
public UserController (UserService userService){
this.userService = userService;
}
public User getUser(String username) {
return userService.getUser(username);
}
}
@Controller
public class UserController {
private UserService userService;
// Setter 注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(String username) {
return userService.getUser(username);
}
}
代码演示依赖注入流程:
// 类注解存储Bean
@Service(value = "userService1")
public class UserService {
public User getUser(String username) {
User user = new User();
user.username = username;
return user;
}
// 方法注解存储Bean
@Bean("userService2")
public UserService get() {
return new UserService();
}
}
@Controller
public class UserController {
// 属性注入
@Autowired
private UserService userService;
public User getUser(String username) {
return userService.getUser(username);
}
}
报错信息:
解决方法:
@Autowired
private UserService userService1;
@Autowired
@Qualifier("userService2")
private UserService userService;
@Resource(name = "userService1")
private UserService userService;
@Resource 也可以实现对象注入,用法于 @Autowired 一样。
属性注入:
优点:使用简单。
缺点:
构造方法注入:
优点:
缺点:
Setter 注入:
优点:通常 Setter 只设置一个属性,所以更符合单一设计原则。
缺点:
静态方法中只能用依赖查找的方式从Spring 中获取 Bean,因为静态方法的加载时机在 Spring 对象注入之前。