利用这篇文章巩固一下Spring框架的基础,因为发现接触到的各种Spring的项目配置杂七杂八,从xml到注解,从properties到json到yaml,他们各有千秋,没有哪一种方式可以绝对取代另一种配置,所以在这里统一介绍一下各种配置方式的内容和利弊,以便随时查看。这并不是一篇Spring框架领域的教程,只是一种技术的补足或是一种投机取巧的学习手段。
spring项目配置实例
原始的Spring是采用纯xml进行配置的,我从github上找了一个规范经典的SSM项目,以下是一些常用的配置,从这里就可以看出xml的基本格式:
ApplicationContext-test.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!------------------------------DB---------------------------------> <!-- 配置数据库相关参数properties的属性 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置Spring事务处理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置基于注解的声明式事务 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 数据库连接池 使用dbcp--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- Connection Info --> <property name="driverClassName" value="$${jdbc.driver}" /> <property name="url" value="$${jdbc.url}" /> <property name="username" value="$${jdbc.username}" /> <property name="password" value="$${jdbc.password}" /> <property name="initialSize" value="5" /> <property name="maxActive" value="200" /> <property name="maxWait" value="60000" /> <property name="minIdle" value="1" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="minEvictableIdleTimeMillis" value="300000" /> </bean> <!-- 配置SqlSessionFactory对象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 配置MyBaties全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml" /> <!-- 扫描entity包 使用别名 --> <property name="typeAliasesPackage" value="com.app.entity" /> <!-- 扫描sql配置文件:mapper需要的xml文件 --> <property name="mapperLocations" value="classpath:mapper/*.xml" /> </bean> <!-- 配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!-- 扫描dao包 --> <property name="basePackage" value="com.app.dao" /> </bean> <!--------------------------------MVC----------------------------------> <!-- 扫描service包下所有使用注解的类型--> <context:component-scan base-package="com.soecode.lyf.service" /> <!-- 配置viewResolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="suffix" value=".jsp"/> <property name="contentType" value="text/html; charset=UTF-8" /> </bean> </beans> <configuration> <!-- 配置全局属性 --> <settings> <!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 --> <setting name="useGeneratedKeys" value="true" /> <!-- 使用列别名替换列名 默认:true --> <setting name="useColumnLabel" value="true" /> <!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} --> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings> </configuration>
jdbc.properties
jdbc.url=jdbc:mysql://xx:3306/xxx jdbc.username=root jdbc.password=123456 jdbc.driver=com.mysql.jdbc.Driver
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> <!-- 配置DispatcherServlet --> <servlet> <servlet-name>seckill-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置springMVC需要加载的配置文件,这里我合并成了一个文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:ApplicationContext-*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>seckill-dispatcher</servlet-name> <!-- 默认匹配所有的请求 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
使用xml配置
传统的xml配置内容已经在前面罗列出不再赘述,有以下几个要点:
引入properties属性文件
有以下两种方式:
<context:property-placeholder location="classpath:jdbc.properties,classpath:redis.properties" /> <!-------旧版本-注意:PropertyPlaceholderConfigurer在3.1之后版本被改为PropertySourcesPlaceholderConfigurer-----> <bean id="jdbcProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath*:jdbc.properties</value> <value>classpath*:redis.properties</value> </list> </property> </bean>
不管使用何种引用方式,PropertySourcesPlaceholderConfigurer全局范围内只有一个会生效。
classpath*代表在导入的jar文件中也会进行查找,若非必要不要使用*,会降低执行效率。
自动扫描包
<context:component-scan base-package="com.app" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
其中,include-filter是可选的,该配置项扫描包下的注解(@Service,@Component,@Repository,@Controller等),这里配置了一个过滤器,指定只扫描包中的Controller注解类,并将其注册为bean。当然,也可以手动配置注入所有的bean,但在可以用注解标注一个bean的今天,几乎已经没有人会这样做了。
使用如下注解可以激活@Resource,@PostConstruct,@PreDestroy,@PersistenceContext,@Autowored,@Required等注解类:
<context:annotation-config />
前者是包含后者的,所以定义了包扫描后,就不必在再注册annotationConfig。
他们本质上其实还是注册了bean。
Spring mvc的常用配置
写视图解析器:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="suffix" value=".jsp"/> <property name="prefix" value="/WEB-INF/"/> <property name="contentType" value="text/html; charset=UTF-8" /> </bean>
这是Spring SSM最常见的一种视图解析器,还有很多解析器在此不详述,可以从这篇博客详细查看ViewResolver 视图解析器
还有一些常用标签:
<!-------------不拦截静态资源,譬如静态目录下的js等----------------------------> <!-------------因为web.xml中配置的拦截器已经拦截了所有 / 的url,所以不加此标签静态资源会被当成servlet请求处理---------------> <mvc:default-servlet-handler/> <!-------------mvc注解驱动,可以使用@RequestMapping等注解--------------------> <mvc:annotation-driven /> <!-------------未被定义为Controller的请求内容将映射为/default------------> <mvc:view-controller path="/" view-name="redirect:/default"/> <!-------------将未定义为Controller的请求按视图解析器映射到资源路径下的/default(/WEB_INF/default.jsp)------------> <mvc:view-controller path="/" view-name="/default"/>
注意:一般情况下 前两个标签会同时存在,否则会引发问题,例如view-controller的请求将直接解析而不再检查RequestMapping。
其他配置项目
启动定时任务(更多资源在Quertz那篇博客内):
<task:annotation-driven/>
导入资源:
<import resource="classpath:com/xxx/fisea.xml" />
使用注解配置
最基本的注解,使用如下注解声明注入一个bean:
@Component:当对组件的层次难以定位的时候使用这个注解
@Controller:表示控制层的组件
@Service:表示业务逻辑层的组件
@Repository:表示数据访问层的组件
以上注解在功能上实际上是相同的,这不是单独使用的,需要配合xml文件中的注解扫描。
而在对应的变量上面使用@Resource、@Autowired、@Qualifier 实现自动注入res 使用@Value("xx")实现自动注入值。
@Qualifier代表byName注入、@Autowired代表byType注入、@Resource可以进行配置,默认使用byName。
使用@Configuration注解配置
可以使用@Configuration和@Bean来取代xml中的注解配置,每一个@Bean标注的方法返回值都对应着一个注入容器中的bean,bean name默认同方法名,而对应的@Configuration标注的类则可以理解为一个xml:
@Configuration public class MiniConfig{ @Bean public User user(){ User user=new User(); user.setName("老李"); return user; } }
引入properties
使用注解引入属性文件:
@PropertySource("classpath:ch2/test.properties") public clss Config{ //@Value注解可以装配bean的属性值,properties值,定义变量 @Value("${server.name}") //装配properties属性 private String normal; @Value("fish") private String name; @Value("#{dataSource.url}") //装配bean的属性 private String jdbc; }
其他配置项
你可能还需要配置xml的一些东西,这种配置形式一般使用在Springboot中:
@Configuration @SpringBootConfiguration //在Springboot中如果用了这个注解 就不要使用@Configuration了(其实是一个东西) @EnableAutoConfiguration //根据项目依赖添加自动配置项 @EnableWebMvc @ComponentScan(basePackages = "com.app") //开启扫描,不指定basePackages扫描改类目录下所有目录 @Import(MaxConfig.class) //引入其他配置 @MapperScan(value="org.fhp.springmybatis.dao") @ImportResource("classpath:spring-mybatis.xml") //使用xml注册bean public class MiniConfig{ }
很多注解内容一看就明白,在这里也就不多说了。
使用Yaml取代properties
properties
.properties文件用于申明一些在项目中常用的值(格式xxx=xxx)。除了以上两种引用方式,最基本的Properties取值是这样的:
Properties properties = new Properties(); // 使用ClassLoader加载properties配置文件生成对应的输入流 InputStream in = PropertiesMain.class.getClassLoader().getResourceAsStream("config/config.properties"); // 使用properties对象加载输入流 properties.load(in); //获取key对应的value值 properties.getProperty(String key);
web.xml
在早期版本,web.xml几乎是web项目必不可少的配置文件,里面记录一些servlrt的常用配置,在不使用MVC框架时,需要用此文件定义servlet与url的对应关系,同时也可以定义过滤器和监听器,还有一些服务器相关的配置项,说一下常用的配置项,当然,有些内容已经过时了:
指定项目名(比如是在Tomcat管理页面显示的内容):
<display-name>fish</display-name>
指定一些初始化参数(应用在整个应用范围内):
<context-param> <param-name>webAppRootKey</param-name> <param-value>fish.root</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-configuration/*.xml</param-value> </context-param>
这样配置的值可以在项目中使用ServletContext.getInitParameter("")读取,这是可选配置,此处定义了一个WebAppRootKey,它的值是一个key,默认是webapp.root,在以上项目中,可以使用System.getProperty("fish.root")获取项目路径,在同一容器下部署多个项目,需配置多个不同的WebAppRootKey。
contextConfigLocation指定了项目要加载的Spring配置文件,默认值为/WEB-INF/applicationontext.xml,多个之间用逗号、空格或是分号隔开。
指定监听器:
<listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>com.app.mq.executor.JobExecutor</listener-class> </listener>
第二个监听器是必须使用的,当然 也可以集成接口,自己重写。
指定过滤器:
<!-- 字符集过滤器 --> <filter> <filter-name>CharacterEncodingFilter</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>
指定servlet:
尾注-Springboot的"约定大于配置"
这篇博客基本涵盖了当前所有主流的项目本地方式,注意,是本地!在实际开发中,还有很多配置方式,比如利用统一配置中心(例如携程的Apollo),实际开发中也不应该拘泥于某一项配置方式,而应是配合使用。
前文中,利用注解配置已经做到了项目去XML,尤其是在Springboot框架下的环境,甚至很难看到xml的身影,后面可能会有单独的博客说这一点的实现。在这里简单介绍一下Springboot的关键特性:约定大于配置。
Springboot开发的项目特点是在我们配置的依赖中有一堆starter,他们不像通常的依赖一样单单只是类库的集成,而是添加了自动扫描配置并注入bean的一些便捷的实现,譬如在大多数时候我们都希望能简化数据库连接的配置,因为很多情况下,我们使用的正是通过搜索引擎搜索到千篇一律的通用配置。Springboot只是简化了这一配置,同时约定从properties或是yml中读取配置好的一些参数以便于更快速的进行开发。想要简单的连接数据库并将数据库连接的配置写入Mybatis,我们只需要在yml里写入有关数据库的一些参数,便可以进行DAO层的相关开发,而不是要配置又多又杂的xml文件或是引入第三方插件,Springboot正在使得Java服务冗杂的初始化过程变得便捷而又快速,这也是一种很好的编程思想, 在开发过程中,我们可以试着写属于自己的starter,并让其在各个服务中进行复用。