最近整理了下之前看到的,用过的spring的一些实用知识,特写出来供同学们参考,感觉前人的成果~
spring容器介绍:
1、 spring容器为父容器,springMvc容器为子容器
2、 父容器由ContextLoaderListener加载,子容器由DispatcherServlet加载
3、 子容器可以访问父容器中的bean,但是父容器不可以访问子容器中的bean
注意点:明确容器之间的关系,明白各自配置文件中的内容的作用域
Spring对于静态变量怎么初始化赋值?
很多时候,我们在工作中需要将一些常用配置写入properties配置文件,然后将值赋给一个静态变量供程序调用,spring默认是没有静态变量赋值的,需要我们自己去实现,请看下面2种方法:
方法一:
1、新建静态变量
public class ConstantUtil {
// 云OSS公共图片路径(正式路径请用pub_img/,测试路径请用test_pub_img/)
public static String OSS_PUB_IMG;
// 云OSS 公共图片前缀url
public static String OSS_PUB_IMG_URL;
}
2、新建配置文件properties,设置静态变量对应的值
3、在spring配置文件中加载配置文件
<!-- 加载配置文件 ,多个用逗号隔开-->
<context:property-placeholder location="classpath:properties/antx_admin.properties" />
4、新建一个类实现spring的InitializingBean和ServletContextAware接口
public class SystemInitBean implements InitializingBean, ServletContextAware {
public void setOssPubImg(String ossPubImg) {
ConstantUtil.OSS_PUB_IMG = ossPubImg;
}
public void setOssPubImgUrl(String ossPubImgUrl) {
ConstantUtil.OSS_PUB_IMG_URL = ossPubImgUrl;
}
@Override
public void setServletContext(ServletContext servletContext) {
// TODO Auto-generated method stub
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
5、然后在spring配置文件中注入SystemInitBean这个类,将properties的值赋给静态变量
<!-- 初始化antx_admin.properties配置文件中的值到静态变量中 -->
<bean class="com.rightknights.web.admin.listener.SystemInitBean">
<property name="ossPubImg" value="${OSS_PUB_IMG}"/>
<property name="ossPubImgUrl" value="${OSS_PUB_IMG_URL}"/>
</bean>
方法二
1、同上,新建静态变量
2、在spring配置文件中,通过propertiesFactoryBean加载配置文件
<!-- 注意这个id为setting,后面要用到 -->
<bean id="setting"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:properties/antx_admin.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8"></property>
</bean>
3、新建初始化类,并在spring配置文件中配置扫描包
@Component
public class ConfigProperty {
private static ConfigProperty insstance;
public static ConfigProperty getInstance(){
return insstance;
}
public ConfigProperty(){
insstance = this;
}
@Value("#{setting['OSS_PATH']}")
private String OSS_PATH;
@Value("#{setting['DOP_URL']}")
private String DOP_URL;
public String getOSS_PATH() {
return OSS_PATH;
}
public void setOSS_PATH(String oSS_PATH) {
OSS_PATH = oSS_PATH;
}
public String getDOP_URL() {
return DOP_URL;
}
public void setDOP_URL(String dOP_URL) {
DOP_URL = dOP_URL;
}
}
4、调用方法
ConfigProperty.getInstance().getOSS_PATH
spring的@value注解使用
在实际开发中,很多时候也需要对普通变量进行初始化赋值,那么我们可以通过spring的@Value注解来解决
1、新建配置文件
2、加载配置文件
<!-- 加载配置文件 ,多个用逗号隔开-->
<context:property-placeholder location="classpath:properties/antx_admin.properties" />
3、在实际代码中使用
@Value("${demo}")
private String demo;
controller层加事务控制(@Transactional)
基本上spring的事务是配置在service层的,而事务生效也是在spring核心容器中,这是事务一致性保证的,如果我们有需求在controller层配置事务,来控制回滚等,那么就需要以下配置:
1、加入约束(有部分多余的约束,自行处理)
<!-- Bean头部 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task"
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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
2、开始事务注解
<!-- controller层事务注解 -->
<tx:annotation-driven/>
spring之BeanWrapperImpl实用举例
BeanWrapperImpl类是对BeanWrapper接口的默认实现,它包装了一个bean对象,缓存了bean的内省结果,并可以访问bean的属性、设置bean的属性值。BeanWrapperImpl类提供了许多默认属性编辑器,支持多种不同类型的类型转换,可以将数组、集合类型的属性转换成指定特殊类型的数组或集合。用户也可以注册自定义的属性编辑器在BeanWrapperImpl中。我们在实际工作中,可以有很多业务场景用到此类才实现通用方法,见下:
1、 需要将一个集合中所有对象某个具体字段的值全部取出来,以便后续使用,正常做法是循环取(如果后面别的集合也有此情况,又得循环),而通过BeanWrapperImpl,我们可以做一个通用的工具类。
第一个为去重的方法
public static <T> Set<T> getPropertieSet(Collection<?> collection, String propertyName, Class<T> clazz) {
Set<T> set = new HashSet<T>();
if (null != collection) {
for (Object item : collection) {
BeanWrapper beanWrapper = new BeanWrapperImpl(item);
@SuppressWarnings("unchecked")
T property = (T) beanWrapper.getPropertyValue(propertyName);
set.add(property);
}
}
return set;
}
public static <T> List<T> getProperties(Collection<?> collection, String propertyName, Class<T> clazz) {
List<T> list = new ArrayList<T>();
if (null != collection) {
for (Object item : collection) {
BeanWrapper beanWrapper = new BeanWrapperImpl(item);
@SuppressWarnings("unchecked")
T property = (T) beanWrapper.getPropertyValue(propertyName);
list.add(property);
}
}
return list;
}
2、 需要根据集合中某个具体字段分组,取出此字段对应的具体对象来进行后续工作。
public static <T> List<T> getProperties(Collection<?> collection, String propertyName, Class<T> clazz) {
List<T> list = new ArrayList<T>();
if (null != collection) {
for (Object item : collection) {
BeanWrapper beanWrapper = new BeanWrapperImpl(item);
@SuppressWarnings("unchecked")
T property = (T) beanWrapper.getPropertyValue(propertyName);
list.add(property);
}
}
return list;
}
3、
/**
*
* @author: kuangkuang
* @Description: 根据collection中T对象的某个属性keyPropertyName;和它对应的具体值,取出对应的该对象
* @Date: 2016年12月21日 上午11:10:48
* @param collection
* @param keyPropertyName
* @param valuePropertyName
* @param allClazz
* @return
* @throws
*/
@SuppressWarnings("unchecked")
public static <K, T> List<T> getBeanByProperty(Collection<T> collection, String keyPropertyName,
K valuePropertyName, Class<K> allClazz) {
List<T> result = new ArrayList<T>();
if (null != collection) {
for (T item : collection) {
BeanWrapper beanWrapper = new BeanWrapperImpl(item);
K property = (K) beanWrapper.getPropertyValue(keyPropertyName);
if (property .equals(valuePropertyName)) {
result.add(item);
}
}
}
return result;
}
4、
/**
* 根据T中某个字段的值来获取在sources里面存在,在targets里面不存在的集合(支持当前传入字段的属性值为基础类型)
*
* @param sources
* @param targets
* @param argumentName(字段名称)
* @return
*/
public static <T> List<T> diffCollection(List<T> sources, List<T> targets, String argumentName) {
List<T> result = new ArrayList<T>();
if (CollectionUtils.isEmpty(targets)) {
return result;
}
if (CollectionUtils.isEmpty(sources)) {
return result;
}
Map<Object, T> map = initMap(sources, argumentName);
for (T t : targets) {
BeanWrapper beanWrapper = new BeanWrapperImpl(t);
Object value = beanWrapper.getPropertyValue(argumentName);
if (map.get(value) == null) {
result.add(t);
}
}
return result;
}
private static <T> Map<Object, T> initMap(List<T> sources, String argumentName) {
Map<Object, T> resultMap = new HashMap<Object, T>();
for (T t : sources) {
BeanWrapper beanWrapper = new BeanWrapperImpl(t);
if (beanWrapper.getPropertyValue(argumentName) == null) {
throw new RuntimeException("当前属性值为null");
}
resultMap.put(beanWrapper.getPropertyValue(argumentName), t);
}
return resultMap;
}
spring之controller层aop编程
实际开发中可能需要对controller每个用户调了什么方法进行一个记录,方便查询,例子如下:
定义一个切点类,对controller进行拦截,在调用controller下面所有方法之前执行前置处理,将登陆用户,ip地址,调了什么方法等信息写入数据库,方便后续管理。
1、 springMvc配置文件开启AspectJ,开启cglib代理(注意加入aop约束信息)
<!-- 启动AspectJ支持 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
2、新建切点类(注意在配置文件中配置扫描包)
@Aspect
@Component
public class ControllerAspect {
private static Logger logger = LoggerFactory.getLogger(ControllerAspect.class);
@Autowired
private TbAspectLogService tbAspectLogService;
@Pointcut("execution(* com.xx.web.admin.controller..*.*(..))")
public void controller() {
}
@Before(value = "controller()")
public void daBefore(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
HttpSession session = request.getSession();
// 读取session中的用户
ShiroUserDo user = (ShiroUserDo) session.getAttribute("user");
if (null == user) {
return;
}
// 请求的IP
String ip = request.getRemoteAddr();
try {
// *========控制台输出=========*//
System.out.println("=====前置通知开始=====");
System.out.println("请求方法:"
+ (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
System.out.println("请求人:" + user.getUserName());
System.out.println("请求IP:" + ip);
// *========数据库日志=========*//
TbAspectLogDo tbAspectLogDo = new TbAspectLogDo();
tbAspectLogDo.setMethod(
(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
tbAspectLogDo.setType(AspectLogTypeEnum.CONTROLLER.getCode());
tbAspectLogDo.setRequestIp(ip);
tbAspectLogDo.setExceptionCode(null);
tbAspectLogDo.setExceptionDetail(null);
tbAspectLogDo.setParams(null);
tbAspectLogDo.setCreater(user.toString());
long currentTimeMillis = System.currentTimeMillis();
tbAspectLogDo.setCreateTime(currentTimeMillis);
tbAspectLogDo.setModifyTime(currentTimeMillis);
// 保存数据库
tbAspectLogService.insert(tbAspectLogDo);
System.out.println("=====前置通知结束=====");
} catch (Exception e) {
// 记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
}
spring全局异常处理(实现HandlerExceptionResolver方式)
有些时候,我们在程序中可能会漏掉某些异常,导致用户直接看到了异常报错页面,这是非常不人性化的体现,对于这些漏网之鱼,spring提供了全局异常处理方案,如下:
1、实现spring的HandlerExceptionResolver(注意在配置文件中配置扫描包)
@Component
public class xxHandlerExceptionResolver implements HandlerExceptionResolver {
private static Logger logger = LoggerFactory.getLogger(xxHandlerExceptionResolver.class);
@Autowired
private TbAspectLogService tbAspectLogService;
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// 把漏网的异常信息记入日志
logger.error("Catch Exception: ", ex);
HttpSession session = request.getSession();
// 读取session中的用户
ShiroUserDo user = (ShiroUserDo) session.getAttribute("user");
if (null == user) {
return new ModelAndView("login");
}
// *========数据库日志=========*//
// 请求的IP
String ip = request.getRemoteAddr();
TbAspectLogDo tbAspectLogDo = new TbAspectLogDo();
tbAspectLogDo.setRequestIp(ip);
tbAspectLogDo.setExceptionCode(ex.getClass().getName());
tbAspectLogDo.setExceptionDetail(ex.getMessage());
tbAspectLogDo.setCreater(user.toString());
long currentTimeMillis = System.currentTimeMillis();
tbAspectLogDo.setCreateTime(currentTimeMillis);
tbAspectLogDo.setModifyTime(currentTimeMillis);
// 保存数据库
tbAspectLogService.insert(tbAspectLogDo);
/* 使用response返回 */
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 设置状态码
response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 设置ContentType
response.setCharacterEncoding("UTF-8"); // 避免乱码
response.setHeader("Cache-Control", "no-cache, must-revalidate");
try {
response.getWriter().write("{\"success\":false,\"msg\":\"" + ex.getMessage() + "\"}");
} catch (IOException e) {
logger.error("与客户端通讯异常:" + e.getMessage(), e);
}
return new ModelAndView();
}
}
自定义注解接收json方式的参数
Spring自带的接收参数的方式有很多,但对于实际开发中可以有一些特殊需求,spring对于这种需求提供了支持,今天以接收json方法的参数为举例,见下:
1、
/**
* 用来标识一个参数,使之以JSON方式来绑定数据(同一方法上只能出现一个该注解)。
*
* @author kuangkuang
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER })
@Documented
public @interface JsonParam {
public String value() default "";
}
2、实现spring的HandlerMethodArgumentResolver参数解析器接口
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
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;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.TypeUtils;
/**
*
* @ClassName: JsonParameterBinder
* @Description: TODO
* @author Comsys-kuangkuang
* @date 2016年11月29日 上午11:53:45
*
*/
public class JsonParameterBinder implements HandlerMethodArgumentResolver {
private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> parameterType = parameter.getParameterType();
String name = parameter.getParameterName();
if (parameterType.getName().equals(Object.class.getName())) {
throw new RuntimeException(String.format("no exact type assigned for parameter '%s'", name));
}
String body = getRequestBody(webRequest);
if (body != null) {
Object obj = null;
try {
obj = JSON.parse(body);
} catch (Exception e) {
return null;
}
return parseResult(parameter, obj);
}
return null;
}
/**
*
* @author: kuangkuang
* @Description: 获取json串
* @Date: 2016年11月28日 上午10:39:56
* @param webRequest
* @return
* @throws
*/
private String getRequestBody(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String jsonBody = (String) webRequest.getAttribute(JSONBODYATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
if (jsonBody == null) {
try {
jsonBody = IOUtils.toString(servletRequest.getInputStream());
webRequest.setAttribute(JSONBODYATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return jsonBody;
}
/**
*
* @author: kuangkuang
* @Description: 将JSON对象转换成指定的用户返回值类型
* @Date: 2016年11月28日 上午10:40:22
* @param parameter
* @param resultObject
* @return
* @throws JSONException
* @throws
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Object parseResult(MethodParameter parameter, Object resultObject)
throws JSONException {
if (resultObject == null) {
throw new JSONException("result is empty.");
}
Object result = null;
Class<?> parameterType = parameter.getParameterType();
boolean isArray = parameterType.isArray();
boolean isCollection = Collection.class.isAssignableFrom(parameterType);
Class<?> componentType = parameterType.getComponentType();
if ((isArray || isCollection) && resultObject instanceof JSONArray) {
if (!isArray) {
componentType = (Class<?>) (((ParameterizedType) parameter
.getGenericParameterType()).getActualTypeArguments()[0]);
}
JSONArray jsonArray = (JSONArray) resultObject;
int size = jsonArray.size();
if (isArray) {
result = Array.newInstance(componentType, size);
} else {
result = new ArrayList(size);
}
for (int i = 0; i < size; i++) {
Object value = jsonArray.getObject(i, componentType);
if (isArray) {
Array.set(result, i, value);
} else {
((List) result).add(value);
}
}
} else {
if (resultObject instanceof JSONObject) {
result = JSONObject.toJavaObject((JSONObject) resultObject,
parameterType);
} else {
result = TypeUtils.castToJavaBean(resultObject, parameterType);
}
}
return result;
}
}