文章目录
Spring框架概述
-
简化企业级应用开发
-
一站式轻量级开源开发框架
-
声明式事务支持,无需手动编程
-
服务器端分为三层结构:web层(Spring提供了springMVC),业务逻辑层(Spring提供了Bean管理,即IOC,还提供了事务管理)和持久层(Spring提供JDBC模板,整合ORM框架)——每一层都提供了解决方案
-
web层需要创建业务层对象,业务层需要创建DAO层对象——》都交由Spring创建
-
关注Spring的核心容器Core Container(IOC相关)
IOC的底层实现原理
- 任务:Web层需要创建业务层的类UserServiceImpl
- 方法1:Web层直接创建接口的实现类——》Web层与业务层产生耦合(因为若要更改为另一个实现类,要修改源代码)
- 方法2:工厂模式,但问题在于将接口和实现类的耦合变为接口与工厂类的耦合
- 程序开发的OCP原则:程序拓展open,源码修改close
- 基于上述分析,选用工厂+反射+配置文件,即IOC的底层原理
- 即:解析XML文件,根据id找到全类名,然后工厂模式通过反射创建对象
- class属性值为全类名(带包的路径)
- 强调:IOC底层原理为通过工厂+反射+配置文件来完成解耦合
Spring入门程序
- schema文件夹中有XML文件的约束
- 创建项目步骤:创建接口+创建实现类
- 选择maven项目的webapp模板
- 自动创建完成后,pom.xml文件中添加依赖,即dependency标签,引入jar包
(spring-core,spring-context,spring-expression,spring-beans,spring-aop,junit[4.12]) - resource文件夹下新建spring配置文件(名字任意),创建好后会默认引入beans约束(最前面一大段)
- 创建java文件夹,然后make source root:作为源码路径,编写代码
Bean配置
- bean标签中id任意,class为全路径
- Spring工厂:ApplicationContext(接口)
- 传入XML(配置文件名)
- getBean方法的参数为id(对应于配置文件中指定的bean),且获取返回值(Object类型)后要强转
<bean id="userService" class="com.imooc.ioc.demo1.UserServiceImpl">
<property name="name" value="李四"/>
</bean>
public void demo2(){
// 创建Spring的工厂, ApplicationContext为接口
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类:
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
IOC与DI概述
- IOC:控制反转,创建对象的控制权反转到spring框架
- DI依赖于IOC,原因如下
- 对象交给spring管理,若对象有依赖的属性——》spring要将依赖的属性注入
- 配置属性:加入子标签property
- 依赖注入:类依赖的属性通过配置文件设置
- DI的原因:在传统方式中,接口没有接口实现类的属性,则不能使用多态性来改变属性,一定要采用接口实现类的引用才行——》需要修改源代码(则有耦合)——》DI能解耦合
Spring的工厂类
- 关注层次树中三个工厂类(FileSystemXmlApplicationContext【加载磁盘系统的配置文件】与ClassPathXmlApplicationContext【加载工程下配置文件】与XmlBeanFactory)
- 从不同路径加载配置文件,对应不同的工厂类
- 老的工厂类BeanFactory
- 新的工厂类(其他两个)多了国际化功能,其次,生成bean实例的时机不同,BeanFactory调用getBean方法才创建实例,而新的工厂类加载配置文件,所有单例模式【默认】配置的类都被实例化
- 新的工厂类中的getBean方法只是获取实例
接口名 接口变量 = new 实现类名(配置文件名称);
接口变量.getBean(“id”)
Bean管理
Bean实例化
- 实例化的三种方式(根据配置bean的方式采用对应方法)
- 调用构造方法(默认调用无参构造器) ——最常用
- 实例工厂:工厂类提供非静态方法,通过工厂类对象来返回bean实例
- 静态工厂:工厂类里提供静态方法(方法名任意),返回Bean实例
<!--第一种:无参构造器的方式-->
<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
<!--第二种:静态工厂的方式,属性为工厂类id,全类名和工厂方法-->
<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
<!--第三种:实例工厂的方式,配置两个bean-->
<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
/**
* Bean2的静态工厂
*/
public class Bean2Factory {
public static Bean2 createBean2(){
System.out.println("Bean2Factory的方法已经执行了...");
return new Bean2();
}
}
//实例工厂
public class Bean3Factory {
public Bean3 createBean3(){
System.out.println("Bean3Factory执行了...");
return new Bean3();
}
}
- 实例工厂的bean配置:要先有工厂的实例,才能创建对象,所以要先配置工厂Bean
Bean管理常用配置
bean标签中常用属性:
- id和name都表示bean的别名
- id是唯一的,name没有唯一约束
特殊字符使用name(如含有\),id不能使用特殊字符
- scope属性用于配置单例或多例(若为多例,则getBean每次调用相当于new新的对象),默认单例
- 指定为request或session表示:创建类的实例,则保持在request和session作用域中
- init-method和destory-method的方法名任意指定,只需要在配置文件中配置上
- 销毁方法只适用于单例
- 类被实例化则调用init-method,一般无返回值,方法名任意
- 工厂关闭,则会销毁类的实例(单例模式前提),从而调用destroy-method方法
<bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown">
<property name="name" value="张三"/>
</bean>
Bean完整的11个生命周期
----
展示bean实例从创建到销毁的全过程
- 实例化(即调用构造器)
- 根据property标签设置属性(要提供set方法,且配置了property子标签,name为属性名,value为属性值)
- 了解bean在spring中的名称,即name值(或者id值)
- 了解工厂信息
- 初始化Bean实例之前执行的方法(实现了BeanPostProcessor接口的类的一个方法) 注:实现了BeanPostProcessor接口的类不需要配置id(spring在生成bean实例后自动引用,且其他地方不使用),只需要配置class即可
- 属性设置后执行的方法(afterPropertiesSet())
- 调用Init-method指定的方法
- 初始化后执行的方法(实现了BeanPostProcessor接口的类的一个方法)
- 执行业务处理(若有调用相关方法的话)
-----下方步骤要关闭工厂才会执行 - 执行DisposableBean接口的destroy方法(重写)
- 执行destroy-method指定的方法
-
关注第5,8
-
若在配置文件中配置了实现了BeanPostProcessor接口的类,则 每个Bean实例化过程中都会调用实现了BeanPostProcessor接口的类的两个方法,即所有类的实例化都会受影响
-
基于上述分析,BeanPostProcessor接口的方法在生成某类的过程中必须执行——》则可对某类产生代理对象(在实现了BeanPostProcessor接口的类的两个方法中产生),从而增强A类的方法(见下方的权限校验)
-
增强功能(AOP思想)
public class Man implements BeanNameAware,ApplicationContextAware,InitializingBean,DisposableBean{
private String name;
public void setName(String name) {
System.out.println("第二步:设置属性");
this.name = name;
}
public Man(){
System.out.println("第一步:初始化...");
}
public void setup(){
System.out.println("第七步:MAN被初始化了...");
}
public void teardown(){
System.out.println("第十一步:MAN被销毁了...");
}
@Override
public void setBeanName(String name) {
System.out.println("第三步:设置Bean的名称"+name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("第四步:了解工厂信息");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第六步:属性设置后");
}
public void run(){
System.out.println("第九步:执行业务方法");
}
@Override
public void destroy() throws Exception {
System.out.println("第十步:执行Spring的销毁方法");
}
}
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//System.out.println("第五步:初始化前方法...");
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
//System.out.println("第八步:初始化后方法...");
if("userDao".equals(beanName)){ //若bean的id为userDao,则完成实例化后,不返回原有的bean
//产生代理类,增强方法,最后返回代理类
Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {//匿名内部类方式
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){//若id是userDao,且为bean中的save方法则增强该方法:先进行权限校验,再调用原有方法(方法被增强了)
System.out.println("权限校验===================");
return method.invoke(bean,args);
}
return method.invoke(bean,args);
}
});
return proxy;//返回代理类
}else{
return bean;//id不是userDao,直接返回原有bean
}
}
}
JDK动态代理
见上方代码
- 实现被代理类在类的实例化过程中,通过JDK动态代理,增强此类
Spring的属性注入
-
DI:创建类实例的同时设置实例属性
-
注入属性的方式
- 构造函数注入
- 属性setter方法注入
- 接口注入
Spring支持前两种
构造方法注入
public class User {
private String name;
private Integer age;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
<bean id="user" class="com.imooc.ioc.demo4.User">
<constructor-arg name="name" value="张三" />
<constructor-arg name="age" value="23"/>
</bean>
- 构造方法+constructor-arg
- 要提供构造方法,构造器参数为对应参数
- constructor-arg标签中还包括index,type等属性
- index:按照下标方式进行属性注入
- type:设置数据类型
set方法注入
<!--Bean的set方法的属性注入==============================-->
<bean id="person" class="com.imooc.ioc.demo4.Person">
<property name="name" value="李四"/>
<property name="age" value="32"/>
<property name="cat" ref="cat"/>
</bean>
<bean id="cat" class="com.imooc.ioc.demo4.Cat">
<property name="name" value="ketty"/>
</bean>
- 要提供getter/setter方法,设置property标签
- name:属性名
- value:普通类型
- ref:引用类型,属性值为其他bean的id/name
p名称空间属性注入
<bean id="cat" class="com.imooc.ioc.demo4.Cat">
<property name="name" value="ketty"/>
</bean>
<!--Bean的p名称空间的属性注入==============================-->
<bean id="person" class="com.imooc.ioc.demo4.Person" p:name="大黄" p:age="34" p:cat-ref="cat"/>
<bean id="cat" class="com.imooc.ioc.demo4.Cat" p:name="小黄"/>
S1:引入 p名称空间
xmlns:p=“http://www.springframework.org/schema/p”
S2:p:name="?"
SpEL注入
<!--Bean的SpEL的属性注入==============================-->
<bean id="category" class="com.imooc.ioc.demo4.Category">
<property name="name" value="#{'服装'}"/>
</bean>
<bean id="productInfo" class="com.imooc.ioc.demo4.ProductInfo"/>
<bean id="product" class="com.imooc.ioc.demo4.Product">
<property name="name" value="#{'男装'}"/>
<property name="price" value="#{productInfo.calculatePrice()}"/>
<property name="category" value="#{category}"/>
</bean>
- #{}:普通值
- #{‘’}:字符串
- 可使用其他类的方法或属性来设置,如 ,PS:要配置productInfo
- 即使是引用变量,仍然使用value=?
特殊文件的属性注入
public class CollectionBean {
private String[] arrs; // 数组类型
private List<String> list;// List集合类型
private Set<String> set; // Set集合类型
private Map<String,Integer> map;// Map集合类型
private Properties properties; // 属性类型
public String[] getArrs() {
return arrs;
}
public void setArrs(String[] arrs) {
this.arrs = arrs;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!--集合类型的属性注入=================================-->
<bean id="collectionBean" class="com.imooc.ioc.demo5.CollectionBean">
<!--数组类型-->
<property name="arrs">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<!--List集合的属性注入-->
<property name="list">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
</list>
</property>
<!--Set集合的属性注入-->
<property name="set">
<set>
<value>ddd</value>
<value>eee</value>
<value>fff</value>
</set>
</property>
<!--Map集合的属性注入-->
<property name="map">
<map>
<entry key="aaa" value="111"/>
<entry key="bbb" value="222"/>
<entry key="ccc" value="333"/>
</map>
</property>
<!--Properties的属性注入-->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">1234</prop>
</props>
</property>
</bean>
</beans>
- 提供set方法
成员变量为如下类型: - 数组类型:property——》list标签——》value/ref标签
- list类型:property——》list标签——》value/ref标签
- set类型:property——》set标签——》value/ref标签
- map类型 :map标签——》entry
- properties:pro标签
应用场景:其他框架需要复杂数据类型的注入
注解方式
使用注解方式管理bean比xml方式更方便
Bean管理的注解
- 加入context约束
<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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
- 开启注解扫描(扫描每个包的类中的注解,由spring管理)
<context:component-scan base-package=“com.imooc”/>
- 用注解@Component("【相当于传统方式的id】")来配置bean
- 三种注解关键字,当前等效
属性注入的注解
- 使用注解方式加在属性上即可,不需要setter方法,若提供了setter方法,要加在setter方法上
- 普通类型为@value
- 对象类型为@Resource(name=“id” ) (等价于@Autowired + @Qualifier(属性的值))
- @Autowired+@Qualifier:强制按照名称注入(只使用@Autowired默认按照类型【成员变量的数据类型和某bean的数据类型】注入,即使名称不同也行)
- 二合一:@Resource
其他注解
生命周期相关
-
@PostConstruct:初始化时执行,相当于init-method
-
@PreDestroy:destroy时执行,相当于destroy-method
-
关闭工厂只能用工厂的实现类关闭,工厂接口没有此方法
Scope
@Scope:表示多例模式或单例模式
混合使用
-
XML方式管理类,注解方式用于属性注入,因为注解不需要set方法
-
<context:component-scan base-package=“com.imooc”/>表示开启注解扫描,即类注入可以——》属性注入肯定也OK啊,不需要写context:annotation-config/
-
context:annotation-config/表示单独开启属性注入方式——》才可使用属性注入