Spring Framework 5.1.10 发布了,更新内容如下:

近期在捯饬spring的注解,现将遇到的问题记录下来,以供遇到同样问题的童鞋解决~

准备开始

首先导入jar包:jcaptcha-my-1.0

/**

 * web 常量

 * @author lx

 *

 */

public abstract class Constants {

/** 用户 session 的 cookie 名称*/

public static final String SESSION_ID = “JSESSIONID”;

}

 

新特性

  • 反向移植
    PR #22485 (不包括
    ShadowingClassLoader 中的 jdk 包)到 5.1
    分支 #23641
  • SimpleCacheManager 不应在 AbstractCacheManager#cacheMap
    上同步 #23635
  • MockClientHttpResponse 失去了原始的 HttpStatus
    代码 #23599
  • BeanUtils.isSimpleValueType() 不应将 void 或 Void
    视为简单值类型 #23573
  • ClassUtils.isPrimitiveOrWrapper(…) 应该将 Void.class 视为 void.class
    的原始包装器 #23572
  • 自定义 DefaultResourceLoader 子类应该能够使用上下文提供的
    ProtocolResolvers #23564
  • 超出 sendBufferSizeLimit
    时应在警告级别记录 #23534
  • 提供一种从有作用域的目标的 bean 名称中获取原始 bean
    名称的方法 #23514
  • 在 ResourceUrlEncodingFilter 中的 requestUri 中找不到 lookupPath
    的索引失败时提高 400(而不是
    500) #23508
  • Jetty WebSocket ExtensionFactory 上的
    IncompatibleClassChangeError #23500
  • Respect already set content-length header for HEAD
    request. #23484
  • Static resource support does not handle requests for a file with %
    character in its
    name #23463
  • 来自 WebFlux 服务器上 WebClient 的 Errors.NativeIoException
    未设置响应状态 #23319

此版本还包含大量 bug
修复,详情可查看更新说明。

(文/开源中国)    

先说明下场景,代码如下:

第一步:创建SessionProvider接口

cn.itcast.common.web.session.SessionProvider

public interface SessionProvider {

public void setAttribute(HttpServletRequest request,String name ,Serializable value);

//获取Session中的值

public Serializable getAttribute(HttpServletRequest request,String name);

//退出登陆

public void logout(HttpServletRequest request,HttpServletResponse response);

//获取Session Id

public String getSessionId(HttpServletRequest request);

}

有如下接口:

第二步:创建HttpSessionProvider实现类

cn.itcast.common.web.session.HttpSessionProvider

/**

 * Session提供类

 * @author lx

 *

 */

public class HttpSessionProvider implements SessionProvider{

 

 

//往Session中设置值

public void setAttribute(HttpServletRequest request,String name ,Serializable value){

HttpSession session = request.getSession();

if(session != null){

session.setAttribute(name, value);

}

}

//获取Session中的值

public Serializable getAttribute(HttpServletRequest request,String name){

HttpSession session = request.getSession(false);

if(session != null){

return (Serializable) session.getAttribute(name);

}else{

return null;

}

}

//退出登陆

public void logout(HttpServletRequest request,HttpServletResponse response){

HttpSession session = request.getSession(false);

if(session != null){

session.invalidate();

}

Cookie c  = new Cookie(Constants.SESSION_ID,null);

c.setMaxAge(0);

response.addCookie(c);

}

//获取Session Id

public String getSessionId(HttpServletRequest request){

return request.getRequestedSessionId();

}

 

}

<pre>

第三步:创建classpath:config/utils.xml

<!–   session 提供类  –>

<bean id=”sessionProvider”  class=”cn.itcast.common.web.session.HttpSessionProvider”/>

public

第四步:创建JcaptchaServlet

 图片 1

interface

第五步:创建classpth:config/captcha.xml

<?xml version=”1.0″ encoding=”UTF-8″?>

<beans xmlns=”” xmlns:xsi=””

xsi:schemaLocation=” “

default-lazy-init=”true”>

 

<bean id=”captchaService” class=”com.octo.captcha.service.multitype.GenericManageableCaptchaService”>

<constructor-arg index=”0″ ref=”imageEngine”/>

<constructor-arg type=”int” index=”1″ value=”180″/>

<constructor-arg type=”int” index=”2″ value=”100000″/>

<constructor-arg type=”int” index=”3″ value=”75000″/>

</bean>

<bean id=”imageEngine” class=”com.octo.captcha.engine.GenericCaptchaEngine”>

<constructor-arg index=”0″>

<list>

<ref bean=”captchaFactory”/>

</list>

</constructor-arg>

</bean>

 

<bean id=”captchaFactory” class=”com.octo.captcha.image.gimpy.GimpyFactory”>

<constructor-arg>

<ref bean=”wordgen”/>

</constructor-arg>

<constructor-arg>

<ref bean=”wordtoimage”/>

