存储Bean对象
之前的配置文件中spring-config.xml中,需要添加才能够把对象存入容器中。而现在只需要在存入容器的类上添加注解即可,提高了开发效率,在这之前需要在配置文件中配置上存储对象的扫描路径,只有被配置的包下的所有类,添加了注解才能被正确识别并保存到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"
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"></content:component-scan>
</beans>
如果不想配置路径,想让Spring从根路径开始搜索,填写 ** ,但是实际开发中不会这样写
使用注解存储Bean对象
类注解:@Controller @Service @Repository @Component @Configuration
@Controller //表示要把对象存入Spring中
public class UserController {
public void sayHi(String name) {
System.out.println("hi: " + name);
}
}
public class App {
public static void main(String[] args) {
//1.得到Spring对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
//2.通过Spring取Bean对象
UserController user = context.getBean("userController", UserController.class);
//3. 使用Bean对象
user.sayHi("张三");
}
}
从Spring中获取Bean对象,Bean对象的命名规则
当使用5大类注解时,默认情况下获取Bean对象,只需要将类名首字母小写即可;当Bean对象的首字母和第二个字母都是大写时,此时需要使用原类名才能获取到Bean对象
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UService user = context.getBean("UService", UService.class);
user.sayHi("李四");
}
@Service
public class UService {
public void sayHi(String name) {
System.out.println("hi: " + name);
}
}
其他类注解,这里就不一一演示了,在Spring中他们的功能都是把对象存入容器中
既然功能都类似,为什么要搞5个注解,一个类注解不就够了吗
这就类似于各个省份的车牌号,之所以不做成全国统一,就是为了方便的识别出一辆车的归属地。而这里搞多个类注解就是为了程序员能快速了解当前类的用途
@Controller(控制器): 归属于业务逻辑层,用来控制用户的行为,用来检查用户参数的有效性合理性。类似于车站的安检
@Service(服务):归属于服务层,用来调用持久化类实现相应的功能。类似于车站的咨询台,可以把乘客分配到各个检票口
@Repository(仓库):归属于持久层,直接和数据库进行交互,通常一个表对应一个@Repository。类似于各个动车执行各自的任务,有的开往北京,有的开去上海。
@Configuration(配置): 归属于配置层,用来配置当前项目的一些信息
@Component(组件): 归属于公共工具类,提供某些公共服务
:::info
五大类注解的关系
:::
点开各个注解的源码后发现,@Component是其他四个类注解的父类
方法注解@Bean
作用:通过@Bean注解把方法返回的对象存入Spring中
class User{
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class UserComponent {
@Bean
public User getUser() {
//伪代码,在spring中是不使用new的
User user = new User();
user.setId(1);
user.setName("王五");
return user;
}
}
注意:@Bean注解要配合类注解一起使用,因为一个项目中方法非常多,如果直接查找所有方法性能太低,所以配合类注解一起使用
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
//通过方法的原名取Bean对象
User user = context.getBean("getUser", User.class);
System.out.println(user.toString());
}
}
:::danger
给Bean取别名
:::
class User{
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class UserComponent {
//取别名后,原来的方法名不能在使用
@Bean(name = {"user1", "user2"})
public User getUser() {
//伪代码,在spring中是不使用new的
User user = new User();
user.setId(1);
user.setName("王五");
return user;
}
}
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
//通过方法的原名取Bean对象
User user = context.getBean("user2", User.class);
System.out.println(user.toString());
}
}
**注意:**取完别名后不能再使用方法名来获取Bean对象了,没起别名时就类似刚提车没帮牌子交警不会抓你,当你办了牌照(起了别名)就不能拆掉牌照上路。
取Bean对象
在上面的代码中我们要先获取Spring上下文对象,再通过getBean()方法获取Bean对象,步骤过于繁琐,因此Spring官方又引入了“对象注入”。
对象注入有三种实现方式,如下
- 属性注入
- Setter注入
- 构造方法注入
属性注入
@Service
public class UserService {
public User getUser(Integer id, String name) {
//伪代码
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
}
@Controller
public class UserController {
//属性注入
@Autowired //相当于userService = new UserService()
private UserService userService;
public User getUser(Integer id, String name) {
return userService.getUser(id, name);
}
}
@Autowired的作用:相当于new UserService(),从Spring中取出UserService对象赋值
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController controller = context.getBean("userController", UserController.class);
System.out.println(controller.getUser(1, "张三"));
}
}
优点:
实现简单
缺点:
- 不如注入不可变对象(final),因为jdk规定final修饰的变量要么直接赋值,要么通过构造方法赋值
- 只适用于ioc容器
- 更容易违背单一设计原则(一个类干一件事,可能注入不相关的对象)
setter注入
@Controller
public class UserController {
//setter注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id, String name) {
return userService.getUser(id, name);
}
}
优点:
相比于属性注入而言,更加符合单一设计原则,毕竟你多写了一个set方法
缺点:
- 不能注入不可变对象
- 注入对象可被修改,因为set是普通方法,可以在其他地方被调用,存在修改的风险
构造方法注入(Spring官方推荐)
@Controller
public class UserController {
//构造方法注入
private UserService userService;
@Autowired //如果只有一个构造方法可以省略
public UserController(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id, String name) {
return userService.getUser(id, name);
}
}
在只有一个构造方法时@Autowired可以被省略,多个构造方法时不能被省略
优点:
- 可以注入一个不可变对象
- 注入的对象不能被修改
- 注入的对象可能被final修饰
- 构造方法是随着类的加载只执行一次,不像set方法可以被到处调用修改
- 注入的对象会被完全初始化(构造方法的特点)
- 通用性更好,不止ioc容器能用
缺点:
没有属性注入实现简单
然而在日常开发中,使用属性注入的更普遍。
@Resource注解
这是由jdk提供的”对象注入“的方式
@Controller
public class UserController {
@Resource //jdk提供的注入关键字
private UserService userService;
public User getUser(Integer id, String name) {
return userService.getUser(id, name);
}
}
:::danger
@Autowired和@Resource的区别
:::
- 出生不同:@Autowired 来着Spring,而@Resource 来着JDK
- 顺序不同:@Autowired是先根据名称获取Bean对象,如果获取不到在根据类型来获取;而@Resource是先根据类型获取Bean对象,在根据名称来获取Bean对象
- 功能不同:@Autowired支持属性注入,setter注入,构造方法注入;而@Resource支持属性注入,setter注入,不支持构造方法注入
- 参数支持不同:@Resource支持更多的参数设置;而@Autowired不支持
解决同一类型多个Bean的报错
@Service
public class UserService {
@Bean
public User getUser1() {
//伪代码
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
@Bean
public User getUser2() {
//伪代码
User user = new User();
user.setId(2);
user.setName("李四");
return user;
}
}
@Controller
public class UserController {
@Resource
private User user;
public User getUser() {
return user;
}
}
获取到两个Bean对象,发生了冲突
处理方法
- 使用@Resource(name=“”)
指定获取getUser1这个方法返回的User对象
- 使用@Qualifier