Spring详解之bean管理

IOC容器

工厂只负责创建对象,而Spring当然不仅仅是一个对象工厂;其核心是一个对象容器,由于具备控制反转的能力,所以也叫它IOC容器;

容器可以理解为存放对象的地方,当然不仅仅是存储,还有对象的管理,包括-创建-销毁-装配; 这样原本程序要做的事情交给了Spring,所以这属于IOC,称之为IOC容器;

Spring有两个容器接口ApplicationContext和BeanFactory ,ApplicationContext是BeanFactory的子接口。它们都可以作为Spring的容器;

两种容器的区别:

> BeanFactory采取的懒加载的方式,在获取对象时才会实例化

> ApplicationContext会在工厂初始化时立即实例化对象

> BeanFactory作为顶级接口主要面向于Spring框架本身,仅提供了基本的容器功能如DI

> ApplicationContext,是BeanFactory的子接口,意味着功能比BeanFactory更多,诸如国际化,注解配置,XML配置等等,因此ApplicationContext使用更多

		>> ApplicationContext的两个实现类区别:
		
		>> ClassPath表示从类路径中获取配置文件,
		
		>> FileSystem表示从文件系统获取配置文件

SpringBean的管理

Bean的实例化

准备工作
1、创建一个maven项目
2、配置maven

		<!-- Maven会自动下载所有Spring核心容器和aop的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>

3、在resources下创建一个名为:applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

后面的bean都是在这个配置文件配置的

4、创建contoller、service包
在这里插入图片描述

1. 使用类构造器(默认无参数)

该方式Bean类中必须存在无参构造器

<bean id="userService" class="com.lbb.service.UserServiceImpl"/>

注解:

id:标识这个bean,可以通过id来获取class,一般用将类型的首字母改为小写

name:和id一样,但是可以写多个

class:全类名,通过反射方式在IOC中创建Bean,要求Bean中必须有无参数的构造器

调用:

public class UserController {
    private UserService userService;
    public UserController() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        userService = (UserService) context.getBean("userService");
    }
}

在上面getBean方法,可以通过id、name、实现类.class、接口.class来获取对象

2. 使用静态工厂方法

工厂:

public class StaticFactory {
    public static UserService getUserService(){
        System.out.println("执行了静态方法");
        return new UserServiceImpl();
    }
}

xml配置:

<bean id="userService" class="com.lbb.service.StaticFactory" factory-method="getUserService"/>

注解:

id:标识这个bean,可以通过id来获取class,一般用将类型的首字母改为小写

class:全类名,指向静态方法对应的类

factory-method:要执行的方法

调用方法一致。

3. 使用实例工厂方法

xml配置:

	<!--  配置factory  -->
    <bean id="factory" class="com.lbb.service.StaticFactory"/>
    <!--  配置userService 调用factory 指定getUserService-->
    <bean id="userService" factory-bean="factory" factory-method="getUserService"/>

这是先配置工厂类,然后在通过factory-bean 调用这个bean,用factory-method指定方法

调用方法不变。

Bean的命名

配置Bean时,可以使用 id 或者 name 属性给bean命名。 id 和 name 属性作用上一样,推荐使用id。

1、id取值要求严格些,必须满足XML的命名规范。id是唯一的,配置文件中不允许出现两个id相同的bean。

2、name取值比较随意,甚至可以用数字开头。在配置文件中允许出现多个name相同的bean,在用getBean()返回实例时,最后的一个Bean将被返回。

	注意:在spring5中name和id一样也不允许有重复的名称。

3、如果没有id,name,则用类的全名作为name,如 ,可以使用 test.Test返回该实例。

4、如果存在多个id和name都没有指定,且类都一样的,如:

	<bean class="com.lbb.service.UserService"/>
	<bean class="com.lbb.service.UserService"/> 
	<bean class="com.lbb.service.UserService"/>

