项目实现思路:
过滤器1(设置编码)--->过滤器2(事务管理)-->DispacherServlet-中央控制器(用于参数处理 invoke 视图处理)-->控制器:*Controller(*业务名字)-->IOC <beans>配置变量-->FruitServic-->FruitDAO调用BaseDAO-->BaseDAO利用jdbc连接数据库从数据库获取资源然后再依次传回来即可.
IOC容器,<beans>配置文件,用DispacherServlet来解析,使得项目高内聚低耦合
1.过滤器1(设置编码)----CharacterEncodingFilter
@WebFilter(urlPatterns = {"*.do"}, initParams = {@WebInitParam(name = "encoding", value = "UTF-8")})
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String encodingStr = filterConfig.getInitParameter("encoding");
if (StringUtil.isNotEmpty(encodingStr)) {
encoding = encodingStr;
}
}
2.过滤器2(事务管理)---openSessionInViewFilter
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try{
TransactionManager.beginTrans();
System.out.println("开启事务....");
filterChain.doFilter(servletRequest, servletResponse);
TransactionManager.commit();
System.out.println("提交事务...");
}catch (Exception e){
e.printStackTrace();
try {
TransactionManager.rollback();
System.out.println("回滚事务....");
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
3.DispacherServlet-中央控制器(用于参数处理 invoke 视图处理)
package com.atguigu.myssm.myspringmvc;
import com.atguigu.myssm.ioc.BeanFactory;
import com.atguigu.myssm.util.StringUtil;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory;
public DispatcherServlet() {
}
public void init() throws ServletException {
super.init();
//之前是在此处主动创建IOC容器的
//现在优化为从application作用域去获取
//beanFactory = new ClassPathXmlApplicationContext();
ServletContext application = getServletContext();
Object beanFactoryObj = application.getAttribute("beanFactory");
if (beanFactoryObj != null) {
beanFactory = (BeanFactory) beanFactoryObj;
} else {
throw new RuntimeException("IOC容器获取失败!");
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
//request.setCharacterEncoding("UTF-8");
//假设url是: http://localhost:8080/pro15/hello.do
//那么servletPath是: /hello.do
// 我的思路是:
// 第1步: /hello.do -> hello 或者 /fruit.do -> fruit
// 第2步: hello -> HelloController 或者 fruit -> FruitController
String servletPath = request.getServletPath();
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(0, lastDotIndex);
Object controllerBeanObj = beanFactory.getBean(servletPath);
String operate = request.getParameter("operate");
if (StringUtil.isEmpty(operate)) {
operate = "index";
}
try {
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for (Method method : methods) {
if (operate.equals(method.getName())) {
//1.统一获取请求参数
//1-1.获取当前方法的参数,返回参数数组
Parameter[] parameters = method.getParameters();
//1-2.parameterValues 用来承载参数的值
Object[] parameterValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
//如果参数名是request,response,session 那么就不是通过请求中获取参数的方式了
if ("request".equals(parameterName)) {
parameterValues[i] = request;
} else if ("response".equals(parameterName)) {
parameterValues[i] = response;
} else if ("session".equals(parameterName)) {
parameterValues[i] = request.getSession();
} else {
//从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
String typeName = parameter.getType().getName();
Object parameterObj = parameterValue;
if (parameterObj != null) {
if ("java.lang.Integer".equals(typeName)) {
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj;
}
}
//2.controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, parameterValues);
//3.视图处理
String methodReturnStr = (String) returnObj;
if (methodReturnStr.startsWith("redirect:")) { //比如: redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else {
super.processTemplate(methodReturnStr, request, response); // 比如: "edit"
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new DispatcherServletException("DispatcherServlet出错了...");
}
}
}
// 常见错误: IllegalArgumentException: argument type mismatch
4.控制器:*Controller --(这里是FruitController,*代表控制类)
package com.atguigu.fruit.controllers;
import com.atguigu.fruit.service.FruitService;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
public class FruitController {
private FruitService fruitService = null ;
private String update(Integer fid , String fname , Integer price , Integer fcount , String remark ){
//3.执行更新
fruitService.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
//4.资源跳转
return "redirect:fruit.do";
}
private String edit(Integer fid , HttpServletRequest request){
if(fid!=null){
Fruit fruit = fruitService.getFruitByFid(fid);
request.setAttribute("fruit",fruit);
//super.processTemplate("edit",request,response);
return "edit";
}
return "error" ;
}
private String del(Integer fid ){
if(fid!=null){
fruitService.delFruit(fid);
return "redirect:fruit.do";
}
return "error";
}
private String add(String fname , Integer price , Integer fcount , String remark ) {
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitService.addFruit(fruit);
return "redirect:fruit.do";
}
private String index(String oper , String keyword , Integer pageNo , HttpServletRequest request ) {
HttpSession session = request.getSession() ;
if(pageNo==null){
pageNo = 1;
}
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
pageNo = 1 ;
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
session.setAttribute("keyword",keyword);
}else{
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
// 重新更新当前页的值
session.setAttribute("pageNo",pageNo);
List<Fruit> fruitList = fruitService.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
//总记录条数
int pageCount = fruitService.getPageCount(keyword);
session.setAttribute("pageCount",pageCount);
return "index" ;
}
}
5.IOC <beans>配置变量,让代码更加的简介,利用解析ioc文件就可以使得代码注入,ioc时在DispacherServlet解析的.
package com.atguigu.myssm.ioc;
import com.atguigu.myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beanMap = new HashMap<>();
private String path = "applicationContext.xml";
public ClassPathXmlApplicationContext() {
this("applicationContext.xml");
}
public ClassPathXmlApplicationContext(String path) {
if (StringUtil.isEmpty(path)) {
throw new RuntimeException("IOC容器的配置文件没有指定...");
}
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path);
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2.创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//3.创建Document对象
Document document = documentBuilder.parse(inputStream);
//4.获取所有的bean节点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class beanClass = Class.forName(className);
//创建bean实例
Object beanObj = beanClass.newInstance();
//将bean实例对象保存到map容器中
beanMap.put(beanId, beanObj);
//到目前为止,此处需要注意的是,bean和bean之间的依赖关系还没有设置
}
}
//5.组装bean之间的依赖关系
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
NodeList beanChildNodeList = beanElement.getChildNodes();
for (int j = 0; j < beanChildNodeList.getLength(); j++) {
Node beanChildNode = beanChildNodeList.item(j);
if (beanChildNode.getNodeType() == Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())) {
Element propertyElement = (Element) beanChildNode;
String propertyName = propertyElement.getAttribute("name");
String propertyRef = propertyElement.getAttribute("ref");
//1) 找到propertyRef对应的实例
Object refObj = beanMap.get(propertyRef);
//2) 将refObj设置到当前bean对应的实例的property属性上去
Object beanObj = beanMap.get(beanId);
Class beanClazz = beanObj.getClass();
Field propertyField = beanClazz.getDeclaredField(propertyName);
propertyField.setAccessible(true);
propertyField.set(beanObj, refObj);
}
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}
6.利用FruitServic,调用FruitDAO,然后再利用sql相关语句完成操作
package com.atguigu.fruit.service;
import com.atguigu.fruit.pojo.Fruit;
import java.util.List;
//业务层面
public interface FruitService {
//获取指定页面的库存列表信息
List<Fruit> getFruitList(String keyword, Integer pageNo);
//添加库存记录信息
void addFruit(Fruit fruit);
//根据id查看指定库存记录
Fruit getFruitByFid(Integer fid);
//删除特定库存记录
void delFruit(Integer fid);
//获取总页数
Integer getPageCount(String keyword);
//修改特定库存记录
void updateFruit(Fruit fruit);
}
7.FruitDAO调用BaseDAO,执行相关sql语句.增删改查,完成数库的调用
package com.atguigu.myssm.basedao;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseDAO<T> {
public final String DRIVER = "com.mysql.cj.jdbc.Driver" ;
// jdbc:mysql://localhost:3306/fruitdb?serverTimezone=UTC
public final String URL = "jdbc:mysql://localhost:3306/fruitdb?serverTimezone=UTC";
public final String USER = "root";
public final String PWD = "admin" ;
protected Connection conn ;
protected PreparedStatement psmt ;
protected ResultSet rs ;
//T的Class对象
private Class entityClass ;
public BaseDAO(){
//getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例
//那么子类构造方法内部首先会调用父类(BaseDAO)的无参构造方法
//因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class
//所以getGenericSuperclass()获取到的是BaseDAO的Class
Type genericType = getClass().getGenericSuperclass();
//ParameterizedType 参数化类型
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
//获取到的<T>中的T的真实的类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
protected Connection getConn(){
try {
//1.加载驱动
Class.forName(DRIVER);
//2.通过驱动管理器获取连接对象
return DriverManager.getConnection(URL, USER, PWD);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null ;
}
protected void close(ResultSet rs , PreparedStatement psmt , Connection conn){
try {
if (rs != null) {
rs.close();
}
if(psmt!=null){
psmt.close();
}
if(conn!=null && !conn.isClosed()){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//给预处理命令对象设置参数
private void setParams(PreparedStatement psmt , Object... params) throws SQLException {
if(params!=null && params.length>0){
for (int i = 0; i < params.length; i++) {
psmt.setObject(i+1,params[i]);
}
}
}
//执行更新,返回影响行数
protected int executeUpdate(String sql , Object... params){
boolean insertFlag = false ;
insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
try {
conn = getConn();
if(insertFlag){
psmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
}else {
psmt = conn.prepareStatement(sql);
}
setParams(psmt,params);
int count = psmt.executeUpdate() ;
if(insertFlag){
if(rs.next()){
return ((Long)rs.getLong(1)).intValue();
}
}
return count ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
close(rs,psmt,conn);
}
return 0;
}
//通过反射技术给obj对象的property属性赋propertyValue值
private void setValue(Object obj , String property , Object propertyValue){
Class clazz = obj.getClass();
try {
//获取property这个字符串对应的属性名 , 比如 "fid" 去找 obj对象中的 fid 属性
Field field = clazz.getDeclaredField(property);
if(field!=null){
field.setAccessible(true);
field.set(obj,propertyValue);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
//执行复杂查询,返回例如统计结果
protected Object[] executeComplexQuery(String sql , Object... params){
try {
conn = getConn() ;
psmt = conn.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
Object[] columnValueArr = new Object[columnCount];
//6.解析rs
if(rs.next()){
for(int i = 0 ; i<columnCount;i++){
Object columnValue = rs.getObject(i+1); //33 苹果 5
columnValueArr[i]=columnValue;
}
return columnValueArr ;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs,psmt,conn);
}
return null ;
}
//执行查询,返回单个实体对象
protected T load(String sql , Object... params){
try {
conn = getConn() ;
psmt = conn.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
//6.解析rs
if(rs.next()){
T entity = (T)entityClass.newInstance();
for(int i = 0 ; i<columnCount;i++){
String columnName = rsmd.getColumnName(i+1); //fid fname price
Object columnValue = rs.getObject(i+1); //33 苹果 5
setValue(entity,columnName,columnValue);
}
return entity ;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
close(rs,psmt,conn);
}
return null ;
}
//执行查询,返回List
protected List<T> executeQuery(String sql , Object... params){
List<T> list = new ArrayList<>();
try {
conn = getConn() ;
psmt = conn.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
//6.解析rs
while(rs.next()){
T entity = (T)entityClass.newInstance();
for(int i = 0 ; i<columnCount;i++){
String columnName = rsmd.getColumnName(i+1); //fid fname price
Object columnValue = rs.getObject(i+1); //33 苹果 5
setValue(entity,columnName,columnValue);
}
list.add(entity);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
close(rs,psmt,conn);
}
return list ;
}
}
8,.BaseDAO利用jdbc连接数据库从数据库获取资源然后再依次传给相应的对象即可.
BaseDAO传给BaseServic
BaseServic传给FruitController
等等,最后返回页面