Tapestry 整合 Acegi

Java面试题:Spring,Spring MVC,Spring Boot 之间什么关系? 点开之前,我不知道这么给力! 阅读详情
If you've read a couple of my last (unanswered) posts, you'll see that I was flailing on getting Acegi and Tapestry to play nicely together-- mostly due to the fact that (a) I'm a noob and (b) Tapestry URLs are all /app?page=Blah...which makes it impossible to distinguish between a Login page you want unsecured and another page you do want secured.

So, with the caveat that there may be a lot of unnecessary configuration garbage I'm about to post, I thought I'd share the steps I went through to get this working.

My environment is: Eclipse 3.1.1, Java 5, Tapestry 4, Acegi 1.0.0 RC2, Tomcat 5.5.12

Step 1) Enable Friendly URLs in Tapestry

Before trying to get Acegi set up on your Tapestry application, you should first enable Friendly URLs to allow fine-grained control of pattern matching in the Acegi objectionDefinitionSource widgets.

Most of these instructions are stolen right out of Kent Tong Tap 4 manual.

Step A: Edit your hivemodule.xml file

<contribution configuration-id="tapestry.url.ServiceEncoders">
        <page-service-encoder id="page" extension="html" service="page"/>
        <direct-service-encoder id="direct" stateless-extension="direct" stateful-extension="sdirect"/>        
        <extension-encoder id="extension" extension="svc" after="*"/>
</contribution>

Step B: Edit your web.xml file

In addition to the standard "/app" mapping, add the url-patterns shown here. Tapestry uses a variety of different url translations to achieve direct links, services, etc. through the friendly-url paradigm.

<servlet-mapping> <servlet-name>edis3-admin</servlet-name> <url-pattern>/app</url-pattern> </servlet-mapping>

<servlet-mapping> <servlet-name>edis3-admin</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>

<servlet-mapping> <servlet-name>edis3-admin</servlet-name> <url-pattern>*.direct</url-pattern> </servlet-mapping>

<servlet-mapping> <servlet-name>edis3-admin</servlet-name> <url-pattern>*.sdirect</url-pattern> </servlet-mapping>

<servlet-mapping> <servlet-name>edis3-admin</servlet-name> <url-pattern>*.svc</url-pattern> </servlet-mapping>

Step 2) Add Acegi Spring configurations

This is bulk of the effort here, but it's mostly just XML file editing and small configuration differences to fit in your environment.

Step A: Add Acegi configurations to your web.xml

Add this filter and then map it to your application. Note that many examples you may see will have the targetClass defined to be AuthenticationProcessingFilter...don't use that one; it's just a subset of the larger filter chain we'll set up in our application context.

<!-- Note: this replaces your original Tapestry filter and filter-mapping -->
<filter> <filter-name>Acegi Filter Chain Proxy</filter-name> <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
    <param-name>targetClass</param-name>        
    <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>

<filter-mapping>
    <filter-name>Acegi Filter Chain Proxy</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Still in the web.xml, add an additional file (in my case, application-context-acegi.xml) to your contextConfigLocation that we'll use to store the Acegi configuration information. I also found it necessary to intercept the 403 error code to provide seamless integration with my application when someone hits and a page they're not authorized to see.

<context-param>
    <param-name>contextConfigLocation</param-name>    
    <param-value>classpath:edis3-ws-client-context.xml, 
                                classpath:application-context-acegi.xml
    </param-value>
</context-param>

<error-page>
    <error-code>403</error-code>
    <location>/AccessDenied.html</location>
</error-page>

Step B: Create the application-context-acegi.xml file

