前言
之前讲了Spring 如何创建和使用对象 , 那种初始的方法需要不停的在XML中写入 Bean对象, 如果有多个Bean对象 ,则也是比较麻烦的一种写法 , 有没有更简单的方式呢? Spring 为我们提供了一种更加简单与快捷的方式来存取Bean对象!那就是使用注解的形式 , 注解的本质就是 ,将一些代码给了第三方去完成, 你只需要调用它就行了 , 本质上还是的在XML中存储, 只不过省略你写的过程了. 交给第三方来完成了
存储Bean对象
① 配置扫描路径 -
告诉系统 扫描那个包下面有我们想要的对象, 配置同样是在XML中 ,XML基础配置不为
有人肯定会说 , 不配行不行 ,让它全部扫描 ,答案当然是可以 , 但是速度会很慢, 就像新华字典 ,按目录指定查找 和一页页查可是区别很大的!
<?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.bit.service"></content:component-scan>
</beans>
② 使用注解存储Bean对象
想要将对象存储在 Spring 中,有两种注解类型可以实现:
- 五大类注解:@Controller、@Service、@Repository. @Component、@Configuration。
这五个类注解, 使用方法是一致的 , 效果上看也是一致的 ,但是使用场景不一致 , 分别有特定的含义 , 相当于做饭里面不同的柜子 , 有的放碗 , 有的放调料 , 有的放食材 , 虽然作用都是一致的 - 存储数据 , 但是使用场景不同 ,
@Controller(控制器存储)
使用@Controller 存储 bean 的代码
@Controller
public class AATEST {
public String f(){
return "AATEST";
}
}
测试代码
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
AATEST aatest = (AATEST) context.getBean("AATEST");
System.out.println(aatest.f());
}
结果图
其他注解的使用方法与@ Controller 一致, 效果也一样, 这里就不一一演示了 .
这里讲一下Spring的调用流程 及 注解的含义
@Controller(控制器):表示的是交互层: 校验参数的合法性;
@Servie:服务层 : 业务组装;
@Repository:持久层 : 实际业务处理;
@Configuration : 配置层 (针对当前的项目做一些设置)
@Component:工具类(基础的工具)
有人会说了 ,不是5大类注解吗 ,怎么四个 还有个@Component 干嘛去了 ,
答案就是 : @Component :标准一个普通的spring Bean类 ,不代表特殊含义 ,但是@Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的 ,算@Component的子类
注意: 即使是用了Content添加了包路径,也是可以使用bean来注入的
getBean里面的命名规则
默认规则 : ID 等于该类的首字母小写的类名 ( 这是因为 , 咱们的类名一般都是大驼峰的命名格式)
特殊规则 : 当第一与第二的字母是大写的时候,ID等于该类的类名
不过也可以在注解里面加上(value属性值),获取的时候,获取属性值就可以了
使用⽅法注解:@Bean
类注解是添加在类上的 ,方法注解是添加在方法上的 , 理论上,如果你把一个类中的所有方法都添加上方法注解 , 那么你的效果跟类注解一模一样 ,
而且,Bean是将返回对象存放到loc容器中, 所有使用Bean必须返回对象
还有一个不方便的地方在于 , 就算你给方法添加注解, 你的类上还必须要有五大类注解之一, 要不然根本获取不到!
说了这么多是不是认为方法注解一无是处, nonono ,如果真的一无是处, 为什么不把它删除了 ,它还是有点用的, 它的作用在于可以通过设置 name 属性给 Bean 对象进⾏重命名操作,然后获取的时候使用name属性就可以了
问使用方法注解Bean,那么这个类其他方法也会存放到loc容器吗?
答: 因为加了5大类注解, 所以整个类都会加入到loc容器的
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
注意:
在使用@Bean注解时 , 获取Bean时, 默认情况下, 如果你没有给Bean起名字, 那么Bean的默认命名 等于 方法名
获取Bean对象
一般使用了 注解的方式来存放 Bean对象 , 那么有三种方式来获取 它, 当然不是上述的getBean()方法 , 你想,既然存放Bean可以优化, 那为啥获取Bean不能优化呢? 下面依次介绍这三种方法- 这里说一下, 这三种方法就是我们第一节提到的,依赖注入DI的实现
重命名Bean对象
默认规则是方法名
@Bean
public User get(){
User user =new User();
// user.getId();
// user.getName();
return user;
}
第一种方式 : @Bean(“”);直接在Bean中写入
@Bean("aaa")
public User get(){
User user =new User();
// user.getId();
// user.getName();
return user;
}
第二种方式 : @Bean(name = " ") 写上name然后写入更改值
@Bean(name ="aaa")
public User get(){
User user =new User();
// user.getId();
// user.getName();
return user;
}
第三种方式 : @Bean(value = " ")写入value值然后写入别名
@Bean(value ="aaa")
public User get(){
User user =new User();
// user.getId();
// user.getName();
return user;
}
重命名扩展 :@Bean(value = {" 别名1", “别名2”) Bean支持多命名方式
@Bean(value ={"aaa","bbb"})
public User get(){
User user =new User();
// user.getId();
// user.getName();
return user;
}
注意 : 当Bean重命名时候, 默认命名规则就失效了
注意: 5大类注解是不能使用多命名的方式的
当多个方法使用同一个Bean注解的名字的话会报错吗?
答:不会报错, Spring有一个默认类的执行顺序, IDEA执行的时候,只会执行权重最高的
这个默认类的执行顺序,可以通过@Order(权值)来设置, 数字越大,权重越大
注: 加载类的时候, 是默认从上往下加载代码的, 如果之前代码先加载过了, 那么接下来遇到相同的Bean注解也不会重复加载.
注意: 使用Bean的时候,不能重载 , 不能重载的根本原因在于:使用@Bean注解的时候,无法传递参数,也就是说,在使用@Bean注解的时候,只能构造无参的方法
@Bean注解的总结
1: Bean注解是可以通过普通方式来获取的,类似于这样
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
Articles articles = context.getBean("articles",Articles.class);
articles.sayHi();
2: 普通方式获取@Bean注解,也就是getBean的内容必须是加了Bean方法的类
3:这种方式是依赖查找, 不是依赖注入, 依赖查找严格要求名称格式,
4: 当你发现失败的时候,
①应该先检查Spring.xml配置文件, 查看路径是否正确
②检查要获取的类,注解是否正确
③找到使用的类,查看名称时候填写正确
1. 属性注⼊
注: 使用@Autowired不能用main方法 , 用main方法得到属性的时候只能使用依赖查找,不能使用依赖注入(因为static类加载的时候在注入之前)*
属于依赖注入
属性注入 属于是: 骂的多, 用的多的方法了! , 为啥如此矛盾, 因为属性注入简单, 骂是因为它简单,出错概率高 , 用也是因为它简单好写!
属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中。
代码如下
@Service
public class StuSTest {
public User getUser(Integer id , String name){
User user = new User();
user.Id = id;
user.name = name;
return user;
}
}
@Controller
public class StudentTest {
@Autowired
public StuSTest studentTest;
public User getUser(Integer id ,String name){
return studentTest.getUser(id,name);
}
}
通过 给想要的使用 的类 ,上面加上 @Autowired就可以直接使用该类的对象, 快不快! 方便不方便!
这里有个前提 : 属性注入不适用于局部变量!
注意: 使用依赖注入的时候, 先通过getType获取类型来查找,如果能够获取到,并且只有唯一的一个, 那么就会直接拿到,但是如果根据类型找到了多个,这时候才会根据名称来对应
获取多个,比如一个类注解,一个@Bean注解返回对象与类注解的一致,那么这不就是俩个了
举例:
使用@Bean注入俩个User对象
package com.java.demo.model;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class Users {
@Bean("user1")
public User user1(){
User user = new User();
user.setName("张三");
return user;
}
@Bean("user2")
public User user2(){
User user = new User();
user.setName("李四");
return user;
}
}
USer对象的本体
package com.java.demo.model;
public class User {
private String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用@AutoWried来获取
package com.java.demo.service;
import com.java.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService2 {
// 属性注入
@Autowired
private User user;
public void sayHi(){
System.out.println(user.toString());
}
}
这个结果会报错,因为找到了俩个User对象
解决方案
在@Autowired注解下面加上 @Qualifier(“user2”)注解,里面是具体的方法名,这样就不会找错了
面试题 - 同类型的Bean注入多个容器,如果解决
解决方案 : 1 将获取的 对象名与方法对应上来,例如User改成User2,这样就获取的是User2的方法,我们上面说过, 但类型唯一的时候是无所谓名字的, 但是当类型不唯一的时候,就是根据名称来获取结果的
@Autowired
private User user2;
解决方案2 : 使用 @Qualifier(“user2”)设置名称
解决方案3 : 使用Resource 设置名称
属性注入的优缺点
缺点 :
①功能性问题 : 不能注入final(不可变对象)
被final修饰的方法, 只能直接赋值 / 或者在构造方法中赋值
②通用性问题 : 只适用于Loc容器
因为依赖@Autowried ,而@Autowried是Spring独有的,所以太过依赖于loc容器,而Set注入虽然也依赖loc容器,但是它还能作为普通方法被调用, 至于构造方法不依赖于loc容器,所以说它的通用性更好
③设计原则问题 : 更容易违背单一设计原则
更容易!
优点 :
使用简单
2. 构造⽅法注⼊ (官方推荐)
如题就是在构造方法上面加上@Autowired , 如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略
@Controller
public class UserController2 {
// 注⼊⽅法2:构造⽅法注⼊
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
构造方法注入的优缺点
缺点 :
①没有属性注入的方式实现简单
优点:
①可以注入一个不可变的对象
②注入的对象不会被修改
③注入对象会被完全初始化
④通用性更好
⑤当构造方法只有一个的时候, @Autowired可以省略
3. Setter 注⼊
如题, Set注入就是在在设置 set ⽅法的时候需要加上 @Autowired 注
解,如下代码所示:
@Controller
public class UserController3 {
// 注⼊⽅法3:Setter注⼊
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
ser注入的优缺点
缺点 :
①:不能注入不可变的对象
②:注入的对象是可以被修改的
优点 : 更加符合单一设计原则
获取Bean对象的注意事项
① 方法要搭配 5大类注解 去使用
② Bean 注解可以设置 name 属性
③ Bean 默认使用是方法名 , 设置了 name属性 就必须 使用 name的 名字
④使用BeanFactory (Spring创建与使用讲过)不能使用Bean注解
依赖注入与new 一个对象有什么区别
spring依赖注入,是指对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。 new一个对象的时候,初始化顺序是: 父类静态块,子类静态块,父类属性(先系统默认值,后直接你赋予的值) ,父类构造器,子类属性,子类构造器。
总结: 来做饭来举例: 加入一个菜品有多种做法 , 且所需要的材料不同, 依赖注入, 是动态的 ,是一开始不确定的,是到点做那个去买那个材料 ,而new一个对象是固定的,是需要将材料全部准备好才能在做的时候找到材料.懂了吧?所以说依赖注入,降低了耦合度!
讲了这么多注入, 下面说一个另外的注入关键字@Resource
在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示:
@Controller
public class UserController {
// 注⼊
@Resource
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
依赖注入与依赖查找的区别
@Autowired 与 @Resource的区别
相同点 : 都可以进行依赖注入
不同点 :
① :功能支持不同 , @Autowired支持所有方式的注入 ,而@Resource不支持构造方法的注入
②:出身地不同 @Autowired 出生与Spring 框架 , 而@Resource 出生于JDK
③:支持的参数类型不同 : @Resource 支持更多的参数, 而@Autowired只支持require参数
④兼容性不同 : 使用@Autowired在IDEA专业版可能会误报