通过注解,来实现更简单的存储和读取对象。但必须要配置扫描路径,就是在 配置文件当作添加 content:component-scan:
<?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: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.beans"></content:component-scan>
</beans>
意思就是,当前要存储的对象是在 com.beans 包下的对象。
五个注解如下:
就像之前存储的话,是要在 配置文件 当中来实现注入,现在直接通过 Controller 就可以了:
@Controller
public class blog {
public void sayHi() {
System.out.println("hello Controller");
}
}
这样就完成了对象的注入,使用原始的读取方式读取对象:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
Blog blog = context.getBean("blog", Blog.class);
//使用 bean
blog.sayHi();
}
}
运行结果如下:
但要注意的是,对象所在的包,只能是在 配置文件 的包下面。
使用 Service 注解:
@Service
public class UserService {
public void sayHi() {
System.out.println("hello Service");
}
}
代码:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserService userService = context.getBean(UserService.class);
//使用 bean
userService.sayHi();
}
}
使用 Repository 注解:
@Repository
public class UserRepository {
public void sayHi() {
System.out.println("hello Repository");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserRepository userRepository = context.getBean(UserRepository.class);
//使用 bean
userRepository.sayHi();
}
}
使用 Configuration 注解,要注意的是,千万不要把 Configuration 写成 Configurable:
@Configuration
public class UserConfiguration {
public void sayHi() {
System.out.println("hello Configuration");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
//使用 bean
userConfiguration.sayHi();
}
}
运行结果如下:
通过 Component 注解来注入对象:
@Component
public class UserComponent {
public void sayHi() {
System.out.println("hello Component");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserComponent userComponent = context.getBean(UserComponent.class);
//使用 bean
userComponent.sayHi();
}
}
明明每个注解都能实现类的注入,那为什么还需要五个呢?因为这就涉及到软件工程方面的知识了,一个软件至少会分为一下四层:
一个软件至少需要这四层,那么这五个类注解,可以更直观的让程序员知道当前类的用途。可以让代码的可读性更高。
查看 Configuration 的源码,发现父类是 Component :
查看 Controller 的源码,发现仍然是依靠 Component 来实现的:
查看 Repository 源码,还是依靠 Component 来实现的:
查看 Service 的源码,发现还是一样:
也就是说,这些注解的作用是一样的,但是对于开发人员的不一样的。Component 是所有类的父类。
Spring 使用五大类注解,生成 bean name:通过查看 AnnotationBeanNameGenerator.class 文件:
当然,字节码文件看起来是很累的,通过 gitee 里面可以找到源文件。在全站搜索 spring frame 相关项目就可以找到了:
然后就是源码了:
在 IDEA 里面查看 class 文件,找到名字相关的方法:
然后发现又调用了这个代码:
然后再次进入:
然后找到文件位置:
再往上找,发现是 JDK 里面的:
所以,只要看这段代码就知道为什么了:
也就是只有第一个字母是大写的,类名才是小写,当前两个字母都是大写的时候,返回的就是原类名了。
听过 Bean 来注入,就是 Bean 方法注解。Bean 注解,是针对方法实现的,只能添加在 方法上面:
先创建 User 对象:
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
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;
}
}
然后添加方法
@Component
public class UserBeans {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
这样就完成了对象的注入。
获取对象
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
User user = context.getBean("user1", User.class);
//使用 bean
System.out.println(user);
}
}
获取 bean 对象也叫做对象装配,是把对象从容器中取出来放到某个类中,有时候也叫对象注入。对象装配的实现方法有三种:
通过 @Autowired 注入:
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi() {
userService.sayHi();
}
}
就是把 UserService 类注入,然后就可以使用类了:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserController userController = context.getBean(UserController.class);
//使用 bean
userController.sayHi();
}
}
通过一个构造方法来实现属性注入:
@Controller
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
运行结果如下:
如果类当中,只有一个构造方法,那么 @Autowird 可以省略。
通过 Setter 方法注入:
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
相同点:都可以完成对象的注入。
不同点:
a)出身不同,@Resource 来自于 JDK,@Autowired 是由 Spring 框架提供的。
b)@Autowired 支持属性注入,构造方法注入 和 Setter 注入,而 @Resource 不支持构造方法注入。
c)支持的参数不同:@Resource 支持更多的参数设置,比如 name、type 设置。而 @Autowired 只支持 required 参数设置。
比如我们有多个 User 对象,那么获取 Bean 的时候。就要通过方法名获取了,但是如果重命名对象。也可以得到,就是在 Bean 后面加个括号,里面重命名就好了:
@Component
public class UserBeans {
@Bean(name = "userInfo")
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
User user = context.getBean("userInfo", User.class);
//使用 bean
System.out.println(user);
}
}
运行结果如下:
如果是重命名多个名字的时候,就用大括号把名字括起来:
@Component
public class UserBeans {
@Bean(name = {"userInfo", "user1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
@Bean 的命名规则: 当没有设置 name 属性的时候,那么 bean 默认的名称就是方法名。当设置了 name 属性之后,只能通过重命名的那么属性对应的值类获取。也就是重命名之后,再使用方法名就获取不到 bean 对象了。
我们把 User 对象(有多个对象)注入到 Controller 当中,看看能不能实现对象的使用:
@Controller
public class UserController {
@Autowired
private User user;
public void sayHi() {
System.out.println("User-> " + user);
}
}
然后通过 UserController 来使用对象:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserController userController = context.getBean(UserController.class);
//使用 bean
userController.sayHi();
}
}
运行结果如下:
报错提示说,User 对象被注入了两个。一个是 userInfo,一个是 user2。因为 Autowired 在注入的时候,是先根据类型来查找,然后就会去找 User 对象,找 User 类型的对象在 Spring 当中有没有。然后就找到了两个,然后就报错。
就是上面这种情况,在扫描的时候,发现了多个对象。解决方案如下:
精确的描述 bean 的名称,就是将注入的名称写对。比如说我们注入 user1 :
@Controller
public class UserController {
@Autowired
private User user1;
public void sayHi() {
System.out.println("User-> " + user1);
}
}
使用 Resource 注解,Resource 是来自 JDK 的,Resource 有 name 属性,就是类似于重命名:
@Controller
public class UserController {
@Resource(name = "user1")
private User user;
public void sayHi() {
System.out.println("User-> " + user);
}
}
通过 Autowired 搭配 Qualifier :
@Controller
public class UserController {
@Autowired
@Qualifier("user1")
private User user;
public void sayHi() {
System.out.println("User-> " + user);
}
}