I created a new file in the WEB-INF/classes location of my project and added the following configuration settings, which I've commented in the code:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

   <!-- ======================== FILTER CHAIN ======================= -->

    <!--  Note: I got rid of the 'rememberMe' and 'switchUser' filters you see in a lot of examples.  They were confusing the debugging, and now that I understand what's going on, they'll be easy to add in later if I need them -->
    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
      <property name="filterInvocationDefinitionSource">
         <value>

            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
                     
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,anonymousProcessingFilter,
exceptionTranslationFilter,filterInvocationInterceptor

         </value>
      </property>
    </bean>

   <!-- ======================== AUTHENTICATION ======================= -->

   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
      <property name="providers">
         <list>
            <ref local="daoAuthenticationProvider"/>
            <ref local="anonymousAuthenticationProvider"/>
         </list>
      </property>
   </bean>

 <!--  NOTE NOTE NOTE NOTE      BE CAREFUL HERE
         The inMemoryDaoImpl DOES NOT support the passwordEncoder -->
    <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
        <property name="userMap">
            <value>
                tom=tvaughan,ROLE_USER,ROLE_SYSTEM_ADMIN
                sue=stillery,ROLE_USER,ROLE_SYSTEM_ADMIN               
                carlos=cfernandez,ROLE_USER,ROLE_USER_ADMIN
                joel=jmoeller,ROLE_USER,ROLE_USER_ADMIN
                tony=tgiaccone,ROLE_USER,ROLE_SERVICE_LIST_ADMIN
                jack=jrodriguez,ROLE_USER,ROLE_INVESTIGATION_ADMIN
                walter=wkelly,ROLE_USER,ROLE_INVESTIGATION_MGR
                anonymous=anonymous,
            </value>
        </property>
    </bean>

   <!-- define, but don't use until you're ready to attach to a non-inMemoryDao -->
   <bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>

   <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
      <property name="userDetailsService"><ref local="inMemoryDaoImpl"/></property>

   </bean>
   <!-- InMemoryDao doesn't encode passwords...it's gotta be plaintext -->
   <!--       <property name="passwordEncoder"><ref local="passwordEncoder"/></property> -->


   <!-- Automatically receives AuthenticationEvent messages -->
   <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/>

   <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
      <property name="authenticationManager"><ref local="authenticationManager"/></property>
      <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
   </bean>

   <!-- Essentially Unused unless you're using Basic Authentication, which we're not -->
   <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
      <property name="realmName"><value>Contacts Realm</value></property>
   </bean>

   <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
      <property name="key"><value>foobar</value></property>
      <property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property>
   </bean>

   <bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
      <property name="key"><value>foobar</value></property>
   </bean>

   <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
   </bean>

   <!-- ===================== HTTP REQUEST SECURITY ==================== -->

   <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
      <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>
   </bean>

   <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      <property name="authenticationFailureUrl"><value>/LoginFailed.html</value></property>
      <property name="defaultTargetUrl"><value>/Home.html</value></property>
      <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
   </bean>

   <bean id="authenticationProcessingFilterEntryPoint"

