第一部分Ioc概述
Ioc的全名即为依赖注入也为控制反转,依赖注入的类型分为构造函数注入、属性注入。
构造函数注入
package com.baobaotao.domain; public class Org { private String id; private String name; public Org(String id,String name) { this.id = id; this.name = name; } }
属性注入
package com.baobaotao.domain; public class Org { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
一、资源访问利器
Resource
Resource作为Spring的资源加载器,拥有对应不同资源类型的实现类。Spring框架使用Resource加载各种资源,其结构如下:
ByteArrayResource:当需要从字节数组加载内容时,ByteArrayResource 是一个不错的选择。
ClassPathResource:类路径下的资源,资源以相对于类路径的方式表示,如:new ClassPathResource("com/baobaotao/beanfactory/bean.xml")。
FileSystemResource:文件系统资源,资源以文件系统路径的方式表示,如:new FileSystemResource("c:\\beans.xml")。
InputStreamResource:以输入流返回表示的资源。
ServletContextResource:为访问Web容器上下文中的资源而设计的类,负责从Web应用根目录中加载资源,它支持以流和Url的方式访问,在WAR解包的情况下,也可以通过File的方式访问,该类还可以直接从JAR包中访问资源。如Resource res3 =
new
ServletContextResource(application,
"/WEB-INF/classes/conf/file1.txt"
);
UrlResource:Url封装了java.net.URL,它使用户能够访问任何可以通过URL表示的资源,如文件系统的资源、HTTP资源、FTP资源等。
ResourceLoader
ResourceLoader 接口仅有一个getResource(String location)的方法,可以根据一个资源地址加载文件资源,不过,资源地址仅支持带资源类型前缀的表达式,不支持Ant风格的资源路径表达式。
ResourcePatternResolver 扩展了ResourceLoader 接口,定义了一个新的接口方法:getResources(String locationPattern),该方法支持带资源类型前缀及Ant风格的资源路径的表达式。
资源加载地址表达式
地址前缀 | 示例 | 对应资源类型 |
classpath: | classpath:com/baobaotao/beanfactory/bean.xml | 从类路径中加载资源,classpath:和classpath:等价,都是相对与类的跟路径。资源文件可以在标准的文件系统中,也可以在jar或者zip的类包中 |
file: | file:/conf/com/baobaotao/beanfactory/bean.xml | 使用URLResource从文件系统目录中装载资源,可采用绝对或相对路径。 |
http:// | http://www.baobaotao/resource/bean.xml | 使用UrlResource从web服务器中装载资源 |
ftp: | ftp://www.baobaotao.com/resource/bean.xml | 使用UrlResource从FTP服务器中装载资源 |
没有前缀 | com/baobaotao/beanfatory/beans.xml | 根据ApplicationContext具体实现类采用对应的类型的Resource |
资源加载器实例
public class Testnew{ public static void main(String[] args) throws IOException{ //以系统文件加载实例 String filepath = "F:/text.txt"; Resource fileresource=new FileSystemResource(filepath); //以class类路径加载实例 String classpath = "baobaotao-servlet.xml"; Resource classsource=new ClassPathResource(classpath); //PathMatchingResourceResolver类实现加载 String resolverpath = "classpath:baobaotao-servlet.xml"; ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources(resolverpath); System.out.println(resources.length); //DefaultResourceLoader获取文件数据 ResourceLoader loader = new DefaultResourceLoader(); Resource resource = loader.getResource("http://www.google.com.hk"); System.out.println(resource instanceof UrlResource); //true //注意这里前缀不能使用“classpath*:”,这样不能真正访问到对应的资源,exists()返回false resource = loader.getResource("classpath:test.txt"); System.out.println(resource instanceof ClassPathResource); //true resource = loader.getResource("test.txt"); System.out.println(resource instanceof ClassPathResource); //true } }
Bean工厂和应用上下文
BeanFactory
BeanFactory作为类工厂,用来创建Spring的所有实例。BeanFactory作为最底层的接口,它有多重实现方式。它的主要实现类结构如下:
BeanFactory实例
public class BeanFactoryTest { public static void main(String[] args) throws IOException{ String classpath = "applicationContext.xml"; Resource classsource=new ClassPathResource(classpath); BeanFactory factory = new XmlBeanFactory(classsource); Org org = factory.getBean("org", Org.class); System.out.println(org.getName()); } }
BeanFactory中有一个主要的方法getBean(),用来获取Bean实例。IoC容器需要获取配置信息才能转化,在生成IoC容器时,需要Resource获取文件资源来构建BeanFactory。
ApplicationContext
ApplicationContext继承了BeanFactory的功能,并扩展了其他功能,如果BeanFactory是Spring的心脏,ApplicationContext就是完整的身躯。Beanfactory以编程的方式实现功能,ApplicationContext以配置的形式实现功能。ApplicationContext的体系结构为:
主要以两种方式实现获取实例:FileSystemApplicationContext和ClassPathXmlApplicationContext。
ClassPathXmlApplicationContext:用来获取类路径下的配置文件,如ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml")。如果有多个配置文件可以使用如ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"com/baobaotao/context/beans1.xml","com/baobaotao/context/beans2.xml"})。
FileSystemXmlApplicationContext:用来获取文件系统路径下的配置文件,如ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml")。如果有多个配置文件可以使用如FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{"com/baobaotao/context/beans1.xml","com/baobaotao/context/beans2.xml"})。
BeanFactory在初始化时并没有实例化Bean,而是在第一次调用时才会初始化。而ApplicationContext在初始化时就会进行实例化所有的Bean。
实例如下:
public class ApplicationContextTest { public static void main(String[] args){ ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[]{"beans1.xml","beans2.xml"}); User user = (User) ctx.getBean("user"); ApplicationContext ctxn = new FileSystemXmlApplicationContext( new String[]{"com/baobaotao/context/beans1.xml", "com/baobaotao/context/beans2.xml"}); Org org = (Org) ctxn.getBean("org"); } }
AnnottationConfigApplicationcontext
在Spring3.0中已经添加类注解的配置方式,将某一个类转换为一个配置文件,其中生成的对象即为Bean实例。类注解的配置方式需要通过@Configuration来标注某一个类为配置信息容器,可以替代配置文件。基于Java的配置类加载Spring的应用上下文需要使用特殊的类加载器AnnottationConfigApplicationcontext。
实例如下:
1、基于java类的配置信息
package testpackage; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baobaotao.service.AccountService; @Configuration public class AppConf { @Bean(name="accountService") public AccountService accountService(){ AccountService accountService = new AccountService(); return accountService; } }
2、配置类加载容器
package testpackage; import javax.security.auth.login.AppConfigurationEntry; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.baobaotao.service.AccountService; public class ConfigTest { public static void main(String[] args){ //将Configuration配置类注册到容器中 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConf.class); AccountService accountService = applicationContext.getBean("accountService",AccountService.class); //如果有多个Configuration配置类注册到容器 AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext(); applicationContext1.register(AppConf.class); applicationContext1.register(DaoConf.class); applicationContext1.refresh(); AccountService accountService1 = applicationContext.getBean("accountService",AccountService.class); } }
WebApplicationContext
WebApplicationContext是为Web准备的管理容器,WebApplicationContext允许从web根目录中加载配置文件完成初始化工作。从WebApplication中获取ServletContext引用,整个web应用上下文对象将作为属性放置在Servlet中,以便web应用环境可以访问到Spring上下文。WebApplicationContex的启动需要有ServletContext实例,也就是说需要有Web容器才能启动初始化工作。目前需要配置监听器ServletContextListener来启动WebApplicationContext。配置如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>baobaotao</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <!-- 从类路径下面加载Spring配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 加载配置文件的监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
通常情况下WebApplicationContext的使用需要配置日志功能,而日志配置的加载需要用到Log4jConfigServlet或Log4jConfigListener监听器。配置如下:
<!-- Log4J信息配置 --> <context-param> <param-name>log4jConfigServlet</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> <servlet> <servlet-name>log4jConfigServlet</servlet-name> <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet>
二、Bean生命周期
BeanFactory中的Bean生命周期
整个Bean的生命周期如下图所示:
具体过程如下:
(1)当调用者调用getBean(beanName)向容器请求某一个Bean时,如果容器注册了InstantiationAwareBeanPostProcessor接口,则在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法。
(2)根据配置情况调用Bean的构造函数或工厂方法实例化Bean。
(3)如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessBeforeInstantiation()方法,对实例化的对象进行一些初始化设置。
(4)如果Bean 配置了属性信息,那么容器在这一步将配置的属性设置到Bean对应的属性中,在设置属性之前将先调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法。
(5)调用Bean中的setXxx()方法设置属性。
(6)如果Bean实现了BeanNameAware接口,则会调用该接口的setBeanName()方法,将配置文件中该Bean对应的名称设置到Bean中。
(7)如果Bean实现了BeanFactoryAware接口,则会调用该接口的setBeanFactory()方法,将BeanFactory容器实例设置到Bean中。
(8)如果BeanFactory装配了BeanPostProcessor后处理器,则将调用BeanPostProcessor接口的Object postProcessBeforeInitialization方法对Bean进行加工操作。其中,传入的bean是当前正在处理的bean,而beanName是当前Bean在配置文件中的配置名,返回加工处理后的bean。用户可以使用该方法对某些Bean进行特殊的处理。Spring容器所提供的AOP、动态代理等功能都通过BeanPostProcessor实现。
(9)如果Bean实现了InitializingBean接口,则将调用该接口的afterPropertiesSet()方法。
(10)如果在配置文件的bean标签中通过init-method属性指定了初始化方法,则会执行这个方法。
(11)如果BeanFactory装配了BeanPostProcessor后处理器,在这里则会执行Object postProcessAfterInitialization()方法对Bean进行加工处理。
BeanPostProcessor后处理器定义了两个方法,分别是postProcessBeforeInitialization()和postProcessAfterInitialization(),分别在第8步和在此处调用。
(12)如果在配置文件中指定了bean的scope=”prototype”,意味着配置的这个bean是多例的,每次获取该bean都会返回一个新实例化的bean,所以在这一步之后Spring容器不再管理多例的Bean,直接将当前生成的实例返回给用户。
对于scope=”singleton”的Bean(默认情况),意味着这个Bean是单例的,就需要把这个Bean缓存到在Spring IOC容器中,用户每次获取时都从这个容器中获取,并且Spring会对这些Bean进行后续的生命周期管理。
(13)对于单例的Bean,容器关闭时,会触发Spring对Bean的后续生命周期的管理工作。如果Bean实现了DisposableBean接口,则会调用该接口的destroy()方法,在这里可以进行释放资源、记录日志等操作。
(14)对于单例的Bean,如果在配置文件中指定了destroy-method属性,Spring则会执行这个属性配置的方法,完成Bean资源释放等操作。
由于以上步骤比较复杂,以下是简短的处理过程介绍:
总结以上过程主要分为以下几步:
Bean自身的方法:构造方法、设置属性的set方法、init-method属性和destroy-method属性。
Bean级的生命周期方法:BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean直接实现。
容器级生命周期接口方法:包括InstantiationAwareBeanPostProcessor和BeanPostProcessor两个接口。
工厂后处理器接口方法:包括AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等类。
实例如下:
第二部分 Ioc容器装配Bean
一个java实例就是一个<bean>,bean的配置为<bean id="org" name="org" class="com.baobaotao.domain.Org">。id为bean的唯一标识,不能在容器中重复,name为bean的名称可以重复,class为实例创建所使用的java类。
一、依赖注入
1、属性注入
属性值为对象类型的配置文件要使用ref引入属性值。
package com.baobaotao.service; import com.baobaotao.dao.OrgDao; import com.baobaotao.domain.Org; public class OrgService { private OrgDao orgDao; public OrgDao getOrgDao() { return orgDao; } public void setOrgDao(OrgDao orgDao) { this.orgDao = orgDao; } public Org findAllOrg(String eid){ Org result = orgDao.findAllOrg(eid); return result; } }
<bean id="orgService" class="com.baobaotao.service.OrgService"> <property name="orgDao" ref="orgDao"></property> </bean>
属性值为普通数据类型要使用value引入属性值
package com.baobaotao.domain; public class Org { private String id; private String name; public Org(String id,String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
<bean name="org" class="com.baobaotao.domain.Org"> <property name="id" value="12345"/> <property name="name" value="青岛大学"/> </bean>
2、构造方法注入
分为按类型匹配、按索引匹配和按名称匹配。在构造属性中如果引入的是普通数据类型使用value引入值,如果是对象类型使用ref引入。
按索引匹配
package com.baobaotao.dao; import com.baobaotao.domain.Org; public class OrgDao { private Org org; public OrgDao(Org org) { this.org = org; } public Org findAllOrg(String eid){ Org org_new = org; return org_new; } }
<bean name="orgDao" class="com.baobaotao.dao.OrgDao"> <constructor-arg index="0" ref="org"></constructor-arg> </bean>
按名称匹配
package com.baobaotao.domain; public class Org { private String id; private String name; public Org(String id,String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
<bean name="org" class="com.baobaotao.domain.Org"> <constructor-arg name="id" value="12345"></constructor-arg> <constructor-arg name="name" value="青岛大学"></constructor-arg> </bean>
3、工厂方法注入
import java.util.HashMap; import java.util.Map; import com.baobaotao.domain.User; public class FactoryTest { private static Map<Integer, User> map = new HashMap<Integer,User>(); static{ map.put(1, new User("Honda","11111")); map.put(2, new User("Audi","22222")); map.put(3, new User("BMW","33333")); } public static User getUser(int id){ return map.get(id); } }
<bean id="user" class="testpackage.FactoryTest" factory-method="getUser"> <constructor-arg value="3"></constructor-arg> </bean>
利用静态工厂方法定义的bean item种, class属性不在是bean的全类名, 而是静态工厂的全类名, 而且还需要指定工厂里的 getBean 静态方法名字和参数。
二、注入参数详解
为实例注入值时分为两类,一类是注入普通数据类型如String、int等,一类是注入其他Bean实例。
1、注入普通数据类型
第一种
<bean name="org" class="com.baobaotao.domain.Org"> <property name="id" value="12345"></property> <property name="name" value="青岛大学"></property> </bean>
第二种
<bean name="org" class="com.baobaotao.domain.Org"> <property name="id"> <value>"12345"</value></property> <property name="name"> <value>"青岛大学"</value></property> </bean>
2、引入其他Bean
在引入其他bean时,主要分为引入同一容器或父容器的bean,引入本地容器的bean,引入父容器的bean。<ref>标签可以通过三个属性进行区分
bean:引用同一容器或父容器bean。
parent:引入父容器bean。
local:引入同一容器bean。
<!--bean1.xml--> <bean name="orgDao" class="com.baobaotao.dao.OrgDao"> </bean> <!--bean2.xml,bean2.xml是bean1的子容器--> <bean name="orgDao" class="com.baobaotao.dao.OrgDao"> </bean> <bean name="orgService" class="com.baobaotao.service.OrgService"> <property name="orgDao"><ref parent="orgDao"/></property> </bean> <bean name="orgService1" class="com.baobaotao.service.OrgService"> <property name="orgDao"><ref bean="orgDao"/></property> </bean> <bean name="orgController" class="com.baobaotao.controller.OrgController"> <property name="orgService"> <ref local="orgService"/></property> </bean>
3、内部Bean
如果该实例自在一个bean中引用,在其他任何地方没有被引用,可以使用内部bean,实例如下:
<bean name="orgDao" class="com.baobaotao.dao.OrgDao"> <property name="org"> <bean class="com.baobaotao.domain.Org"> <property name="id" value="123"/> <property name="name" value="青岛大学"/> </bean> </property> </bean>
4、集合类型属性
如果集合中的元素之为普通数据类型就使用<value>标签引用,如果元素为其他的bean就用<ref>标签引用
list类型
<bean name="org" class="com.baobaotao.domain.Org"> <property name="list"> <list> <value>12345</value> <value>67890</value> </list> </property> </bean>
set类型
<bean name="org" class="com.baobaotao.domain.Org"> <property name="set"> <set> <value>12345</value> <value>67890</value> </set> </property> </bean>
map类型
<bean name="org" class="com.baobaotao.domain.Org"> <property name="map"> <map> <entry> <key><value>12345</value></key> <value>67890</value> </key> </map> </property> </bean>
5、简化配置方式
在对属性值注入时,可以对标签进行简化操作。如
<property name=""><value></value></property>改为<property name="" value=""></property>
<property name=""><ref bean=""></ref></property>改为<property name="" ref=""></property>
<ref local=""></ref>和<ref parent=""></ref>没有对应的简化
6、使用P命名空间简化配置
<bean name="org" class="com.baobaotao.domain.Org" p:id="12345" p:name="青岛大学" /> <bean name="orgDao" class="com.baobaotao.dao.OrgDao" p:org-ref="org" />
7、继承
如果多个实例拥有多个相同的属性,可以为它们设置一个父实例。例如
<bean name="org" class="com.baobaotao.domain.Org" abstract="true"> <property name="id" value="12345"></property> <property name="name" value="青岛大学"></property> </bean> <bean name="org1" parent="org"> <property name="id" value="12345"></property> </bean> <bean name="org2" parent="org"> <property name="name" value="青岛大学"></property> </bean>
在父实例的<bean>标签中设置abstract属性,在子实例的<bean>标签中设置parent属性。
8、整合多个配置文件
在一个大型的应用系统中可能存在多个xml配置文件,要想合并多个配置文件,主要有两种方法。
方法一:在web.xml配置信息中以逗号隔开,进行加载
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml, classpath:applicationContext.xml1, classpath:applicationContext.xml2 </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
方法二:在其中一个xml配置文件中进行<import>引入
<import resource="classpath:applicationContext.xml1"/> <import resource="classpath:applicationContext.xml2"/> <import resource="classpath:applicationContext.xml3"/>
三、Bean作用域
每一个Bean都会有一个作用范围,如果超出这个范围,就会生成新的Bean。现在Spring分为5种作用域。
singleton:只会加载容器时创建一次实例。如果不指定Bean的作用域,Spring默认使用singleton作用域。在容器启动时会将所有的Bean进行实例化,这样虽然需要耗费一定时间,但是可以带来两个好处:1、提前了解某些实例的配置问题。2、将加载好的实例放到内存可以提高调用的效率。
<bean name="orgController" class="com.baobaotao.controller.OrgController" scope="singleton"> <property name="orgService" value="orgService"></property> </bean>
prototype:每次通过容器获取Bean实例时都会重新创建一个新的实例。prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。在Spring的配置中可以通过增加RequestContextListener监听器来负责request作用域生效。但是在Srping2.0之前是通过RequestContextFilter来监听。
<filter> <filter-name>requestContextFilter</filter-name> <filter-class> org.springframework.web.filter.RequestContextListener </filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<bean id="loginAction" class="com.abc.LoginAction" scope="request" />
session:对于每次HTTP Session,也就是横跨整个Session中的所有HTTP请求。使用session定义的Bean都产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。
作用域依赖问题
如果一个singleton作用域的Bean引用其他作用域的实例,可以通过动态代理获取到响应的实例。如boss实例引用request作用域的car实例。
<bean id="car" class="com.abc.Car" scope="request" > <aop:scoped-proxy/> <bean id="boss" class="com.abc.Boss" scope="singleton" />
boss实例会根据所在的HTTP请求查找响应HTTP中的实例car。
三、基于注解的配置
1、使用注解定义Bean
Spring的Xml配置是将class类与Bean信息进行分离,采用注解的方式是在class类上进行标注。
spring没有采用约定优于配置的策略,spring要求显示指定搜索哪些路径下的Java文件。spring容器在在启动时会扫描指定包中的java类,将会把标注注解的的java类全部注册成spring Bean。
@Component:标准一个普通的spring Bean类。
@Controller:标注一个控制器组件类。
@Service:标注一个业务逻辑组件类。
@Repository:标注一个DAO组件类
package com.baobaotao.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.baobaotao.domain.User; import com.baobaotao.service.UserService; @Controller public class UserController{ @Autowired private UserService userService; @RequestMapping(value="login") public String login(HttpServletRequest request,User user){ System.out.println("=========aaaaaaaa===="); String result = userService.login(user.getUserName(), user.getPassword()); return "success"; } } package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Service public class UserService{ @Autowired private UserDao userDao; public String login(String name,String password) { String pass = userDao.findUserByName(name); return "success"; } } package com.baobaotao.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao{ public String findUserByName(String userName) { return "abc"; } } package com.baobaotao.dao; import com.baobaotao.domain.Org; @Component("orgDao") public class OrgDao { private Org org; public void OrgDao(Org org) { this.org = org; } public Org findAllOrg(String eid){ Org org_new = org; return org_new; } }
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:c="http://www.springframework.org/schema/c" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.baobaotao.dao"></context:component-scan> <context:component-scan base-package="com.baobaotao.service"></context:component-scan> <context:component-scan base-package="com.baobaotao.controller"></context:component-scan> </beans>
通过标签<context:component-scan>来扫描指定包下面的注解java类,同时可以添加过滤条件查找基包下的子包中的java类进行实例化。如
<context:component-scan base-package="com.baobaotao.controller" resource-pattern="ann/*.class"> </context:component-scan>
该扫描标签主要是扫描基包com.baobaotao.controller下的ann子包中的java类进行实例化。
2、@Autowired自动装配Bean
@Autowired自动注入:
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Service(request=true) public class UserService{ @Autowired private UserDao userDao; public String login(String name,String password) { String pass = userDao.findUserByName(name); return "success"; } }
@Autowired在注入实例时会查找复合条件的Bean进行注入,默认会按照类型匹配的方式进行匹配。如果找不到匹配的Bean,request=true时会抛出异常,如果request=false,则不会抛出异常。但是在按照类型匹配时如果有两个Bean都为同一类型时,只能显示的标注注入名称。如现在UserDao类有两个名为userDao和otherUserDao,此时需要使用@Qualifilter进行显示说明。
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Service(request=true) public class UserService{ @Autowired @Qualifier("userDao") private UserDao userDao; public String login(String name,String password) { String pass = userDao.findUserByName(name); return "success"; } }
@Autowired对方法类自动注入:
可以使用@Autowired对方法中的参数进行注入,如果参数中的类只有一个实例可以在方法上方直接使用@Autowired,如果只有一个参数,但参数的类型有多个名称,可以使用@Qualifier进行显示标注。如
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Service(request=true) public class UserService{ private UserDao userDao; private OrgDao orgDao @Autowired @Qualifier("userDao") public String login(UserDao userDao) { String pass = userDao.findUserByName(name); return "success"; } @Autowired public String check(@Qualifier("userDao") UserDao userDao,@Qualifier("oserDao")OrgDao orgDao) { String pass = userDao.findUserByName(name); return "success"; } }
3、@Resource和@Inject
@Resource是通过名称进行注入,需要在注入时显示的表明名称,如果没有名称属性,就会选择变量名称作为注入名称,但是没有request参数判断注入正确性。但是@Inject的使用方法与@Autowired相同。如
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Service(request=true) public class UserService{ @Resource("userDao") private UserDao userDao; public String login(String name,String password) { String pass = userDao.findUserByName(name); return "success"; } }
@Scope作用范围
在每个类的上方通过标注scope可以指定该类实例的作用范围。
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Scope("singleton") @Service(request=true) public class UserService{ @Resource("userDao") private UserDao userDao; public String login(String name,String password) { String pass = userDao.findUserByName(name); return "success"; } }
四、基于Java类的配置
使用java类提供Bean信息:
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Configuration public class Appl{ @Bean("userDao") public UserDao userDao() { UserDao userDao = new UserDao(); return userDao; } }
上述实例中@Configuration会将Appl类注解为一个Spring容器,容器中的@Bean会将每个方法返回的实例标注为一个Bean,@Bean如果没有属性值,默认Bean的名称为类名。上述的实例相当于如下xml配置。
<bean id="userDao" class="com.baobaotao.dao.UserDao"/>
如果多个Bean在不同的Configuration配置类中,要想引用不同类之间的实例,可以通过@Autowired进行自动装配,因为@Configuration在注解时已经注入@Component注解,可以作为一个普通的实例进行调用。如
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Configuration public class DaoConfig{ @Bean("userDao")
@Scope("singleton") public UserDao userDao() { UserDao userDao = new UserDao(); return userDao; } }
package com.baobaotao.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baobaotao.dao.UserDao; @Configuration public class ServiceConfig{ @Autowired DaoConfig daoConfig; @Bean("userService") public UserService userService() { UserService userService = new UserService(); userService.setUserDao(daoConfig.userDao); return userService; } }
基于java类的配置信息启动Spring容器
1、启动Spring容器可以使用代码启动
package testpackage; import javax.security.auth.login.AppConfigurationEntry; import org.apache.catalina.core.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.baobaotao.service.AccountService; public class JavaConfigTest { public static void main(String[] args){ //将Configuration配置类注册到容器中 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConf.class); AccountService accountService = applicationContext.getBean(AccountService.class); //如果有多个Configuration配置类注册到容器 AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext(); applicationContext1.register(AppConf.class); applicationContext1.register(DaoConf.class); applicationContext1.refresh(); AccountService accountService1 = applicationContext.getBean(AccountService.class); } }
如果有多个Configuration类需要注册到容器,可以将多个Configuration类注册注册到容器中。也可以在一个Configuration类中引入其他的多个Configuration类,如下实例
package testpackage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import com.baobaotao.dao.UserDao; @Configuration @Import(AppConf.class) public class DaoConf { @Bean public UserDao userDao(){ UserDao userDao = new UserDao(); return userDao; } }
2、通过XML配置加载Configuration中的Bean
如果在Configuration类中注册了多个Bean,可以将Configuration类放到配置文件中,在加载配置文件时将Bean添加到容器中。实例如下
package testpackage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import com.baobaotao.dao.UserDao; @Configuration @Import(AppConf.class) public class DaoConf { @Bean public UserDao userDao(){ UserDao userDao = new UserDao(); return userDao; } }
xmlns:c="http://www.springframework.org/schema/c" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="testpackage" resource-pattern="DaoConf.class"></context:component-scan> </beans>
3、通过Configuration加载XML中的Bean
除了java类加载Configuration类将Bean添加到容器,将Configuration类放到xml配置文件中,在加载配置文件时将Bean添加到容器外,可以将xml配置文件中的bean加载到Configuration类,在java类启动容器时,加载Configuration类中的所有容器,实例如下
xmlns:c="http://www.springframework.org/schema/c" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.baobaotao.dao"></context:component-scan> <context:component-scan base-package="com.baobaotao.service"></context:component-scan> <context:component-scan base-package="com.baobaotao.controller"></context:component-scan> <bean name="org" class="com.baobaotao.domain.Org"> <constructor-arg name="id" value="12345"></constructor-arg> <constructor-arg name="name" value="青岛大学"></constructor-arg> </bean> <bean name="orgDao" class="com.baobaotao.dao.OrgDao"> <constructor-arg index="0" ref="org"></constructor-arg> </bean> <bean name="orgService" class="com.baobaotao.service.OrgService"> <property name="orgDao" value="orgDao"></property> </bean> <bean name="orgController" class="com.baobaotao.controller.OrgController"> <property name="orgService" value="orgService"></property> </bean> </beans>
package testpackage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; import com.baobaotao.dao.UserDao; @Configuration @ImportResource("classpath:applicationContext.xml") public class DaoConf { @Bean public UserDao userDao(){ UserDao userDao = new UserDao(); return userDao; } }
@Configuration:将java类标注为Spring容器
@Import:引入带有@Configuration的java类。
@ImportResource:引入spring配置文件.xml
第三部分 容器的进阶
一、引用外部属性文件
为了能够将外部的文件信息引入到容器中,Spring提供了多种方式将外部信息加载到容器中。属性文件中的每个属性的属性名和属性值一一对应。属性文件如下:
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc\:mysql\://localhost\:3306/shop user=root password=root
1、PropertyPlaceholderConfigurer引用外部属性文件:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <!-- PropertyPlaceholderConfigurer类中有个locations属性,接收的是一个数组,即我们可以在下面配好多个properties文件 --> <array> <value>classpath:conn.properties</value> </array> </property> </bean>
<bean id="dataSource" class="${dataSource}"> <!-- 这些配置Spring在启动时会去conn.properties中找 -->
<property name="driverClass" value="${driverClass}" />
<property name="jdbcUrl" value="${jdbcUrl}" />
<property name="user" value="${user}" />
<property name="password" value="${password}" />
</bean>
2、使用context:property-placeholde引用属性文件:
<context:property-placeholder location="classpath:conn.properties"/><!-- 加载配置文件 --> <!-- com.mchange.v2.c3p0.ComboPooledDataSource类在c3p0-0.9.5.1.jar包的com.mchange.v2.c3p0包中 --> <bean id="dataSource" class="${dataSource}"> <!-- 这些配置Spring在启动时会去conn.properties中找 --> <property name="driverClass" value="${driverClass}" /> <property name="jdbcUrl" value="${jdbcUrl}" /> <property name="user" value="${user}" /> <property name="password" value="${password}" /> </bean>
3、通过注解引入属性文件
package testpackage; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration @ImportResource("classpath:common.properties") public class MyDataResource { @Value("${userName}") private String userName; public String getUserName(){ return this.userName; } }
二、容器事件
容器事件中主要包括三个实体:
1、事件源:事件的产生者。
2、事件监听器注册表:所有的监听器都要保存在注册表中。
3、事件广播器:事件与事件注册表之间的桥梁。
事件类
ApplicationEvent继承了了普通事件接口EventObject,ApplicationEvent拥有两个子类:ApplicationContextEvent和RequestHandleEvent。
事件监听器接口
ApplicationListener接口继承了EventListener,并定义了一个方法onApplicationEvent(E event),该方法接收事件进行处理。在Spring3.0之后新增SmartApplicationListener作为ApplicationListener的子类。
事件广播器
事件广播器SimpleApplicationEventMulicaster类继承AbstractApplicationEventMulticaster接口,AbstractApplicationEventMulticaster接口继承ApplicationEvent接口。
实例
定义事件类
public class SmsSendEvent extends ApplicationContextEvent { private String target; public SmsSendEvent(ApplicationContext source, String target) { super(source); this.target = target; } public String getTarget() { return target; } }
定义监听器
public class SmsSendListener implements ApplicationListener<SmsSendEvent> { public void onApplicationEvent(SmsSendEvent event) { System.out.println("向 "+event.getTarget()+" 发送短信"); } }
实现ApplicationContextAware 接口类,并具有发布事件的能力
public class SmsSender implements ApplicationContextAware { private ApplicationContext context; /** * 容器启动时,注入容器实例 * * @param applicationContext * @throws BeansException */ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } /** * 发送短信 * @param target */ public void send(String target) { System.out.println("准备发送短信"); SmsSendEvent event = new SmsSendEvent(context, target); context.publishEvent(event);//向容器中所有注册了该事件的监听器发送该事件 } }
bean 配置
<?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 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 事件监听器--> <bean class="net.deniro.spring4.event.SmsSendListener"/> <bean id="SmsSender" class="net.deniro.spring4.event.SmsSender"/> </beans>
测试监听器
public static void main(String[] args){ SmsSender sender= (SmsSender) context.getBean("SmsSender"); sender.send("1892929xxxx"); }