面试阿里,字节,美团必看的Spring的Bean管理详解
2021-01-17 13:15
IOC容器
工厂只负责创建对象,而Spring当然不仅仅是一个对象工厂,其核心是一个对象容器,其具备控制反转的能力,所以也称为IOC容器。
帮助我们存放对象,并且管理对象,包括:创建、销毁、装配,这样就将原本由程序自己完成的工作,交给了框架来完成,称为IOC容器。
学习的核心也就在于如何将对象放在Spring中,以及如何从Spring中取出来。
Spring有两个容器接口:
ApplicationContext
BeanFactory
ApplicationContext是BeanFactory的子接口,它们都可以作为Spring的容器。
区别:
BeanFactory采取懒加载方式(会有延迟),在获取对象时才会实例化
ApplicationContext在工厂初始化时立即实例化对象
BeanFactory作为顶级接口主要面向于Spring框架本身,仅提供了基本的容器功能,如DI
ApplicationContext时BeanFactory的子接口,意味着功能比BeanFactory更多,诸如国际化(根据用户的地理位置对产品的表现形式进行一些变化,例如文字、样式),注解配置,XML配置等等,因此ApplicationContext使用更多
ApplicationContext的个实现类的区别:
ClassPath表示从类路径获取配置文件
FileSystem表示从文件系统获取配置文件
Spring Bean管理
Bean的实例化(初始化,相当于我们之前的new)
1.创建一个maven项目
2.配置maven
org.springframework spring-context5.2.2.RELEASE
3.在resource下创建一个名为applicationContext.xml的配置文件
自动生成:在这里配置Bean
4.创建contoller、service对应的包
准备工作已完成,下面书写Bean实例化的方式
方式一 使用无参构造器
使用该方式时,Bean类中必须要有无参构造
测试
public class UserController { private UserService service; public UserController() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过指定文件路径加载Spring配置文件,一般很少用 //FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\ideaProject\\SpringDemo2\\src\\main\\resources\\applicationContext.xml"); //根据id或name获取 service = (UserService) context.getBean("userService"); //通过接口类来获取 //service = context.getBean(UserService.class); //通过具体类来获取 //service = context.getBean(UserServiceImpl.class); } public void say(){ service.say(); } public static void main(String[] args) { new UserController().say(); } }
方式二 使用静态工厂方式
创建一个工厂类
public class factory { public static UserService getService(){ System.out.println("执行了静态工厂方法"); return new UserServiceImpl(); } }
xml配置
方式三 使用实例工厂方式
xml文件
调用:
service = (UserService) context.getBean("userService3");
首先需配置工厂类,然后通过factory-bean调用刚刚命名的那个Bean,写入id,用factory-method指定要使用的工厂的方法。
Bean的命名
配置Bean时,可以使用 id 或者 name 属性给bean命名。 id 和 name 属性作用上一样,推荐使用id。
id取值要求严格些,必须满足XML的命名规范。id是唯一的,配置文件中不允许出现两个id相同的bean。
name取值比较随意,甚至可以用数字开头。在配置文件中允许出现多个name相同的bean,在用getBean()返回实例时,最后的一个Bean将被返回。
注意:在spring5中name和id一样也不允许有重复的名称。
如果没有id,name,则默认使用类的全名作为name,如 ,
如果存在多个id和name都没有指定,且类都一样的,如:
则可以通过getBean(“完整类名#索引”)来获得,如:getBean(“com.cx.service.UserService#1”),索引从0开始,若要获取第一个则可以忽略索引,直接写类名。
name中可以使用分号(“;”)、空格(“ ”)或逗号(“,”)来给这个Bean添加多个名称(相当于别名 alias 的作用)。如:name=“a b c d”等同于 name=“a,b,c,d” 这样写相当于有 1 2 3 4(4个)个标识符标识当前bean ,而id中的任何字符都被作为一个整体 ;
如果既配置了 id ,也配置了 name ,则两个都生效。当然也不能重复;
当注解中出现与xml配置中相同的id或相同name时,优先使用xml中的配置
Bean的作用域
类别 说明
作用域就是指作用范围:
单例则表示对象的作用范围是整个Spring容器,
而prototype则表示不管理作用范围,每次get就直接创建新的。
request、session和 application三种作用域仅在基于web的应用中使用
生命周期
init和destory
Spring提供了非入侵(不强制类继承或实现)方式的生命周期方法,可以在Bean的初始化以及销毁时做一些额外的操作
注意:
destroy仅仅在scope为singleton时有效
Bean的完整生命周期
1 构造对象
2 设置属性
3 了解Bean在容器中的name
4 了解关联的beanFactory
5 初始化前处理
6 属性设置完成
7 自定义初始化方法
8 初始化后处理
9 业务方法
10 Bean销毁方法
11 自定义销毁方法
依赖注入
依赖指的是档期啊对象在运行过程中需要使用到的其他参数或者对象;
Spring可以帮助我们完成这个依赖关系的建立;
说的简单点即把你需要的参数给你,而你不管参数是怎么来的,只管用即可,以达到尽可能地解耦。
for example
controller中需要service对象,Spring可以把service自动丢到controller中,你不需要关注service对象是怎么来的,用就可以了
要使用依赖注入,必须先在需要依赖的一方(controller)中为被依赖的一方(service)定义属性,用于接收注入;
构造方法注入
bean:
书写user
public class User { private Integer id; private String name; private String sex; private PC pc; }
还有有参/无参构造,getter/setter方法,以及toString
user中需要的pc类
public class PC { private String model; }
在applicationContext.xml中注入,构造方法注入
测试代码:
public void test1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) context.getBean("user"); System.out.println(user); } public static void main(String[] args) { new TestController().test1(); }
setter方法注入
在上述中,还可以使用下述注入
注意:setter方法注入配置要求User中必须有无参构造方法
C和P命名标签
上面的两种依赖注入方法,如果在需要注入的依赖较多时导致xml显得很臃肿,C名称空间来简化xml中标签的书写。
需要先在xml中进行声明
xmlns:c="http://www.springframework.org/schema/c"
使用:
同理p命名标签也需要在xml中进行声明
xmlns:p="http://www.springframework.org/schema/p" ```![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/23140115-145fffd6f77114d3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 使用: ```java
SpEL注入
SpEL即Spring Expression Language的缩写,与JSTL一样是表达式语言,可以支持使用更加复杂的语法注入依赖,包括标准数学运算符,关系运算符,逻辑运算符,条件运算符,集合和正则表达式等;
语法: #{表达式}
用例:
容器类型的注入
xml:
1 2 3 4
1 2 3 4 1 2 3 4
上述写法同样适用于构造函数注入:
123
强调:java Spring的依赖注入要么通过构造函数,要么通过setter
接口注入不是一种注入方式,只不过由于OOP的多态,Spring在按照类型注入时,会在容器中查找类型匹配的Bean,如果没有则查找该类的子类,如果容器中有多个匹配的子类Bean时会抛出异常。
注解配置
注解配置Bean
通用注解
? @Component 用于在Spring中加入Bean
MVC场景下
@Controller 等价于 @Component 标注控制层
@Service 等价于 @Component 标注业务层
@Repository 等价于 @Component 标注数据访问层(DAO)
在实现上暂时没有任何不同,在底层源码里就是给Component取了别名,仅仅是为了对Bean进行分层是结构更清晰
使用步骤:
1.需要依赖context和aop两个jar包 要注册的Bean 测试代码 如果注解中没有指定id,则默认使用简单类名且小写开头,例如:上述的user。 注解注入 上述注入属性配置使用${}取配置文件里面的值: 首先需要加载配置文件,在applicationContext.xml加入: @Autowired 使用: dao层: service实现层: 接口层 controller层 测试: @Qualifier 使用: 上面PersonService可以这样写: 注意: 若通过类型注入,则当Spring中存在多个类型都匹配的Bean时直接报错,演示如下: 接口: 两个实现类: 3 这时候就会报错,如果要这样用必须指定Bean的name,通过name来区分。 @Resource Qualifier和Autowired书写繁琐,@Resource可将两个标签的功能整合,即注入指定ID的Bean: @Resource标准注解是java注解,但是在jdk中没有导入,要配置导入,在pom.xml配置: 注解: 可以利用name属性指定要查找的id 也可通过type指定类型,当出现相同类型的多个Bean时抛出异常 Resource可以设置name,也可以设置type.如果不设置的话,会先按照类型找,找不到会再按照属性名来寻找。 Scope 用于标注Bean的作用域 因为注解的表达能力有限,很多时候无法满足使用需求;我们可以将注解和XML配合使用,让XML负责管理Bean,注解仅负责依赖注入; 与注解扫描的不同之处,注解扫描用于将Bean放入容器中同时进行依赖注入,而后者仅仅是在已存在的Bean中进行依赖注入; 配置applicationContext.xml文件,开启注解: 当配置文件中的内容过多是不便于维护,Spring支持多配置文件 方法1: 在初始化容器时指定要加载的所有配置文件 方法2: 在配置文件中使用import来引入其他的配置文件 import 里的其他写法: 前缀 含义 基于XML配置 Bean的实例化:
2.配置applicationContext.xml
添加命名空间(idea下 输入@Component
public class User {
private Integer id;
private String name;
private String sex;
private PC pc;
}
public void test1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
@value用于对基本类型的注入
@Autowired将容器中的其他Bean注入到属性中,自动装配
@Qualifier(“BeanId”)指定要注入的Bean的Id,需要和Autowired一起使用
@Resource 相当于: @Autowired 和@Qualifier 一起使用.
@value @Value("12")
private int age;
@Value("tom")
private String name;
@Value("#{1+2+3}")
private String pwd;
@Value("${jdbc.user}") //注入属性配置
private String user;
自动注入一个Bean,默认按照类型来注入
required属性用于设置属性是否是必须的默认为true,如果为true找不到会报错,如果false则会为空,不会报错
User 类:@Component
public class User {
@Value("1")
private Integer id;
@Value("cc")
private String name;
@Value("男")
private String sex;
@Value("小米")
private PC pc;
}
@Repository
public class PersonDao {
public void smile(){
System.out.println("ta笑了");
}
}
@Service
public class PerSonServiceImpl implements PersonService {
@Autowired
private PersonDao personDao;
public void smile(){
personDao.smile();
}
}
public interface PersonService {
public void smile();
}
@Controller
public class PersonController {
@Autowired
private PersonService personService;
public void smile() {
personService.smile();
}
}
@Test
public void test1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonController bean = context.getBean(PersonController.class);
bean.smile();
}
当我们需要指定注入的bean的id时,Autowired不能配置,这时候要和Qualifier一起使用,用于明确指定要注入的Bean的ID(注意一定要一起使用)@Service
public class PerSonServiceImpl implements PersonService {
@Autowired
@Qualifier("personDaoImpl")
private PersonDao personDao;
public void smile(){
personDao.smile();
}
}
public interface PersonDao {
}
@Repository()
public class PersonDaoImpl1 implements PersonDao{
}
@Repository()
public class PersonDaoImpl2 implements PersonDao{
}
注入:@Component
public class UserService {
@Autowired
private PersonDao personDao;
}
Resource默认按照使用属性名称作为name查找,查找失败则使用类型查找@Service
public class PerSonServiceImpl implements PersonService {
@Resource(name = "personDaoImpl")
private PersonDao personDao;
public void smile(){
personDao.smile();
}
}
@Repository()
@Scope("prototype") //每次get都创建新的
public class PersonDao {
public void smile(){
System.out.println("笑了");
}
}
XML与注解配合使用
多配置文件的使用:
@Test
public void test3(){
//读取配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
//获取bean
PersonController personController= (PersonController) context.getBean("personController");
personController.smile();
}
classpath: 从classpath加载第一个匹配的文件
classpath*: 从classpath加载所有匹配的文件(用于成功分为多个jar包时)
file: 按绝对路径加载文件
http: 按URL路径加载文件
最后来一波~~~~~~Spring的Bean管理的方式比较
Bean的命名:通过id或name指定
Bean的注入:构造方法或setter方法注入(p或c命名标签)
生命周期,Bean的作用范围:init-method,destroy-method,围scope属性,destroy仅仅在scope为singleton时有效
使用场景:Bean来自于第三方
基于注解配置
Bean的实例化:@component、@Controller、@Service、@Repository
Bean的命名:@component(“name")
Bean的注入:@AutoWired按类型注入、@Qualifier按名称注入
生命周期,Bean的作用范围:@PostConstruct初始化、@PreDestroy销毁、@Scope设置作用范围
使用场景:Bean的实现由自己开发
文章标题:面试阿里,字节,美团必看的Spring的Bean管理详解
文章链接:http://soscw.com/index.php/essay/43211.html