前提
这篇博文是这套Spring学习笔记的第三篇——配置篇,主要内容包含Spring框架的获取以及如何创建一个可以作为基础模板的使用Spring框架的Java Web工程。如果需要了解有关Spring的综述信息或博文的索引信息,请移步:
《综述篇》
获取Spring框架源码
就像游戏中的好装备,开发者不会让玩家轻易获得一样,凡是被人们广为传唱的好软件、优秀源代码,几乎没有一个的网站上有“Download”这个按钮。我猜这可能算是一种筛选吧,即“能被小小的困难轻易打败的人不配拥有宝剑”之类…
言归正传,首先看一下Spring的官方网站:
并没有Download按钮。PROJECT?不存在的。只不过依次点击PROJECT->SPRING FRAMEWORK会跳转到Spring框架的综述页面,里面有一部分有用的信息如下:
这是一份Spring Framework个版本对JDK的最低要求说明。因为我的PC和服务器装的都是JDK 8.0,所以选择4.0以上版本的Spring Framework就可以。
最终,Spring Framework官方仓库地址还是被我找到了(鬼知道是怎么找到的)。打开之后是一份比较长的列表,包含了Spring Framework所有的版本。可以根据上述对应关系和自己机器上JDK的版本选择合适的Spring Framework版本。
以4.x的最后一个版本4.3.9为例,点击4.3.9.RELEASE进入该目录,然后点击第一个文件“~dist.zip”,下载Spring Framework 4.3.9的源码。
下载完成后打开压缩包,把libs文件夹解压出来,然后打开解压生成的libs文件夹
可以看到,一共61个文件,且三个三个成组出现的,每组都包含X.jar,X-javadoc.jar和X-source.jar。我们挑出所有的X.jar,一共21个,把他们复制出来放进一个文件夹中,这些就是Spring Framework 4.3.9的所有源码的Jar包。
创建Java Web工程
为了保持一致性,我们继续使用NetBeans作为开发用的IDE,关于其下载和安装与创建一个Java Web工程的过程请完全按照我的《Java Web的编写》这篇博文,看到“开始撸码!”这个章节之前就可以了。示例工程的名称仍为MyFirstWebApp
数据库也沿用《MySQL数据库的建立》这篇博文中的教程,创建一个名为myfirstapp的数据库,其中只有一张user表,包含Id、用户名和密码三个字段。
其中略有不同的是:
①我将所提供的云盘资源中MySql Connector的版本提升到了5.1.46,因为继续用3.1.14版本会报错;
②需要以相同的方式将上述Spring Framework那21个Jar包也一并添加到库中去;
③需要额外添加dbcp2、pool2和aspectjweaver的Jar包到库中。
dbcp2:链接 密码:qzu4
pool2:链接 密码:9pe7
aspectjweaver:链接 密码:6q0z
创建Spring配置文件
依次右键点击源包->新建->Spring XML配置文件
命名为 applicationContext 后点击下一步
勾选aop、context、p和tx这四个选项 后点击完成
新生成的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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
">
</beans>
注意:接下来所添加的代码全部都是在<beans>标签中(就是上述代码倒数第二行的空行处)做的。
添加组件扫描
<context:component-scan base-package="com.implementist.MyFirstWebApp.dao"/>
<context:component-scan base-package="com.implementist.MyFirstWebApp.service"/>
这两行的作用是让IoC容器扫描这两个包,自动注入所需的依赖。 当然目前工程中的源包只创建到了MyFirstWebApp这一层,上述的dao和service包尚未创建。
配置数据源和jdbc模板
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/myfirstapp?characterEncoding=utf8"
p:username="root"
p:password="" />
<!-- 配置Jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
这里有两个bean:
第一个dataSource是数据源。有没有感觉很眼熟?其实它和不管Java文件中配置数据源还是XML文件里配置数据源都挺像的,都是些字符串,Url、端口、数据库、数据库账号、密码这些,没啥看不懂的,就不细说了。
第二个是jdbc模板,它引用了上面定义的dataSource作为数据源。 说到这个词可能大家没有印象,你回到《综述篇》去看一下就知道了,Spring就是把与数据库相关的定义连接、处理异常、关闭连接等各种重复累赘的代码封装到了Jdbc这个类中,以此来减少开发者实现数据库操作所需的代码量。
配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
关于事务的定义我目前还没有完全理解,但这段代码是必须的,后面的代码会引用transactionManager这个Bean。
配置AOP
<!-- 通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 -->
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceMethod"
expression=" execution(* com.implementist.MyFirstWebApp.service..*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
因为对于AOP本身就还没有理解透,因此这部分代码暂时也不知道怎么解释,后面的博文一定会解释清楚的。
启动Spring容器
注意:以下操作在web.xml中的<web-app>标签中进行
<!-- 从类路径(即默认包)下加载我们之前创建的Spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 启动Spring容器的监听器 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
这段代码使Java Web程序在启动时自动加载applicationContext.xml中的配置信息并启动Spring容器。
配置请求调度器
此时需要在源包下面以与applicationContext.xml同样的方式创建一个spring-mvc.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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.implementist.MyFirstWebApp.controller"/>
</beans>
注意:以下操作在web.xml中的<web-app>标签中进行
现在以一个servlet作为请求分派器:
<servlet>
<!-- 此处定义分派器的名称为dispatcher -->
<servlet-name>dispatcher</servlet-name>
<!-- 这个DispatcherServlet是Spring框架中的类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 从类路径(即默认包)下加载我们创建的Spring配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet映射 -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 这里指定接受任意请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
下面进入到Java代码的编写中,我们以一个简单的用户登录功能为例
四个包与四个类
首先说一下创建包的方式,后面四个包的创建方式相同,名字不同:
①首先看到工程结构,经过我们之前的配置,变成了如下的样子:
②此时依次右键点击源包->新建->Java包
把包名换成我们需要的包名之后点击完成按钮就可以了。
domain包与User类
我们先创建一个com.implementist.MyFirstWebApp.domain包,然后在包里面创建一个User类
private int id;
private String username;
private String password;
......
一共这三个字段,getter和setter省略。
dao包和UserDAO
创建一个和上面前缀相同的dao包,再在包里面创建一个UserDAO类
@Repository //注①
public class UserDAO {
@Autowired //注②
private JdbcTemplate jdbcTemplate;
User user = null;
public User queryUserByUserName(String username) {
String sqlStatement = "SELECT * FROM user WHERE UserName=?";
jdbcTemplate.query(sqlStatement, new Object[]{username}, (rs) -> {
if (rs.isFirst()) {
user = new User();
user.setId(rs.getInt("Id"));
user.setUsername(username);
user.setPassword(rs.getString("Password"));
}
});
return user;
}
}
这个DAO中目前只写了按用户名查找用户信息的函数。
注释:
①在添加组件扫描那一小节中,有dao这个包名,当我们给这个类冠以@Repository这个注解时,Spring容器在扫描dao包中的类时,会把UserDAO识别为一个bean,亦即这个类的实例化工作之后就交给Spring IoC容器来管理了;
②还记得我们在applicationContext.xml中配置的数据源和jdbcTemplate吗,当我们在程序中的任何位置定义一个jdbcTemplate变量并给他冠以@Autowired注解时,IoC容器会自动使用配置文件中的信息来实例化这个变量。
service包和UserService
创建一个和上面前缀相同的service包,再在包里面创建一个UserService类
@Service //注①
public class UserService {
@Autowired //注②
private UserDAO userDAO;
//判断用户名和密码是否正确
public boolean verifyLogin(String username, String password) {
User user = userDAO.queryUserByUserName(username);
return null != user && password.equals(user.getPassword());
}
}
注释:
①和dao相同,service包也会被扫描,被冠以@Service注解,指定它是一个服务层的类,和UserDAO类似;
②和jdbcTemplate类似,但是不同之处在于这个userDAO不是从配置文件里找的,因为UserDAO类被冠以了@Repository注解,所以IoC容器会创建一个其实例给userDAO这个变量引用。
controller包和UserController
创建一个和上面前缀相同的controller包,再在包里面创建一个UserController类
@Controller //注①
public class UserController {
@Autowired //注②
private UserService userService;
@RequestMapping("Login") //注③
private void handleLoginRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
try (PrintWriter out = response.getWriter()) {
request.setCharacterEncoding("utf-8");
//获得请求中传来的用户名和密码
String username = request.getParameter("UserName").trim();
String password = request.getParameter("Password").trim();
//密码验证结果
boolean result = userService.verifyLogin(username, password);
out.write(String.valueOf(result));
}
}
}
注释:
①controller这个包同样也会被Spring容器扫描到,不同的是我们把它的组件扫描定义在了spring-mvc.xml文件中了。被冠以@Controller的类被指定为控制层的类。
②自动装配userService,老生常谈了。
③请求映射,只有叫做“Login的请求才会被handleLoginRequest函数处理”
最后一步
一般情况下,所有该写的代码都写完了,但是我想额外加上一步。按照上述配置,我们登录时需要使用形如以下的链接:
http://1.1.1.1/Login
同理,注册的时候就需要:
http://1.1.1.1/Register
这样URL实在是太“丑”了,我们希望URL后面不跟请求类型,服务器自动根据请求参数中的RequestType判断是哪种请求,那么我们需要在controller包里额外加一个RequestController
@Controller
public class RequestController {
@RequestMapping("")
public ModelAndView dispatch(HttpServletRequest request, HttpServletResponse response) {
String RequestType = request.getParameter("RequestType");
return new ModelAndView("forward:" + RequestType);
}
}
说明
这段代码的意思是:提取任意请求中的RequestType参数,然后根据请求类型把请求转发出去。此时,如果RequestType=Login,请求就会分配给handleLoginRequest这个函数去处理。
工程的整体结构
所有的代码都写完了,我们看一下这个工程的结构
分门别类,是不是看着很清爽!
测试一下
现在,部署一下WAR包,我们启动XAMPP控制面板上的Tomcat和MySQL,打开浏览器,在URL中输入:
http://服务器IP/?ReuqestType=Login&UserName=Jackson&Password=1234567890
容许我偷个懒,就不往服务器上放了,在PC上测试一下。
解释:
①为了测试方便,我用GET方式发送的请求,如果在客户端写明用POST方式请求,参数就不会出现在URL中,试想一下,URL中"?"以及后面的字符都没有了,是不是很干净?!
②这里的URL并未出现MyFirstWebApp这个工程名,是因为,我在Tomcat完成了对其的部署之后,将XAMPP/tomcat/webapps/MyFirstWebApp这个文件夹的名字改成了"ROOT",ROOT是Tomcat服务器的默认Web APP路径,访问时不需要加工程名称。
后记
1.这篇博文确实比较长,从时间上来说,断断续续得写我一共花了三天时间。所谓万事开头难,配置的过程确实繁琐,但是这整个小模板建立起来之后,在当前项目上,以后随着学习的深入,就可以不断地扩展;在以后的项目中也可以直接复制过去,改改包名就可以复用了。
2.关于代码的问题,我原本想打包一下这个工程分享给大家的。但是后来想了一下,其实这只是第一步,代码也不难,要学习一个东西还是需要去犯错的。因此就不给什么模板代码了,我会尽量保证博文中的代码、逻辑、顺序没有缺失和错误。
3.如果在实现中出现了难以解决的问题,请直接在博文下方评论或者发邮件到我的邮箱:[email protected]