则可以通过getBean(“完整类名#索引”)来获得,如:getBean(“com.yh.service.UserService#1”),索引从0开始,若要获取第一个则可以忽略索引,直接写类名.

5、name中可以使用分号(“;”)、空格(“ ”)或逗号(“,”)来给这个Bean添加多个名称(相当于别名 alias 的作用)。如:name=“a b c d”等同于 name=“a,b,c,d” 这样写相当于有 1 2 3 4(4个)个标识符标识当前bean ,而id中的任何字符都被作为一个整体 ;

6、如果既配置了 id ,也配置了 name ,则两个都生效。当然也不能重复;

7、当注解中出现与xml配置中相同的id或相同name时,优先使用xml中的配置

Bean的作用域
类别说明
singleton默认值; 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在。
prototype每次从容器中调用Bean时,都返回一个新的实例;
request每次HTTP请求都会创建一个新的Bean,仅适用于WebApplicationContext环境
session同一个HTTP Session 共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext 环境
applicationBean的作用域为ServletContext ,仅适用于WebApplicationContext环境。

作用域就是指作用范围:单例则表示对象的作用范围是整个Spring容器,而prototype则表示不管理作用范围,每次get就直接创建新的。request、session和 application三种作用域仅在基于web的应用中使用

生命周期

init和destroy
Spring提供了非入侵(不强制类继承或实现)方式的生命周期方法,可以在Bean的初始化以及销毁时做一些额外的操作

<bean id="service" class="com.yh.service.UserService" scope="singleton"
      init-method="init" destroy-method="destroy"/>
<!--
init-method		用于初始化操作的方法
detroy-method		用于销毁操作的方法

这两个都是指定方法名
-->

注意:destroy仅在scope为singleton时有效

Bean的完整生命周期
在这里插入图片描述
执行顺序及其含义:

1 构造对象

2 设置属性

3 了解Bean在容器中的name

4 了解关联的beanFactory

5 初始化前处理

6 属性设置完成

7 自定义初始化方法

8 初始化后处理

9 业务方法

10 Bean销毁方法

11 自定义销毁方法

依赖注入

依赖指的是当前对象在运行过程中需要使用到的其他参数或对象,Spring可以帮助我们来完成这个依赖关系的建立,说的简单点就是把你需要参数的给你,而你不用管参数怎么来的,已达到尽可能的解耦 ;

比如:

Controller 中需要Service对象,Spring可以把Service自动丢到你的Controller中,你不需要关系Service是怎么来的,用就完了;要使用依赖注入,必须现在需要依赖的一方(Controller)中为被依赖的一方(Service)定义属性,用以接收注入;

构造方法注入

bean:

public class User {
    private Integer id;
    private String username;
    private String pwd;
}

需要添加有参/无参构造,geter和seter方法,tostring方法

xml:

	<!--依赖注入-->
    <bean id="user" class="com.lbb.pojo.User">
    	<!--按参数位置注入           -->
        <constructor-arg index="0" value="1"/>
        <!--按参数名称注入        -->
        <constructor-arg name="username" value="lbb"/>
        <!--参数类型为其他bean对象时value换成ref        -->
        <constructor-arg name="pwd" ref="pwd"/>
        <!--type指定类型不常用        -->
        <!--<constructor-arg type="java.lang.String" name="username" value="lbb"/>-->
    </bean>
    <!--user需要的依赖phoneBean-->
    <bean id="pwd" class="com.lbb.pojo.Pwd"/>

注解:

constructor-arg: 注入有参构造

name: 参数名

index:第几个参数,从0开始

value:参数值

ref:参数连接到另一个bean

type:参数类型。
c命名空间

上面通过嵌套标签constructor的方式注入依赖,在需要注入的依赖较多时导致xml显得很臃肿,C名称空间来简化xml中<constructor-arg>标签的书写

使用前需要先在xml头部进行声明:

xmlns:c="http://www.springframework.org/schema/c"

在这里插入图片描述
使用:

上面构造方法注入可以改为:

<!--c命名空间的使用-->
<bean id="user" class="com.lbb.pojo.User" c:_0="2" c:username="jerry" c:phone-ref="pwd"></bean>

注解:

c:username   指定为username参数赋值

c:_0    			指定为构造函数的第1个参数赋值

c:pwd-ref   指定为构造函数的pwd参数赋值为id为"pwd"的Bean
setter方法注入

首先类里面要有setter方法和空参构造器
xml:

<bean id="user1" class="com.lbb.pojo.User">
        <property name="id" value="1"/>
        <property name="username" value="lbb"/>
        <property name="pwd" value="123456"/>
</bean>

注解

property :用于setter注入属性

name:属性名

value:注入的值

ref:参数连接到另一个bean
p命名标签

同样的p命名空间则是用于简化<property>标签的书写

声明:

xmlns:p="http://www.springframework.org/schema/p"

使用:

上面setter注入可以写为:

<bean id="user3" class="com.lbb.pojo.User" p:id="1" p:username="lbb" c:pwd="123456"/>

注解:

p:id 			指定为id属性赋值
p:username   	指定为username属性赋值
p:pwd-ref   	为pwd属性赋值为id为"pwd"的Bean
SpEL注入

SpEL即Spring Expression Language的缩写,与JSTL一样是表达式语言,可以支持使用更加复杂的语法注入依赖,包括标准数学运算符,关系运算符,逻辑运算符,条件运算符,集合和正则表达式等;

语法: #{表达式}

用例:

<!--SpEL    -->
<bean id="user" class="com.lbb.pojo.User">
<!--<property name="name" value="#{'lbb'}"/>-->           <!--字符常量-->
<!--<property name="age" value="#{100.0}"/>-->              <!--数字常量-->
<!--<property name="phone" value="#{phone}"/>-->            <!--对象引用-->
<!--<property name="name" value="#{phone.model.concat(' lbb')}"/>--> <!--方法调用-->
<!--<property name="age" value="#{1+100}"/>-->              <!--算数符-->
<!--<property name="name" value="#{1>100}"/>-->       <!--比较符-->
<!--<property name="name" value="#{true or false}"/>-->     <!--逻辑符-->
<!--<property name="name" value="#{1 > 0?1:0}"/>-->         <!--三目-->
</bean>
容器类型的注入

xml:

 <bean id="userdemo" class="com.lbb.pojo.UserDemo">
        <!--注入set-->
        <property name="set">
            <set>
                <value>1</value>
                <value>2</value>
                <value>3</value>
                <value>4</value>
            </set>
        </property>
        <!--注入list-->
        <property name="list">
            <list>
                <value>1</value>
                <value>2</value>
                <value>3</value>
                <value>4</value>
            </list>
        </property>
        <!--注入map-->
        <property name="map">
            <map>
                <entry key="1" value="1"/>
                <entry key="2" value="2"/>
                <entry key="3" value="3"/>
                <entry key="4" value="4"/>
            </map>
        </property>
        <!--注入properties-->
        <property name="properties">
            <props>
                <prop key="1">1</prop>
                <prop key="2">2</prop>
                <prop key="3">3</prop>
                <prop key="4">4</prop>
            </props>
        </property>
    </bean>

上述写法同样适用于构造函数注入:

<bean class="service.Person">
        <constructor-arg name="name" value="jerry"/>
        <constructor-arg name="properties">
            <props>
                <prop key="pwd">123</prop>
            </props>
        </constructor-arg>
  </bean>

强调:Spring的依赖注入要么通过构造函数,要么通过setter

接口注入不是一种注入方式,只不过由于OOP的多态,Spring在按照类型注入时,会在容器中查找类型匹配的Bean,如果没有则查找该类的子类,如果容器中有多个匹配的子类Bean时会抛出异常。

注解配置

注解配置Bean

通用注解

@Component 用于在Spring中加入Bean

MVC场景下

@Controller 等价于 @Component 标注控制层

​ @Service 等价于 @Component 标注业务层

​ @Repository 等价于 @Component 标注数据访问层(DAO)

在实现上暂时没有任何不同,在底层源码里就是给Component取了别名,仅仅是为了对Bean进行分层是结构更清晰

使用步骤:

1.需要依赖context和aop两个jar包
2.配置applicationContext.xml

添加命名空间(idea下 输入<context:可自动补全,指定扫描的注解所在的包:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	   https://www.springframework.org/schema/beans/spring-beans.xsd   		
	   http://www.springframework.org/schema/context 
	   https://www.springframework.org/schema/context/spring-context.xsd">
		
		<!--指定要扫描注解的包 会自动扫描子包   -->
        <context:component-scan base-package="com.lbb"/>

</beans>

3.要注册的Bean:
Component 、 Controller 、Service 、Repository,这四个用法相同,如下:

@Component("user")
public class User {
    private Integer id;
    private String username;
    private String pwd;
    }

若注解中没有指定id则默认使用简单类名且小写开头,例如:User默认是user

4、使用

public class UserController {
    private User user;
    public UserController() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        user = (User) context.getBean("user");
    }
}
注解注入
@Value用于对基本类型属性进行注入

@Autowired将容器中的其他Bean注入到属性中,默认按类名注入

@Qualifier("BeanID") 指定要注入的Bean的ID,需要和Autowired一起使用

Value

public class User {
	@Value("1215")
    private Integer id;
    @Value("root")
    private String username;
    @Value("#{100+1}")
    private String pwd;
    }

上面基本的应用,还可以注入属性配置使用${}取配置文件里面的值:

首先需要加载配置文件,在applicationContext.xml加入:

<context:property-placeholder location="jdbc.properties"/>

使用:

	@Value("${jdbc.user}") //注入属性配置
    private String info1;

Autowired

Autowired默认自动注入类型一致的Bean; required属性用于设置属性是否是必须的默认为true,如果为true找不到会报错,如果false则会为空,不会报错

使用:
User 类

//加入spring管理
@Repository
public class User {
    @Value("12")	//注入值
    private Integer id;
    @Value("lbb")
    private String username;
    @Value("123456")
    private String pwd;
    }

UserService 接口:

public interface UserService {

    void sayHi();
}

UserServiceImpl类

//加入spring管理
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private User user; //注入到user属性

    @Override
    public void sayHi() {
        System.out.println(user);
    }
}

