1Spring整合Struts2
web应用中配置spring容器
首先在Struts2中整合Spring需要另外加入的包:
struts2-spring-plugin-2.1.8.1.jar,spring.jar,log4j-1.2.15.jar
其中spring.jar是从下载的Spring包中的dist目录下。
struts2-spring-plugin.jar是在Struts2的包的lib目录下。如果缺少该包tomcat会有error filterStart异常。
一般加入struts2的需要的包和上面的包就可以了,如果启动报aspectj方面的错则还需要加上aspectjweaver.jar。
然后在web.xml中进行配置让其随web服务启动而创建。
对于使用Spring的Web应用,无需手动创建Spring容器,而是通过配置文件,声明式地创建Spring容器。因此在web应用中创建Spring容器有如下两种方式:
一种是直接在web.xml文件中配置创建Spring容器。
另一种是利用第三方MVC框架的扩展点,创建Spring容器。
我们采用第一种方式:
Spring提供了一个ServletContextListener的一个实现类ContextLoaderListener,该类可以作为Listener使用,它会在创建时自动查找WEB-INF下的applicationContext.xml文件。因此如果只有一个applicationContext.xml配置文件,则只需要在web.xml文件中增加如下配置片段即可。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
如果有多个配置文件需要载入,可考虑在web.xml中使用<context-param.../>元素来确定配置文件的文件名。ContextLoaderListener加载时,会查找名为contextConfigLocation的初始化参数。如:
<context-param>
<!--参数名为contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--多个配置文件之间以逗号隔开,/WEB-INF/conf/spring/applicationContext*.xml表示加载spring下所有以applicationContext开头的xml文件-->
<param-value>/WEB-INF/conf/spring/applicationContext*.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!--使用ContextLoaderListener初始化Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
*****************
如果没有使用contextConfigLocation指定配置文件,则Spring自动查找/WEB-INF/applicationContext.xml配置文件;如果有contextConfigLocation,则利用该参数确定的配置文件,如果无法找到合适的配置文件,Spring将无法正常初始化。
*****************
关于applicationContext.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 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="myService" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
<property name="ms" ref="myService"/>
</bean>
</beans>
applicationContext.xml中用于配置<bean>进行依赖注入。
Spring根据指定配置文件创建WebApplicationContext对象,并将其保存到Web应用的ServletContext中,大部分情况下,应用中Bean无法感受到ApplicationContext的存在,只要利用ApplicationContext的IoC即可。如果需要在应用中获得ApplicationContext对象,可以通过如下代码获取:
WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(servletContext);
Spring还提供了一个特殊的类ContextLoaderServlet,该Servlet在启动时,也会自动查找WEB-INF路径下的applicationContext.xml文件,为了让ContextLoaderServlet随应用启动而启动,应将此Servlet配置成load-on-startup的Servlet。如果只有一个配置文件为applicationContext.xml则在web.xml文件中增加的配置为:
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<load-on-startup>1</load-on-startup>的值小一点,有助于ApplicationContext更快的初始化。
该Servlet用于提供后台服务,主要用于创建Spring容器,无须相应客户请求,因此无需为它配置<servlet-mapping/>元素。如果有多个配置文件或配置文件名不是applicationContext.xml则一样使用上面的<context-param/>元素来确定多个配置文件。
由于Servlet规范,Listener比Servlet优先加载,因此,采用ContextLoaderListener创建ApplicationContext的时机更早。
经过上面的配置,Spring和Struts2已经整合到一起。下面是一个应用整合后的实例。
Spring和Struts整合后的实例:
以前控制器是如何获得业务逻辑组件的呢?是通过new关键字创建业务逻辑组件,然后调用业务逻辑组件的方法,根据业务逻辑组件方法的返回值来确定结果,最后导向视图。
控制器如果访问到Spring容器中的业务逻辑组件(就是模型)呢?为了让Action(就是控制器)访问Spring的业务逻辑组件,有两种策略:
一种是Spring容器负责管理控制器Action,并利用依赖注入为控制器注入业务逻辑组件。
另一种是利用Spring的自动装配,Action将会自动从Spring容器中获取所需要的业务逻辑组件。
***********使用让Spring容器管理控制器通过为Action注入**************
Spring插件提供了一种伪Action,当我们在struts.xml文件中配置Action时,通常需要制定class属性,该属性就是用于创建Action实例的实现类。当Spring插件允许我们制定class属性时,不再指定Action的实际实现类,而是指定Spring容器中的BeanID。这种整合策略的关键:当struts2将请求转发给指定的Action时,Struts2中的该Action只是一个"傀儡",它只是一个代号,并没有指定实际的实现类,而隐藏在该Action下的Spring容器中的Action实例,才是真正处理用户请求的控制器。下面我们来进行演示:
首先我们书写一个login.jsp,其内容是输入用户名/密码进行登录。
然后我们书写一个LoginAction用于对前面登录的验证:
package lee;
public class LoginAction{
private String username;
private String password;
private String tip;
//MyService是一个业务逻辑组件
private MyService ms;
public void setMs(MyService ms){
this.ms=ms;
}
public MyService getMs(MyService ms){
return ms;
}
public void setUsername(String username){
this.username=username;
}
public String getUsername(String username){
return username;
}
public void setPassword(String password){
this.password=password;
}
public String getPassword(String Password){
return Password;
}
public void setTip(String tip){
this.tip=tip;
}
public String getTip(String tip){
return tip;
}
public String execute() throws Exception{
if(ms.valid(username,password)){
setTip("哈哈,整合成功了");
return "success";
}
else{
return "error";
}
}
}
上面的程序提供了一个MyService组件,并为该组件提供了setter和getter方法,通过setter方法,就可以让Spring管理Action和MyService组件的依赖关系,避免控制器和业务组件之间的硬编码耦合(采用new创建)。在execute()方法我们调用MyService组件的valid()来进行验证。下面我们在applicationContext.xml文件中进行配置,将LoginActin所需要的组件MyService组件注入进来。
<?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-2.0.xsd">
<bean id="myService" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
<property name="ms" ref="myService"/>
</bean>
</beans>
在Struts2的中如何使用被Spring管理的这个Action。那么在struts.xml文件中的配置如下:
<package name="lee" extends="struts-default">
<action name="Login" class="loginAction">
<result name="error">/error.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
</package>
可以看到class的取值不再是一个实际的实现类,而是Spring容器的BeanID:loginAction。这样被Sprign容器管理的loginAction就会被使用到。
在开发中所有的业务组件都是有接口和实现类两部分组成。上例中使用的MyService业务组件我们分成MyService接口和MyService的实现类MyServiceImpl。
MyService接口类如下:
package lee;
public interface MyService{
public boolean valid(String username,String password);
}
实现MyService接口类的MyServiceImpl如下:
package lee;
public class MyServiceImpl implements MyService{
//实现接口的valid方法
public boolean valid(String username,String password){
if(username.equals("kk") && password.equals("123")){
return true;
}
return false;
}
}
将该业务组件部署到Spring容器中,配置业务逻辑组件的片段如下:
<bean id="myService" class="lee.MyServiceImpl"/>
然后就可以在applicationContext.xml中将此业务逻辑组件注入到Action的中。如
<property name="ms" ref="myService"/>
上面实例是采用Spring容器管理Action控制器,它也有一些不足之处:
Spring管理action,必须将所有的Action配置在Spring容器中,而struts.xml文件中还需要配置一个"伪Action",从而导致配置文件臃肿。
Action的业务逻辑组件接容器的注入,将降低可读性。
***********使用自动装配******************
在这种策略下,Action还是由Spring插件创建,Spring在创建Action实例时,利用Spring的自动装配策略,将对应的业务逻辑组件注入Action实例,这种整合策略配置文件简单,但控制器和业务逻辑组件耦合较高。
采用自动装配,我们还是采用传统方式配置struts2的Action,配置Action时一样指定其具体的实现类。
所谓自动装配,即让spring自动管理Bean与Bean之间的依赖关系,无须使用ref显式指定依赖Bean,Spring容器会自动检查XML配置文件内容,为主调Bean注入依赖Bean,自动装配可以减少配置文件的工作量,但会降低依赖关系的透明性和清晰性。如果我们不指定自动装配,系统默认使用按name自动装配。
采用Spring插件的自动装配策略需要在Struts2通过struts.objectFactory.spring.autoWire常量指定,该常量可以接受如下几个值:
name,根据属性名自动装配。Spring插件会查找容器中全部Bean,找出其中id属性与Action所需的业务逻辑组件同名的Bean,将该Bean实例注入到Action实例中。
type,根据属性类型自动装配,Spring插件会查找容器中全部Bean,找出其类型恰好与Action所需的业务逻辑组件相同的Bean,将该Bean实例注入到Action实例。如果有多个这样的Bean,就抛出一个致命的异常,如果没有匹配的Bean,则什么都不发生,属性不会被设置。
auto,Spring插件会自动见此需要使用那种自动装配方式。
constructor,与type类似,区别是constructor使用构造器来构造注入所需参数,而不是使用设值注入方式。
对于上面的Spring容器管理Action实例,如果换成Spring自动装配,首先需要在struts.xml为struts.objectFactory.spring.autoWire常量指定值,Action的配置仍然采用struts2的方式指定具体实现类。
<constant name="struts.objectFactory" value="spring"/>
<constant name="struts.objectFactory.spring.autoWire" value="type"/>
<package name="lee" extends="struts-default">
<action name="Login" class="lee.LoginAction">
<result name="error">/error.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
</package>
我们选择根据属性名进行自动装配,那么我们在applicationContext.xml中需要配置与逻辑组件名(private MyService ms;)相同的BeanID。如:
<bean id="ms" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
</bean>
**************************
基于注释的配置
基于注释的配置越来越流行,Spring 2.5顺应了这种趋势,提供了完全基于注释配置Bean,装配Bean的功能。可以使用基于注释的Spring IoC替换原来基于XML的配置。
注释配置相对于XML配置具有很多的优势:
它可以充分利用java的反射机制获取类结构的信息,这些信息可以有效减少配置的工作。
注释和java代码位于一个文件中,有助于增强程序的内聚性。
Spring不但支持自己定义的@Autowired(byType自动装配)和(@Qualifier("name")精确装配byName)的注释,还支持由JSR-250规范定义的注释,它们分别是@Resource,@PostConstruct以及@PerDestroy。
@Resource
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,@Resource默认按byName自动注入。@Resource有两个属性name和type,Spring将@Resource注释的name属性解析为Bean的名字,而type属性则解析为Bean的类型,所以如果@Resource使用了name属性,则使用byName的自动注入策略,而使用来type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,将通过反射机制使用byName自动注入策略。
@Resource注释位于Spring发布包的lib/j2ee/common-annotations.jar类包中,因此使用之前必须将其加入到项目的类库中。例如:
package com.baobaotao;
import javax.annotation.Resource;
public class Boss {
// 自动注入类型为 Car 的 Bean
@Resource(type=Car.class)
private Car car;
// 自动注入 bean 名称为 office 的 Bean
@Resource(name = "office")
private Office office;
}
要让 JSR-250 的注释生效,除了在 Bean 类中标注这些注释外,还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
CommonAnnotationBeanPostProcessor 实现了BeanPostProcessor接口,它负责扫描使用了 JSR-250 注释的Bean,并对它们进行相应的操作。
@PostConstruct 和 @PreDestroy
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期,笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述,有兴趣的读者可以查阅。
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。例如:
package com.baobaotao;
import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Boss {
@Resource
private Car car;
@Resource(name = "office")
private Office office;
@PostConstruct
public void postConstruct1(){
System.out.println("postConstruct1");
}
@PreDestroy
public void preDestroy1(){
System.out.println("preDestroy1");
}
…
}
使用< context:annotation-config />简化配置
Spring2.1添加了一个新的context的Schema命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。
AutowiredAnnotationBeanPostProcessor和 CommonAnnotationBeanPostProcessor就是处理这些注释元数据的处理器。但是直接在Spring配置文件中定义这些 Bean显得比较笨拙。Spring为我们提供了一种方便的注册这些BeanPostProcessor的方式,这就是< context:annotation-config />:
<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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
</beans>
<context:annotation-config />将隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor以及 RequiredAnnotationBeanPostProcessor这4个BeanPostProcessor。
在配置文件中使用context命名空间之前,必须在 <beans> 元素中声明 context 命名空间
<context:annotation-config />主要是代替我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能。
使用 @Component
虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义 —— 也就是说,在 XML 配置文件中定义 Bean,通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。
@Component,标注一个普通的Spring Bean类。当我们不知道这个类是哪一层的就使用它来注释。
@Controller,标注一个控制器组件类。
@Service,标注一个业务逻辑组件类。
@Repository,标注一个DAO组件类。
被上面标注的类在Spring中它们对应的<bean>元素的id是类名的第一个字母小写。
下面,我们完全使用注释定义 Bean 并完成 Bean 之间装配:
使用 @Component 注释的 Car.java
package com.baobaotao;
import org.springframework.stereotype.Component;
@Component
public class Car {
…
}
仅需要在类定义处,使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个Bean:
使用 @Component 注释的 Office.java
package com.baobaotao;
import org.springframework.stereotype.Component;
@Component
public class Office {
private String officeNo = "001";
…
}
这样,我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了,如果我们在spring的配置文件中使用了声明了注释装配,就不用再使用@Autowired了,spring会自动对类的属性进行注入。
使用 @Component 注释的 Boss.java
package com.baobaotao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("boss")
public class Boss {
@Autowired
private Car car;
@Autowired
private Office office;
…
}
@Component 有一个可选的入参,用于指定 Bean 的名称,在 Boss 中,我们就将 Bean 名称定义为“boss”。一般情况下,Bean 都是 singleton 的,需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了,所以大可不必指定 Bean 的名称。
注意在使用 @Component 注释后,Spring 容器必须启用类扫描机制(通过<context:component-scan ../>配置)以启用注释驱动Bean 定义和注释驱动Bean自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:
清单 23. 简化版的 beans.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"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byType">
<!--下面这句是声明使用注释配置,这样Spring会自动对属性按照byType的方式进行注入-->
<context:annotation-config />
<!--下面启动Spring 容器必须启用类扫描机制-->
<context:component-scan base-package="com.baobaotao,com.baofacade"/>
</beans>
这里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。
<context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式,通过下表说明:
扫描过滤方式
注释, 假如 com.baobaotao.SomeAnnotation 是一个注释类,我们可以将使用该注释的类过滤出来。
类名指定, 通过全限定类名进行过滤,如您可以指定将 com.baobaotao.Boss纳入扫描,而将 com.baobaotao.Car 排除在外。
正则表达式,通过正则表达式定义过滤的类,如下所示: com\.baobaotao\.Default.*
AspectJ 表达式,通过 AspectJ 表达式定义过滤的类,如下所示: com. baobaotao..*Service+
下面是一个简单的例子:
<context:component-scan base-package="com.baobaotao">
<context:include-filter type="regex"
expression="com\.baobaotao\.service\..*"/>
<context:exclude-filter type="aspectj"
expression="com.baobaotao.util..*"/>
</context:component-scan>
值得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。
使用<context:component-scan/>自动注入时,我们在类中要使用相应的set/get方法,否则注入不进去,比如UserService中需要自动注入UserDao类,那么需要给出UserDao对应的set/get方法。而使用@Resource,@AutoWire注解注入则不需要给出相应的set/get方法。
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标,如以下代码所示:
通过 @Scope 指定 Bean 的作用范围
package com.baobaotao;
import org.springframework.context.annotation.Scope;
…
@Scope("prototype")
@Component("boss")
public class Boss {
…
}
这样,当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了。
采用具有特殊语义的注释
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。
*********注意Struts的业务逻辑控制器类(Action类)配置在Spring自动注入或配置注入时,必须将业务逻辑控制器类配置为@Scope("prototype")*******
简单的说,Spring的自动注解,首先是使用@Component,@Controller,@Service或@Repository来简化的在Spring配置文件配置<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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byType">
<!--下面这句是声明使用注释配置,这样Spring会自动对属性按照byType的方式进行注入-->
<context:annotation-config />
<context:component-scan base-package="com.baobaotao,com.baofacade"/>
将那些有属性需要注入的类扫描进来,然后由Spring进行自动注入。这样就完成了Spring自动注入。
************
Spring2.5.6和struts2,log4j整合后的web.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts 2.0 Hello World</display-name>
<!-- spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- struts2 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/* </url-pattern>
</filter-mapping>
<!-- log4j -->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/conf/log/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- end log4j -->
<!-- CharacterEncoding Filter -->
<filter>
<filter-name>SetCharacterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SetCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!--// End CharacterEncoding Filter -->
</web-app>
***********************
在SSH,SSI应用中,action相关的bean往往设置成“prototype”(多例模式)。
为什么要设置成多例模式(prototype)?不设置成多例模式有什么问题?
这是由struts2的特性决定的。struts2的action集成了MVC中的M(model)和C(control)两层,它的变量可以直接从
页面获得数据,但这些变量都不是线程安全的变量 。如果不设置成多例模式,这个action就是线程不安全的,在多线程请求
时,就有可能出现一些不可预知的错误。
首先要搞清楚的是线程的共享资源,共享资源是多线程中每个线程都要访问的类变量或实例变量,共享资源可以是单个
类变量或实例变量,也可以是一组类变量或实例变量。多线程程序可以有多个共享资源
什么是线程安全
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
************************
在Spring中提供了字符编码过滤器,可以将我们的请求转换为统一的字符编码,这样不用自己在去编写一个字符编码过滤器了。如:
<!-- CharacterEncoding Filter -->
<filter>
<filter-name>SetCharacterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SetCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!--// End CharacterEncoding Filter -->
encoding----->用来指定一个具体的字符集
forceEncoding------->Spring的早期版本这个参数作用很单一,当request中已经被指定了一个字符集的时候是否再将用endcoding对应的字符集设置到request中去。举个例子来说明,假如说过滤器就像上面那样被配置到web.xml了,当请求被提交之后,过滤器会判断request.getCharacterEncoding()是否为null,如果是null那么就会进行request.setCharacterEncoding("UTF-8")的操作,如果不是null那么过滤器什么也不会做。
web应用中配置spring容器
首先在Struts2中整合Spring需要另外加入的包:
struts2-spring-plugin-2.1.8.1.jar,spring.jar,log4j-1.2.15.jar
其中spring.jar是从下载的Spring包中的dist目录下。
struts2-spring-plugin.jar是在Struts2的包的lib目录下。如果缺少该包tomcat会有error filterStart异常。
一般加入struts2的需要的包和上面的包就可以了,如果启动报aspectj方面的错则还需要加上aspectjweaver.jar。
然后在web.xml中进行配置让其随web服务启动而创建。
对于使用Spring的Web应用,无需手动创建Spring容器,而是通过配置文件,声明式地创建Spring容器。因此在web应用中创建Spring容器有如下两种方式:
一种是直接在web.xml文件中配置创建Spring容器。
另一种是利用第三方MVC框架的扩展点,创建Spring容器。
我们采用第一种方式:
Spring提供了一个ServletContextListener的一个实现类ContextLoaderListener,该类可以作为Listener使用,它会在创建时自动查找WEB-INF下的applicationContext.xml文件。因此如果只有一个applicationContext.xml配置文件,则只需要在web.xml文件中增加如下配置片段即可。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
如果有多个配置文件需要载入,可考虑在web.xml中使用<context-param.../>元素来确定配置文件的文件名。ContextLoaderListener加载时,会查找名为contextConfigLocation的初始化参数。如:
<context-param>
<!--参数名为contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--多个配置文件之间以逗号隔开,/WEB-INF/conf/spring/applicationContext*.xml表示加载spring下所有以applicationContext开头的xml文件-->
<param-value>/WEB-INF/conf/spring/applicationContext*.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!--使用ContextLoaderListener初始化Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
*****************
如果没有使用contextConfigLocation指定配置文件,则Spring自动查找/WEB-INF/applicationContext.xml配置文件;如果有contextConfigLocation,则利用该参数确定的配置文件,如果无法找到合适的配置文件,Spring将无法正常初始化。
*****************
关于applicationContext.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 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="myService" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
<property name="ms" ref="myService"/>
</bean>
</beans>
applicationContext.xml中用于配置<bean>进行依赖注入。
Spring根据指定配置文件创建WebApplicationContext对象,并将其保存到Web应用的ServletContext中,大部分情况下,应用中Bean无法感受到ApplicationContext的存在,只要利用ApplicationContext的IoC即可。如果需要在应用中获得ApplicationContext对象,可以通过如下代码获取:
WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(servletContext);
Spring还提供了一个特殊的类ContextLoaderServlet,该Servlet在启动时,也会自动查找WEB-INF路径下的applicationContext.xml文件,为了让ContextLoaderServlet随应用启动而启动,应将此Servlet配置成load-on-startup的Servlet。如果只有一个配置文件为applicationContext.xml则在web.xml文件中增加的配置为:
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<load-on-startup>1</load-on-startup>的值小一点,有助于ApplicationContext更快的初始化。
该Servlet用于提供后台服务,主要用于创建Spring容器,无须相应客户请求,因此无需为它配置<servlet-mapping/>元素。如果有多个配置文件或配置文件名不是applicationContext.xml则一样使用上面的<context-param/>元素来确定多个配置文件。
由于Servlet规范,Listener比Servlet优先加载,因此,采用ContextLoaderListener创建ApplicationContext的时机更早。
经过上面的配置,Spring和Struts2已经整合到一起。下面是一个应用整合后的实例。
Spring和Struts整合后的实例:
以前控制器是如何获得业务逻辑组件的呢?是通过new关键字创建业务逻辑组件,然后调用业务逻辑组件的方法,根据业务逻辑组件方法的返回值来确定结果,最后导向视图。
控制器如果访问到Spring容器中的业务逻辑组件(就是模型)呢?为了让Action(就是控制器)访问Spring的业务逻辑组件,有两种策略:
一种是Spring容器负责管理控制器Action,并利用依赖注入为控制器注入业务逻辑组件。
另一种是利用Spring的自动装配,Action将会自动从Spring容器中获取所需要的业务逻辑组件。
***********使用让Spring容器管理控制器通过为Action注入**************
Spring插件提供了一种伪Action,当我们在struts.xml文件中配置Action时,通常需要制定class属性,该属性就是用于创建Action实例的实现类。当Spring插件允许我们制定class属性时,不再指定Action的实际实现类,而是指定Spring容器中的BeanID。这种整合策略的关键:当struts2将请求转发给指定的Action时,Struts2中的该Action只是一个"傀儡",它只是一个代号,并没有指定实际的实现类,而隐藏在该Action下的Spring容器中的Action实例,才是真正处理用户请求的控制器。下面我们来进行演示:
首先我们书写一个login.jsp,其内容是输入用户名/密码进行登录。
然后我们书写一个LoginAction用于对前面登录的验证:
package lee;
public class LoginAction{
private String username;
private String password;
private String tip;
//MyService是一个业务逻辑组件
private MyService ms;
public void setMs(MyService ms){
this.ms=ms;
}
public MyService getMs(MyService ms){
return ms;
}
public void setUsername(String username){
this.username=username;
}
public String getUsername(String username){
return username;
}
public void setPassword(String password){
this.password=password;
}
public String getPassword(String Password){
return Password;
}
public void setTip(String tip){
this.tip=tip;
}
public String getTip(String tip){
return tip;
}
public String execute() throws Exception{
if(ms.valid(username,password)){
setTip("哈哈,整合成功了");
return "success";
}
else{
return "error";
}
}
}
上面的程序提供了一个MyService组件,并为该组件提供了setter和getter方法,通过setter方法,就可以让Spring管理Action和MyService组件的依赖关系,避免控制器和业务组件之间的硬编码耦合(采用new创建)。在execute()方法我们调用MyService组件的valid()来进行验证。下面我们在applicationContext.xml文件中进行配置,将LoginActin所需要的组件MyService组件注入进来。
<?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-2.0.xsd">
<bean id="myService" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
<property name="ms" ref="myService"/>
</bean>
</beans>
在Struts2的中如何使用被Spring管理的这个Action。那么在struts.xml文件中的配置如下:
<package name="lee" extends="struts-default">
<action name="Login" class="loginAction">
<result name="error">/error.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
</package>
可以看到class的取值不再是一个实际的实现类,而是Spring容器的BeanID:loginAction。这样被Sprign容器管理的loginAction就会被使用到。
在开发中所有的业务组件都是有接口和实现类两部分组成。上例中使用的MyService业务组件我们分成MyService接口和MyService的实现类MyServiceImpl。
MyService接口类如下:
package lee;
public interface MyService{
public boolean valid(String username,String password);
}
实现MyService接口类的MyServiceImpl如下:
package lee;
public class MyServiceImpl implements MyService{
//实现接口的valid方法
public boolean valid(String username,String password){
if(username.equals("kk") && password.equals("123")){
return true;
}
return false;
}
}
将该业务组件部署到Spring容器中,配置业务逻辑组件的片段如下:
<bean id="myService" class="lee.MyServiceImpl"/>
然后就可以在applicationContext.xml中将此业务逻辑组件注入到Action的中。如
<property name="ms" ref="myService"/>
上面实例是采用Spring容器管理Action控制器,它也有一些不足之处:
Spring管理action,必须将所有的Action配置在Spring容器中,而struts.xml文件中还需要配置一个"伪Action",从而导致配置文件臃肿。
Action的业务逻辑组件接容器的注入,将降低可读性。
***********使用自动装配******************
在这种策略下,Action还是由Spring插件创建,Spring在创建Action实例时,利用Spring的自动装配策略,将对应的业务逻辑组件注入Action实例,这种整合策略配置文件简单,但控制器和业务逻辑组件耦合较高。
采用自动装配,我们还是采用传统方式配置struts2的Action,配置Action时一样指定其具体的实现类。
所谓自动装配,即让spring自动管理Bean与Bean之间的依赖关系,无须使用ref显式指定依赖Bean,Spring容器会自动检查XML配置文件内容,为主调Bean注入依赖Bean,自动装配可以减少配置文件的工作量,但会降低依赖关系的透明性和清晰性。如果我们不指定自动装配,系统默认使用按name自动装配。
采用Spring插件的自动装配策略需要在Struts2通过struts.objectFactory.spring.autoWire常量指定,该常量可以接受如下几个值:
name,根据属性名自动装配。Spring插件会查找容器中全部Bean,找出其中id属性与Action所需的业务逻辑组件同名的Bean,将该Bean实例注入到Action实例中。
type,根据属性类型自动装配,Spring插件会查找容器中全部Bean,找出其类型恰好与Action所需的业务逻辑组件相同的Bean,将该Bean实例注入到Action实例。如果有多个这样的Bean,就抛出一个致命的异常,如果没有匹配的Bean,则什么都不发生,属性不会被设置。
auto,Spring插件会自动见此需要使用那种自动装配方式。
constructor,与type类似,区别是constructor使用构造器来构造注入所需参数,而不是使用设值注入方式。
对于上面的Spring容器管理Action实例,如果换成Spring自动装配,首先需要在struts.xml为struts.objectFactory.spring.autoWire常量指定值,Action的配置仍然采用struts2的方式指定具体实现类。
<constant name="struts.objectFactory" value="spring"/>
<constant name="struts.objectFactory.spring.autoWire" value="type"/>
<package name="lee" extends="struts-default">
<action name="Login" class="lee.LoginAction">
<result name="error">/error.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
</package>
我们选择根据属性名进行自动装配,那么我们在applicationContext.xml中需要配置与逻辑组件名(private MyService ms;)相同的BeanID。如:
<bean id="ms" class="lee.MyServiceImpl"/>
<bean id="loginAction" class="lee.LoginAction" scope="prototype">
</bean>
**************************
基于注释的配置
基于注释的配置越来越流行,Spring 2.5顺应了这种趋势,提供了完全基于注释配置Bean,装配Bean的功能。可以使用基于注释的Spring IoC替换原来基于XML的配置。
注释配置相对于XML配置具有很多的优势:
它可以充分利用java的反射机制获取类结构的信息,这些信息可以有效减少配置的工作。
注释和java代码位于一个文件中,有助于增强程序的内聚性。
Spring不但支持自己定义的@Autowired(byType自动装配)和(@Qualifier("name")精确装配byName)的注释,还支持由JSR-250规范定义的注释,它们分别是@Resource,@PostConstruct以及@PerDestroy。
@Resource
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,@Resource默认按byName自动注入。@Resource有两个属性name和type,Spring将@Resource注释的name属性解析为Bean的名字,而type属性则解析为Bean的类型,所以如果@Resource使用了name属性,则使用byName的自动注入策略,而使用来type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,将通过反射机制使用byName自动注入策略。
@Resource注释位于Spring发布包的lib/j2ee/common-annotations.jar类包中,因此使用之前必须将其加入到项目的类库中。例如:
package com.baobaotao;
import javax.annotation.Resource;
public class Boss {
// 自动注入类型为 Car 的 Bean
@Resource(type=Car.class)
private Car car;
// 自动注入 bean 名称为 office 的 Bean
@Resource(name = "office")
private Office office;
}
要让 JSR-250 的注释生效,除了在 Bean 类中标注这些注释外,还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
CommonAnnotationBeanPostProcessor 实现了BeanPostProcessor接口,它负责扫描使用了 JSR-250 注释的Bean,并对它们进行相应的操作。
@PostConstruct 和 @PreDestroy
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期,笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述,有兴趣的读者可以查阅。
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。例如:
package com.baobaotao;
import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Boss {
@Resource
private Car car;
@Resource(name = "office")
private Office office;
@PostConstruct
public void postConstruct1(){
System.out.println("postConstruct1");
}
@PreDestroy
public void preDestroy1(){
System.out.println("preDestroy1");
}
…
}
使用< context:annotation-config />简化配置
Spring2.1添加了一个新的context的Schema命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。
AutowiredAnnotationBeanPostProcessor和 CommonAnnotationBeanPostProcessor就是处理这些注释元数据的处理器。但是直接在Spring配置文件中定义这些 Bean显得比较笨拙。Spring为我们提供了一种方便的注册这些BeanPostProcessor的方式,这就是< context:annotation-config />:
<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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
</beans>
<context:annotation-config />将隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor以及 RequiredAnnotationBeanPostProcessor这4个BeanPostProcessor。
在配置文件中使用context命名空间之前,必须在 <beans> 元素中声明 context 命名空间
<context:annotation-config />主要是代替我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能。
使用 @Component
虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义 —— 也就是说,在 XML 配置文件中定义 Bean,通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。
@Component,标注一个普通的Spring Bean类。当我们不知道这个类是哪一层的就使用它来注释。
@Controller,标注一个控制器组件类。
@Service,标注一个业务逻辑组件类。
@Repository,标注一个DAO组件类。
被上面标注的类在Spring中它们对应的<bean>元素的id是类名的第一个字母小写。
下面,我们完全使用注释定义 Bean 并完成 Bean 之间装配:
使用 @Component 注释的 Car.java
package com.baobaotao;
import org.springframework.stereotype.Component;
@Component
public class Car {
…
}
仅需要在类定义处,使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个Bean:
使用 @Component 注释的 Office.java
package com.baobaotao;
import org.springframework.stereotype.Component;
@Component
public class Office {
private String officeNo = "001";
…
}
这样,我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了,如果我们在spring的配置文件中使用了声明了注释装配,就不用再使用@Autowired了,spring会自动对类的属性进行注入。
使用 @Component 注释的 Boss.java
package com.baobaotao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("boss")
public class Boss {
@Autowired
private Car car;
@Autowired
private Office office;
…
}
@Component 有一个可选的入参,用于指定 Bean 的名称,在 Boss 中,我们就将 Bean 名称定义为“boss”。一般情况下,Bean 都是 singleton 的,需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了,所以大可不必指定 Bean 的名称。
注意在使用 @Component 注释后,Spring 容器必须启用类扫描机制(通过<context:component-scan ../>配置)以启用注释驱动Bean 定义和注释驱动Bean自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:
清单 23. 简化版的 beans.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"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byType">
<!--下面这句是声明使用注释配置,这样Spring会自动对属性按照byType的方式进行注入-->
<context:annotation-config />
<!--下面启动Spring 容器必须启用类扫描机制-->
<context:component-scan base-package="com.baobaotao,com.baofacade"/>
</beans>
这里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。
<context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式,通过下表说明:
扫描过滤方式
注释, 假如 com.baobaotao.SomeAnnotation 是一个注释类,我们可以将使用该注释的类过滤出来。
类名指定, 通过全限定类名进行过滤,如您可以指定将 com.baobaotao.Boss纳入扫描,而将 com.baobaotao.Car 排除在外。
正则表达式,通过正则表达式定义过滤的类,如下所示: com\.baobaotao\.Default.*
AspectJ 表达式,通过 AspectJ 表达式定义过滤的类,如下所示: com. baobaotao..*Service+
下面是一个简单的例子:
<context:component-scan base-package="com.baobaotao">
<context:include-filter type="regex"
expression="com\.baobaotao\.service\..*"/>
<context:exclude-filter type="aspectj"
expression="com.baobaotao.util..*"/>
</context:component-scan>
值得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。
使用<context:component-scan/>自动注入时,我们在类中要使用相应的set/get方法,否则注入不进去,比如UserService中需要自动注入UserDao类,那么需要给出UserDao对应的set/get方法。而使用@Resource,@AutoWire注解注入则不需要给出相应的set/get方法。
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标,如以下代码所示:
通过 @Scope 指定 Bean 的作用范围
package com.baobaotao;
import org.springframework.context.annotation.Scope;
…
@Scope("prototype")
@Component("boss")
public class Boss {
…
}
这样,当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了。
采用具有特殊语义的注释
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。
*********注意Struts的业务逻辑控制器类(Action类)配置在Spring自动注入或配置注入时,必须将业务逻辑控制器类配置为@Scope("prototype")*******
简单的说,Spring的自动注解,首先是使用@Component,@Controller,@Service或@Repository来简化的在Spring配置文件配置<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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byType">
<!--下面这句是声明使用注释配置,这样Spring会自动对属性按照byType的方式进行注入-->
<context:annotation-config />
<context:component-scan base-package="com.baobaotao,com.baofacade"/>
将那些有属性需要注入的类扫描进来,然后由Spring进行自动注入。这样就完成了Spring自动注入。
************
Spring2.5.6和struts2,log4j整合后的web.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts 2.0 Hello World</display-name>
<!-- spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- struts2 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/* </url-pattern>
</filter-mapping>
<!-- log4j -->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/conf/log/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- end log4j -->
<!-- CharacterEncoding Filter -->
<filter>
<filter-name>SetCharacterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SetCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!--// End CharacterEncoding Filter -->
</web-app>
***********************
在SSH,SSI应用中,action相关的bean往往设置成“prototype”(多例模式)。
为什么要设置成多例模式(prototype)?不设置成多例模式有什么问题?
这是由struts2的特性决定的。struts2的action集成了MVC中的M(model)和C(control)两层,它的变量可以直接从
页面获得数据,但这些变量都不是线程安全的变量 。如果不设置成多例模式,这个action就是线程不安全的,在多线程请求
时,就有可能出现一些不可预知的错误。
首先要搞清楚的是线程的共享资源,共享资源是多线程中每个线程都要访问的类变量或实例变量,共享资源可以是单个
类变量或实例变量,也可以是一组类变量或实例变量。多线程程序可以有多个共享资源
什么是线程安全
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
************************
在Spring中提供了字符编码过滤器,可以将我们的请求转换为统一的字符编码,这样不用自己在去编写一个字符编码过滤器了。如:
<!-- CharacterEncoding Filter -->
<filter>
<filter-name>SetCharacterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SetCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!--// End CharacterEncoding Filter -->
encoding----->用来指定一个具体的字符集
forceEncoding------->Spring的早期版本这个参数作用很单一,当request中已经被指定了一个字符集的时候是否再将用endcoding对应的字符集设置到request中去。举个例子来说明,假如说过滤器就像上面那样被配置到web.xml了,当请求被提交之后,过滤器会判断request.getCharacterEncoding()是否为null,如果是null那么就会进行request.setCharacterEncoding("UTF-8")的操作,如果不是null那么过滤器什么也不会做。