`
yaoweinan
  • 浏览: 132744 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

1Spring整合Struts2

阅读更多
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异常。

然后在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加载时,会查找名为contextConfigLacation的初始化参数。如:
<context-param>
<!--参数名为contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--多个配置文件之间以逗号隔开-->
<param-value>/WEB-INF/daoContext.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.spring.autoWire" value="name"/>
<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 命名空间


使用 @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组件类。

下面,我们完全使用注释定义 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 容器必须启用类扫描机制以启用注释驱动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 />
<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/> 移除了。

默认情况下通过 @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自动注入。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics