牛客过第二遍

1、spring事务管理

Spring事务管理

声明式事务:

1 通过XML配置,声明某方法的事务特征

2、通过注解,声明某方法的事务特征,注解@Transactional 

@Transactional 注解参数讲解

isolation :隔离级别;propagation :传播机制

传播机制比较难理解,这里粗略说一下

假如在A方法中,调用了B方法,而B方法是事务操作,则A为B的外部事务。

编程式事务:

通过TransactionTemplate管理事务,并通过它执行数据库的操作

package com.nowcoder.community.service;
 
import com.nowcoder.community.dao.AlphaDao;
import com.nowcoder.community.dao.DiscussPostMapper;
import com.nowcoder.community.dao.UserMapper;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.util.CommunityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date;
 
@Service
//@Scope("prototype")
public class AlphaService {
 
    @Autowired
    private AlphaDao alphaDao;
 
    @Autowired
    private UserMapper userMapper;
 
    @Autowired
    private DiscussPostMapper discussPostMapper;
 
    @Autowired
    private TransactionTemplate transactionTemplate;
 
    public AlphaService() {
//        System.out.println("实例化AlphaService");
    }
 
    @PostConstruct
    public void init() {
//        System.out.println("初始化AlphaService");
    }
 
    @PreDestroy
    public void destroy() {
//        System.out.println("销毁AlphaService");
    }
 
    public String find() {
        return alphaDao.select();
    }
 
    // REQUIRED: 支持当前事务(外部事务),如果不存在则创建新事务.
    // REQUIRES_NEW: 创建一个新事务,并且暂停当前事务(外部事务).
    // NESTED: 如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会REQUIRED一样.
    
    //isolation 隔离性 propagation 传播机制
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public Object save1() {
        // 新增用户
        User user = new User();
        user.setUsername("alpha");
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
        user.setEmail("[email protected]");
        user.setHeaderUrl("http://image.nowcoder.com/head/99t.png");
        user.setCreateTime(new Date());
        userMapper.insertUser(user);
 
        // 新增帖子
        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle("Hello");
        post.setContent("新人报道!");
        post.setCreateTime(new Date());
        discussPostMapper.insertDiscussPost(post);
 
        Integer.valueOf("abc");
 
        return "ok";
    }
//通过TransactionTemplate进行局部的事务回滚
    public Object save2() {
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
        return transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                // 新增用户
                User user = new User();
                user.setUsername("beta");
                user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
                user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
                user.setEmail("[email protected]");
                user.setHeaderUrl("http://image.nowcoder.com/head/999t.png");
                user.setCreateTime(new Date());
                userMapper.insertUser(user);
 
                // 新增帖子
                DiscussPost post = new DiscussPost();
                post.setUserId(user.getId());
                post.setTitle("你好");
                post.setContent("我是新人!");
                post.setCreateTime(new Date());
                discussPostMapper.insertDiscussPost(post);
 
                Integer.valueOf("abc");
 
                return "ok";
            }
        });
    }
 
}

2、项目中cookie的使用及原理

什么是cookie,即浏览器的一种缓存数据,为了弥补http无状态的缺陷。

对于注册的验证码功能,采用特定的验证码生成工具生成验证码;在这里,项目中并没有采用某一个变量去接收这个验证码的值,而是采用了cookie,即每个客户端都拥有它们自己的cookie,假如采用全局变量去接收,则很容易产生并发的问题;采用cookie即将每个用户都隔离开来,各自用各自浏览器的cookie。

对于登陆状态这个功能,主要就是拿浏览器的cookie去redis中找状态是否过期。同样的,并不会用项目中的某个变量去接收ticket,这样同上会产生并发问题。cookie中存储ticket可以保证不同用户都可以存储它们自己的ticket,并可以拿其去进行验证

cookie的用法如下:

cookie.setPath是一个方法,用于设置cookie的path属性。path属性指定了哪些URL的请求可以携带cookie。如果不设置path属性,那么默认值是创建cookie的应用的路径,这意味着只有同一个应用可以访问这个cookie。如果设置了path属性,那么所有以该路径为前缀的URL都可以访问这个cookie。例如,如果设置了cookie.setPath(“/test”),那么/test, /test/, /test/a, /test/b等URL都可以访问这个cookie,但是/, /doc, /fr/test等URL则不能访问这个cookie。

