5大注解
@Controller 控制器
@Service 服务
@Repository 仓库
@Componet 组件
@Configuration 配置
五大类注解的使用
//他们都是放在同一个目录下,不同的类中 只不过这里粘贴到一起
//控制器
@Controller
public class UserController {
public void SayHello(){
System.out.println("Hello");
}
}
//服务
@Service
public class UserService {
public void doService(){
System.out.println("Service");
}
}
//仓库
@Repository
public class UserRepository {
public void DoRepository(){
System.out.println("Repository");
}
}
//组件
@Component
public class UserComponent {
public void doComponent(){
System.out.println("Component");
}
}
//配置
@Configuration
public class UserConfiguration {
public void doConfiguration(){
System.out.println("userConfiguration");
}
}
//启动类
public class App {
public static void main(String[] args) {
//获取spring上下文
ApplicationContext context=new ClassPathXmlApplicationContext("Spring-config.xml");
//获取Controller bean对象
//使用注解默认是小驼峰
UserController userController=context.getBean("userController",UserController.class);
userController.SayHello();
//获取Service bean对象
UserService userService=context.getBean("userService",UserService.class);
userService.doService();
//获取Repository bean对象
UserRepository userRepository=context.getBean("userRepository",UserRepository.class);
userRepository.DoRepository();
//获取Component bean对象
UserComponent userComponent=context.getBean("userComponent",UserComponent.class);
userComponent.doComponent();
//获取Configuration bean对象
UserConfiguration userConfiguration=context.getBean("userConfiguration",UserConfiguration.class);
userConfiguration.doConfiguration();
}
}
总结:Controller(控制器)、Service(服务)、Repository(仓库)、Configuration(配置)都是基于Component 他们的作用都是将Bean存储到Spring中
获取bean时,都需要将类的首字母小写
命名规则
默认情况下,使用5大类注解获取bean名称,要将首字母小写
特例:当首字母和第二个字母都是大写的时候,那么获取bean名称时,使用原名称即可
创建类时,如果要把类放入spring内管理,那么你的类名一定要遵守命名规则
1.首字母大写,后续小写
2.首字母大写,第二个字母也是大写
为什么要这样命名呢?
查看源码
ctrl+鼠标左键进入
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
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);
}
可以看到源码是这么写的
如果为空时,返回源字符串
如果长度大于1 并且 字符串第二个字符串和首字符都是大写的时候,也返回源字符串
只有当这两个条件不成立的时候,就把字符串第一个数改为小写
测试
//这个获取bean对象时,需要首字母小写
String userComponent = "UserComponent";
//这个首字母和第二个字母都是大写,获取bean对象时,使用源类名即可
String sComponent = "SComponent";
System.out.println(Introspector.decapitalize("UserComponent"));
System.out.println(Introspector.decapitalize("SComponent"));
存储bean对象
创建一个普通类,创建一个输入数据类
这样就把User放入spring内管理,并且初始化,初始化的方法必须加Bean
为什么bean必须和五大类的其中一个配合用才可以呢?
性能原因,spring不可能把全部的类全加入到spring内管理,那太消耗性能了,所以必须先表名五大类其中一个,加了类注解,再类注解下加了bean 方法注解,才会把该类加入到spring内管理
Bean
Spring内管理的对象统称为Bean
方法的命名规则就是方法名
当多个类使用同一个方法名,并且对象都存入spring中时,可以给方法名起别名
起别名的方法有三种
@Bean(name="UserBean")
//取多个名称
@Bean(name={"studentbean","stubean"})
@Bean("StudentBean")
//重命名方法名 获取bean对象时就之间写重命名的即可
//当重命名后,原来的方法名就不能使用了
DI注入
DI注入时机取决于bean的作用域,比如默认情况bean的作用域为单例模式,当需要对象时,才会注入。
DI注入都是从Spring内获取,Spring内没有的就没法注入
对象注入有三种
- 属性注入 (Field Injection)
- Setter注入 (Setter Injection)
- 构造方法注入 (Constructor Injection)
@Autowired
属性注入(Field Injection)
@Autowired //添加注解
private UserService userService;//获取userService对象
public void SayHello(){
userService.doService();//使用userService方法
System.out.println("Hello");
}
启动类内不能注入,因为static执行速度是非常快的,在属性还没注入时,main函数已经执行了。所以main执行时,获取不到从spring获取的对象。
优点
现简单,使用方便,注入之后即可使用
缺点
1.不能注入被final修饰的对象
原因:
final
关键字在Java中表示一个字段是不可变的,它必须在声明时或在构造函数中被初始化,并且之后不能被修改。当你使用依赖注入时,注入的值通常是在运行时由容器设置的,这意味着属性的值是在对象创建之后才确定的。但是,由于
final
字段必须在构造函数中被初始化,这就产生了一个矛盾:你不能在构造函数中给final
字段赋值,因为那时注入的值还没有确定。
除非直接赋值或在构造方法内赋值
2.通用性问题 只适用于IoC框架(容器)
3.更容易违背单一设计原则
单一职责原则指出,一个类应该只有一个引起变化的原因,即一个类应该只负责一项任务。当使用
@Autowired
进行属性注入时,可能会导致以下问题:
- 职责不清晰:如果一个类中有多个
@Autowired
注解的属性,那么这个类可能承担了多个职责。这违反了单一职责原则,因为如果这些依赖中的任何一个发生变化,这个类可能需要修改。- 依赖过多:一个类如果依赖了太多的其他类,那么它就变得复杂,难以理解和维护。这可能导致类之间的耦合度增加,违反了高内聚低耦合的设计原则。
Setter注入(Setter Injection)
//setter注入 (setter Injection)
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
优点
符合单一设计原则 (一个setter只针对一个对象)
缺点
1.不能注入一个不可变(final)的对象 (和属性注入一样)
原因:
final
关键字在Java中表示一个字段是不可变的,它必须在声明时或在构造函数中被初始化,并且之后不能被修改。当你使用依赖注入时,注入的值通常是在运行时由容器设置的,这意味着属性的值是在对象创建之后才确定的。但是,由于
final
字段必须在构造函数中被初始化,这就产生了一个矛盾:你不能在构造函数中给final
字段赋值,因为那时注入的值还没有确定。
2.注入对象可能会被改变 (致命问题) set方法可能会被调用多次 被调用就有被修改的风险
构造方法注入(Constructor Injection)
官方推荐做法
//构造方法注入(Constructor Injection)
private UserService userService;
@Autowired
public UserController(UserService userService){
this.userService=userService;
}
当当前类只有一个构造方法,那@Autowired可以省略
一个构造方法内可以注入多个对象
//构造方法注入(Constructor Injection)
private UserService userService;
private UserRepository userRepository;
@Autowired
public UserController(UserService userService,UserRepository userRepository){
this.userService=userService;
this.userRepository=userRepository;
}
一个类可以有多个构造方法,但是只允许一个构造方法有@Autowired注解(DI注入构造只允许有一个)
并且,构造方法的参数只允许有在spring内存储的,其他参数没有办法传入构造方法,所以会报错。
优点
1.可以注入final修饰的对象
final修饰的变量必须赋初始值
赋值只有两种方法
1.创建时直接赋值
2.在构造方法中赋值
构造注入刚好满足第二种需求,所以构造注入允许注入final的对象
2.注入对象不会被修改
构造方法只能执行一次,所以赋值完就不会再赋值
3.依赖对象在使用前会被完全初始化
使用前一定会被构造方法初始化
4.通用性更好,当不使用IoC容器时,构造方法依旧奏效
@Resource
也是实现对象注入
注入方式也是同样的属性注入、setter注入
但Resource不支持构造注入
方式也是一样的,只是注解不同
属性注入
// 属性注入
//读取spring中名为userService的对象,赋值给当前注入的对象
//有了这个name 所以即使下面的名称不一致,依靠上面的name依旧可以找到指定对象
@Resource(name="userService")
private UserService userService;
Resource可以指定注入对象的名称
Setter注入
//setter注入
private UserController userController;
@Resource
public void setUserController(UserController userController) {
this.userController = userController;
}