原則的レベルでは、[Spring MVCのを学ぶために一緒に@SessionAttribute]の使用を習得します

各1

あなたはナルトの皆にない場合は、あなたを認識したが、ときナルト上の私たちのすべては、あなたを認識することができます

序文

注釈名は、意味の役割であるModelとの同期属性session次の要求(例えばリダイレクトシーン〜など)の便利な使用の間でセッション。
が、Session現在のシーンの終わりの前と後の完全な分離の概念がますます弱まっようになったが、それはWeb開発者であれば、私はまだ強くすることをお勧めしますので、当然、あなたは離れて、この知識を投げ、私はあなたのことができるようにすることを示唆しています熟練の使用は@SessionAttribute、本明細書に、通常の開発を簡素化するために-あなたとピットへ

@SessionAttribute

以下のための注釈のみクラスの注釈、要求のうちのように、パラメータを渡しますしかし、まったく同じではない:一般的なだけのために使用されるパラメータの一時的な移転ではなく、長期保存には、データの長期保存はまだ配置する必要がありインチ (このような一時的なリダイレクト間の伝統的な価値観として、非常に便利ではこのアノテーションを使用します)SessionAttribute
@SessionAttributeSession

== ==公式の説明:ときに@SessionAttributeマークされController、そのモデルのモデルにプロパティを追加するときに、名前/種類指定されたアノテーションに基づいて、これらのプロパティを検討する試合は、セッションを置くために渡しされる場合試合は上に置かれていSessonますが起動するまでSessionStatus.setComplete()消え方法を~~~

// @since 2.5   它只能标注在类上
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {

    // 只有名称匹配上了的  Model上的属性会向session里放置一份~~~
    @AliasFor("names")
    String[] value() default {};
    @AliasFor("value")
    String[] names() default {};

    // 也可以拿类型来约束
    Class<?>[] types() default {};
}

この文を理解する注意:ユーザーが呼び出すことができSessionStatus.setComplete、クリアするには、この方法が唯一の明確であるSessionAttribute引数には、それだけには適用されませんSessionパラメーターそれはAPIを使用して自分自身を入れて、セッション内で使用@SessionAttribute〜ノート解放かのいくつかの違いに行きます

デモショー

ここでは、比較的単純な例で何を示して@SessionAttributeそれがありません:

@Controller
@RequestMapping("/sessionattr/demo")
@SessionAttributes(value = {"book", "description"}, types = {Double.class})
public class RedirectController {

    @RequestMapping("/index")
    public String index(Model model, HttpSession httpSession) {
        model.addAttribute("book", "天龙八部");
        model.addAttribute("description", "我乔峰是个契丹人");
        model.addAttribute("price", new Double("1000.00"));

        // 通过Sesson API手动放一个进去
        httpSession.setAttribute("hero", "fsx");

        //跳转之前将数据保存到Model中,因为注解@SessionAttribute中有,所以book和description应该都会保存到SessionAttributes里(注意:不是session里)
        return "redirect:get";
    }

    // 关于@ModelAttribute 下文会讲
    @RequestMapping("/get")
    public String get(@ModelAttribute("book") String book, ModelMap model, HttpSession httpSession, SessionStatus sessionStatus) {
        //可以从model中获得book、description和price的参数
        System.out.println(model.get("book") + ";" + model.get("description") + ";" + model.get("price"));

        // 从sesson中也能拿到值
        System.out.println(httpSession.getAttribute("book"));
        System.out.println("API方式手动放进去的:" + httpSession.getAttribute("hero"));
        // 使用@ModelAttribute也能拿到值
        System.out.println(book);

        // 手动清除SessionAttributes
        sessionStatus.setComplete();
        return "redirect:complete";
    }

    @RequestMapping("/complete")
    @ResponseBody
    public String complete(ModelMap modelMap, HttpSession httpSession) {
        //已经被清除,无法获取book的值
        System.out.println(modelMap.get("book"));
        System.out.println("API方式手动放进去的:" + httpSession.getAttribute("hero"));
        return "sessionAttribute";
    }

}

我々は唯一の入り口へのアクセスを要求する必要があり/indexますが、直接、次のようにコンソール出力は見ることができます:

天龙八部;我乔峰是个契丹人;1000.0
天龙八部
API方式手动放进去的:fsx
天龙八部
null
API方式手动放进去的:fsx

下記のブラウザ:
ここに画像を挿入説明
知人ジュニアパートナーが、この場合、注意深く観察することができ、それは私が上記の言った理論的な知識の証拠です。

@SessionAttributeノートパラメータセットは持っている三つのカテゴリーにそれを使用する方法を:

  1. ビュー(例えば、JSPページ、等)の観点により、request.getAttribute()又はsession.getAttribute取得
  2. 閲覧要求にビューを戻さsession.getAttributeまたはモデルからで取得した(これはまた、より一般的に使用されます)
  3. 自動的に後方に対応する要求元プロセッサにパラメータを設定するModelタイプまたはパラメータ有する@ModelAttribute(に関連して内部パラメータコメントを@ModelAttribute我々の焦点での使用であるべきです)

一例として、私たちはその実行のレベル、本当のマスター、それを分析するには、以下の原則から、その基本的な使い方を知っています。

SessionAttributesHandler

名前の意味を参照してください、それは@SessionAttributesこの注釈の解析コアであるプロセッサ、。管理@SessionAttributes特定のセッション属性をタグ付けを委託し、最終的に保存されているSessionAttributeStore達成するために。

// @since 3.1
public class SessionAttributesHandler {

    private final Set<String> attributeNames = new HashSet<>();
    private final Set<Class<?>> attributeTypes = new HashSet<>();

    // 注意这个重要性:它是注解方式放入session和API方式放入session的关键(它只会记录注解方式放进去的session属性~~)
    private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
    // sessonAttr存储器:它最终存储到的是WebRequest的session域里面去(对httpSession是进行了包装的)
    // 因为有WebRequest的处理,所以达到我们上面看到的效果。complete只会清楚注解放进去的,并不清除API放进去的~~~
    // 它的唯一实现类DefaultSessionAttributeStore实现也简单。(特点:能够制定特殊的前缀,这个有时候还是有用的)
    // 前缀attributeNamePrefix在构造器里传入进来  默认是“”
    private final SessionAttributeStore sessionAttributeStore;

    // 唯一的构造器 handlerType:控制器类型  SessionAttributeStore 是由调用者上层传进来的
    public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
        Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
        this.sessionAttributeStore = sessionAttributeStore;

        // 父类上、接口上、注解上的注解标注了这个注解都算
        SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
        if (ann != null) {
            Collections.addAll(this.attributeNames, ann.names());
            Collections.addAll(this.attributeTypes, ann.types());
        }
        this.knownAttributeNames.addAll(this.attributeNames);
    }

    // 既没有指定Name 也没有指定type  这个注解标上了也没啥用
    public boolean hasSessionAttributes() {
        return (!this.attributeNames.isEmpty() || !this.attributeTypes.isEmpty());
    }

    // 看看指定的attributeName或者type是否在包含里面
    // 请注意:name和type都是或者的关系,只要有一个符合条件就成
    public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
        Assert.notNull(attributeName, "Attribute name must not be null");
        if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
            this.knownAttributeNames.add(attributeName);
            return true;
        } else {
            return false;
        }
    }

    // 把attributes属性们存储起来  进到WebRequest 里
    public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
        attributes.forEach((name, value) -> {
            if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
                this.sessionAttributeStore.storeAttribute(request, name, value);
            }
        });
    }

    // 检索所有的属性们  用的是knownAttributeNames哦~~~~
    // 也就是说手动API放进Session的 此处不会被检索出来的
    public Map<String, Object> retrieveAttributes(WebRequest request) {
        Map<String, Object> attributes = new HashMap<>();
        for (String name : this.knownAttributeNames) {
            Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
            if (value != null) {
                attributes.put(name, value);
            }
        }
        return attributes;
    }

    // 同样的 只会清除knownAttributeNames
    public void cleanupAttributes(WebRequest request) {
        for (String attributeName : this.knownAttributeNames) {
            this.sessionAttributeStore.cleanupAttribute(request, attributeName);
        }
    }


    // 对底层sessionAttributeStore的一个传递调用~~~~~
    // 毕竟可以拼比一下sessionAttributeStore的实现~~~~
    @Nullable
    Object retrieveAttribute(WebRequest request, String attributeName) {
        return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
    }
}