</constructor-arg>

</bean>

 

<bean id=”wordgen” class= “com.octo.captcha.component.word.wordgenerator.RandomWordGenerator”>

<!–可选字符–>

<constructor-arg>

<value>aabbccddeefgghhkkmnnooppqqsstuuvvwxxyyzz</value>

</constructor-arg>

</bean>

 

<bean id=”wordtoimage” class=”com.octo.captcha.component.image.wordtoimage.ComposedWordToImage”>

<constructor-arg index=”0″>

<ref bean=”fontGenRandom”/>

</constructor-arg>

<constructor-arg index=”1″>

<ref bean=”backGenUni”/>

</constructor-arg>

<constructor-arg index=”2″>

<ref bean=”decoratedPaster”/>

</constructor-arg>

</bean>

 

<bean id=”fontGenRandom” class=”com.octo.captcha.component.image.fontgenerator.RandomFontGenerator”>

<!–最小字体–>

<constructor-arg index=”0″>

<value>26</value>

</constructor-arg>

<!–最大字体–>

<constructor-arg index=”1″>

<value>34</value>

</constructor-arg>

<constructor-arg index=”2″>

<list>

<bean class=”java.awt.Font”>

<constructor-arg index=”0″><value>Arial</value></constructor-arg>

<constructor-arg index=”1″><value>0</value></constructor-arg>

<constructor-arg index=”2″><value>32</value></constructor-arg>

</bean>

</list>

</constructor-arg>

</bean>

<bean id=”backGenUni” class=”com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator”>

<!–背景宽度–>

<constructor-arg index=”0″>

<value>110</value>

</constructor-arg>

<!–背景高度–>

<constructor-arg index=”1″>

<value>50</value>

</constructor-arg>

</bean>

 

<bean id=”decoratedPaster” class=”com.octo.captcha.component.image.textpaster.DecoratedRandomTextPaster”>

<!–最大字符长度–>

<constructor-arg type=”java.lang.Integer” index=”0″>

<value>4</value>

</constructor-arg>

<!–最小字符长度–>

<constructor-arg type=”java.lang.Integer” index=”1″>

<value>4</value>

</constructor-arg>

<!–文本颜色–>

<constructor-arg index=”2″>

<ref bean=”colorGen”/>

</constructor-arg>

<!–文本混淆–>

<constructor-arg index=”3″>

<list>

<!–<ref bean=”baffleDecorator”/>–>

</list>

</constructor-arg>

</bean>

<bean id=”baffleDecorator” class=”com.octo.captcha.component.image.textpaster.textdecorator.BaffleTextDecorator”>

<constructor-arg type=”java.lang.Integer” index=”0″><value>1</value></constructor-arg>

<constructor-arg type=”java.awt.Color” index=”1″><ref bean=”colorWrite”/></constructor-arg>

</bean>

<bean id=”colorGen” class=”com.octo.captcha.component.image.color.SingleColorGenerator”>

<constructor-arg type=”java.awt.Color” index=”0″>

<ref bean=”colorBlack”/>

</constructor-arg>

</bean>

<bean id=”colorWrite” class=”java.awt.Color”>

<constructor-arg type=”int” index=”0″>

<value>255</value>

</constructor-arg>

<constructor-arg type=”int” index=”1″>

<value>255</value>

</constructor-arg>

<constructor-arg type=”int” index=”2″>

<value>255</value>

</constructor-arg>

</bean>

<bean id=”colorBlack” class=”java.awt.Color”>

<constructor-arg type=”int” index=”0″>

<value>50</value>

</constructor-arg>

<constructor-arg type=”int” index=”1″>

<value>50</value>

</constructor-arg>

<constructor-arg type=”int” index=”2″>

<value>50</value>

</constructor-arg>

</bean>

</beans>

