• Tag Archives: spring

Spring自定义注解(parameter)

1、新建注解接口VerifyAccount.java:

package com.zero4j.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface VerifyAccount {

	String paramName() default "token";
	
	String permission() default "";
}

2、创建注解对应的切面类VerifyAccountAspect.java:

package com.zero4j.annotation;

import net.sf.json.JSONObject;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
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 org.springframework.web.multipart.support.MissingServletRequestPartException;

import com.zero4j.model.account.Account;
import com.zero4j.model.token.util.TokenStaticUtil;

@Aspect
@Component
public class VerifyAccountAspect implements HandlerMethodArgumentResolver{

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(VerifyAccount.class);
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		
		String token = webRequest.getParameter("token");
		
		 JSONObject out = new JSONObject();
		
		if(token==null||token.equals("")){
			out.put("status", 401);
			out.put("message", "请先注册并登录后再进行此操作");
			out.put("debug", "缺少参数token或为空");
			//ResponseStaticUtil.write(response,out);
			System.out.println(out.toString());
			//return;
		}
		Account account = TokenStaticUtil.getAccount(token);
		if(account==null){
			out.put("status", 401);
			out.put("message", "请先注册并登录后再进行此操作");
			out.put("debug", "token对应的account为空");
			//ResponseStaticUtil.write(response,out);
			System.out.println(out.toString());
			throw new MissingServletRequestPartException("account");
			//return;
		}
		
		return account;
	}
	
}

注意要实现接口:HandlerMethodArgumentResolver

3、最后要在SpringMVC的配置java类中加入对应的代码:

package com.zero4j.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.zero4j.annotation.VerifyAccountAspect;

@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {


	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
		
		resolvers.add(new VerifyAccountAspect());
		
		WebMvcConfigurer.super.addArgumentResolvers(resolvers);
	}

}

注意这行:resolvers.add(new VerifyAccountAspect());

Spring自定义注解(method)

先定义一个注解接口 VerifyToken.java :

package com.zero4j.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface VerifyToken {

	//权限参数
	String permission() default "";
	
}

再在同目录中定义一个切面类VerifyTokenAspect.java:

package com.zero4j.annotation;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.zero4j.model.account.Account;
import com.zero4j.model.permission.util.PermissionStaticUtil;
import com.zero4j.model.token.util.TokenStaticUtil;
import com.zero4j.util.ResponseStaticUtil;

@Aspect
@Component
public class VerifyTokenAspect {

	@Pointcut("@annotation(com.zero4j.annotation.VerifyToken)")	//这是annotation对应类的所在位置,若目录不同,则填写完整路径,如:com.zero4j.annotation.VerifyToken
    private void pointcut(){
		
	}
	
	@Around("pointcut()&&@annotation(verifyToken)")
	public void around(ProceedingJoinPoint pjp, VerifyToken verifyToken) throws Throwable{
		
		//System.out.println("annotation执行前");
		
		//System.out.println("verifyToken.permission() = "+verifyToken.permission());
		
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        
        String token = request.getParameter("token");
        
        JSONObject out = new JSONObject();
		
		if(token==null||token.equals("")){
			out.put("status", 401);
			out.put("message", "请先注册并登录后再进行此操作");
			out.put("debug", "缺少参数token或为空");
			ResponseStaticUtil.write(response,out);
			//System.out.println(out.toString());
			return;
		}
		Account account = TokenStaticUtil.getAccount(token);
		if(account==null){
			out.put("status", 401);
			out.put("message", "请先注册并登录后再进行此操作");
			out.put("debug", "token对应的account为空");
			ResponseStaticUtil.write(response,out);
			//System.out.println(out.toString());
			return;
		}
		
		if(!(verifyToken.permission()==null||verifyToken.permission().equals(""))){
			if(PermissionStaticUtil.verify(account.getId(), verifyToken.permission())==false){
				out.put("status", 403);
				out.put("message", "你无权进行本操作");
				out.put("debug", "你没有"+verifyToken.permission()+"权限");
				ResponseStaticUtil.write(response,out);
				//System.out.println(out.toString());
				return;
			}
		}
		
		if(out.size()==0){
			pjp.proceed();
		}

	}

}

最后在需要切入的地方加上注解即可:

	@VerifyToken(permission="adminLog_read")
	@RequestMapping(value="", method=RequestMethod.GET)
	public void list(HttpServletRequest request, HttpServletResponse response,
			@RequestParam(required=false) String token,
			@RequestParam(required=false) Integer offset,
			@RequestParam(required=false) Integer limit,
			@RequestParam(required=false) String startTime,
			@RequestParam(required=false) String endTime,
			@RequestParam(required=false) String accountNickname,
			@RequestParam(required=false) String accountMobile,
			@RequestParam(required=false) String description
		){
		
		........

		}

 