このクラスは、SessionAttributeいわゆるCRUDを:これらの属性のコア処理機能が含まれています。あなたはさらにそれがどのように動作するかを理解したいので、それは入り口を扱うことになるので、彼らが来なければならないでしょうModelFactoryアップ〜

モデルファクトリー

Spring MVC以下のための@SessionAttribute入り口の処理動作、中ModelFactory.initModel()にあるメソッドの意志@SessionAttributeのコメントを分析、処理、およびメソッドの完了後には、属性を同期します。

ModelFactoryするために使用されているモデルを維持し、特に二つの機能が含まれています。

  • プロセッサは、初期化を実行する前に、Model
  • 処理後に実行するModel同期の更新に対応するパラメータSessionAttributes(ない全額が、これらの条件を満たしています)
// @since 3.1
public final class ModelFactory {
    // ModelMethod它是一个私有内部类,持有InvocableHandlerMethod的引用  和方法的dependencies依赖们
    private final List<ModelMethod> modelMethods = new ArrayList<>();
    private final WebDataBinderFactory dataBinderFactory;
    private final SessionAttributesHandler sessionAttributesHandler;

    public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods, WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
    
        // 把InvocableHandlerMethod转为内部类ModelMethod
        if (handlerMethods != null) {
            for (InvocableHandlerMethod handlerMethod : handlerMethods) {
                this.modelMethods.add(new ModelMethod(handlerMethod));
            }
        }
        this.dataBinderFactory = binderFactory;
        this.sessionAttributesHandler = attributeHandler;
    }


    // 该方法完成Model的初始化
    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
        // 先拿到sessionAttr里所有的属性们(首次进来肯定木有,但同一个session第二次进来就有了)
        Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
        // 和当前请求中 已经有的model合并属性信息
        // 注意:sessionAttributes中只有当前model不存在的属性,它才会放进去
        container.mergeAttributes(sessionAttributes);
        // 此方法重要:调用模型属性方法来填充模型  这里ModelAttribute会生效
        // 关于@ModelAttribute的内容  我放到了这里:https://blog.csdn.net/f641385712/article/details/98260361
        // 总之:完成这步之后 Model就有值了~~~~
        invokeModelAttributeMethods(request, container);

        // 最后,最后,最后还做了这么一步操作~~~
        // findSessionAttributeArguments的作用:把@ModelAttribute的入参也列入SessionAttributes(非常重要) 详细见下文
        // 这里一定要掌握:因为使用中的坑坑经常是因为没有理解到这块逻辑
        for (String name : findSessionAttributeArguments(handlerMethod)) {
        
            // 若ModelAndViewContainer不包含此name的属性   才会进来继续处理  这一点也要注意
            if (!container.containsAttribute(name)) {

                // 去请求域里检索为name的属性,若请求域里没有(也就是sessionAttr里没有),此处会抛出异常的~~~~
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
                }
                // 把从sessionAttr里检索到的属性也向容器Model内放置一份~
                container.addAttribute(name, value);
            }
        }
    }


    // 把@ModelAttribute标注的入参也列入SessionAttributes 放进sesson里(非常重要)
    // 这个动作是很多开发者都忽略了的
    private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
        List<String> result = new ArrayList<>();
        // 遍历所有的方法参数
        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
            // 只有参数里标注了@ModelAttribute的才会进入继续解析~~~
            if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
                // 关于getNameForParameter拿到modelKey的方法,这个策略是需要知晓的
                String name = getNameForParameter(parameter);
                Class<?> paramType = parameter.getParameterType();

                // 判断isHandlerSessionAttribute为true的  才会把此name合法的添加进来
                // (也就是符合@SessionAttribute标注的key或者type的)
                if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
                    result.add(name);
                }
            }
        }
        return result;
    }

    // 静态方法:决定了parameter的名字  它是public的,因为ModelAttributeMethodProcessor里也有使用
    // 请注意:这里不是MethodParameter.getParameterName()获取到的形参名字,而是有自己的一套规则的

    // @ModelAttribute指定了value值就以它为准,否则就是类名的首字母小写(当然不同类型不一样,下面有给范例)
    public static String getNameForParameter(MethodParameter parameter) {
        ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
        String name = (ann != null ? ann.value() : null);
        return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter));
    }

    // 关于方法这块的处理逻辑,和上差不多,主要是返回类型和实际类型的区分
    // 比如List<String>它对应的名是:stringList。即使你的返回类型是Object~~~
    public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
        ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
        if (ann != null && StringUtils.hasText(ann.value())) {
            return ann.value();
        } else {
            Method method = returnType.getMethod();
            Assert.state(method != null, "No handler method");
            Class<?> containingClass = returnType.getContainingClass();
            Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
            return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
        }
    }

    // 将列为@SessionAttributes的模型数据,提升到sessionAttr里
    public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
        ModelMap defaultModel = container.getDefaultModel();
        if (container.getSessionStatus().isComplete()){
            this.sessionAttributesHandler.cleanupAttributes(request);
        } else { // 存储到sessionAttr里
            this.sessionAttributesHandler.storeAttributes(request, defaultModel);
        }

        // 若该request还没有被处理  并且 Model就是默认defaultModel
        if (!container.isRequestHandled() && container.getModel() == defaultModel) {
            updateBindingResult(request, defaultModel);
        }
    }

    // 将bindingResult属性添加到需要该属性的模型中。
    // isBindingCandidate:给定属性在Model模型中是否需要bindingResult。
    private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
        List<String> keyNames = new ArrayList<>(model.keySet());
        for (String name : keyNames) {
            Object value = model.get(name);
            if (value != null && isBindingCandidate(name, value)) {
                String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
                if (!model.containsAttribute(bindingResultKey)) {
                    WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
                    model.put(bindingResultKey, dataBinder.getBindingResult());
                }
            }
        }
    }

    // 看看这个静态内部类ModelMethod
    private static class ModelMethod {
        // 持有可调用的InvocableHandlerMethod 这个方法
        private final InvocableHandlerMethod handlerMethod;
        // 这字段是搜集该方法标注了@ModelAttribute注解的入参们
        private final Set<String> dependencies = new HashSet<>();

        public ModelMethod(InvocableHandlerMethod handlerMethod) {
            this.handlerMethod = handlerMethod;
            // 把方法入参中所有标注了@ModelAttribute了的Name都搜集进来
            for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
                if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
                    this.dependencies.add(getNameForParameter(parameter));
                }
            }
        }
        ...
    }
}

