Spring中Bean的相关注解
目录
1.Spring IoC&DI
2.关于Bean存储的相关注解(类注解与方法注解)
Bean的获取方式
类注解和方法注解的重命名
2.1 类注解
2.1.1 @Controller
2.1.2 @Service
2.1.3 @Repository
2.1.4 @Component
2.1.5 @Configuration
2.2 方法注解-@Bean
2.2.1 定义多个对象
2.2.2 扫描路径-@ComponentScan
3.DI注入方式-@Autowired
3.1 属性注入
3.2 Setter方法注入
3.3 构造方法注入
3.4 三种注入方式的优缺点
3.5 @Autowired存在的问题及解决方法
1.Spring IoC&DI
Spring是包含了众多工具方法的一个IoC容器
- IoC(Inversion of Control,控制反转),是Spring的一个核心思想,简单来说就是将对象之间层层的依赖关系反转过来(一开始是使用方创建并控制依赖对象,现在是把依赖对象注入到当前对象中),使得依赖对象无论发生什么改变,当前类都不受影响,大大降低了代码的耦合度
- IoC容器就是创建并管理这些对象的容器,不再需要用户自己去创建对象,而是交给IoC容器去创建并对对象进行统一管理(将对象的控制权交给Spring)
- DI(Dependency Injection,依赖注入),是IoC的一种具体实现,依赖注入是一个过程,在Ioc容器在创建Bean时,去提供运行时所依赖的资源,这个资源就是对象,简单点说,依赖注入就是把对象取出来放到某个类的属性中
2.关于Bean存储的相关注解(类注解与方法注解)
Bean:Spring是一个IoC容器,而IoC容器中装的就是Bean(对象)
Bean的获取方式
Bean的获取方式有很多,常用的是以下三种
Object getBean(String name) throws BeansException | 根据Bean名称获取Bean |
<T> T getBean(String name, Class<T> requiredType) throws BeansException | 根据Bean名称和类型获取Bean |
<T> T getBean(Class<T> requiredType) throws BeansException | 根据类型获取Bean |
Bean名称:当使用的是类注解时,Bean的名称为类名的小驼峰(第一个单词以小写字母开始,后面每个单词首字母大写),如果类名前两个字母都是大写,则Bean名称为类名;当使用的是方法注解时,Bean名称就是方法名
注:对同一个类获取多次Bean,指向的是同一个对象(类似单例模式,有且仅有一个对象)
类注解和方法注解的重命名
类注解重命名:类注解+(新名称)
//启动类代码
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserComponent userComponent=(UserComponent) context.getBean("user1");//获取名称为user1的Bean//如果再去获取原来名称为userComponent的Bean,会报错userComponent.sayHi();}
}//UserComponent类
package com.example.demo;
import org.springframework.stereotype.Component;
@Component("user1")//重命名为user1
public class UserComponent {public void sayHi(){System.out.println("Hi, I'm UserComponent");}
}
方法注解重命名:@Bean+(新名称)
//启动类代码
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserInfo userInfo1=(UserInfo) context.getBean("user1");UserInfo userInfo2=(UserInfo) context.getBean("user2");System.out.println(userInfo1);System.out.println(userInfo2);}
}//BeanTest类
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class BeanTest {@Bean("user1")//重命名为user1public UserInfo userInfo1(){return new UserInfo("zhangsan",1);}@Bean("user2")//重命名为user2public UserInfo userInfo2(){return new UserInfo("lisi",2);}
}
注:类注解和方法注解的重命名的名称可以有多个, 使用{ }来标识,例如@Component({"user1","user2"})
2.1 类注解
类注解总共有五种,这些注解都有各自不同的应用场景
- @Controller:控制层(Controller),接收请求,对请求进行处理并进行响应
- @Service:业务逻辑层(Service),处理具体的业务逻辑
- @Repository:数据访问层(Dao),负责数据访问操作
- @Configuration:配置层,处理项目中的一些配置信息
- @Component:元注解,上述四种注解都含有@Component
2.1.1 @Controller
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserController userController=(UserController) context.getBean("userController");userController.sayHi();}
}//UserController类
package com.example.demo;
import org.springframework.stereotype.Controller;
@Controller//将对象存储到Spring中
public class UserController {public void sayHi(){System.out.println("Hi,I'm UserController");}
}
2.1.2 @Service
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserService userService=(UserService) context.getBean("userService");userService.sayHi();}
}//UserService类
package com.example.demo;
import org.springframework.stereotype.Service;
@Service//将对象存储到Spring中
public class UserService {public void sayHi(){System.out.println("Hi, I'm UserService");}
}
2.1.3 @Repository
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserRepository userRepository=(UserRepository) context.getBean("userRepository");userRepository.sayHi();}
}//UserRepository类
package com.example.demo;
import org.springframework.stereotype.Repository;
@Repository//将对象存储到Spring中
public class UserRepository {public void sayHi(){System.out.println("Hi,I'm UserRepository");}
}
2.1.4 @Component
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserComponent userComponent=(UserComponent) context.getBean("userComponent");userComponent.sayHi();}
}//UserComponent类
package com.example.demo;
import org.springframework.stereotype.Component;
@Component//将对象存储到Spring中
public class UserComponent {public void sayHi(){System.out.println("Hi, I'm UserComponent");}
}
2.1.5 @Configuration
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserConfiguration userConfiguration=(UserConfiguration) context.getBean("userConfiguration");userConfiguration.sayHi();}
}//UserConfiguration类
package com.example.demo;
import org.springframework.context.annotation.Configuration;
@Configuration//将对象存储到Spring中
public class UserConfiguration {public void sayHi(){System.out.println("Hi,I'm UserConfiguration");}
}
2.2 方法注解-@Bean
当一个类需要多个对象,或者需要使用外部包里的类时,使用类注解无法满足需求,这时就需要用到方法注解@Bean
2.2.1 定义多个对象
//启动类代码(从Spring中获取Bean)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);//如果这里使用类名获取Bean,会由于得到多个Bean而报错,所以需要使用上述Bean获取方式中的第1或第2种UserInfo userInfo1=(UserInfo) context.getBean("userInfo1");//获取方法名为userInfo1对应的BeanUserInfo userInfo2=(UserInfo) context.getBean("userInfo2");//获取方法名为userInfo2对应的BeanSystem.out.println(userInfo1);System.out.println(userInfo2);}
}//BeanTest类
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component//方法注解需要搭配类注解一起使用,否则会报错
public class BeanTest {@Beanpublic UserInfo userInfo1(){return new UserInfo("zhangsan",1);}@Beanpublic UserInfo userInfo2(){return new UserInfo("lisi",2);}
}
2.2.2 扫描路径-@ComponentScan
Spring的扫描路径默认为启动类(@SpringBootApplication注释的类)所在的路径,如果路径下有被类注解注释的类,就会将该类的对象存储到Spring中。如果想要修改默认路径,可以通过@ComponentScan来指定扫描路径
//添加到启动类中,路径要和默认路径不同
@ComponentScan("com.example.demo.test")
//指定单个扫描路径
@ComponentScan({"com.example.demo.test","com.example.demo.test1"})
//指定多个扫描路径
3.DI注入方式-@Autowired
@Autowired注释的作用主要是从Spring中获取对象,注入到对象的使用方(根据类型注入)。对于DI注入,Spring提供了三种方式,分别是属性注入,Setter方法注入以及构造方法注入
3.1 属性注入
属性注入使用@Autowire实现,以下是使用属性注入实现Service类注入到Controller类的一个简单例子
//启动类代码(获取UserController对象中,调用sayHi方法)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserController userController=(UserController) context.getBean("userController");userController.sayHi();}
}//UserController类
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {//使用属性注入将Service类注入到Controller类中@Autowiredprivate UserService userService;public void sayHi(){System.out.println("Hi,I'm UserController");userService.sayHi();}
}//UserService类
package com.example.demo;
import org.springframework.stereotype.Service;
@Service
public class UserService {public void sayHi(){System.out.println("Hi, I'm UserService");}
}
3.2 Setter方法注入
Setter方法注入即在设置set方法时加上@Autowired注解
//UserController类
//启动类代码和UserService类与属性注入中一致
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {private UserService userService;//使用Setter方法注入将Service类注入到Controller类中@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("Hi,I'm UserController");userService.sayHi();}
}
3.3 构造方法注入
构造方法注入即在类的构造方法中实现注入
注:使用构造方法注入时,如果注入的类只有一个构造方法,那么@Autowired可以省略,如果注入的类有多个构造方法,那么需要添加@Autowired来明确指定使用哪个构造方法,否则会报错
//UserController类
//启动类代码和UserService类与属性注入中一致
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {private UserService userService;//使用构造方法注入将Service类注入到Controller类中@Autowiredpublic UserController(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("Hi,I'm UserController");userService.sayHi();}
}
3.4 三种注入方式的优缺点
属性注入
- 优点:简介,方便使用
- 缺点:只能用于Ioc容器;不能注入一个Final修饰的属性
Setter方法注入
- 优点:方便在类实例之后,重新对该对象进行配置或者注入
- 缺点:不能注入一个Final修饰的属性;注入对象可能会被改变
构造方法注入
- 优点:可以注入Final修饰的属性;注入的对象不会被修改;依赖对象被使用前一定会被完全初始化;通用性好,更换任何框架都可适用
- 缺点:注入多个对象时,代码比较繁琐
3.5 @Autowired存在的问题及解决方法
当同一个类型存在多个Bean时,使用@Autowired会存在问题
//启动类代码
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(Demo3Application.class, args);UserController userController=context.getBean(UserController.class);userController.sayHi();}
}//UserController类
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {@Autowired//注入UserInfoprivate UserInfo userInfo;public void sayHi(){System.out.println("Hi,I'm UserController");System.out.println(userInfo);}
}//BeanTest类
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class BeanTest {@Beanpublic UserInfo userInfo1(){return new UserInfo("zhangsan",1);}@Beanpublic UserInfo userInfo2(){return new UserInfo("lisi",2);}
}
@Autowired注解会根据类型UserInfo去查找Bean,找到userInfo1和userInfo2两个方法,由于查找到的Bean只能是唯一的,所以程序会报错(非唯一的Bean)。那么如何解决这个问题呢?Spring提供了几个解决方法:@Primary,@Qualifier,@Resource
@Primary:当同个类型存在多个Bean注入时,添加@Primary注解用来确定默认的实现
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
public class BeanTest {@Primary//指定该Bean为Bean的默认实现@Beanpublic UserInfo userInfo1(){return new UserInfo("zhangsan",1);}@Beanpublic UserInfo userInfo2(){return new UserInfo("lisi",2);}
}
@Qualifier:添加@Qualifier(Bean的名称),指定当前要注入的Bean(@Qualifier需要搭配@Autowired使用,不能单独使用)
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {@Qualifier("userInfo1")//指定Bean名称为userInfo1@Autowiredprivate UserInfo userInfo;public void sayHi(){System.out.println("Hi,I'm UserController");System.out.println(userInfo);}
}
@Resource:与@Qualifier用法相同,也是指定Bean的名称进行注入,但不需要搭配@Autowired
package com.example.demo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {@Resource(name="userInfo1")//指定Bean名称为userInfo1并注入private UserInfo userInfo;public void sayHi(){System.out.println("Hi,I'm UserController");System.out.println(userInfo);}
}
除了上述三种使用注解的解决方法,还可以通过改变对象名称,使其与要注入的Bean的名称相同
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {@Autowiredprivate UserInfo userInfo1;//注入Bean名称为userInfo1,则对象名称也为userInfo1public void sayHi(){System.out.println("Hi,I'm UserController");System.out.println(userInfo1);}
}