这里所说的Spring框架是指SSM中的Spring,Spring是一个用于治理组件(Java类)的容器,它蕴含并治理对象的生命周期。Spring有两个核心技术,即IOC和AOP。
IOC
BeanFactory接口提供了治理任意类型的对象的预约义机制,提供了一些根底性能。
ApplicationContext接口是对继承了BeanFactory,它在BeanFactory的根底上实现更多的定制化的性能。该接口是IOC容器的具体体现,它负责实例化,配置和拆卸对象,这些对象以及对象之间的关系由配置文件(XML文件,Java正文)定义,容器会读取这些配置文件,从而创立和治理对象。
只须要向IOC容器提供简略的POJO类以及配置文件,IOC容器就会主动创立出对象并对对象进行治理
IOC容器创建对象:
实例化容器,须要向容器结构器提供字符串模式的bean配置文件资源门路:
实例化容器的同时,Spring会读取bean配置文件并实例化bean配置文件中配置的类;应用时,只须要调用容器的getBean办法即可
实例化容器并获取实例:// create and configure beansApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");// 拿到实例PetStoreService service = context.getBean("petStore", PetStoreService.class);// 应用实例List<String> userList = service.getUsernameList();
配置xml文件:
service.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"> <!-- services --> <!--实例化一个对象当时所需的配置--> <!--id是该bean的惟一辨认符,class是须要被实例化的类的全限定类名--> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <!--property对应类的各个属性,name为属性名,ref:属性值可能是另一个类实例化的对象,所以用ref来援用其它的bean的id--> <!--ref与id显示了合作类之间的依赖关系--> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --></beans>
然而,实际上不应该应用getBean或者相似的办法来获取实例而是通过配置文件,应用依赖注入的办法应用实例
dao.xml的例子
与service.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"> <bean id="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for data access objects go here --></beans>
让bean定义跨多文件也是常见的做法:
<beans> <!--为了应用定义在其它xml文件中的bean,应用**import**标签引入其它xml配置文件,留神门路--> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <!--前斜杠会被疏忽,并且尽量不要应用前斜杠--> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/></beans>
1.3.1为bean起别名(不了解)
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/><alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
1.3.2实例化bean的三种形式
办法一: 应用构造函数进行实例化
在这种形式中,只须要满足(1)该类要有无参的构造函数,这一步是为了防止在实例化时间接提供属性的值,(2)提供各个属性的get和set办法,这一步是为了通过<property>标签为第一步中的实例的属性赋值
示例:
<bean id="exampleBean" class="examples.ExampleBean"/><bean name="anotherExample" class="examples.ExampleBeanTwo"/>
note:倡议当参数的数目超过四个时,就要提供无参的构造方法和为各个属性赋值的get和set办法,这样做的益处:
1.不必在实例化对象时就给出属性值
2.属性值能够灵便批改
办法二:应用动态工厂
这种办法不必创立工厂实例,因而工厂类中要有一个静态方法来创立bean实例。
配置实例:
<bean id="clientService" <!--class指定动态工厂的全限定类名--> class="examples.ClientService" <!--factory-method指定返回bean实例的动态工厂办法--> factory-method="createInstance"/>
动态工厂类示例:
public class ClientService { <!--创立bean实例--> private static ClientService clientService = new ClientService(); <!--这里的无参构造方法是上一行new一个bean实例用的--> private ClientService() {} <!--返回bean实例的办法--> public static ClientService createInstance() { return clientService; }}
办法三:应用实例工厂进行实例化
该办法实际上创立了工厂对象,因而不须要有返回bean对象的静态方法
配置示例:
<!-- 先创立实例工厂的bean --><bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --></bean><!-- 再用实例工厂的bean创立指标bean --><bean id="clientService" <!--指定实例工厂bean,与动态工厂的区别是实例工厂在这里应用factory-bean而动态工厂在这里应用class--> factory-bean="serviceLocator" <!--指定返回指标bean实例的工厂办法--> factory-method="createClientServiceInstance"/>
实例工厂类示例:
public class DefaultServiceLocator { //动态属性,类被加载的同时创立初始化该属性 private static ClientService clientService = new ClientServiceImpl(); //须要创立工厂实例,因而返回bean实例的办法不必要是静态方法 public ClientService createClientServiceInstance() { return clientService; }}
另外,一个工厂也能够蕴含多个办法
1.4依赖项
依赖就是类之间的调用关系,如果一个类A在它的外部应用了另一个类B,那么就说这两个类之间产生了依赖。
依赖注入是个过程,就是将B类放入A类的这个过程,这个过程能够通过A类的构造函数实现,也能够通过set办法实现。
1.1.4依赖注入
一、基于构造函数的依赖注入
基于构造函数的 DI 是通过容器调用具备多个参数的构造函数来实现的,每个参数代表一个依赖项。
容器是通过参数类型进行参数匹配的,如果参数是不同类型并且没有继承关系,那么无需在 <constructor-arg/>元素中显式指定结构函数参数索引或类型或名称。
例:
package x.y;public class ThingOne { //两个参数属于不同类 public ThingOne(ThingTwo thingTwo, ThingThree thingThree) { // ... }}
<beans> <bean id="beanOne" class="x.y.ThingOne"> <!--无需在constructor-arg标签中进行任何显式提醒--> <constructor-arg ref="beanTwo"/> <constructor-arg ref="beanThree"/> </bean> <bean id="beanTwo" class="x.y.ThingTwo"/> <bean id="beanThree" class="x.y.ThingThree"/></beans>
如果参数类型是根本数据类型,因为提供的都是字符串,容器无奈辨认参数的指标类型,须要揭示容器参数的指标类型,而后Spring的类型转换能力将提供的字符串类型的值转换为指标类型
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <!--指标类型是int--> <constructor-arg type="java.lang.String" value="42"/> <!--指标类型是String--></bean>
如果参数是同种类型,能够应用索引进行提醒,索引下标从0开始
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/></bean>
最好用的还是间接应用结构函数参数名称(不是属性名)进行提醒
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/></bean>
二、基于set办法的依赖注入
基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数static工厂办法来实例化bean后调用bean的setter办法来实现的。
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/></bean><bean id="anotherExampleBean" class="examples.AnotherBean"/><bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
总结:基于构造函数和基于动态工厂,基于工厂形式创立Bean实例的时候,都是通过<constructor-arg/>提供参数的,不同的是基于构造函数和动态工厂都是通过class指定类,而工厂办法是通过factory-bean指定的;基于set办法是通过<property/>提供参数的。
动态工厂注入:
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/></bean><bean id="anotherExampleBean" class="examples.AnotherBean"/><bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
基于构造函数和基于set办法能够混用,那么如何抉择呢?
举荐对于必须的属性,通过构造函数进行注入,而对于可选属性以及可能批改的属性,通过get办法进行注入
1.4.2具体的依赖配置
idref标签:援用另一个bean的id值,而不是真正的bean对象。
<bean id="theTargetBean" class="..."/><bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean"/> </property></bean>
成果等同于下边的这个,然而能避免theTargetBean这个bean不存在而引起谬误
<bean id="theTargetBean" class="..." /><bean id="client" class="..."> <property name="targetName" value="theTargetBean"/></bean>
外部bean,它仅存在于一个bean的外部,其它bean无奈调用它,因而也不须要id(即便设置了,容器也不会将它的id看作标识符)
<bean id="outer" class="..."> <!-- instead of using a reference to a target bean, simply define the target bean inline --> <property name="target"> <!--外部bean--> <bean class="com.example.Person"> <!-- this is the inner bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property></bean>
汇合与汇合的合并
<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key="a ref" value-ref="myDataSource"/> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property></bean>
子bean中的汇合能够继承(合并)父bean中的汇合,如果汇合是有序的,父汇合中的元素会在子集合的元素之前
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean><beans>
强类型汇合
如果在类中定义了汇合中元素的类型,那么在进行依赖注入时,Spring的类型转换机制会主动的将传递的参数转换为适合的类型
public class SomeClass { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; }}
<beans> <bean id="something" class="x.y.SomeClass"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean></beans>
空和null
<bean class="ExampleBean"> <property name="email" value=""/></bean>
等价于exampleBean.setEmail("");
而<bean class="ExampleBean"> <property name="email"> <null/> </property></bean>
等价于exampleBean.setEmail(null);
p属性与<property/>标签能够替换,然而p属性不够灵便
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person"> <property name="name" value="Jane Doe"/> </bean></beans>
depends-on属性能够设置创立和销毁的工夫依赖,实际上,depends-on显式指定了一个类对另一个类的依赖关系,创立该bean前,会先创立被依赖的类的实例,销毁该bean前,也会先销毁被依赖的bean的实例。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> <property name="manager" ref="manager" /></bean><bean id="manager" class="ManagerBean" /><bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
1.4.4 提早加载
容器在启动时会创立bean的单例:默认状况下,作为初始化过程的一部分,ApplicationContext实现会急迫地创立和配置所有 单例bean。通常,这种预实例化是可取的,因为配置或周围环境中的谬误会立刻发现,而不是几小时甚至几天之后提早加载lazy-init属性:将本来实例化bean的工夫从容器启动时提早到容器被第一次申请时
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/><bean name="not.lazy" class="com.something.AnotherBean"/>
然而当提早加载的bean是另一个单例bean的依赖时,容器仍会在启动时创立该提早加载bean的实例,起因是要满足另一个bean的依赖关系,这种状况下能够应用容器级别的提早加载属性:default-lazy-init
1.4.5 主动拆卸
应用主动拆卸,Spring通过Java的反射机制在实例化bean时能够主动的为bean的属性,办法入参拆卸。这时就不必在配置文件中手动配置关联类
主动拆卸属性:
package com.baobaotao;import org.springframework.beans.factory.annotation.Autowired;public class Boss { @Autowired private Car car; @Autowired private Office office; …}
配置文件:
<!-- 该 BeanPostProcessor 将主动起作用,对标注 @Autowired 的 Bean 进行主动注入 --> <bean class="org.springframework.beans.factory.annotation. AutowiredAnnotationBeanPostProcessor"/> <!-- 移除 boss Bean 的属性注入配置的信息 --> <bean id="boss" class="com.baobaotao.Boss"/> <bean id="office" class="com.baobaotao.Office"> <property name="officeNo" value="001"/> </bean> <bean id="car" class="com.baobaotao.Car" scope="singleton"> <property name="brand" value=" 红旗 CA72"/> <property name="price" value="2000"/> </bean>
默认不应用主动拆卸,如果应用主动拆卸,默认是byType,除此之外还有byName,constructor
bean的作用范畴
容器创立的bean实例默认是单例的
容器在创立bean实例时,对于bean实例的范畴有四个可选项:
1.singleton 对于每个Spring IoC容器的单个bean实例的单个bean定义的范畴 只创立该bean的惟一实例,所有申请和援用都只应用这个实例
2.prototype 对于任何对象实例的单个bean定义的范畴,每次对该bean的申请都创立该bean的实例(向容器申请ClassB,容器会创立并返回ClassB的实例:)
applicationContext.getBean(ClassB.class);
倡议将有状态(属性)的bean创立为prototype,无状态的创立为单例模式
3.request 每次HTTP申请生命周期的单个bean定义范畴;即,每个HTTP申请返回一个bean实例。仅在ApplicationContext的上下文中无效
4.session 单个bean定义的HTTP会话生命周期的范畴。仅在ApplicationContext的上下文中无效
5.global session 单个bean定义的全局HTTP会话的生命周期。个别地在门户导入的信息组件的上下文中无效。仅在ApplicationContext的上下文中无效
6.application 单个bean定义的一个ServletContext的生命周期。仅在ApplicationContext的上下文中无效
与其余范畴相比,Spring 不治理原型 bean 的残缺生命周期。容器实例化、配置和以其余形式组装原型对象并将其传递给客户端由客户端进行治理,而没有该原型实例的进一步记录。因而原型bean所占用的资源要手动开释。
1.4.6 办法注入
管制反转:本来创建对象是一个对象须要另一个对象时,就显式的创立一个对象进去。管制反转是将对象的创立权交给容器,容器对立创立并治理对象,当一个对象须要依赖另一个对象时,容器将创立好的对象注入给它。
留神因为对象有单例的和非单例的(prototype),因而当一个单例的对象依赖另一个非单例的对象时,应用本来的注入办法就会呈现问题:单例对象仅在容器被初始化时创立一次,此时它依赖的非单例对象也被注入,后续应用单例对象时不会再创立新的非单例对象,这与非单例对象的初衷相违反,还会造成线程平安问题。
此时有两种解决办法
第一种:显式的调用Spring容器,创立一个新的B类的实例。
毛病是造成造成Spring的强耦合
@Componentpublic class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; public void printClass() { System.out.println("This is Class A: " + this); getClassB().printClass(); } public ClassB getClassB() { return applicationContext.getBean(ClassB.class); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
第二种办法就是应用办法注入,这是针对办法的注入形式,容器会依据办法的返回值创立一个与办法返回值类型雷同的新的对象。从而不必显式的调用容器,实现解耦
基于注解:
@Componentpublic class ClassA { public void printClass() { System.out.println("This is Class A: " + this); getClassB().printClass(); } @Lookup public ClassB getClassB() { return null; }}
应用@Lookup标签对办法的签名有所要求,要满足:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
基于xml:
package fiona.apple;// no more Spring imports!public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method?在xml文件中进行配置,容器会创立一个与该办法返回值类型雷同的对象 protected abstract Command createCommand();}
xml文件:
<!-- a stateful bean deployed as a prototype (non-singleton) --><bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --></bean><!-- commandProcessor uses statefulCommandHelper --><!--Lookup是工作在类中的办法上的,这段配置的含意是:让CommandManager类中的createCommand办法返回一个id为myCommand的bean实例--><bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="myCommand"/></bean>
作用域 Bean 作为依赖项
当单例bean依赖于作用域bean(request,session等等)时,就会呈现问题。单例bean是在容器初始化时创立的,此时容器会试图注入单例bean依赖的作用域bean,但因为此时还不在作用域bean的生命周期中(一次申请或者一次会话),因而作用域bean还没有被创立,所以依赖就会呈现谬误。
解决这一问题的办法是应用代理,创立单例bean时注入依赖项的代理而不是依赖项自身(自身尚未被创立),该代理裸露与依赖项雷同的办法,因而单例bean会将它当作依赖项。当单例调用依赖项的办法时,真正的依赖项曾经被创立,代理会将调用交给真正的依赖项进行解决。
代理分为两类,一类是基于接口的代理(依赖项是一个接口);另一类是基于类的代理(依赖项是一个类);应用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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- an HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <!-- instructs the container to proxy the surrounding bean --> <aop:scoped-proxy/> </bean> <!-- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.something.SimpleUserService"> <!-- a reference to the proxied userPreferences bean --> <property name="userPreferences" ref="userPreferences"/> </bean></beans>
基于接口的代理
<!-- DefaultUserPreferences implements the UserPreferences interface --><bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/> <!--关掉基于类的代理即可--></bean><bean id="userManager" class="com.stuff.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>
1.6自定义bean的性质
1.6.1生命周期回调
回调由三种形式:1.基于注解的形式;2.基于xml配置文件;3.实现接口
实现接口方式会造成耦合,因而不举荐应用这种形式
三种形式的执行程序(回调办法名不同时每种配置的办法都执行一次。如果办法名雷同,只执行一次)为:
初始化回调:类结构器->注解——>接口——>xml配置
销毁回调:注解——>接口——>xml配置
三种办法的示例:
1.基于注解
public class User { @PostConstruct public void init() { //增加初始化动作 }}
基于xml
public class ExampleBean { public void init() { // do some initialization work }}
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
基于继承
public class AnotherExampleBean implements InitializingBean { @Override public void afterPropertiesSet() { // do some initialization work }}
Spring 容器保障在为 bean 提供所有依赖项后立刻调用配置的初始化回调,这意味着 AOP 拦截器等尚未利用于 bean。
1.6.2 ApplicationContextAware和BeanNameAware
能够通过实现ApplicationContextAware接口来获取ApplicationContext(容器),然而不举荐应用这种形式,因为会造成代码和Spring框架的耦合(代码晓得了Spring的存在),举荐应用主动拆卸形式获取。
主动拆卸形式
只须要在类中定义ApplicationContext属性,并在该属性上增加@Autowired标签,框架就会在容器中寻找与该属性相匹配的对象(byType,byName)
@AutowiredApplicationContext context;
ApplicationContextAware接口
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
还有一种形式是通过监听器获取ApplicationContext对象
https://blog.csdn.net/asd0001...
1.7 Bean继承
子bean能够继承父bean的范畴,构造函数参数值,属性值,并且能够抉择增加新值。为子bean指定的范畴,初始化办法,销毁办法和动态工厂办法都会笼罩父bean的设置。
如果将父bean设置为形象bean,那么该父bean将专门用于被继承,不能被实例化。
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <!--将该bean设置为形象类型,不能创立实例--> <property name="name" value="parent"/> <property name="age" value="1"/></bean><bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <!--指定了初始化办法,该指定会笼罩父bean中的设置--> <property name="name" value="override"/> <!-- age will inherit the value of 1 from the parent bean definition--></bean>