1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @author 791202.com */ @Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtils.applicationContext = applicationContext; } public static <T> T getBean(Class<T> requiredType) { return applicationContext.getBean(requiredType); } public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } public static <T> T getBean(String name, Class<T> requiredType) throws BeansException { return applicationContext.getBean(name, requiredType); } } |
标签:spring
@ConditionalOnClass实现根据判断某个类是否在classpath中来动态创建bean
前言
假设我要配置一个TiEasyRocketProducer bean,但是测试环境下可能不一定有它的依赖类DefaultMQProducer.class,这个时候就可以根据@ConditionalOnClass和@Profile来动态创建bean。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import com.alibaba.rocketmq.client.producer.DefaultMQProducer; import com.alibaba.rocketmq.client.producer.SendStatus; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import org.apache.commons.lang.BooleanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.UUID; public class TiEasyRocketProducer { private static final Logger LOGGER = LoggerFactory.getLogger(MiProducer.class); private Cache<String, Producer> producerCache = CacheBuilder.newBuilder().build(); private DefaultMQProducer producer; public MiProducer(String groupName) throws Exception { producer = new DefaultMQProducer(groupName); producer.setNamesrvAddr(namesrvUrl); producer.setSendMsgTimeout(12000); producer.setCompressMsgBodyOverHowmuch(4096); producer.setInstanceName(UUID.randomUUID().toString().replace("-", "")); producer.start(); } public Boolean sendMessage(String topic, String tags, String content, int delayLevel) throws Exception{ return this.sendMessage(topic, tags, content, delayLevel, null); } public Boolean sendMessage(String topic, String tags, String content) throws Exception{ return this.sendMessage(topic, tags, content, 0, null); } public Boolean sendMessage(String topic, String tags, String content, String businessId) throws Exception{ return this.sendMessage(topic, tags, content, 0, businessId); } public Boolean sendMessage(String topic, String tags, String content, int delayLevel, String businessId) throws Exception { byte[] body = content.getBytes(StandardCharsets.UTF_8); com.alibaba.rocketmq.common.message.Message message = new com.alibaba.rocketmq.common.message.Message(topic, tags, body); if (delayLevel > 0) message.setDelayTimeLevel(delayLevel); com.alibaba.rocketmq.client.producer.SendResult sendResult = producer.send(message); LOGGER.debug("message send result: {}", sendResult); return sendResult.getSendStatus() == SendStatus.SEND_OK || sendResult.getSendStatus() == SendStatus.SLAVE_NOT_AVAILABLE; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import com.alibaba.rocketmq.client.producer.DefaultMQProducer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration @ConditionalOnClass(DefaultMQProducer.class) public class RocketMQConfig { @Value("${spring.application.name}") private String appName; @Bean("testProducer") @Profile("test") public TiEasyRocketProducer testProducer() throws Exception { return new TiEasyRocketProducer(appName); } } |
使用@ConfigurationProperties注解实现springboot里配置映射到Java类上
yml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
tieasy: mqs: instances: internal: appId: com.tieasy.specialEquipment appSecret: namesrcAddr: 110.21.56.49:1234 consumeThreadMax: 32 consumeThreadMin: 8 external: appId: com.tieasy.specialEquipment appSecret: namesrcAddr: 110.21.50.30:1235 consumeThreadMax: 32 consumeThreadMin: 8 topics: crane: T_CRANE_DEVICE lift: T_LIFT_DEVICE wuxi: deviceAdd: T_INSP_WUXI_DeviceAdd deviceEdit: T_INSP_WUXI_DeviceEdit deviceDelete: T_INSP_WUXI_DeviceDelete xuzhou: deviceStatus: T_INSP_XUZHOU_DeviceStatus deviceData: T_INSP_XUZHOU_DeviceData deviceAdd: T_INSP_XUZHOU_DeviceAdd deviceDelete: T_INSP_XUZHOU_DeviceDelete bazhou: T_INSP_BAZHOU_Notify |
映射类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Map; import java.util.Optional; @Data @Component @ConfigurationProperties("tieasy.mqs") public class MqsProperties { public static final String INSTANCE_INTERNAL = "internal"; public static final String INSTANCE_EXTERNAL = "external"; private Map<String, RocketMQProperties> instances; private TopicProperties topics; public RocketMQProperties getInstanceByName(String instanceName){ return Optional.ofNullable(instances) .map(m -> m.get(instanceName)) .orElse(null); } @Data public static class TopicProperties { private String crane; private String lift; private WuxiTopics wuxi; private XuzhouTopics xuzhou; private String bazhou; } @Data public static class WuxiTopics { private String deviceAdd; private String deviceEdit; private String deviceDelete; } @Data public static class XuzhouTopics { private String deviceStatus; private String deviceData; private String deviceAdd; private String deviceDelete; } @Data public static class RocketMQProperties { private String appId; private String appSecret; private String namesrcAddr; private Integer consumeThreadMax = 32; private Integer consumeThreadMin = 8; } } |
启动类
1 2 3 4 5 6 7 8 9 |
@SpringBootApplication @EnableConfigurationProperties public class MySpringbootApplication { public static void main(String[] args) { SpringApplication.run(MySpringbootApplication.class, args); } } |
使用
1 2 |
@Autowired private MqsProperties mqsProperties; |
spring profile 与 maven profile 多环境管理
实际开发中一个项目至少对应开发、测试、生成三种环境,如何方便的管理多环境是今天的要讨论的主题
Spring Profile
Spring Profile 是 Spring 提供的多环境管理方案。

如图,每种环境都对应一个 properties
文件,然后在application.properties
中配置一下要使用的环境
1 |
spring.profiles.active=dev |
上面配置匹配的是 application-dev.properties
,如果写的是test,则匹配 application-test.properties
。也就是说,Spring Profile 对配置文件的命名有要求,必须是 application-
开头
除了配置环境外,一些不随环境而变化的配置也应该放到 application.properties
中,application-.properties
最好只存放与环境相关的配置项
以上就是 Spring Profile 给出的多环境管理方案。通过改变 spring.profiles.active
的值来切换不同的环境。这种方法简单易懂,但有两个问题。
- 每次切换环境要手动修改
spring.profiles.active
的值 - 打包的时候,要手动删除其它环境的配置文件,不然其它环境的敏感信息就都打包进去了
为了解决这两个问题,我们需要 maven profile 的配合
maven profile
maven 的 profile 可以让我们定义多套配置信息,并指定其激活条件,然后在不同的环境下使用不同的profile配置。
profile 的定义位置
在maven中有两个地方可以配置 profile
pom.xml
中:这里面定义的 profile 作用范围是当前项目{user}/.m2/settings.xml
中:这里面定义的 profile 作用范围是所有使用了该配置文件的项目
settings.xml
中的 profile
不同的地方 profile 中能定义的信息也不相同
- 由于settings.xml作用范围宽泛, profile 中只能定义一些公共信息,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"> ... <profiles> <profile> <id>...</id> <activation>...</activation> <repositories>...</repositories> </profile> </profiles> ... </settings> |
- id:该 profile 的唯一标识
- activation:在哪些情况下激活 profile,这里面有多种策略可供选择,只要满足其中一个条件就激活
- repositories:远程仓库
由于能配置的东西有限,一般都会将 maven profile 配置在pom.xml
pom.xml
中 的profile
pom.xml
中:profile 能定义的东西就非常多了,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<profiles> <profile> <id>..</id> <activation>...</activation> <build>...</build> <modules>...</modules> <repositories>...</repositories> <pluginRepositories>...</pluginRepositories> <dependencies>...</dependencies> <reporting>...</reporting> <dependencyManagement>...</dependencyManagement> <distributionManagement>...</distributionManagement> </profile> </profiles> |
当然我们的目的也不是把它配全,而是解决 Spring Profile 遗留下来的两个问题。
首先看第一个问题
问题1
“每次切换环境要手动修改spring.profiles.active
的值”
这个问题就可以通过配置 profile 解决,在pom的根节点下添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<profiles> <profile> <id>dev</id> <activation> <!-- activeByDefault 为 true 表示,默认激活 id为dev 的profile--> <activeByDefault>true</activeByDefault> </activation> <!-- properties 里面可以添加自定义节点,如下添加了一个env节点 --> <properties> <!-- 这个节点的值可以在maven的其他地方引用,可以简单理解为定义了一个叫env的变量 --> <env>dev</env> </properties> </profile> <profile> <id>test</id> <properties> <env>test</env> </properties> </profile> <profile> <id>prod</id> <properties> <env>prod</env> </properties> </profile> </profiles> |
如上,定义了三套环境,其中id为dev的是默认环境,三套环境中定义了叫 env的“变量”
如果你用的是idea编辑器,添加好后,maven控件窗口应该会多出一个 Profiles,其中默认值就是上面配置的dev

最小化的 profiles 已经配置好了,通过勾选上图中的Profiles,就可以快速切换 maven的 profile 环境。
现在 maven profile 可以通过 勾选上图中的Profiles 快速切换环境
Spring Profile 还得通过 手动修改spring.profiles.active
的值来切环境
现在的问题是怎样让 maven profile的环境与Spring Profile一一对应,达到切换maven profile环境时,Spring Profile环境也被切换了
还记得maven profile 中定义的 env “变量”吗,现在只需要把
1 |
spring.profiles.active=dev |
改成
1 |
spring.profiles.active=@env@ |
就将maven profile 与 Spring Profile 环境关联起来了
当maven profile 将 环境切换成 test 时,在pom中定义的id为test的profile环境将被激活,在该环境下env的值是test,maven插件会将 @env@ 替换为 test,这样Spring Profile的环境也随之发生了改变。从上面可以看出,自定义的”变量”env的值还不能乱写,要与Spring Profile的环境相对应。

总结
- 第一步,在
pom
文件中配置 profiles - 第二步,在
application.properties
配置文件中添加spring.profiles.active=@env@
问题2
打包的时候,要手动删除其它环境的配置文件,不然其它环境的敏感信息就都打包进去了
解决这个问题需要在pom根节点下中配置 build 信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<build> <resources> <resource> <directory>src/main/resources</directory> <excludes> <!--先排除application开头的配置文件--> <exclude>application*.yml</exclude> </excludes> </resource> <resource> <directory>src/main/resources</directory> <!--filtering 需要设置为 true,这样在include的时候,才会把 配置文件中的@env@ 这个maven`变量`替换成当前环境的对应值 --> <filtering>true</filtering> <includes> <!--引入所需环境的配置文件--> <include>application.yml</include> <include>application-${env}.yml</include> </includes> </resource> </resources> <!--<plugins>--> <!-- <plugin>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-maven-plugin</artifactId>--> <!-- </plugin>--> <!--</plugins>--> </build> |
- directory:资源文件所在目录
- includes:需要包含的文件列表
- excludes:需要排除的文件列表
如上,配置了两个 <resource>
,第一个先排除了src/main/resources目录下所有 application 开头是配置文件,第二个在第一个的基础上添加了所需的配置文件。注意 application-${env}.yml
,它是一个动态变化的值,随着当前环境的改变而改变,假如当前环境是 id叫 dev的 profile,那么env的值为 dev。
这样配置后,maven在build时,就会根据配置先排除掉指定的配置文件,然后根据当前环境添加所需要的配置文件。
使用spring自定义参数解析器和自定义注解实现可插拔的获取登陆用户信息
前言
Web系统里,调接口时,通常需要获取当前登陆系统的用户信息,实现方法有很多,常见的是在aop切面里拦截接口进行是否鉴权验证,然后获取当前用户信息。
本篇文章,主要介绍的是另一种方式,spring的自定义参数解析器HandlerMethodArgumentResolver,结合自定义注解实现。
使用HandlerMethodArgumentResolver可以灵活的给某个接口做统一的参数处理,废话不多说,以下是实现步骤。
一、定义一个注解
改注解是用于标记待处理的参数,这边就是我们的User。
1 2 3 4 5 |
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface SecurityUser { // http://791202.com/ } |
二、自定义参数处理类SecurityUserMethodArgumentResolver
让它实现HandlerMethodArgumentResolver,需要根据自己的业务重写两个方法。
- supportsParameter:用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument。
- resolveArgument:真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; @Slf4j public class SecurityUserMethodArgumentResolver implements HandlerMethodArgumentResolver { // 类型是User并且被@SecurityUser修饰的参数才被处理 @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(User.class) && parameter.hasParameterAnnotation(SecurityUser.class); } // 这边我使用的是shiro安全框架,其它的也一样,就是获取到当前登陆用户,然后返回 @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, NativeWebRequest webRequest, WebDataBinderFactory factory) { Subject subject = SecurityUtils.getSubject(); if (subject.isAuthenticated()) { return subject.getPrincipal(); } else { throw new AuthenticationException("未登录"); } } } |
三、controller类里使用
1 2 3 4 |
@PostMapping(value = "/xxxxxxx", produces = "application/json;charset=UTF-8") public void xxxxxx(@RequestBody CustomerDTO customer, @SecurityUser User user) { user.getXXX... } |
Spring的版本GA、PRE、SNAPSHOT区别说明
GA:General Availability,正式发布的版本,官方推荐使用此版本。在国外都是用GA来说明release版本的
PRE: 预览版,内部测试版. 主要是给开发人员和测试人员测试和找BUG用的,不建议使用
SNAPSHOT: 快照版,可以稳定使用,且仍在继续改进版本
Eclipse里Spring MVC4+Spring 4+Mybatis3+junit4+Maven3.0 整合搭建
创建maven工程
点击Eclipse菜单栏File->New->Ohter->Maven->选择项目目录->next->选择项目类型


搜索web,创建项目->next

填写groupId和artifact Id->finish

项目配置
右击项目-new

创建如下几个文件

配置build path

分别修改输出路径为
- src/main/resources对应target/classes
- src/main/java对应target/classes
- src/test/resources对应target/test-classes
- src/test/java对应target/test-classes

设置JDK版本


设置部署程序集(Web Deployment Assembly)

把两个test目录去掉!!!
到此项目算是部署完成了。
spring mvc+spring+mybatis配置
pom.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xxl.maven_web</groupId> <artifactId>xxl_maven_web</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>xxl_maven_web Maven Webapp</name> <url>http://maven.apache.org</url> <build> <finalName>xxl_maven_web</finalName> </build> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-mock</artifactId> <version>2.0.8</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.7</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.6.RELEASE</version> <scope>test</scope> </dependency> </dependencies> </project> |
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5" > <!-- 区分项目名称,防止默认重名 --> <context-param> <param-name>webAppRootKey</param-name> <param-value>maven.xxl_web.root</param-value> </context-param> <!-- Spring的log4j监听器 --> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</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> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 设置Spring容器加载配置文件路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-*.xml</param-value> </context-param> <!-- Spring view分发器 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> <!--拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。 拦截/,restful风格 弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。解决办法看dispatcher--> </servlet-mapping> <!-- Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。 可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext); DispatcherServlet是一个Servlet,可以同时配置多个,每个 DispatcherServlet有一个自己的上下文对象(WebApplicationContext),称为子上下文(子容器),子上下文可以访问父上下文中的内容, 但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时, 会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是DispatcherServlet.class.getName() + ".CONTEXT"。 可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>/login.jsp</welcome-file> </welcome-file-list> </web-app> |
dispatcher-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- <mvc:annotation-driven /> --> <!--注解说明 <context:annotation-config />--> <!-- @Controller 声明Action组件 @Service 声明Service组件 @Service("myMovieLister") @Repository 声明Dao组件 @Component 泛指组件, 当不好归类时. @RequestMapping("/menu") 请求映射 @Resource 用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName") @Autowired 用于注入,(spring提供的) 默认按类型装配 @Transactional( rollbackFor={Exception.class}) 事务管理 @ResponseBody @Scope("prototype") 设定bean的作用域 --> <context:component-scan base-package="com.xxl.app.**.control" /> <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/> <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/> <bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" > <!--<property name="messageConverters"> <list> <ref bean="byteArray_hmc" /> <ref bean="string_hmc" /> </list> </property>--> </bean> <!-- <bean id="byteArray_hmc" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />处理.. <bean id="string_hmc" class="org.springframework.http.converter.StringHttpMessageConverter" />处理.. --> <bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <!-- 只会拦截@RequestMapping的URL <property name="interceptors"> <list> <bean class="com.mvc.MyInteceptor"></bean> </list> </property> --> </bean> <!-- freemarker的配置 --> <bean id="freemarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/view/" /> <property name="defaultEncoding" value="UTF-8" /> <property name="freemarkerSettings"> <props> <prop key="template_update_delay">10</prop> <prop key="locale">zh_CN</prop> <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop> <prop key="date_format">yyyy-MM-dd</prop> <prop key="number_format">#.##</prop> </props> </property> </bean> <!-- FreeMarker视图解析 如返回userinfo。。在这里配置后缀名ftl和视图解析器。。 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name="suffix" value=".html" /> <property name="contentType" value="text/html;charset=UTF-8" /> <property name="exposeRequestAttributes" value="true" /> <property name="exposeSessionAttributes" value="true" /> <property name="exposeSpringMacroHelpers" value="true" /> </bean> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="i18n/messages" /> <!-- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".html" /> </bean> --> <!-- 全局拦截器 会拦截静态资源 Spring为我们提供了: org.springframework.web.servlet.HandlerInterceptor接口, org.springframework.web.servlet.handler.HandlerInterceptorAdapter适配器, 实现这个接口或继承此类,可以非常方便的实现自己的拦截器。 有以下三个方法: Action之前执行: public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler); 生成视图之前执行 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView); 最后执行,可用于释放资源 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) <mvc:interceptors > <mvc:interceptor> <mvc:mapping path="/user/*" /> /user/* <bean class="com.mvc.MyInteceptor"></bean> </mvc:interceptor> </mvc:interceptors> --> <!-- 总错误处理 这里主要的类是SimpleMappingExceptionResolver类,和他的父类AbstractHandlerExceptionResolver类。 具体可以配置哪些属性,我是通过查看源码知道的。 你也可以实现HandlerExceptionResolver接口,写一个自己的异常处理程序。 <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView"> <value>/error/error</value> error页面可以通过Exception e = (Exception)request.getAttribute("exception")获得异常信息 </property> <property name="defaultStatusCode"> <value>500</value> </property> <property name="warnLogCategory"> <value>org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</value> </property> </bean>--> <!-- 对静态资源文件的访问 方案一 (二选一) 会把"/**" url,注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler处理并返回. DefaultServletHttpRequestHandler使用就是各个Servlet容器自己的默认Servlet. <mvc:default-servlet-handler/> --> <!-- 对静态资源文件的访问 方案二 (二选一)/images/**映射到ResourceHttpRequestHandler进行处理, location指定静态资源的位置.可以是web application根目录下、jar包里面,这样可以把静态资源压缩到jar包中。 cache-period 可以使得静态资源进行web cache <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/> <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>--> </beans> |

applicationContext-bean.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<?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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <context:component-scan base-package="com.xxl.app.**.service" /> <!-- 用于持有ApplicationContext,可以使用SpringContextHolder.getBean('xxxx')的静态方法得到spring bean对象 --> <bean class="com.xxl.app.base.SpringContextHolder" lazy-init="false" /> <!-- 使用annotation注解方式配置事务 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 使用JDBC事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 引入配置文件 --> <context:property-placeholder location="classpath:ini.properties"/> <!--创建jdbc数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <property name="maxActive" value="${maxActive}"/> <property name="maxIdle" value="${maxIdle}"/> <property name="minIdle" value="${minIdle}"/> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis.xml"/> <!-- <property name="mapperLocations"> <list> 表示在包或以下所有目录中,以-mapper.xml结尾所有文件 <value>classpath:config/**/*-mapper.xml</value> </list> </property> --> </bean> <!-- 配置mybatis dao注册,所有dao都继承sqlMapper annotationClass:当指定了annotationClass的时候,MapperScannerConfigurer将只注册使用了annotationClass注解标记的接口。 markerInterface:markerInterface是用于指定一个接口的,当指定了markerInterface之后,MapperScannerConfigurer将只注册继承自markerInterface的接口。 如果上述两个属性都指定了的话,那么MapperScannerConfigurer将取它们的并集,而不是交集。即使用了annotationClass进行标记或者继承自markerInterface 的接口都将被注册为一个MapperFactoryBean。 除了用于缩小注册Mapper接口范围的属性之外,我们还可以指定一些其他属性,如: sqlSessionFactory:这个属性已经废弃。当我们使用了多个数据源的时候我们就需要通过sqlSessionFactory来指定在注册MapperFactoryBean的时候需要使用的SqlSessionFactory, 因为在没有指定sqlSessionFactory的时候,会以Autowired的方式自动注入一个。换言之当我们只使用一个数据源的时候,即只定义了一个SqlSessionFactory的时候我们就可以不给 MapperScannerConfigurer指定SqlSessionFactory。 sqlSessionFactoryBeanName:它的功能跟sqlSessionFactory是一样的,只是它指定的是定义好的SqlSessionFactory对应的bean名称。 sqlSessionTemplate:这个属性已经废弃。它的功能也是相当于sqlSessionFactory的,因为就像前面说的那样,MapperFactoryBean最终还是使用的SqlSession的getMapper 方法取的对应的Mapper对象。当定义有多个SqlSessionTemplate的时候才需要指定它。对于一个MapperFactoryBean来说SqlSessionFactory和SqlSessionTemplate只需要其中一个就可以了, 当两者都指定了的时候,SqlSessionFactory会被忽略。 sqlSessionTemplateBeanName:指定需要使用的sqlSessionTemplate对应的bean名称。--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xxl.app.**.dao"/> <property name="markerInterface" value="com.xxl.app.base.dao.SqlMapper"/> </bean> </beans> |
mybaitis.xml
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 在这个文件放置一些全局性的配置 <typeAliases> <typeAlias type="com.*.*.bean.*" alias="*"/> </typeAliases> --> </configuration> |
单元测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import com.xxl.app.base.service.IIndexService; @ContextConfiguration(locations={"classpath:applicationContext-bean.xml"}) @RunWith(SpringJUnit4ClassRunner.class) @Transactional //如果是true不会改变数据库数据,如果是false会改变数据 @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true) public class IndexService { @Autowired private IIndexService indexService; @Test public void TestIndex(){ indexService.doIndex(); } } |