关于Spring+Hibernate时,packagesToScan无法扫描到jar中实体类的解决办法

由于要做分布式,因此不同的微服务中会用到相同的实体类,因此需要将实体类拿到独立的项目中去,然后生成为jar来被不同的项目引用。

但是问题来了,当我使用MyEclipse进行Export导出成jar,然后由其他微服务引用此jar时,发现hibernate并没有扫描到jar中的实体类。

于是我翻了好多资料,找到以下两种解决办法:

第一种:在配置文件中指定jar中的注解实体类

		<!-- 手动加载类映射文件(如果类在jar包中,则需要使用这种方式引入才会有有效) -->
		<property name="annotatedClasses">
			<list>
				<value>com.zero4j.model.test.Test</value>
			</list>
		</property>

但是这样做有个缺点,就是无法使用“*”号来进行通配,所以当你有十个类的时候,list中就得有十个value。

第二种(推荐):使用MyEclipse导出公共实体类时,要勾选“Add directory entiriess”

49f3fe9dd03935ab16bbeaa65c80b51

然后在需要引入该jar的项目中重新引入即可。

原因分析:

默认方式生成的jar包中,只含有class文件,而并没有我们大众所知的文件夹目录结构。可能我们大多数人认为com.test.Student类,Student类文件就应该在com文件夹下的test文件夹里,这其实是片面的,是一个误区!

com.test真正的含义是package包域名,就好比.net里的命名空间,它只是为了区分、汇总、唯一标识不同的类而提出的概念,跟文件夹目录层次结构是两回事,我们只是习惯上用文件夹目录来展示package而已。但package却不一定非要用过文件夹目录来展示。

我们可以用下面这段代码来进一步说明这个问题:

	public static void main(String[] args) throws Exception {
		// 项目中jar包所在物理路径
		String jarName = "E:/Work/stsf_skisok_product/WebRoot/WEB-INF/lib/testpackage.jar";
		JarFile jarFile = new JarFile(jarName);
		Enumeration<JarEntry> entrys = jarFile.entries();
		while (entrys.hasMoreElements()) {
			JarEntry jarEntry = entrys.nextElement();
			System.out.println(jarEntry.getName());
		}				
	}

默认生成的jar包,运行结果如下:

META-INF/MANIFEST.MF
com/wang/util/DateStyle.class
com/wang/util/PropertiesUtil$1.class
com/wang/util/PropertiesUtil.class
com/wang/util/Week.class
com/wang/util/DateUtil.class
com/wang/vo/request/hotel/test/PopularCityRequest.class
com/wang/vo/request/hotel/test/EconomicsRequest.class
com/wang/vo/request/hotel/test/HotelProductVouchRequest.class
com/wang/vo/request/hotel/test/QueryOrderListRequest.class
com/wang/vo/request/hotel/test/HotelListQueryRequest.class
com/wang/vo/request/hotel/test/RoomReserveRequest.class
com/wang/vo/request/hotel/test/HotelOneQueryRequest.class
com/wang/vo/request/hotel/test/HotelBrandRequest.class

如果勾选Add directory entries选项生成的jar包,运行结果如下:

META-INF/MANIFEST.MF
com/
com/wang/
com/wang/util/
com/wang/util/DateStyle.class
com/wang/util/PropertiesUtil$1.class
com/wang/util/PropertiesUtil.class
com/wang/util/Week.class
com/wang/util/DateUtil.class
com/wang/vo/
com/wang/vo/request/
com/wang/vo/request/hotel/
com/wang/vo/request/hotel/test/
com/wang/vo/request/hotel/test/PopularCityRequest.class
com/wang/vo/request/hotel/test/EconomicsRequest.class
com/wang/vo/request/hotel/test/HotelProductVouchRequest.class
com/wang/vo/request/hotel/test/QueryOrderListRequest.class
com/wang/vo/request/hotel/test/HotelListQueryRequest.class
com/wang/vo/request/hotel/test/RoomReserveRequest.class
com/wang/vo/request/hotel/test/HotelOneQueryRequest.class
com/wang/vo/request/hotel/test/HotelBrandRequest.class

这样也就解释了为何打成jar包后用getResource获取资源url总是为空的原因了。

close