ModelFactoryヘルプinitializeメソッドはコントローラの前に呼び出されModelたモデル、およびコールの後に、それを更新します

  • 初期設定でマークされたメソッドを呼び出すことにより、@ModelAttribute使用方法一時記憶セッションでモデルを充填性を。
  • 更新の際にモデル属性の同期セッションが欠落している場合、また追加されますBindingResult属性。

コアルールのデフォルト名についてはConventions.getVariableNameForParameter(parameter)、この方法、私は上記の例を与え、各タイプの一般的な出力値を説明し、あなたはそれを覚えています。参考:春のMVCを学ぶために[一緒に]レベルの原則からHandlerMethod、InvocableHandlerMethod、ServletInvocableHandlerMethodの使用を習得

パラメータ@SessionAttributeの二つの条件を満足する必要があります。

  1. では@SessionAttribute、パラメータ名や注釈の種類を設定します
  2. プロセッサ(Controller)に設定されたパラメータにModel(自動的SessionAttrにおけるそのようなプロセスの終わりに同期)

概要

この記事では説明し@SessionAttribute、治療の基本原則を、とも与えたDemo〜あなたが開発を簡素化することを望んで、豊作を持っている必要があり、それを読むために驚くことではないがダウンし、その基本的な使用を紹介します

関連読書

使用HandlerMethod、InvocableHandlerMethodを習得するための原則レベルでは、春のMVCで学校を[ServletInvocableHandlerMethod]

知識交換

==最後に:あなたはあなたにこの記事が参考に思われる場合は、賞賛の聖歌を指すように望むことができます。もちろん、友人のサークルは、より小さなパートナーも見ているので、共有する作者本人许可的~==を

技術内容に興味を持っている場合、グループWX交換に参加することができますJava高工、架构师3群
グループは、2次元コードを失敗した場合、WX番号を追加してください:fsx641385712(または2次元コードがWXの下でスキャンされます)。そして注:"java入群"単語は、手動でグループに招待されます

記事の場合格式混乱または图片裂开「ここをクリック:説明リンク-テキストリンク-テキストリンクを

おすすめ

転載: www.cnblogs.com/fangshixiang/p/11299545.html