UserController 类

@Controller//加入spring管理
public class UserController {

    @Autowired //注入userService
    private UserService userService;

    public void runn(){
        userService.sayHi();
    }
}

测试类:

public class MyTest {
    @Test
    public void test3(){
        //读取配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        UserController userController = (UserController) context.getBean("userController");
        userController.runn();
    }
}

Qualifier
当我们需要指定注入的bean的id时,Autowired不能配置,这时候要和Qualifier一起使用,用于明确指定要注入的Bean的ID(注意一定要一起使用)

使用:

上面userController可以这样写:

@Controller//加入spring管理
public class UserController {

    @Autowired //注入userService
    @Qualifier("userServiceImpl")//指定导入id
    private UserService userService;
    public void runn(){
        userService.sayHi();
    }
}

注意:

若通过类型注入,则当Spring中存在多个类型都匹配的Bean时直接报错,演示如下:

接口:

public interface PersonDao {
}

两个实现类:

@Repository()
public class PersonDaoImpl1 implements PersonDao{
}
@Repository()
public class PersonDaoImpl2 implements PersonDao{
}

注入:

@Component
public class UserService {
    @Autowired
    private PersonDao personDao;
}

这时候就会报错,如果要这样用必须指定Bean的name,通过name来区分。

Resource

Qualifier和Autowired书写繁琐,@Resource可将两个标签的功能整合,即注入指定ID的Bean:

