Spring
Spring简介
Spring是一个IOC 容器,可以帮助我们管理对象的创建获取使用销毁
且非侵入式(轻量级),具备 IOC 、 AOP 、 容器(对象工厂) 、 组件化 、 一站式
spring环境创建过程
- Spring运行期间需要 使用日志包 打印日志
- 借助Spring的 IOC容器的API ClassPathXmlApplicationContext;将配置文件解析为java中的 IOC容器对象
- 类似Web项目 启动时服务器将项目对应的web.xml配置文件创建为ServletContext对象,可以管理 内部的Servlet
Spring IOC容器
IOC原理
对象由容器创建:控制反转
- IOC: 如果需要使用bean的对象,首先将类的配置文件交给Spring IOC容器(配置文件)
- 需要使用 直接通过容器获取对象即可(省略了 创建过程)
DI是依赖注入. IOC思想的具体实现 , 在配置文件中配置bean并注入属性值
IOC是一种反转控制的思想, DI是对IOC思想的具体实现.
IOC容器类的继承关系
类能够更好的使用方法和属性来调用使用
- BeanFactory IOC容器的基本实现, 面向Spring框架本身
- ApplicationContext BeanFactory的子接口, 提供了更多高级的特性,面向框架的使用者的.
- ConfigurableApplicationContext 额外提供了 close refresh方法. 支持我们进行关闭,刷新的操作
- ClassPathXmlApplicationContext 从类路径下读取xml文件并创建IOC容器对象
- FileSystemXmlApplicationContext 从文件系统下读取xml文件并创建IOC容器对象
- ConfigurableApplicationContext 额外提供了 close refresh方法. 支持我们进行关闭,刷新的操作
- ApplicationContext BeanFactory的子接口, 提供了更多高级的特性,面向框架的使用者的.
- WebApplicationContext : 专门为web应用准备的.
获取bean的方式
User user = app.getBean(User.class);//根据类型从IOC容器中获取bean
User user = (User) app.getBean("user_xia");//User.class / 根据bean的 id属性值获取唯一的一个bean
- 通过类型获取 ,必须保证该类型配置了唯一一个bean
- 通过配置文件中的bean的 id属性值获取唯一的一个bean
Spring FactoryBean生产bean
通过FactoryBean接口来定义规范,spring容器直接判断并强转为FactoryBean接口调用相关方法
private String username;
//用户通过SpringIOC容器调用工厂类 获取bean时 容器自动调用
@Override
public User getObject() throws Exception {
return new User(9527, username, "123456", "xxx@12.com");
}
//获取Bean的类型
@Override
public Class<?> getObjectType() {
return User.class;
}
//控制bean是否为单例模式
@Override
public boolean isSingleton() {
return true;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
通过在配置文件暴露出来 可以动态设置值来让spring容器读取设置动态值
<bean class="com.hey.bean.UserFactoryBean" id="userFactoryBean">
<property name="username" value="gaowei"></property>
</bean>
//从容器中获取bean时 ,factorybean返回的是getObject方法返回的那个bean
User user1 = ctx.getBean("userFactoryBean", User.class);
与BeanFactory的区别
BeanFactory:
IOC容器的顶级约束
FactoryBean:
用来管理bean对象的创建的
组件注解-通过注解配置管理bean
之前的方式通过bean标签一个个的配置javabean交给IOC管理过于麻烦,为了让代码更加简洁,Spring提供了基于注解的方式配置javabean给容器
- @Component 普通组件 标识一个受Spring IOC容器管理的组件
- @Repository 持久层的组件
- @Service 业务层组件
- @Controller 控制层组件
组件注解和Autowired自动装配需导aop包
- 组件注解必须导入aop jar包
通过后置处理器处理@Autowired
在指定要扫描的包时,context:component-scan 元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。
即在init前 后置处理器前处理方法会检查该bean有无autowired注解,有则根据autowired的value值找aop容器对应id的bean,无则查找对应class的bean,如果没找到,或者找到多个,都会报错,除非设置required=false
,或者@Qualifiter指定bean的id
@Autowired可用于数组和集合类型
- @Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
- @Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
- @Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
组件注解和Autowird注入bean属性流程与注意事项
注解与Autowired配置步骤:
1、在想要配置到容器中的类名上 使用注解即可
2、需要在IOC容器配置文件中 指定开启自动注解扫描
context:component-scan :开启IOC组件扫描功能
base-package: 指定要自动扫描的包
resource-pattern="dao/*.class" 指定保留扫描的包,其他的排除
当前标签默认包下的所有注解都扫描
》 默认情况:use-default-filters="true" 如果需要排除指定类型的注解,可以在标签内使用context:exclude-filter排除
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
- 一般只会排除 Repository、Service、Controller,Component就会排除所有的组件
》如果不使用默认的扫描规则:use-default-filters="false" ,需要使用context:include-filter指定要扫描的组件注解
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
3、使用IOC容器对象时,就可以使用注解标注的bean
可以使用type获取,也可以使用类名首字母小写作为id获取
Spring默认将类名首字母小写作为id绑定给bean,找不到用类型匹配,但是类型需要只有该类型唯一的对象,不然不知道匹配哪一个
我们也可以通过注解 指定自定义id:@Component("u")
4、Spring基于注解的 自动装配: 以后经常使用
原理:后置处理器,在对象创建后,查找对象的 自动装配的注解 对属性进行赋值(根据类型或者 id去查找对应的值)操作
自动装配的前提: 需要被装配的bean 和需要使用值的bean 都需要配置到容器中
需要被自动装配的属性,需要在属性上使用 @Autowired注解
自动装配的顺序:
IOC容器 先会根据 属性的属性名作为id去ioc容器中匹配对应的元素进行装配
如果属性名和要装配的bean的id值不同,解决方法:
- 修改属性名和javabean的id值相同 private UService userService;
- 给javabean的组件注解中设置id值 和 属性名一样 @Service("service")
如果id找不到,再根据类型去匹配(自己的类型以及子类型的对象)进行装配
如果该类型或该类型的实现类的类型 容器中配置了多个,会报错
如果属性名和要装配的bean的id不同,但是容器中又配置了该类型的多个bean,解决
使用:@Qualifier("service")在属性上 指定 要自动装配的bean的id
如果自动装配对应的类型的对象在容器中没有,@Autowired 默认必须给该属性自动装配上,导致报错
如果希望代码能够正常执行需要@Autowired(required=false)
<context:component-scan base-package="com.hey" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
@Service("service")
@Autowired
@Qualifier("userService")
private UService s;
@Resource 方式
指定类型自动装配:
@Resource(type=UserService2.class)
private UService s;
@Resource(name="service")
因为具体的实现类型如果不同,是可以确定的
@Inject
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。
配置文件
配置bean
属性值注入
<bean class="com.hry.bean.User" id="user_xia">
</bean>
-
setter方法注入(实际调用的是setter方法)
<property name="username" value="laowang"></property>
-
通过构造器注入
可以通过 name/type/index三种方式 指定 构造器参数的匹配
name: 唯一匹配 推荐
type: 需要保证 位置和构造器参数位置相同 不推荐使用
index:从0开始计算<constructor-arg type="java.lang.String" value="huxinyan" index="1"></constructor-arg>
-
Spring2.5版本后:可以通过p名称空间 注入属性值 (使用前必须 在当前xml文件中引入支持p名称空间的xml约束)
<bean class="com.hey.bean.User" id="user_p" p:username="2qweqwe" p:email="xxx@12.com" p:id="111" p:password="12123213"></bean>
属性值区分-依赖注入可以使用的值
-
字面量:直接通过字符串表示的值 使用value 或者是来进行注入
-
特殊字符: 可以使用实体. 或者使用 <![CDATA[ .... ]]>
-
ref引用:通过id引用 , 指定复杂类型的数据
需要先配置一个需要使用的对象或者是来引用IOC容器中的bean对象
-
级联属性操作
``` -
内部bean 外部不可使用
<property name="gf"> <bean class="com.hey.bean.GirlFriend" p:cailiao="塑料" p:price="100"></bean> </property>
-
集合属性
集合属性管理
数组和list
- 内部
<property name="list" ref="list"> <!-- 内部创建的list只能在内部使用 --> <list> <ref bean="user_p"/> <null/> <bean class="com.hey.bean.User" p:username="huxinyan"></bean> </list> </property>
- 外部
<!-- 可以在配置文件中引入util名称空间,可以在页面中单独创建集合 --> <util:list id="list" list-class="java.util.LinkedList" value-type="com.hey.bean.User"> <ref bean="user_p"/> <null/> <bean class="com.hey.bean.User" p:username="huxinyan111"></bean> </util:list>
map
以entry设值 map中的一个元素 一个键值对结构
- 内部
<property name="map"> <map> <entry> <key> <value>key1</value> </key> <ref bean="user_p"/> </entry> <entry> <key> <value>key2</value> </key> <null /> </entry> <entry> <key> <value>key3</value> </key> <bean class="com.hey.bean.User" p:username="xxxxxxx"></bean> </entry> </map> </property>
- 外部
<util:map id="map" key-type="java.lang.String" value-type="com.hey.bean.User"> <entry> <key> <value>key1</value> </key> <ref bean="user_p"/> </entry> <entry> <key> <value>key2</value> </key> <null /> </entry> <entry> <key> <value>key3</value> </key> <bean class="com.hey.bean.User" p:username="AAAAA"></bean> </entry> </util:map>
提取配置信息到配置文件中
为的是更加灵活,抽取配置信息到文件中,暴露出来,且与代码解耦,便于修改(修改不影响代码)
- 如果需要在当前ioc配置文件中使用另外的配置文件,需要使用context名称空间将配置文件 加载到当前配置中(容器加载为对象,读取配置文件到这里,就将其他配置文件的信息以流对象形式读取到该容器上下文环境中)
- 如果需要指定从类路径下加载,可以使用classpath:指定
- 注意:propertie文件中 key名 需要加标记 如mysql.username
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 然后可以使用${key} 从配置文件中取出对应的值 -->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="druidDataSource">
<property name="url" value="${mysql.url}"></property>
<property name="driverClassName" value="${mysql.driver}"></property>
<!-- ${username} 操作系统也会使用username保存当前用户的用户名存到配置文件中,如果我们也是用username会冲突 -->
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
</bean>
基于XML的自动装配
- 手动装配:以value或ref的方式明确指定属性值都是手动装配。
- 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
不推荐使用,不知道到底匹配的是哪个值
autowire="default" 不自动装配
autowire="byType" 根据类型自动装配
当前bean的引用属性,如果在容器中存在类型对应的bean,容器就会自动将bean设置给该属性
- 该方式必须保证要装配的类型只能配置一个
autowird="byName" 根据对象的属性名作为id去容器中匹配对应的对象进行装配
如果属性名和bean的id不一样则装配失败
推荐使用之前的value、ref的方式明确指定 要注入的属性值
例如:
<bean id="xiaohong" class="com.hey.bean.GirlFriend" p:price="10000" p:cailiao="不锈钢"></bean>
<bean id="gf" class="com.hey.bean.GirlFriend" p:price="100" p:cailiao="塑料"></bean>
<!--
通过p:gf=""
value
p:gf-ref=""
ref
这种方式手动指定注入的属性值 就是手动装配
-->
<bean class="com.hey.bean.User" p:username="xiashengsheng" autowire="byName"></bean>
bean生命周期
scope
- singleton :默认值 单例
ioc容器初始化后立即创建对象,以后获取的都是同一个 - prototype : 多例,每次获取都新创建
ioc容器初始化后不会创建对象,以后每次获取时新创建 - request: web项目中 每次请求会创建一次该对象
- session: web项目中 每次会话 会创建一次该对象
init和destory的选择
- springIOC为了方便我们管理bean对象的初始化创建 也可以让我们自己指定初始化方法和销毁方法
- IOC容器创建对象时 :init-method=“init” destroy-method=“destroy”
作用
init可以检验bean设置参数的合法正确性,以及从持久化数据中将数据加载到内存中
init类似书城项目的get方法再将bean返回给用户前做参数安全处理
destroy可以释放资源,以及持久化数据
生命周期流程
- 构造器 (单例在spring容器启动时加载配置文件读取时立即反射创建)
- setter方法设置属性值 配置文件的property配置
- 调用后缀处理器的 postProcessBeforeInitialization
- 调用自己编写的初始化方法:检查bean的属性值的范围
- 调用后缀处理器的postProcessAfterInitialization
- 使用bean对象
- 容器正常关闭时会自动调用销毁方法 :收尾操作(必须有close关闭操作来提醒spring容器将要关闭以进行收尾工作,如果强行关闭,则无法知晓)
后置处理器
IOC容器为了方便我们在bean外部的代码中也可以管理bean的生命周期,提供了后置处理器,一旦在spring容器中创建,就管理所有的bean
使用步骤
- 创建类实现BeanPostProcessor
- 实现方法
- 在IOC配置文件中配置后置处理器(IOC容器会自动使用)
<bean class="com.hey.bean.MyBeanPostProcessor"></bean>
代码示例与后置处理器两个方法作用
//在bean的初始化方法调用之前调用: 用来检查bean的属性值的安全
/**
* 参数1:
* 正在初始化的bean对象,可以用来检查
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization ==> " +beanName+"即将初始化....");
//后缀处理器处理完bean之后必须将bean返回,否则ioc容器不能继续初始化bean
return bean;
}
//在bean的初始化方法调用之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor.postProcessAfterInitialization ==> " +beanName+"已经初始完成....");
return bean;
}
注意
- 因为单例模式bean是容器一开始创建就加载,所以后面再获取user对象,也不会再创建对象了,直接用一开始创建的对象,name(id)获取到的是null,就返回null,生命周期相关方法也只调用1次
- 如果后缀处理器返回null,那么后处理方法将不会执行,因为null值调用不了方法
User user = applicationContext.getBean(User.class);
System.out.println(user);
User user2 = applicationContext.getBean("user",User.class);