目录
EncodingFilter:过滤器,我这个过滤器就是防止在传输过程中的时候产生乱码:
MVC
MVC是一种软件架构模式,把整个软件分为三层:Model,View,Controller
Model:模型---负责获取并数据,并且处理数据,返回给controller
entity:数据库的实体类
service:业务控制层,其余的话都交给service
dao:databaseObject数据模型层:只操作数据库
View:视图---看见的页面,渲染数据,渲染页面,负责展示而且允许用户编辑来自应用程序的Model对象,View对象用来构建用户界面,与用户交互。
Controller:控制器---servlet,接请求,给出响应,负责传递数据,监听各种事件,管理其他对象生命周期
目的:
耦合度(代码之间的关联关系)分层,把程序分层,改bug的时候,不用找的眼花缭乱,方便找
降低耦合,重用性,可维护性比较高
调用关系:
View层发起请求---Controller---Servlet---Dao---Servlet---Controller---View
MVC优缺点
优点
- 多视图共享一个模型,大大提高了代码的可重用性
- MVC 三个模块相互独立,松耦合架构,降低耦合度
- 控制器提高了应用程序的灵活性和可配置性
- 有利于软件工程化管理
总之,我们通过 MVC 设计模式最终可以打造出一个松耦合+高可重用性+高可适用性的完美架构。
缺点
- 原理复杂
- 增加了系统结构和实现的复杂性
- 视图对模型数据的低效率访问
MVC 并不适合小型甚至中型规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序,通常得不偿失,所以对于 MVC 设计模式的使用要根据具体的应用场景来决定。
实例
接下来通过一个简单的项目结构,来展示一下:
controller包代表的是控制器,用来接请求,给响应
dao包代表数据模型层,只用来操作数据库
entiy包代表实体类,用来储存元素并提供get/set/构造器方法
filter包代表的是过滤器,用来筛选信息的
service包代表的是业务控制层,其余的“脏活累活都交给他干”
util包代表工具类
因为是结合实例写的,所以会有关于每个类中的一些做项目时产生的问题,就直接解决了
controller:
我们之前的做法是一个servlet就只处理一个请求
那么一个servlet能不能处理过个post请求?可以,利用反射:
VipController类:
@WebServlet("*.do")
public class VipController extends HttpServlet {
//调用service
private VipService vipService = new VipServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的路径
String servletPath = req.getServletPath();
System.out.println(servletPath);
//截取地址:截取成eg:vip
String method =servletPath.substring(1);
method = method.substring(method.lastIndexOf("/")+1,method.length()-3);
//利用反射执行vip方法
// 拿到当前类对象
try {
Method method1 = getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
method1.invoke(this,req,resp);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void vip(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String gender = req.getParameter("gender");
String name = req.getParameter("name");
System.out.println(username+password+gender+name);
Vip vip = new Vip(username,password,gender,name);
int i = vipService.register(vip);
if(i>0){
resp.sendRedirect(req.getContextPath()+"/success.html");
}else{
resp.sendRedirect(req.getContextPath()+"/fail.html");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
dao:
VipDao接口:定义一个save方法:
public interface VipDao {
int save(Vip vip) throws Exception;
}
VipDaoImpl类:操作数据库
public class VipDaoImpl extends DAOImpl<Vip> implements VipDao {
@Override
public int save(Vip vip) throws Exception {
String sql ="insert into mvc(username,password,gender,name,salt) values (?,?,?,?,?)";
System.out.println("sql");
return update(sql,vip.getUsername(),vip.getPassword(),vip.getGender(),vip.getName(),vip.getSalt());
}
}
entiy:
Vip实体类:
先整个序列化,然后创建对应数据库里所有的属性。并赋予get/set方法
package mvc.entiy;
import java.io.Serializable;
public class Vip implements Serializable {
private static final long serialVersionUID=-12389731289739127L;
private Integer id;
private String username;
private String password;
private String gender;
private String profile;
private String name;
private String salt;
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public Vip(){
}
public Vip(String username, String password, String gender, String name) {
this.username = username;
this.password = password;
this.gender = gender;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Vip{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", profile='" + profile + '\'' +
", name='" + name + '\'' +
'}';
}
}
filter:
EncodingFilter:过滤器,我这个过滤器就是防止在传输过程中的时候产生乱码:
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
System.out.println("filter");
servletRequest.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
}
}
service:
VipService:注册的方法
public interface VipService {
//注册的方法
int register(Vip vip);
}
VipServiceImpl:(脏活累活给我干)
这里面有个不算拓展的拓展:进行密码的加密处理,毕竟密码不可能直接就存到数据库里,万一数据库被黑客或者某些人攻击或者窃取,这些密码不久流失了吗
进行密码加密:
——调用在工具类里写的getSalt方法,获取盐,加到密码上
——利用MD5加密处理:MD5Util.stringToMD5(密码);
——再把密码存到Vip的set方法里
密码在加盐加密过后登录的时候怎么办?
——登录的时候,还得按照加盐加密的形式来对比,还得需要拿着123456密码和之前加密的 时候的盐再按照规则加密过后再比对
——问题:我们能找到上次加密的时候用的那个盐吗? 我们需要把加密时候用的盐记录下 来,登录的时候还需要使用!!!
——唯一的办法,就是把盐存在数据库里
public class VipServiceImpl implements VipService {
private VipDao dao = new VipDaoImpl();
@Override
public int register(Vip vip) {
//注册的业务执行的就是保存的操作
try {
//密码的加密处理
//生成盐
String salt = MD5Util.getSalt();
String newPassword = MD5Util.stringToMD5(vip.getPassword() + salt);
vip.setPassword(newPassword);
// vip.setPassword(MD5Util.stringToMD5(vip.getPassword() + MD5Util.getSalt()));
System.out.println("service");
vip.setSalt(salt);
return dao.save(vip);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
util:工具类
DAO:
public interface DAO<T> {
/**
* 更新
* @return
*/
int update(String sql,Object ... args) throws Exception;
/**
* 通用的查询所有
*/
List<T> getForList(String sql,Object... args) throws Exception;
/**
* 通用的查询单个
*/
T get(String sql,Object...args) throws Exception;
/**
* 查询某一个列的值,统计
*/
<E> E getForValue(String sql,Object ... args) throws SQLException;
}
DAOImpl:
public class DAOImpl<T> implements DAO<T> {
private QueryRunner runner = null;
private Class<T> type;
/**
* 这个构造器中在做的事:
* 为了获取Class<T> type = Teacher.class
*/
public DAOImpl() {
runner = new QueryRunner(JDBCUtil.getDataSource());
// 获得当前类的带有泛型类型的父类(运行期this其实是DAOImpl的某个子类)
ParameterizedType ptClass = (ParameterizedType) this.getClass().getGenericSuperclass();
type = (Class<T>) ptClass.getActualTypeArguments()[0];
}
/**
* 通用的增删改
* @param sql
* @param args
* @return
* @throws Exception
*/
@Override
public int update(String sql, Object... args) throws Exception {
System.out.println("成功");
return runner.update(sql,args);
}
/**
* 根据sql语句查询多条记录
* @param sql
* @param args
* @return
* @throws Exception
*/
@Override
public List<T> getForList(String sql, Object... args) throws Exception {
return runner.query(sql,new BeanListHandler<>(type),args);
}
/**
* 根据sql语句查询一条记录
* @param sql
* @param args
* @return
* @throws Exception
*/
@Override
public T get(String sql, Object... args) throws Exception {
return runner.query(sql,new BeanHandler<>(type),args);
}
/**
* 根据sql语句查询某一列的值
* @param sql
* @param args
* @return
* @param <E>
* @throws SQLException
*/
@Override
public <E> E getForValue(String sql, Object... args) throws SQLException {
return (E) runner.query(sql,new ScalarHandler<>(),args);
}
}
JDBCUtil
用德鲁伊来连接数据库
public class JDBCUtil {
private static final DataSource DATA_SOURCE;
static {
Properties properties = new Properties();
try {
properties.load(JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
DATA_SOURCE = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DataSource getDataSource() {
return DATA_SOURCE;
}
}
MD5Util:
加密方法
public class MD5Util {
/*
这个方法的参数是原始密码,在controller通过request.getParameter("")接到的写在密码框中的密码
返回值是经过加密处理过后的密码
加盐 salt
MD5+盐 加密
*/
public static String getSalt(){
// 从我定义的一组数据中拿出几个字母或者数字或者符号当作盐
String words="qwertyuioplkjhgfdsazxcvbnm,./;'[]~!@#$%^&*()_+-=0123456789";
StringBuilder strb = new StringBuilder();
for (int i = 0; i <8 ; i++) {
//从我定义的数据中取x个字符当做盐,这里取了八个
// 随机取,生成一个0到字符串的长度的随机数
strb.append(words.charAt((int)Math.floor(Math.random()*words.length()-1)));
}
return strb.toString();
}
public static String stringToMD5(String str){
return DigestUtils.md5Hex(str.getBytes());
}
}
总结
今天使用MVC架构练习登录、注册系统,这样写的代码耦合度较低,使用较方便,通过这次练习能初步认识项目内容,MVC架构的代码量极大(对我来说),各类间的联系很强,互相调用调来调去调得我快晕😵了,行百里路半九十接下来六十天将进入实战阶段,我能在实战得到锻炼得到提升,加油坚持就是胜利!!!