response.addCookie是一个方法,用于将cookie添加到HTTP响应中,从而发送给客户端浏览器。这个方法需要一个Cookie对象作为参数,Cookie对象可以用Cookie(name, value)构造器创建,其中name和value是字符串类型。

例如,如果要创建一个名为user,值为Tom的cookie,并将其添加到响应中,可以使用以下代码:

Cookie cookie = new Cookie(“user”, “Tom”); response.addCookie(cookie);

这样,客户端浏览器就会收到一个包含user=Tom的Set-Cookie头,并将其保存在本地。

     Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
      //设置可以携带cookie的路径
            cookie.setPath(contextPath);
      //设置cookie的生存时间
            cookie.setMaxAge(expiredSeconds);
      //将cookie添加到HTTP响应中,从而发送给客户端浏览器
            response.addCookie(cookie);

3、登陆拦截器

拦截器示例:

1、定义拦截器,实现HandlerInterceptor,根据自己的需求重写prehandle等方法

2、配置拦截器,为他指定拦截、排除的路径

拦截器应用:

1、在请求开始时查询登录用户

2、在本次请求中持有用户数据

3、在模板视图上显示用户数据

4、在请求结束时清理用户数据

示例如下代码所示

package com.nowcoder.community.controller.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class  AlphaInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    // 在Controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: " + handler.toString());
        return true;
    }

    // 在Controller之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.debug("postHandle: " + handler.toString());
    }

    // 在TemplateEngine之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.debug("afterCompletion: " + handler.toString());
    }
}

需要配置webconfig使得拦截器生效

package com.nowcoder.community.config;

import com.nowcoder.community.controller.interceptor.AlphaInterceptor;
import com.nowcoder.community.controller.interceptor.LoginRequiredInterceptor;
import com.nowcoder.community.controller.interceptor.LoginTicketInterceptor;
import com.nowcoder.community.controller.interceptor.MessageInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AlphaInterceptor alphaInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                 //excludePathPatterns排除以下路径,在以下路径拦截器并不会作用
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")   //以下路径拦截器生效
                .addPathPatterns("/register", "/login");

    }

}

输入请求路径login,看看日志输出,检查拦截器是否发生作用

 打印出handler,说明login触发了LoginController类内部的方法,从而拦截器效果

具体拦截器的操作见上一篇文章

3、用户信息的存储

ThreadLocalusers是一个用于存储每个线程独有的User对象的类,它可以保证每个线程都可以访问自己的User对象,而不会受到其他线程的影响ThreadLocal类提供了get()和set()方法来获取和设置当前线程的User对象。ThreadLocal类还可以通过重写initialValue()方法来指定User对象的初始值1 。

在这个项目中,采用ThreadLocal来存储用户的信息,确保每个用户线程访问到的都是自己的User对象,不受到其他用户线程的影响

/**
 * 持有用户信息,用于代替session对象.
 */
@Component
public class HostHolder {

    private ThreadLocal users = new ThreadLocal<>();

    public void setUser(User user) {
        users.set(user);
    }

    public User getUser() {

        return users.get();
    }

    public void clear() {
        users.remove();
    }

}

我们可以看看Threadlocal.set和get方法的源码,可以看出对Threadlocal变量操作,都是对于当前线程的变量进行操作,具有隔离性,其他线程无法操作当前线程内Threadlocal的值

   /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

4、fastjson的使用

即将java字符串,对象等转换为json对象,或将json对象转换为java字符串对象等,输出结果如下图所示

    //转换为json字符串
    public static String getJSONString(int code, String msg, Map map) {
        JSONObject json = new JSONObject();
        json.put("code", code);
        json.put("msg", msg);
        if (map != null) {
            for (String key : map.keySet()) {
                json.put(key, map.get(key));
            }
        }
        return json.toJSONString();
    }
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("name", "zhangsan");
        map.put("age", 25);
        System.out.println(getJSONString(0, "ok", map));
    }

 

你可能感兴趣的:(spring,java,mybatis)