工厂只负责创建对象,而spring当然不仅仅是一个对象工厂;其核心是一个对象容器,由于具备控制反转的能力,所以也叫它ioc容器;
容器可以理解为存放对象的地方,当然不仅仅是存储,还有对象的管理,包括-创建-销毁-装配; 这样原本程序要做的事情交给了spring,所以这属于ioc,称之为ioc容器;
spring有两个容器接口applicationcontext是beanfactory的子接口。它们都可以作为spring的容器;
beanfactory作为顶级接口主要面向于spring框架本身,仅提供了基础基本的容器功能如di
该方式bean类中必须存在无参构造器
<bean id="userservice1" class="com.yyh.serviceimpl.userserviceimpl"/>
xml配置:
<bean id="userservice" class="com.yyh.serviceimpl.servicefactory" factory-method="getservice"/>
工厂:
import com.yyh.service.userservice; public class servicefactory { public static userservice getservice() { system.out.println("factory static run!"); return new userserviceimpl(); } }
xml配置:
<!--工厂bean--> <bean id="servicefactory" class="com.yyh.serviceimpl.servicefactory"/> <!--service bean--> <bean id="userservice2" factory-bean="servicefactory" factory-method="getservice2"/>
工厂添加方法:
public userservice getservice2() { system.out.println("factory instance run!"); return new userserviceimpl(); }
配置bean时,可以使用 id 或者 name 属性给bean命名。 id 和 name 属性作用上一样,推荐使用id。
id取值要求严格些,必须满足xml的命名规范。id是唯一的,配置文件中不允许出现两个id相同的bean。
name取值比较随意,甚至可以用数字开头。在配置文件中允许出现多个name相同的bean,在用getbean()返回实例时,最后的一个bean将被返回。
注意:在spring5中name和id一样也不允许有重复的名称。
如果没有id,name,则用类的全名作为name
如 <bean class="test.test">
,可以使用 getbean("test.test") 返回该实例。
如果存在多个id和name都没有指定,且类都一样的,如:
<bean class="com.yh.service.userservice"/> <bean class="com.yh.service.userservice"/> <bean class="com.yh.service.userservice"/>
则可以通过getbean(“完整类名#索引”)来获得,如:getbean("com.yh.service.userservice#1")
,索引从0开始,若要获取第一个则可以忽略索引,直接写类名
name中可以使用分号(“;”)、空格(“ ”)或逗号(“,”)来给这个bean添加多个名称(相当于别名 alias 的作用)。如:
" name=“a b c d”等同于 name=“a,b,c,d” 这样写相当于有 1 2 3 4(4个)个标识符标识当前bean id=“1 2 3 4” 这样写相当于有 “1 2 3 4”(1个)个标识符标识当前bean "
而id中的任何字符都被作为一个整体 ;
如果既配置了 id ,也配置了 name ,则两个都生效。当然也不能重复;
当注解中出现与xml配置中相同的id或相同name时,优先是用xml中的配置
类别 | 说明 |
---|---|
singleton | 默认值; 在spring ioc容器中仅存在一个bean实例,bean以单例方式存在。 |
prototype | 每次从容器中调用bean时,都返回一个新的实例; |
request | 每次http请求都会创建一个新的bean,该作用域仅适用于webapplicationcontext环境 |
session | 同一个http session 共享一个bean,不同session使用不同bean,仅适用于webapplicationcontext 环境 |
application | bean的作用域为servletcontext ,仅适用于webapplicationcontext环境。 |
作用域就是指作用范围:单例则表示对象的作用范围是整个spring容器,而prototype则表示不管理作用范围,每次get就直接创建新的
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时有效 因为多例情况下
执行顺序及其含义:
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 user2 { private string name; private int age; private phone phone; public user2(string name, int age, phone phone) { this.name = name; this.age = age; this.phone = phone; } @override public string tostring() { return "user2{" + "name='" + name + '\'' + ", age=" + age + ", phone=" + phone + '}'; } }
xml:
<!--依赖注入--> <bean id="user" class="com.yh.demo2.user2"> <!--按参数名称注入 --> <constructor-arg name="name" value="jerry"/> <!--按参数位置注入 --> <constructor-arg index="1" value="18"/> <!--参数类型为其他bean对象时value换成ref --> <constructor-arg name="phone" ref="phone"/> <!--type指定类型不常用 --> <!--<constructor-arg type="java.lang.string" name="name" value="jerry"/>--> </bean> <!--user需要的依赖phonebean--> <bean id="phone" class="com.yh.demo2.phone"/>
依然对上面的user2类的依赖进行注入
<!--setter方法注入(属性注入) --> <bean id="user2" class="com.yh.demo2.user2"> <property name="name" value="jerry"/> <!--注入常量值--> <property name="age" value="20"/> <property name="phone" ref="phone"/> <!--注入其他bean--> </bean>
注意:上述配置要求user2必须存在空参构造器
上面通过嵌套标签constructor的方式注入依赖,在需要注入的依赖较多时导致xml显得很臃肿,c名称空间来简化xml中<constructor-arg>
标签的书写
使用前需要先在xml头部进行声明
xmlns:c="http://www.springframework.org/schema/c"
使用:
<!--c命名空间的使用--> <bean id="user3" class="com.yh.demo2.user2" c:name="jerry" c:_1="21" c:phone-ref="phone"></bean> <!-- c:name 指定为name参数赋值 c:_1 指定为构造函数的第2个参数赋值 c:phone-ref 指定为构造函数的第phone参数赋值为id为"phone"的bean -->
同样的p命名空间则是用于简化<property>
标签的书写
声明:
xmlns:p="http://www.springframework.org/schema/p"
使用:
<bean id="user4" class="com.yh.demo2.user2" p:name="jerry" p:age="20" p:phone-ref="phone"/> <!-- p:name 指定为name属性赋值 p:age 指定为age属性赋值 p:phone-ref 为phone属性赋值为id为"phone"的bean -->
spel即spring expression language的缩写,与jstl一样是表达式语言,可以支持使用更加复杂的语法注入依赖,包括标准数学运算符,关系运算符,逻辑运算符,条件运算符,集合和正则表达式等;
语法:#{表达式}
用例:
<!--spel --> <bean id="user5" class="com.yh.demo2.user2"> <!--<property name="name" value="#{'jerry'}"/>--> <!--字符常量--> <!--<property name="age" value="#{100.0}"/>--> <!--数字常量--> <!--<property name="phone" value="#{phone}"/>--> <!--对象引用--> <!--<property name="name" value="#{phone.model.concat(' jerry')}"/>--> <!--方法调用--> <!--<property name="age" value="#{1+100}"/>--> <!--算数符--> <!--<property name="name" value="#{'11' > '22'}"/>--> <!--比较符--> <!--<property name="name" value="#{true or false}"/>--> <!--逻辑符--> <!--<property name="name" value="#{1 > 0?1:0}"/>--> <!--三目--> </bean>
<!-- 容器数据类型注入--> <bean id="user100" class="com.yh.demo2.user3"> <!--set注入 --> <property name="set"> <set> <value>3</value> <value>3</value> <value>a</value> </set> </property> <!--list注入 --> <property name="list"> <list> <value>3</value> <value>3</value> <value>a</value> </list> </property> <!--map注入 --> <property name="map"> <map> <entry key="name" value="jerry"/> <entry key="age" value="18"/> <entry key="sex" value="man"/> </map> </property> <!--properties注入 --> <property name="properties"> <props> <prop key="jdbc.user">root</prop> <prop key="jdbc.password">admin</prop> <prop key="jdbc.driver">com.mysql.jdbc.driver</prop> </props> </property> </bean>
强调:spring的依赖注入要么通过构造函数,要么通过setter,什么接口注入都tm扯犊子;
接口注入不是一种注入方式,只不过由于oop的多态,spring在按照类型注入时,会在容器中查找类型匹配的bean,如果没有则查找该类的子类,如果容器中有多个匹配的子类bean时会抛出异常,坑了一堆人,然后就开始意淫给这个问题取个名字吧.....接口注入.....
通用注解
@component 用于在spring中加入bean
mvc场景下
@controller 等价于 @component 标注控制层
@service 等价于 @component 标注业务层
@repository 等价于 @component 标注数据访问层(dao)
在实现上没有任何不同,仅仅是为了对bean进行分层是结构更清晰
使用步骤:
1.需要依赖context和aop两个jar包
2.添加命名空间
3.指定扫描的注解所在的包
<?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 http://www.springframework.org/schema/context/spring-context.xsd"> <!--指定要扫描注解的包 --> <context:component-scan base-package="com.yh.demo"/> </beans>
要注册的bean:
import org.springframework.stereotype.component; @component("userservice) public class userservice { public string hello(string name){ return "hello " + name; } }
测试:
public class tester { @test public void test(){ applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml"); userservice service = (userservice) context.getbean("userservice"); system.out.println(service.hello("jerry")); } }
若注解中没有指定id则默认使用简单类名且小写开头,userservice
@value用于对基本类型属性进行注入
@autowired将容器中的其他bean注入到属性中
@qualifier("beanid") 指定要注入的bean的id
准备userdao类:
import org.springframework.stereotype.repository; @repository("userdao") public class userdao { public void save(){ system.out.println("user saved!"); } }
userservice类:
import org.springframework.beans.factory.annotation.autowired; import org.springframework.beans.factory.annotation.value; import org.springframework.stereotype.component; @component("userservice") public class userservice { @value("hello")//基本类型 private string info; //@autowired(required = false) //默认为true表示属性时必须的不能为空 @autowired //注入类型匹配的bean //@qualifier("userdao") //明确指定需要的beanid private userdao userdao; //set/get..... }
测试:
import org.junit.test; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class tester { @test public void test(){ applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml"); userservice service = (userservice) context.getbean("userservice"); system.out.println(service.getinfo());//普通属性测试 service.getuserdao().save();//对象属性测试 } }
autowired默认自动注入类型一致的bean;required属性用于设置属性是否是必须的默认为true
qualifier需要和autowired搭配使用,用于明确指定要注入的bean的id
当spring中存在多个类型都匹配的bean时直接报错
接口:
public interface persondao { }
两个实现类:
@repository() public class persondaoimpl1 implements persondao{ }
@repository() public class persondaoimpl2 implements persondao{ }
注入:
import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.component; @component("userservice") public class userservice { @autowired private persondao persondao; }
qualifier和autowired书写繁琐,@resource可将两个标签的功能整合,即注入指定id的bean
@resource标准注解的支持是jsr-250中定义的,所以时使用需要导入扩展包,maven依赖如下:
<dependency> <groupid>javax.annotation</groupid> <artifactid>javax.annotation-api</artifactid> <version>1.3.2</version> </dependency>
resource默认按照使用属性名称作为id查找,查找失败则使用类型查找
可以利用name属性指定通过id查找
也可通过type指定类型,当出现相同类型的多个bean时抛出异常
import javax.annotation.resource; @component("userservice") public class userservice { //@resource()//默认按照id/name //@resource(name="xx")//指定name //@resource(type = persondaoimpl1.class) //指定type @resource(name="xx",type = persondaoimpl1.class)//同时指定name和type private persondao persondao; }
用于标注bean的作用域
@repository() @scope("prototype") //每次get都创建新的 public class userdao { public void save(){ system.out.println("user saved!"); } }
因为注解的表达能力有限,很多时候无法满足使用需求;我们可以将注解和xml配合使用,让xml负责管理bean,注解仅负责属性注入;
如对本文有疑问, 点击进行留言回复!!
网友评论