@Resource标准注解是java注解,但是在jdk中没有导入,要配置导入,在pom.xml配置:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

注解:

Resource默认按照使用属性名称作为name查找,查找失败则使用类型查找

可以利用name属性指定要查找的id

也可通过type指定类型,当出现相同类型的多个Bean时抛出异常

使用:

@Controller//加入spring管理
public class UserController {
//    @Autowired //注入userService
//    @Qualifier("userServiceImpl")//指定导入id
    @Resource(name = "userServiceImpl")
    private UserService userService;
    public void runn(){
        userService.sayHi();
    }
}

Resource可以设置name,也可以设置type.如果不设置的话,会先按照类型找,找不到会再按照属性名来寻找。

Scope

用于标注Bean的作用域

@Repository()
@Scope("prototype") //每次get都创建新的 
public class UserDAO {
    public void save(){
        System.out.println("user saved!");
    }
}
XML与注解配合使用

因为注解的表达能力有限,很多时候无法满足使用需求;我们可以将注解和XML配合使用,让XML负责管理Bean,注解仅负责依赖注入;

与注解扫描的不同之处,注解扫描用于将Bean放入容器中同时进行依赖注入,而后者仅仅是在已存在的Bean中进行依赖注入;

配置applicationContext.xml文件,开启注解:

<!--使用xml默认是不扫描注解的,所以要配置下面开启注解-->
        <context:annotation-config/>

多配置文件的使用:

当配置文件中的内容过多是不便于维护,Spring支持多配置文件

方法1:

在初始化容器时指定要加载的所有配置文件

 @Test
    public void test3(){
        //读取配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
        //获取bean
        UserController userController = (UserController) context.getBean("userController");
        userController.runn();
    }

方法2:

在配置文件中使用import来引入其他的配置文件

<import resource="applicationContext2.xml"/>

import 里的其他写法:

前缀含义
classpath:从classpath加载第一个匹配的文件
classpath*:从classpath加载所有匹配的文件(用于成功分为多个jar包时)
file:按绝对路径加载文件
http:按URL路径加载文件

链接:Spring详解之AOP

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值