传统的Servlet开发方式的不足
- 对同一个类型的业务要进行多个Servlet的编写
将业务申请放在一个servlet中(DispatcherServlet)接收所有请求,最后进行分发。 - 对象管理不方便
- 将对象的创建和属性的注入存放在xml文件中,对xml文件中的bean和类进行匹配,放入Map中。
- 对需要创建的类上添加上注解,然后通过注解对属性进行匹配放入Map中
Code(列举几个主要类)
DispatcherServlet.java
- 接收输入的url,并将对应的url存放在map中
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("有请求进来了 ");
Map applicationContext = (Map)getServletContext().getAttribute("applicationContext"); //存放类名和对应的对象
Map handlerMapping = (Map)getServletContext().getAttribute("handlerMapping"); //存放url下所有的方法
String requestURI = req.getRequestURI();
String[] split = requestURI.split("/");
String baseUrl = "/" + split[1];
String actionUrl = "/" + split[2];
String url = baseUrl + actionUrl;
Method method = (Method)handlerMapping.get(url); // /goods/add,根据url找到方法
try {
method.setAccessible(true);
//调用具体的处理请求的方法
method.invoke(applicationContext.get(split[1] + "Controller"),req,resp); //创建对象执行方法
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
ContextLoadListener
- 在初始化方法中扫描注解并进行对应操作
public class ContextLoadListener implements ServletContextListener {
public List<String> allClassNameList = new ArrayList<>();
public Map<String,Object> beanFactory = new HashMap<>();
public Map<String, Method> handerMapping = new LinkedHashMap<>();
/**
*
* Tomcat启动时执行
* 在tomcat启动时加载(初始化)容器,创建容器中的对象
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//1.扫描包(由使用者来配置)
//读入需要扫描的包名
Properties properties = new Properties();
try {
properties.load(DispatcherServlet.class.getClassLoader().getResourceAsStream("application.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String basePackageName = properties.getProperty("scan-package");
//把包名转成classPath的路径
String basePackagePath = basePackageName.replaceAll("\\.", "/");
//扫描指定包下面的所有的类
doScan(basePackagePath);
System.out.println(allClassNameList);
//2.判断扫描出来的所有的类哪些增加了 @Compoent @Controller @Service @Repostory 把这些添加了注解的类创建对象,加入到bean工厂中
for (String className : allClassNameList) {
Class<?> aClass = null;
try {
aClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if(aClass.isAnnotationPresent(Component.class)){
String beanName = aClass.getAnnotation(Component.class).value();
if("".equals(beanName)){
char[] simpleNameArray = aClass.getSimpleName().toCharArray();
simpleNameArray[0] += 32;
beanName = aClass.getSimpleName();
}
try {
beanFactory.put(beanName,aClass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
if(aClass.isAnnotationPresent(Service.class)){
String beanName = aClass.getAnnotation(Service.class).value();
if("".equals(beanName)){
char[] simpleNameArray = aClass.getSimpleName().toCharArray();
simpleNameArray[0] += 32;
beanName = aClass.getSimpleName();
}
try {
beanFactory.put(beanName,aClass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
if(aClass.isAnnotationPresent(Controller.class)){
String beanName = aClass.getAnnotation(Controller.class).value();
if("".equals(beanName)){
char[] simpleNameArray = aClass.getSimpleName().toCharArray();
simpleNameArray[0] += 32;
beanName = aClass.getSimpleName();
}
try {
beanFactory.put(beanName,aClass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
if(aClass.isAnnotationPresent(Repository.class)){
String beanName = aClass.getAnnotation(Repository.class).value();
Object obj = Proxy.newProxyInstance(ContextLoadListener.class.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.isAnnotationPresent(Query.class)){
//查询操作
String sql = method.getAnnotation(Query.class).value();
List<Map<String, Object>> mapList = JdbcUtils.getQueryRunner().query(sql, new MapListHandler());
return mapList;
}
if(method.isAnnotationPresent(Update.class)){
//增删改操作
String sql = method.getAnnotation(Update.class).value();
int update = JdbcUtils.getQueryRunner().update(sql,args);
return update;
}
return null;
}
});
if("".equals(beanName)){
char[] simpleNameArray = aClass.getSimpleName().toCharArray();
simpleNameArray[0] += 32;
beanName = aClass.getSimpleName();
}
beanFactory.put(beanName,obj);
}
}
//3.依赖注入
beanFactory.forEach((beanName,obj) -> {
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true); //强问
//普通的String的value值
if(declaredField.isAnnotationPresent(StringValue.class)){
try {
declaredField.set(obj,declaredField.getAnnotation(StringValue.class).value());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//注入的是对象
if(declaredField.isAnnotationPresent(Qualifier.class)){
String bName = declaredField.getAnnotation(Qualifier.class).value();
Object o = beanFactory.get(bName);
try {
declaredField.set(obj,o);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
});
//4.测试
MemberController memberController = (MemberController) beanFactory.get("memberController");
memberController.add();
//5.解析包含@Controller注解的类的方法,看哪些方法上有@RequestMapping
beanFactory.forEach((beanName,obj)->{
if(obj.getClass().isAnnotationPresent(Controller.class)){
String baseUrl = "";
//解析Controller上面有无@RequestMapping
if(obj.getClass().isAnnotationPresent(RequestMapping.class)){
baseUrl = obj.getClass().getAnnotation(RequestMapping.class).value();
}
Method[] declaredMethods = obj.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
String url = declaredMethod.getAnnotation(RequestMapping.class).value();
String allUrl = (baseUrl + url).trim().replaceAll("// ","/");
System.out.println(allUrl);
handerMapping.put(allUrl,declaredMethod);
}
}
}
});
sce.getServletContext().setAttribute("applicationContext",beanFactory);
sce.getServletContext().setAttribute("handlerMapping",handerMapping);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
public void doScan(String basePackagePath){
//获取classPath路径在硬盘中的url
URL url = DispatcherServlet.class.getClassLoader().getResource(basePackagePath);
//从url中获取文件对象(绝对路径)
File basePackageFile = new File(url.getFile());
for(File file : basePackageFile.listFiles()){
if(file.isDirectory()){
doScan(basePackagePath + "/" + file.getName()); //递归
}else{
allClassNameList.add(basePackagePath.replaceAll("/","\\.") + "." + file.getName().replaceAll(".class",""));
}
}
}
}
Spring与Servlet的整合
.pom文件
导入Spring和Servlet依赖
<!--只需要引入这一个依赖即可,因为这个依赖包含 beans core context web ..-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
web.xml
配置Spring的配置文件和监听器
<!--Spring的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<!-- 监听tomcat的启动,在启动时初始化容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
application.xml
配置组件扫描器
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置注解扫描器-->
<context:component-scan base-package="com.lyx.spring"/>
</beans>