EmployeeService {

第六步:编写JcaptchaServlet

/**

 * 提供验证码图片的Servlet

 */

@SuppressWarnings(“serial”)

public class JcaptchaServlet extends HttpServlet {

public static final String CAPTCHA_IMAGE_FORMAT = “jpeg”;

 

private ImageCaptchaService captchaService;

private SessionProvider session;

 

@Override

public void init() throws ServletException {

WebApplicationContext appCtx = WebApplicationContextUtils

.getWebApplicationContext(getServletContext());

captchaService = (ImageCaptchaService) BeanFactoryUtils

.beanOfTypeIncludingAncestors(appCtx, ImageCaptchaService.class);

session = (SessionProvider) BeanFactoryUtils

.beanOfTypeIncludingAncestors(appCtx, SessionProvider.class);

}

 

@Override

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

byte[] captchaChallengeAsJpeg = null;

// the output stream to render the captcha image as jpeg into

ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

try {

// get the session id that will identify the generated captcha.

// the same id must be used to validate the response, the session id

// is a good candidate!

 

String captchaId = session.getSessionId(request);

BufferedImage challenge = captchaService.getImageChallengeForID(

captchaId, request.getLocale());

// Jimi.putImage(“image/jpeg”, challenge, jpegOutputStream);

ImageIO.write(challenge, CAPTCHA_IMAGE_FORMAT, jpegOutputStream);

} catch (IllegalArgumentException e) {

response.sendError(HttpServletResponse.SC_NOT_FOUND);

return;

} catch (CaptchaServiceException e) {

response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

return;

}

// catch (JimiException e) {

// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

// return;

// }

 

captchaChallengeAsJpeg = jpegOutputStream.toByteArray();

 

// flush it in the response

response.setHeader(“Cache-Control”, “no-store”);

response.setHeader(“Pragma”, “no-cache”);

response.setDateHeader(“Expires”, 0);

response.setContentType(“image/” + CAPTCHA_IMAGE_FORMAT);

 

ServletOutputStream responseOutputStream = response.getOutputStream();

responseOutputStream.write(captchaChallengeAsJpeg);

responseOutputStream.flush();

responseOutputStream.close();

}

}

public

第七步:配置web.xml

<!– 配置Jcaptcha 验证码–>

<servlet>

<servlet-name>jcaptcha</servlet-name>

<servlet-class>cn.itcast.common.captcha.JcaptchaServlet</servlet-class>

</servlet>

 

<servlet-mapping>

<servlet-name>jcaptcha</servlet-name>

<url-pattern>/captcha.svl</url-pattern>

</servlet-mapping>

EmployeeDto getEmployeeById(Long id);
}

第八步:编写登陆页面front_page/buyer/login.jsp

<li>

<label for=”captcha”>验证码:</label>

<span class=”bg_text small”>

<input type=”text” id=”captcha” name=”captcha” maxLength=”7″/>

</span>

<img src=”/captcha.svl” onclick=”this.src=’/captcha.svl?d=’+new Date()” class=”code” alt=”换一张” /><a href=”javascript:void(0);” onclick=”$(‘.code’).attr(‘src’,’/captcha.svl?d=’+new Date())” title=”换一张”>换一张</a>

</li>

</pre>

同时有下述两个实现类 EmployeeServiceImpl和EmployeeServiceImpl1:

[

图片 2

复制代码

](javascript:void(0); “复制代码”)

<pre>

@Service(“service”)

public

class

EmployeeServiceImpl

implements

EmployeeService {

public

EmployeeDto getEmployeeById(Long id) {

return

new

EmployeeDto();
}
}

@Service(“service1”)

public

class

EmployeeServiceImpl1

implements

EmployeeService {

public

EmployeeDto getEmployeeById(Long id) {

return

new

EmployeeDto();
}
}

</pre>

[

图片 3

复制代码

](javascript:void(0); “复制代码”)

调用代码如下:

[

图片 4

复制代码

](javascript:void(0); “复制代码”)

<pre>

@Controller
@RequestMapping(

“/emplayee.do”

)

public

class

EmployeeInfoControl {

@Autowired

EmployeeService employeeService;

@RequestMapping(params

= “method=showEmplayeeInfo”

)

public

void

showEmplayeeInfo(HttpServletRequest request, HttpServletResponse
response, EmployeeDto dto) {
#略
}
}

</pre>

[

图片 5

复制代码

](javascript:void(0); “复制代码”)

在启动tomcat时报如下错误:

[

图片 6

复制代码

](javascript:void(0); “复制代码”)

<pre>

org.springframework.beans.factory.BeanCreationException: Error creating
bean with name ’employeeInfoControl’: Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: com.test.service.EmployeeService
com.test.controller.EmployeeInfoControl.employeeService; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
unique bean of type [com.test.service.EmployeeService] is defined:
expected single matching bean but found 2: [service1, service2]

</pre>

[

图片 7

复制代码

](javascript:void(0); “复制代码”)

其实报错信息已经说得很明确了,在autoware时,由于有两个类实现了EmployeeService接口,所以Spring不知道应该绑定哪个实现类,所以抛出了如上错误。

这个时候就要用到@Qualifier注解了,qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,我们修改调用代码,添加@Qualifier注解,需要注意的是@Qualifier的参数名称必须为我们之前定义@Service注解的名称之一!

[

图片 8

复制代码

](javascript:void(0); “复制代码”)

<pre>

@Controller
@RequestMapping(

“/emplayee.do”

)

public

class

EmployeeInfoControl {

@Autowired
**@Qualifier(**

“service”

)
EmployeeService employeeService;

@RequestMapping(params

= “method=showEmplayeeInfo”

)

public

void

showEmplayeeInfo(HttpServletRequest request, HttpServletResponse
response, EmployeeDto dto) {
#略
}
}

</pre>

[

图片 9

复制代码

](javascript:void(0); “复制代码”)