由于servlet容器和spring容器并不是同一个,所以当需要向servlet中注入spring bean是有以下操作:
1、使用proxy servlet(代理servlet),将实际servlet加入spring bean管理,在代理servlet的init方法中找到被代理servlet bean,后续请求处理由被代理servlet处理
2、使用Spring的Autowired注解(在servlet的bean属性中加入此注解,由spring容器自动注入)
代理servlet实现向servlet注入spring bean真的好麻烦,所以我们选择注解实现自动注入,但是不禁又想了想,既然spring可以通过扫描属性注解来自动注入spring bean,那么我们自己定义注解扫描是不是也可以呢?
首先,定义自定义注解
/**
* Servlet注入的bean属性注解, 注入操作由AbstractBaseServlet完成
* @author jiashun
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @
interface AnnotationServletBean {
}
定义AbstractBaseServlet
/**
* 所有Servlet的基类, 用来实现向servlet注入bean
*/
public abstract class AbstractBaseServlet extends HttpServlet {
private static final long serialVersionUID =
1L;
/**
* 日志对象
*/
private static Logger LOG = LogFactory.getLogger(AbstractBaseServlet.class);
/**
* servlet初始化
*/
public void init()
throws ServletException {
/**
* 根据Servlet上下文获取spring上下文
*/
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
String className =
this.getClass().getName();
Field[] beanFields = getServletBeanFields();
if (beanFields.length ==
0) {
LOG.info(
"在类[" + className +
"]中未找到具有AnnotationServletBean注解的属性, 注入 spring bean操作结束");
return;
}
LOG.info(
"向类 [" + className +
"] 注入spring bean 操作开始");
try {
for (Field field : beanFields) {
field.setAccessible(
true);
Object value = ctx.getBean(field.getName());
field.set(
this, value);
LOG.info(
"向类[" + className +
"]注入spring bean [" + field.getName() +
"]成功");
}
}
catch (Exception e) {
LOG.error(
"注入 spring bean 操作失败", e);
}
LOG.info(
"向类 [" + className +
"] 注入spring bean 操作结束");
}
/**
* 获取具有AnnotationServletBean属性的servlet属性集合
* @return
* 具有AnnotationServletBean注解的属性集合, 不为null
*/
private Field[]
getServletBeanFields() {
Field[] fields = ReflectUtil.getDeclaredFields(
this.getClass());
if (fields.length >
0) {
List<Field> fieldList =
new ArrayList<>();
for (Field field : fields) {
if (field.getAnnotation(AnnotationServletBean.class) !=
null) {
fieldList.add(field);
}
}
fields = fieldList.toArray(
new Field[fieldList.size()]);
}
return fields;
}
}
AbstractBaseServlet的大致思想就是在init方法中找到继承类中具有AnnotationServletBean注解的属性,然后通过属性名获取spring bean,再通过反射向继承类注入spring bean属性。
然后我们写一个继承类
/**
* 通用登录Servlet
* @author jiashun
*/
public class LoginServlet extends AbstractBaseServlet {
private static final long serialVersionUID =
1L;
@AnnotationServletBean
private WebAppInitializer webappInitializer;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write(
"Hello, This is LoginServlet !");
}
}
WebAppInitializer是我自己的项目用到的服务初始化bean,已经发布为spring bean。 我们可以看到LoginServlet的webappInitializer属性具有AnnotationServletBean注解,那么在LoginServlet初始化时会调用执行AbstractBaseServlet的init方法来注入spring bean(具体注入操作就是注入具有AnnotationServletBean注解的属性)
访问LoginServlet服务打印日志:
[WebApp] [INFO] [
2017-
05-
07 09:
48:
59,
645] [http-nio-
8080-exec-
14]
com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet
.java:
44) | 向类 [
com.jiashun.webapp.common.servlet.LoginServlet] 注入spring bean 操作开始
[WebApp] [INFO] [
2017-
05-
07 09:
48:
59,
647] [http-nio-
8080-exec-
14]
com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet
.java:
50) | 向类[
com.jiashun.webapp.common.servlet.LoginServlet]注入spring bean [webappInitializer]成功
[WebApp] [INFO] [
2017-
05-
07 09:
48:
59,
647] [http-nio-
8080-exec-
14]
com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet
.java:
55) | 向类 [
com.jiashun.webapp.common.servlet.LoginServlet] 注入spring bean 操作结束