154、如何在Web项目中配置Spring的IoC容器?
答:如果需要在Web项目中使用Spring的IoC容器,可以在Web项目配置文件web.xml中做出如下配置:
<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>155、如何在Web项目中配置Spring MVC?
答:要使用Spring MVC需要在Web项目配置文件中配置其前端控制器DispatcherServlet,如下所示:
<web-app><servlet><servlet-name>example</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>example</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping></web-app>**说明:*上面的配置中使用了.html的后缀映射,这样做一方面不能够通过URL推断采用了何种服务器端的技术,另一方面可以欺骗搜索引擎,因为搜索引擎不会搜索动态页面,这种做法称为伪静态化。
156、Spring MVC的工作原理是怎样的?
答:Spring MVC的工作原理如下图所示:
下面是 Spring MVC 的完整工作流程图:
下面针对每个步骤给出对应的代码或配置示例,帮助理解 Spring MVC 的完整工作流程。
① 客户端发起请求
客户端通过浏览器或 HTTP 客户端向服务器发送请求,例如访问http://localhost:8080/example/user/list。该请求会先被部署在 web.xml 中的前端控制器 DispatcherServlet 拦截。
② DispatcherServlet 通过 HandlerMapping 查找 Handler
DispatcherServlet 收到请求后,会调用 HandlerMapping 根据请求的 URL 找到对应的 Handler(即 Controller 中的处理方法)。HandlerMapping 的常见配置方式如下:
<!-- 启用 Spring MVC 注解驱动,自动注册 RequestMappingHandlerMapping 等组件 --><mvc:annotation-driven/><!-- 配置视图解析器(可选,配合步骤⑥使用) --><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/views/"/><propertyname="suffix"value=".jsp"/></bean>对应的 Controller 示例:
@ControllerpublicclassUserController{@RequestMapping("/user/list")publicStringlist(Modelmodel){model.addAttribute("users",userService.findAll());return"userList";// 逻辑视图名}}③④ HandlerAdapter 封装并调用 Handler
HandlerAdapter 是一个适配器,它用统一的接口对各种 Handler 中的方法进行调用。Spring MVC 默认注册了RequestMappingHandlerAdapter,负责调用带有@RequestMapping注解的方法。通常无需额外配置,但可以通过<mvc:annotation-driven/>自动注册。
⑤ Handler 返回 ModelAndView
Handler 方法处理完请求后,会返回一个ModelAndView对象(或通过Model参数 + 返回视图名的方式),其中包含了数据模型以及视图的逻辑名称。例如上面的list方法返回逻辑视图名userList,同时通过model.addAttribute携带了用户列表数据。
⑥ ViewResolver 解析逻辑视图
DispatcherServlet 会借助 ViewResolver 将逻辑视图名解析为真实的视图对象。例如使用InternalResourceViewResolver时,逻辑视图名userList会被解析为/WEB-INF/views/userList.jsp:
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/views/"/><propertyname="suffix"value=".jsp"/></bean>⑦ 视图渲染模型数据
得到真正的视图对象后,DispatcherServlet 会利用视图对象对模型数据进行渲染。例如 JSP 视图会通过 JSTL 标签将模型中的数据输出到页面:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <body> <table> <c:forEach items="${users}" var="user"> <tr> <td>${user.name}</td> <td>${user.age}</td> </tr> </c:forEach> </table> </body> </html>⑧ 返回响应给客户端
视图渲染完成后,DispatcherServlet 将最终的响应内容返回给客户端。响应可能是一个普通的 HTML 页面,也可以是 XML 或 JSON 字符串,还可以是一张图片或者一个 PDF 文件。例如返回 JSON 数据时,只需在 Controller 方法上添加@ResponseBody注解:
@ControllerpublicclassUserController{@RequestMapping(value="/user/list",produces="application/json")@ResponseBodypublicList<User>listJson(){returnuserService.findAll();}}① 客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
② DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。
③ 在这个地方Spring会通过HandlerAdapter对该处理器进行封装。
④ HandlerAdapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。
⑤ Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。
⑥ ModelAndView的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。
⑦ 当得到真正的视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染。
⑧ 客户端得到响应,可能是一个普通的HTML页面,也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件。
157、如何在Spring IoC容器中配置数据源?
答:
DBCP配置:
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"><propertyname="driverClassName"value="${jdbc.driverClassName}"/><propertyname="url"value="${jdbc.url}"/><propertyname="username"value="${jdbc.username}"/><propertyname="password"value="${jdbc.password}"/></bean><context:property-placeholderlocation="jdbc.properties"/>C3P0配置:
<beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"><propertyname="driverClass"value="${jdbc.driverClassName}"/><propertyname="jdbcUrl"value="${jdbc.url}"/><propertyname="user"value="${jdbc.username}"/><propertyname="password"value="${jdbc.password}"/></bean><context:property-placeholderlocation="jdbc.properties"/>提示: DBCP的详细配置在第153题中已经完整的展示过了。
158、如何配置事务增强?
答:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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"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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- this is the service object that we want to make transactional --><beanid="fooService"class="x.y.service.DefaultFooService"/><!-- the transactional advice --><tx:adviceid="txAdvice"transaction-manager="txManager"><!-- the transactional semantics... --><tx:attributes><!-- all methods starting with 'get' are read-only --><tx:methodname="get*"read-only="true"/><!-- other methods use the default transaction settings (see below) --><tx:methodname="*"/></tx:attributes></tx:advice><!-- ensure that the above transactional advice runs for any execution of an operation defined by the FooService interface --><aop:config><aop:pointcutid="fooServiceOperation"expression="execution(* x.y.service.FooService.*(..))"/><aop:advisoradvice-ref="txAdvice"pointcut-ref="fooServiceOperation"/></aop:config><!-- don't forget the DataSource --><beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"><propertyname="driverClassName"value="oracle.jdbc.driver.OracleDriver"/><propertyname="url"value="jdbc:oracle:thin:@localhost:1521:orcl"/><propertyname="username"value="scott"/><propertyname="password"value="tiger"/></bean><!-- similarly, don't forget the PlatformTransactionManager --><beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean><!-- other <bean/> definitions here --></beans>159、选择使用Spring框架的原因(Spring框架为企业级开发带来的好处有哪些)?
答:可以从以下几个方面作答:
- 非侵入式:支持基于POJO的编程模式,不强制性的要求实现Spring框架中的接口或继承Spring框架中的类。
- IoC容器:IoC容器帮助应用程序管理对象以及对象之间的依赖关系,对象之间的依赖关系如果发生了改变只需要修改配置文件而不是修改代码,因为代码的修改可能意味着项目的重新构建和完整的回归测试。有了IoC容器,程序员再也不需要自己编写工厂、单例,这一点特别符合Spring的精神"不要重复的发明轮子"。
- AOP(面向切面编程):将所有的横切关注功能封装到切面(aspect)中,通过配置的方式将横切关注功能动态添加到目标代码上,进一步实现了业务逻辑和系统服务之间的分离。另一方面,有了AOP程序员可以省去很多自己写代理类的工作。
- MVC:Spring的MVC框架是非常优秀的,从各个方面都可以甩Struts 2几条街,为Web表示层提供了更好的解决方案。
- 事务管理:Spring以宽广的胸怀接纳多种持久层技术,并且为其提供了声明式的事务管理,在不需要任何一行代码的情况下就能够完成事务管理。
- 其他:选择Spring框架的原因还远不止于此,Spring为Java企业级开发提供了一站式选择,你可以在需要的时候使用它的部分和全部,更重要的是,你甚至可以在感觉不到Spring存在的情况下,在你的项目中使用Spring提供的各种优秀的功能。
160、Spring IoC容器配置Bean的方式?
答:
- 基于XML文件进行配置。
- 基于注解进行配置。
- 基于Java程序进行配置(Spring 3+)。
packagecom.jackfrued.bean;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;@ComponentpublicclassPerson{privateStringname;privateintage;@AutowiredprivateCarcar;publicPerson(Stringname,intage){this.name=name;this.age=age;}publicvoidsetCar(Carcar){this.car=car;}@OverridepublicStringtoString(){return"Person [name="+name+", age="+age+", car="+car+"]";}}packagecom.jackfrued.bean;importorg.springframework.stereotype.Component;@ComponentpublicclassCar{privateStringbrand;privateintmaxSpeed;publicCar(Stringbrand,intmaxSpeed){this.brand=brand;this.maxSpeed=maxSpeed;}@OverridepublicStringtoString(){return"Car [brand="+brand+", maxSpeed="+maxSpeed+"]";}}packagecom.jackfrued.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importcom.jackfrued.bean.Car;importcom.jackfrued.bean.Person;@ConfigurationpublicclassAppConfig{@BeanpublicCarcar(){returnnewCar("Benz",320);}@BeanpublicPersonperson(){returnnewPerson("骆昊",34);}}packagecom.jackfrued.test;importorg.springframework.context.ConfigurableApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importcom.jackfrued.bean.Person;importcom.jackfrued.config.AppConfig;classTest{publicstaticvoidmain(String[]args){// TWR (Java 7+)try(ConfigurableApplicationContextfactory=newAnnotationConfigApplicationContext(AppConfig.class)){Personperson=factory.getBean(Person.class);System.out.println(person);}}}下面是三种配置方式的对比,帮助理解各自的适用场景与优缺点:
| 配置方式 | 配置示例 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 基于XML配置 | <bean id="person" class="com.jackfrued.bean.Person"> | 早期Spring项目、需要集中管理大量Bean、对代码零侵入的遗留系统 | 集中管理、与代码完全解耦、便于统一维护和替换实现 | 配置冗长、可读性差、类型安全弱、修改需重启 |
| 基于注解配置 | @Component+@Autowired | 现代Spring Boot项目、团队协作开发、追求开发效率 | 简洁直观、类型安全、就近声明、开发效率高 | 依赖关系分散在代码中、Bean扫描范围需注意、调试时不易全局查看 |
| 基于Java配置 | @Configuration+@Bean | 需要动态创建Bean、复杂条件装配、无XML的纯Java项目 | 类型安全、支持复杂逻辑、可编程化装配、IDE友好 | 配置与代码耦合、Bean较多时代码量增加 |
如何选择:
- 新项目:优先推荐注解配置,配合Spring Boot的自动装配,开发效率最高。
- 需要动态或条件装配:使用Java配置,可以在
@Bean方法中编写判断逻辑,灵活创建Bean。 - 遗留系统或需要集中管理:使用XML配置,便于在不改动代码的情况下调整Bean定义。
- 实际开发中:三种方式可以混合使用,例如用注解声明组件、用Java配置组装复杂依赖、用XML管理第三方库的Bean,各取所长。