MVC框架二

1、IOC与DI

IOC和DI分别是控制反转和依赖注入。所谓的控制反转就是将对象的创建权交给框架,而依赖注入则是利用框架设置属性。在MCV框架中,有两种方式进行IOC和DI:

1、xml的bean配置


    
    

bean在之前已经解释过,class后面是完整类名,name是属性名,value是属性值。

2、包扫描和四类注解

在开始使用mvc框架的时候,我配置了包扫描路径为controller包,现在将这个扫描路径扩大



在mvc中,有四种注解可以帮助我们创建对象

//dao层用注解
@Repository
//service层注解
@Service
//controller层注解
@Controller
//不是经典三层的其它类注解
@Component

目前来说,这四种注解的作用是一样的,但是不保证以后mvc框架针对不同注解有另外的优化,所以这里遵循约定,严格按照所属层级使用注解。

3、使用框架创建的实例

为了验证1和2方法是否真的创建了一个实例,编写一个类:

package com.fan.dao;

import cn.hutool.db.Db;
import com.fan.entity.ForumUser;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

@Repository
public class UserDao {
    public UserService(){
        System.out.println("UserDao正在实例化!");
    }
    //根据账户和密码获取用户(登录验证)
    public ForumUser selectUserByAccountAndPassword(String account, String password) {
        String sql = "select * from forumUser where account= ?  and password=?;" ; 
        try {
            List list = Db.use().query(sql, ForumUser.class,account,password);
            if (list.size() > 0) {
                return list.get(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

当服务器启动用户登录时,控制台会输出UserDao正在实例化!利用bean配置会得到同样的结果。那么如何将框架实例化出来的对象取出来使用呢?这里有一个注解@Autowiredprivate,将这个注解写在对象上,这个对象的引用就会指向框架创建出来的对象的引用。

//此时,这个userDao已经不是空引用了,可以调用方法和属性
@Autowired
private UserDao userDao;

2、经典三层结构

在mvc框架中,有一个经典的三层结构:控制层->业务层->数据层,这三层结构理论上是一层一层往下调用的,不允许跨层调用。也就是说,控制层调用业务层的对象,业务层调用数据层,但是控制层不能够直接调用数据层。而且控制层不能够出现数据相关的操作,这里主要是指与数据库相关的语句,这些操作应该被放在数据层。就拿用户登录验证举例:

//dao层-数据层
package com.fan.dao;

import cn.hutool.db.Db;
import com.fan.entity.ForumUser;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

//IOC
@Repository
public class UserDao {
    //根据账户和密码获取用户(登录验证)
    public ForumUser selectUserByAccountAndPassword(String account, String password) {
        String sql = "select * from forumUser where account= ?  and password=?;" ; 
        try {
            List list = Db.use().query(sql, ForumUser.class,account,password);
            if (list.size() > 0) {
                return list.get(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

//service层-服务层
package com.fan.service;

import com.fan.dao.UserDao;
import com.fan.entity.ForumUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

//IOC
@Service
public class UserService {
    //取出框架里的UserDao实例
    @Autowired
    private UserDao userDao;

    //根据账户和密码获取用户(登录验证)
    public ForumUser selectUserByAccountAndPassword(String account, String password) {
        //service层调用dao层方法
        return userDao.selectUserByAccountAndPassword(account, password);
    }
}

//控制层-controller层
package com.fan.controller;

import com.fan.entity.ForumUser;
import com.fan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

//IOC
@Controller
@RequestMapping("/login")
public class LoginController {
    //取出实例
    @Autowired
    private UserService userService;

    @RequestMapping("/verify")
    public ModelAndView login(String account, String password, HttpServletRequest req) {
        ModelAndView mav = new ModelAndView("/error");
        //调用service层方法
        ForumUser forumUser = userService.selectUserByAccountAndPassword(account, password);
        try {
            if (forumUser != null) {
                req.getSession().setAttribute("ForumUser", account);
                mav = new ModelAndView("/loginsuccess");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mav;
    }
}

当然,三层分层并不是必须遵守的,逐层调用也不是强制规范,一切设计都要根据实际情况来。

3、线程安全

关于多线程的安全问题,之前已经讨论过,这里简单说一下。在mvc框架中,IOC创建对象使用的是单例模式,所以打上注解或者bean配置的类,最终只会生成一个实例。对于非静态的方法来说,如果每个线程都能生成各自的实例,那么线程就是安全的,如果是共用一个实例,也就是框架生成的单例,那么就有可能出现线程安全问题。对于这种情况可以使用栈封闭、锁和ThreadLocale封闭。静态方法本身也是线程安全的,只要不去操作外部的静态变量,那么就可以在多线程中直接使用。

4、Service层与Dao层的分工

现有一论坛,要求用户可以删除自己的帖子,并在删除帖子时要将对应跟帖一并删除。帖子和跟帖分别在两张表中,跟帖表中有用户uid和帖子pid。在PostDao和CommentDao分别写根据帖子pid删除内容的方法

@Autowired
private JdbcTemplate jdbcTemplate;
//CommentDao层根据帖子id删除对应评论
    public void deleteCommentByPid(int pid) {
        String sql = "delete from cmt where pid=?";
        jdbcTemplate.update(sql, pid);
    }
//PostDao层根据帖子id删除用户帖子
    public void deletePostById(int id) {
        String sql="delete from post where id=?";
        jdbcTemplate.update(sql,id);
    }

删除帖子并附带删除其回帖属于帖子的业务,所以在PostService对这两个Dao进行调用

    @Autowired
    private PostDao postDao;
    @Autowired
    private CommentDao commentDao;
//事务管理
    @Autowired
    private TransactionTemplate trans;
//根据帖子pid删除帖子和该帖子的评论
    public int deletePostById(int pid) {
        int success = 0;
        trans.execute(new TransactionCallback<>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                postDao.deletePostById(pid);
                commentDao.deleteCommentByPid(pid);
                return 1;
            }
        });
        success = 1;
        return success;
    }

在这里即体现出框架分层的作用,对于不同的数据操作,只需要组合调用dao层方法就可以实现业务,业务越复杂就越能体现出分层的好处,与此同时在controller层,对service的调用还可以进一步优化

@Autowired private PostService postService;

@RequestMapping("/deletePost")
    public void deletePostById(int pid, int uid, HttpServletResponse resp, HttpSession session) throws IOException {
        //从session域中取出论坛用户数据,将登陆用户id与发帖哟用户id比对,只有两者一直才可以删帖
        int aUid = session.getAttribute("ForumUser")).getId();
        int success = 0;
        if (aUid == uid) {
            success = postService.deletePostById(pid);
        }
        resp.getWriter().print(success);
    }
}

你可能感兴趣的:(MVC框架二)