class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
      <property name="loginFormUrl"><value>/Login.html</value></property>
      <property name="forceHttps"><value>false</value></property>
   </bean>

   <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
      <property name="allowIfAllAbstainDecisions"><value>false</value></property>
      <property name="decisionVoters">
         <list>
            <ref bean="roleVoter"/>
         </list>
      </property>
   </bean>

   <!-- An access decision voter that reads ROLE_* configuration settings -->
   <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/>  
  

   <!-- Note the order that entries are placed against the objectDefinitionSource is critical.
        The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
        Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last -->     
                                    
   <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
      <property name="objectDefinitionSource">
         <value>                            
                 PATTERN_TYPE_APACHE_ANT
                 /media/*=ROLE_ANONYMOUS,ROLE_USER
                 /styles/*=ROLE_ANONYMOUS,ROLE_USER
                 /AccessDenied.html*=ROLE_ANONYMOUS,ROLE_USER
                 /Login.html*=ROLE_ANONYMOUS,ROLE_USER
                 /Logout.html*=ROLE_ANONYMOUS,ROLE_USER
                 /Login,loginForm.sdirect*=ROLE_ANONYMOUS,ROLE_USER
                 /LoginFailed.html*=ROLE_ANONYMOUS,ROLE_USER
                 /Home.html*=ROLE_ANONYMOUS,ROLE_USER
                 /asset.svc*=ROLE_ANONYMOUS,ROLE_USER
                 /ManageUsers.html*=ROLE_SYSTEM_ADMIN,ROLE_USER_ADMIN
                 /**=ROLE_USER
         </value>
      </property>
   </bean>
</beans>

Some notes about this configuration:
1) If you don't create a generic "ROLE_USER" to define someone who isn't anonymous, then you need to add every single role to every single pattern in your filterInvocationInterceptor. That's a pain, so I just have every user defined to be a member of ROLE_USER in addition to their "real" role (e.g. ROLE_SYSTEM_ADMIN).

2) I can't emphasize the passwordEncoder gotchya enough...I lost a whole day trying to figure out why I kept getting redirected to the login page after I swear I correctly logged in. For the purposes of getting up and running, it's easy to use the inMemoryDaoImpl, but just be sure to comment out the use of the PasswordEncoder that you may have cut & pasted from demo code you'll find on this board and others.

Step 3) Create a Login page

If you're using a branding template (i.e. Border component), it's a good idea to comment it out as you're getting set up because Acegi will log the hell out of attempts to access images, javascript, stylesheets, etc.

Step A: Add the HTML page to your WEB-INF

<html jwcid="$content$">
<!-- body jwcid="@branding:BaseBorder" -->
<body>
<h4>EDIS3 Login</h4>

<p class="errorMessage"><span jwcid="errorMsg"/></p>
<p>Please login:</p>

    <form jwcid="loginForm">
      <table border="0">
        <tr><td>Username:</td><td><input type="text" jwcid="username"/></td></tr>
        <tr><td>Password:</td><td><input type="password" jwcid="password"/></td></tr>
        <tr><td>&nbsp;</td><td><input type="submit" value="Login"/></td></tr>
      </table>
    </form>
<pre>
&lt;property name="userMap"&gt;
  &lt;value&gt;
    tom=tvaughan,ROLE_USER,ROLE_SYSTEM_ADMIN
    sue=stillery,ROLE_USER,ROLE_SYSTEM_ADMIN               
    carlos=cfernandez,ROLE_USER,ROLE_USER_ADMIN
    joel=jmoeller,ROLE_USER,ROLE_USER_ADMIN
    tony=tgiaccone,ROLE_USER,ROLE_SERVICE_LIST_ADMIN
    jack=jrodriguez,ROLE_USER,ROLE_INVESTIGATION_ADMIN
    walter=wkelly,ROLE_USER,ROLE_INVESTIGATION_MGR
    anonymous=anonymous,
  &lt;/value&gt;
&lt;/property&gt;   
</pre>
   
<style type="text/css">
.security_debugging {
  background-color: silver;
  margin: 1em;
  padding: 1em;
  border: 1px dashed black;
}
</style>
<div class="security_debugging">
<p>Current Authentication Information:<br/>
User: <span jwcid="user"><strong>user</strong></span><br/>
Details: <span jwcid="details" raw="true">details</span><br/>
</p>
</div>
   
</body>
</html>

Step B: Define the Login.page file

Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 4.0//EN"
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">

<page-specification class="gov.usitc.edis.pages.Login">

    <description>EDIS Login</description>

    <meta key="page-title" value="EDIS Login"/>
   
     <component id="loginForm" type="Form">
       <binding name="listener" value="listener:login"/>
     </component>
     <component id="username" type="TextField">
       <binding name="value" value="username"/>
     </component>
    <component id="password" type="TextField">
       <binding name="value" value="password"/>
       <binding name="hidden" value="true"/>
     </component>    
     <component id="errorMsg" type="Delegator">
       <binding name="delegate" value="beans.delegate.firstError"/>
     </component>
     <component id="user" type="Insert">
         <binding name="value" value="user"/>
     </component>
     <component id="details" type="Insert">
         <binding name="value" value="details"/>
     </component>     
</page-specification>

Step C: Add the Login.java file to your source tree

package gov.usitc.edis.pages;

import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.RedirectException;
import org.apache.tapestry.html.BasePage;
import org.apache.tapestry.valid.ValidationDelegate;

import org.apache.tapestry.annotations.Bean;
import org.apache.tapestry.annotations.InjectObject;
import org.apache.tapestry.event.PageBeginRenderListener;
import org.apache.tapestry.event.PageEvent;



public abstract class Login extends BasePage implements PageBeginRenderListener {
   
    @SuppressWarnings("unused")
    private final static Log LOG = LogFactory.getLog(Login.class);   
      
    private String username;
    private String password;
   
    @Bean
    public abstract ValidationDelegate getDelegate();
   
    public void login(IRequestCycle cycle) throws RedirectException {
        String acegiUrl = cycle.getAbsoluteURL("/j_acegi_security_check?j_username="+getUsername()+"&j_password="+getPassword());
        LOG.info("Throwing redirect exception to '" + acegiUrl + "'");
        throw new RedirectException(acegiUrl);       
    }   
   
    public void pageBeginRender(PageEvent event){

    }
   
    public String getUser() {
        Authentication myAuth = SecurityContextHolder.getContext().getAuthentication();
        return myAuth.getName();
    }
   
    public String getDetails() {
        Authentication myAuth = SecurityContextHolder.getContext().getAuthentication();
        if(myAuth == null) {
            return "Authorization object is null";
        } else {
            StringBuffer b = new StringBuffer();
            b.append("<ul>");
            b.append("  <li>principal = " + myAuth.getPrincipal() + "</li>");
            b.append("  <li>credentials = " + myAuth.getCredentials() + "</li>");
            b.append("  <li>isAuthenticated = " + myAuth.isAuthenticated() + "</li>");
            b.append("  <li>Granted Authorities = ");
            GrantedAuthority[] gas = myAuth.getAuthorities();
            for(int i=0; i<gas.length; i++) {
                b.append(gas[i] + " ");
            }
            b.append("</li>");
            b.append("  <li>Details = " + myAuth.getDetails() + "</li>");
            b.append("  <li>Class = " + myAuth.getClass() + "</li>");           
            b.append("</ul>");
            //return myAuth.toString();
           
            return b.toString();
           }
    }
  


    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }  
}

Step 4) Add additional support pages

In the Acegi configuration context file from Step #2, you'll find references to "LoginFailed.html" and "Logout.html", and in the web.xml, there's a reference to "AccessDenied.html".

Using the same basic code for the Login page, flush out the other pages.

Because I'm still in the development phase of this project, I find it pretty helpful to put some SecureContext/Authentication display at the bottom of these pages so I know what the current authentication looks like and so I can verify expected behavior.

Step 5) Testing, Logging

I was playing around with trying to get some meaningful JUnit tests on my application working yesterday and wasn't having much success...it's pretty complicated to spool up a simple test, and even then I'm not sure unit testing is what I should be doing--- this seems more like in the realm of functional or possibly integration testing using tools other than JUnit. Stay tuned.

I found that I learned a lot about the Acegi flow of control by turning up debugging...just add this to your log4j.properties files and follow along; just be sure to remove your branding assets from your page first.

log4j.logger.org.acegisecurity=DEBUG

---------------------------------------------------------------------------------------------------------------------------------------

Tapestry is completely oblivious to the presence of Acegi...Acegi operates at the URL request level, before Tapestry even tries to respond to a request.

If you keep getting the login page with your credentials showing up as "Anonymous", make sure you aren't using a PasswordEncoder if you have an InMemoryDaoImpl as your AuthenticationProvider...

In the application I'm working on right now, we have our own business object named "EdisUser" that has the username, address, phone number, etc. in it that is completely independent of Acegi. We use Acegi to challenge you at the login page. Assuming you login correctly, we then go an grab your EdisUser object out of the DB and "do stuff" with that POJO. If we need to check your permission to do something, we can grab your logged-in/valid authentication object from Acegi's SecurityContext, but it's rare that we need to do that.

The only real touch point between Tapestry and Acegi in the login use case is if you use Tapestry to handle the form component on the Login.html page. In my situation, my username field is called "username" and my password field is called "password." Those fields are components defined in the Login.page file which uses the Login.java object as a backing POJO.

My form submit in Login.java looks like this:

public void login(IRequestCycle cycle) throws RedirectException {
         String ciphertext = getCipherText(getPassword());
       
        String acegiUrl = cycle.getAbsoluteURL(
                "/j_acegi_security_check?j_username=" +
                getUsername() +
                "&j_password=" +
                ciphertext);
        LOG.info("Throwing redirect exception to '" + acegiUrl + "'");
       
            throw new RedirectException(acegiUrl);       
}

So you see that Tapestry basically assembles a servlet-style URL and throws a redirect exception to that url. In the web.xml, that /j_acegi_security_check is listened for and winds up in the authenticationProcessingFilter defined in my application-context.xml file.

Tapestry is aware of the username and password because Tapestry is responsible for rendering and processing the login form. So on the Login.html page, you'll see code like this:

<form jwcid="loginForm">
      <table border="0">
        <tr><td>Username:</td><td><input type="text" jwcid="username"/></td></tr>
        <tr><td>Password:</td><td><input type="password" jwcid="password"/></td></tr>
        <tr><td>&nbsp;</td><td><input type="submit" value="Login"/></td></tr>
      </table>
    </form>

Notice that the jwcid of the fields are not j_username and j_password.

When a user fills in those fields and posts the form, tapestry routes the strings down to the Login.java class for processing. In my case, it's the "login" method that handles that form posting.

In the login() method, I finesse the username and password strings** and then throw a redirect exception to the servlet that Acegi is listening to:

 String acegiUrl = cycle.getAbsoluteURL(
        "/j_acegi_security_check?j_username=" +
        getUsername() +
        "&j_password=" +
        ciphertext);
    LOG.info("Throwing redirect exception to '" + acegiUrl + "'");
       
    throw new RedirectException(acegiUrl);

Note that in the 'acegiUrl' I am using the j_username and j_password parameter names.

Ok, so Acegi sees that post to the j_acegi_security_check servlet (mapped in the web.xml file, remember) and goes and does its thing. Assuming the login was valid, it then redirects the user to whatever your 'defaultTargetUrl' value is. Mine is configured like this:

<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      <property name="authenticationFailureUrl"><value>/LoginFailed.html</value></property>
      <property name="defaultTargetUrl"><value>/Home.html</value></property>
      <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
   </bean>

So now the user is looking at the Home.html page, rendered courtesy of Tapestry. That page was backed by the Home.java class. If the Home.java class needs to get that user's info, it can make a call like this (for example):

public void pageBeginRender(PageEvent event){   
        Authentication myAuth =
             SecurityContextHolder.getContext().getAuthentication();
        if(myAuth == null) {
            LOG.info("Authorization object is null");
        } else {
            String name = myAuth.getName();
            EdisUser currentUser = getUserByName(name);
            // now do stuff with my EdisUser business object
        }
    }

Hope this helps,
Tom

**= in my situation, I encrypt the password with the Md5Encoder before sending it to the j_acegi_security_check servlet. Check out hispacta.blogspot.com if you're curious as to why.






Mumax3代码模板:从基础设置到材料参数优化 本文提供了一份详尽的Mumax3微磁学模拟代码模板,从基础网格设置、边界条件到核心材料参数(如钴的Msat、Aex、Ku1)配置与优化均有涵盖。文章深入解析了特征长度计算、初始化方法、弛豫与动力学模拟流程,并分享了并行计算与参数扫描等效率提升技巧,旨在帮助用户快速上手并优化模拟工作流。 阅读详情

相关推荐

rv1126如何切换720p和1080p

切换720p和1080p可以使用modetest,但是需要将这两种模式都添加到connector中去 添加一个新的mode到connector中去,其实内核中已经有相关接口了,需要做一些小改动。 1.不采用设备树的方式去配置,将720p和1080p的timing参数配置放在数组中。 2.这里的printer和设备树中的.compatible = "printer"一致。 3.将其添加到connector中去 static int panel_simple_get_fixed_modes(struct

weixin_42097108的博客 841

打造Tapestry5中的智能的错误页面。

我们一般需要为生产和开发环境准备两套error page。 tapestry在开发环境下的error page做得非常漂亮。 非常详细, 但是在生产环境下就不能暴露太多的信息。 而且error page的外观也需要定制。 这时候tapestry默认的error page就不行了。 所以我们期望的是在开发的时候我们需要tapestry自带的error page, 而在生产环境下我们需要定制自己的er...

邓胤的家当 238

esp32s3连接语言大模型实现实时语音对话功能

esp32s3连接语言大模型实现实时语音对话功能。支持小程序实现Wifi配网;语音唤醒词唤醒ESP32-S3;自定义唤醒词模型训练;百度语音识别语音合成api访问;自定义角色agent;独立电源供电;按键开关机;1.28TFT触摸屏唤醒

Tapestry入门及进阶一

Tapestry开发一个Web Application,对一个新手来说有点困难的,Tapestry由于不同于以前的Web Presentation Framework,所以不可讳言,学习曲线比较长这是事实。我先讲讲一个Web Application的大体结构:以JBuider9为开发工具,你要先建立一个工程,例如是名称是TapestryExmaple,它的workspace是F:/myproje

TangAiYun专栏 2252

java.lang.Exception: org.apache.tapestry.BindingException

java.lang.Exception: org.apache.tapestry.BindingException:  nested exception is java.io.InvalidClassException: org.springframework.transaction.TransactionSystemException;

u012598738的专栏 848

org.apache.tapestry.BindingException: Unable to read OGNL expression ‘<parsed OGNL expression>‘ of $

我的项目用的是tapestry框架 代码在本地正常运行,提交到测试环境后,报了这个异常,css和js文件都丢失了。 本来我以为是request.getContextPath()的问题 因为有一行报错是: Caused by: ognl.NoSuchPropertyException: $BasePage_7.request。 我还找了半天关于解决request.getContextPath()报错的博客 点我! jsp页面中request.getContextPath()问题的解决方法 后来我发现真正的问题

道友 800

Java开发中,spring mvc 的线程怎么调用?

今天逛知乎,看到最近很多人都在问spring mvc 的线程http://www.maiziedu.com/course/java/ 的启动问题,觉得挺有意思的,那哥们儿问的也听仔细,下面的回答也很详尽,分享出来,希望遇对遇到类似问题的Java开发程序猿有所帮助。 问题: 在用spring mvc架构的网站上,设一线程在虚拟机启动时运行,线程里有一全局静态变量N,run()方法里面...

maiziedu的博客 571

java中审核订单流程图_Java必备主流技术流程图

https://juejin.im/post/5d214639e51d4550bf1ae8df1、Spring的生命周期Spring作为当前java最流行性、最强大的轻量级容器框架,了解熟悉Spring的生命周期非常有必要容器启动后,对bean进行初始化按照bean的定义,注入属性检测该对象是否实现xxxAware接口,并将相关的xxxAware实例注入给bean,如BeanNameAware等以...

weixin_35901475的博客 804

java基础面试题

1. 类和对象的区别 答:类是一个独立的程序单位,他应该有个类名,它的内部包含了属性和服务两个主要部分 对象其实就是构成系统的一个基本单位 ,一个对象由一组属性和一组服务组成的 说白了,类就像一台机器,而对象就是类身上的零件 2. 接口 答:接口只能具有抽象方法,一个类可以实现多个接口,当类实现了接口以后就必须 实现接口中的所有的抽象方法 3. JAVA中类的六种关系 1.继承关系 2.聚合关系 3.依赖关系 4.组合关系 5.实现关系 6.关联关系 4. 为什么要用spring 答:基于POJO的轻量级

Barbaresco_的博客 1191

70道Java开发面试题及答案,linux实用教程於岳第三版答案

5. ==和EQUALS的区别 关于== 1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==),比较的是他们的值。 2.复合数据类型() 当他们用(==)进行比较的时候,比较的是他们在内存中的存放地 equals Java 语言里的 equals方法其实是交给开发者去覆写的,让开发者自己去定义满足什么条件的两个Object是equal的。 6.String,StringBuilder,

m0_63174529的博客 636

Spring 中获取 request 的几种方法,及其线程安全性分析

Spring 中获取 request 的几种方法,及其线程安全性分析 编程迷思 Java编程 今天 来源:编程迷思 www.cnblogs.com/kismetv/p/8757260.html   概述   在使用Spring MVC开发Web系统时,经常需要在处理请求时使用request对象,比如获取客户端ip地址、请求的url、header中的属性(如cookie、授权信...

qq_24602265的博客 412

[No000016E]Spring 中获取 request 的几种方法,及其线程安全性分析

前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性。 原创不易,如果觉得文章对你有帮助,欢迎点赞、评论。文章有疏漏之处,欢迎批评指正。 欢迎转载,转载请注明原文链接:http://www.cnblogs.com/kismetv/p/8757260.html 目录 概述 如何测试线程安全性 方法1:Controller中加参数...

weixin_30716141的博客 59

Spring MVC 过时了吗?

这个流程的核心在于,它将一个原始的 HTTP 请求,通过一系列高度可配置的组件,干净利落地转换为对普通 Java 方法的调用,并将返回值再优雅地转换为 HTTP 响应。:它在 2026 年依然有新的版本发布(如 Spring Framework 7.x),有活跃的安全更新,并持续集成最新的技术趋势(虚拟线程、GraalVM、Spring AI)。Spring MVC 熟悉的编程模型,结合 Spring AI 的强大能力,使得 Java 开发者能够快速、可靠地构建 AI 驱动的应用程序。

油墨香^-^的博客 1112

Spring MVC 全面详解(Java 主流 Web 开发框架)

Spring MVC是Spring框架的核心Web模块,采用MVC模式,提供灵活的企业级Web开发方案。2026年主流版本为Spring Framework 7.0.x和Spring Boot 4.0.x,支持Java 25、Jakarta EE 11和AI集成。其核心优势包括依赖注入、灵活配置和强大扩展性,适用于REST API和企业应用开发。核心架构基于DispatcherServlet,通过HandlerMapping、Controller等组件处理请求,支持注解优先配置和RESTful风格。高级特性

likuoelie的博客 777

springMVC、spring、控制反转、依赖注入、MyBatis、springBoot、springSecurity、Java多线程、Redis(缓冲击穿,穿透、雪崩、热点数据集中失效)

什么是springMVC springMVC是一个基于MVC架构的,用来简化WEB应用程序的框架;属于表现层的框架。 springMVC的工作原理 用户发送请求到前端控制器,前端控制器接受到请求调用处理器映射器,处理器映射器根据请求的URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则一并生成)返回给前端控制器,前端控制器通过处理器 适配器调用处理器,然后执行控制器,执行完成后返回视图和模型,处理器适配器将控制器执行结果视图和模型返回给前端控制器;前端控制器将视图和模型传给视图解析器,解析后返回具

mzl_sx的博客 6383

JDK 21 虚拟线程(Virtual Threads)对 Spring MVC 与 Spring WebFlux 的影响深度说明文档

摘要 JDK 21虚拟线程(JEP 444)通过轻量级用户态线程(内存占用仅500B,创建微秒级)实现阻塞式代码的非阻塞性能。对Spring MVC是革命性提升:无需修改代码即可支持10万+并发,吞吐量提升10倍+(15,000 req/s),内存消耗降低99%。Spring Boot 3.1+通过spring.web.threads.virtual=true即可启用。 对Spring WebFlux,虚拟线程虽技术可行但强烈不推荐:与响应式设计哲学冲突,增加调度开销且无性能增益。WebFlux本就能用少量

python15397的博客 1041

【Java面试】Spring MVC

Java面试Spring MVC

pipizhen_的博客 995

JavaWeb02-Java Web框架对比:Spring MVC vs Struts

本文对比了Java Web开发中两大主流框架Spring MVC和Struts2。Struts2基于拦截器架构,提供强大表单处理但面临严重安全漏洞问题;Spring MVC则依托Spring生态系统,以注解驱动和灵活配置为特点,成为企业级应用首选。文章详细介绍了两种框架的开发环境搭建(Maven配置、web.xml设置),并分析了它们的历史演变、核心特点及当前市场地位(Struts2份额下降,Spring MVC主导)。通过架构原理、代码示例和优缺点的系统比较,为开发者技术选型提供参考依据。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

AI回答 | spring,springboot,spring MVC,servlet, spring web之间的联系与支持

向GPT提问:spring,springboot,spring MVC,servlet, spring web之间的联系与支持

你个无聊小demo的博客 1340

从 Java 的 Spring Boot MVC 转向 Go 语言开发的差异变化

从 Java 的 Spring Boot MVC 转向 Go 语言开发,虽然核心的 Web 开发思想相通,但在技术栈和实现方式上会有明显差异。以下是具体对比和转型建议

qq_51586702的博客 1416
上一篇: 用tapestry4.0.x生成验证码
下一篇: 将Tapestry框架打包的实现
deadswan000
博客等级 码龄19年 3粉丝 80原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值