【总结-含源代码】Spring Security学习总结一

(2008年08月20日)发表于BlogJava-首页技术区
     摘要: 在认识Spring Security之前,所有的权限验证逻辑都混杂在业务逻辑中,用户的每个操作以前可能都需要对用户是否有进行该项操作的权限进行判断,来达到认证授权的目的。类似这样的权限验证逻辑代码被分散在系统的许多地方,难以维护。AOP(Aspect Oriented Programming)和Spring Security为我们的应用程序很好的解决了此类问题,正如系统日志,事务管理等这些系统级的服务一样,我们应该将它作为系统一个单独的”切面”进行管理,以达到业务逻辑与系统级的服务真正分离的目的,Spring Security将系统的安全逻辑从业务中分离出来。   阅读全文

Hibernating 2008-08-20 10:25 发表评论
阅读全文...
本站相关内容:

源代码解读Spring+Hibernate(JPA)的LazyLoadException异常


    好久的笔记了,趁刚好休息整理文档,翻出这一部分,稍加整理后,就发上来给大家共享一下,希望对各位有所帮助。

    关于LazyLoadException异常,使用过Hibernate O/R Mapping工具的人应该都遇到过,网上也是有很多解决的方案,其中Spring提供的一个方案就是在web.xml增加一个filter,示例代码如下:

<filter>  
    <filter-name>entityManager</filter-name>  
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  
</filter>  
  
<filter-mapping>  
    <filter-name>entityManagerFilter</filter-name>  
    <url-pattern>*.action</url-pattern>  
</filter-mapping> 

  解决办法有了,接下来应该会有人好奇:这个配置filter后它是如何工作的?
  下面来分析一下这个功能实现的源代码, 不过之前,比较重要的是了解,为何会出现lazyload exception的异常发生。

   下面我模拟写了一段代码,这段代码就会发生该异常
   注:只是为了说明,相关的代码就省略了。

@Entity
public class Room {

 @Id
 @Column(length=32)
 private String id;

 @Column(length=20)
 private code;
 
 @OneToMany(mappedBy="room") //default is use lazy load strategy 
 private Set desks;
}

@Entity
public class Desk {

     @Id
     @Column(length=32)
     private String id;
    
     @Column(length=20)
     private code;
     
     @ManyToOne
     private Room root;

}

public class RoomSerivce {

    @Transactional(readOnly=true)
    public Room getRoomById(String roomId) {
      Assert.notBlank(roomId, "room'id is null);
        return getDao().findById(roomId);
    
    }

}

 1 public class RoomServiceTest {
 2 
 3     public static void main(String[] args[]) {
 4     
 5         //get service from spring beanfactory
 6         RoomService service = SpringContext.getSerivce("roomService");
 7         Assert.notNull(service, " roomService bean not exsit");
 8     
 9       Room room = service.getRoomById("1");
10       //here lazy exception throw out
11       Set Desks = room.getDesks();
12       CollectionsUtils.toString(Desks);
13     }
14 }

   分析这段代码,我们不难发现,在RoomServiceTest这个测试的例子中,因为使用了基于Annotation的声明性事务,所以在RoomSerivce.getRoomById方法运行结束后,事务就已经提交了。但示例中Room实体与Desk实例的关系使用的是lazy fetch的策略,此时Room对象中的desks集合还是为空。
当执行到下面两句时(这才真正使用到desks集合时)
  Set Desks = room.getDesks();
  CollectionsUtils.toString(Desks);
Hibernate就会根据原来lazy设定的方式,取EntityManager, 根据它从数据库查询 Desk实现的数据,这时上面我们已经提到,事务已经随getRoomById方法的运行结束提交. EntityManager对象也已经关闭。此时再调用 EntityManager操作,就会报EntityManager has been closed 异常(lazy load exception)

   ok, 清楚这块,大家有时可能也猜想到了Spring这个解决方案是怎么处理的了。
   Spring的TransactionInterceptor 其就是通过AOP负责拦截着所有针对事务TransactionManager的操作.
   这样Spring就可以针对lazy异常进行拦截了。

   清楚上面的后,下面的代码是非常好理解了,来看一下OpenEntityManagerInViewFilter的代码:
我加了些注释,大家很容易明白:

 1     protected void doFilterInternal(
 2             HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 3             throws ServletException, IOException {
 4 
 5       //通过WebApplicationContext,从Web服务中取得context实例后,根据EntityManagerFactory.class类型
 6       //取得EntityManagerFacotry实例
 7         EntityManagerFactory emf = lookupEntityManagerFactory(request);
 8         boolean participate = false;
 9 
10         //如果静态方法hasResource已经有EntityManagerFactory实例了,就不用再通过
11         //EntityManagerFactory创建一个新EntityManger了
12         if (TransactionSynchronizationManager.hasResource(emf)) {
13             // Do not modify the EntityManager: just set the participate flag.
14             participate = true;
15         }
16         else {
17             logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
18             try {
19             //通过EntityManagerFactory创建一个新EntityManger,并通过bindResource方法
20             //保存到TransactionSynchronizationManager中
21             //这样,通TransactionSynchronizationManager的getResource方法取得EntityMangerHolder
22                 EntityManager em = createEntityManager(emf);
23                 TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
24             }
25             catch (PersistenceException ex) {
26                 throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
27             }
28         }
29 
30         try {
31             filterChain.doFilter(request, response);
32         }
33 
34         finally {
35             if (!participate) {
36             //每次请求结束后,就把EntityManager关闭
37                 EntityManagerHolder emHolder = (EntityManagerHolder)
38                         TransactionSynchronizationManager.unbindResource(emf);
39                 logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter");
40                 EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
41             }
42         }
43     }
44    

    上面的代码就不用多解释了, 到现在已经很清楚知道Spring针对 Hibernate的Lazy问题是怎么解决的。
    当然我们可以扩展到除Web服务以外,来实现解决lazy的问题。(我们自己来管理TransactionSynchronizationManager就可以了)
   
    当然Spring针对 Hibernate(非JPA的实现)原理也是一样,只是它针对的SessionFactory,也是由TransactionSynchronizationManager来统一管理。
   
    最后如果大家如还有不清楚的,欢迎一起讨论。
   
Good Luck!
Yours Matthew!



x.matthew 2008-10-11 18:01 发表评论

Spring配置总结


作者: robustwang  链接: http://robustwang.javaeye.com/blog/210678  发表时间: 2008年07月02日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

web.xml
载入Log4j配置
<context-param><!--Log4j配置 在同一容器中部署多个应用不能使用默认的webAppRootKey,必须指定唯一KEY,以免冲突-->
<param-name>webAppRootKey</param-name>
<param-value>itservice.root</param-value>
<!--在log4j.properties中设置日志路径log4j.appender.file.File=${itservice.root}/WEB-INF/itservice.log-->
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>



载入Spring配置文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
<!--可载入多个配置文件分隔符 , ; \t \n -->
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>



字符编码过滤器
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>



配置延迟加载时使用OpenSessionInView
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<!--指定对Spring配置中哪个sessionFactory使用OpenSessionInView-->
<param-value>sessionFactory_itdb</param-value>
</init-param>
</filter>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Struts-config.xml
<action input="/index.jsp" name="loginActionForm" parameter="method" path="/loginAction" scope="session" type="org.springframework.web.struts.DelegatingActionProxy" validate="true" />

<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/classes/action-servlet.xml" />
<!--将spring配置中关于ACTION的配置独立到一个action-servlet.xml文件中-->
</plug-in>
ApplicationContext.xml
<!--直接使用hibernate配置文件-->
<bean id="sessionFactory_itdb" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate_itdb.cfg.xml</value>
</property>
</bean>



<!--使用JNDI DataSource-->
<bean id="it_dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/itdb</value>
</property>
</bean>

<!-- Spring配置DataSource -->
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:init.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${dataSource.driverClassName}"></property>
<property name="url" value="${dataSource.url}"></property>
<property name="username" value="${dataSource.username}"></property>
<property name="password" value="${dataSource.password}"></property>
</bean>
<!-- *********************Hibernate*********************** -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mappingResources">
<list>
<value>com/usish/shweb/hbm/ShwebFile.hbm.xml</value>
<value>com/usish/shweb/hbm/ShwebLog.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">30</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
</props>
</property>
</bean>



<!--******************TransactionManager***********************-->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="baseTxProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="get*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<!--AOP TX-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" propagation="NEVER"/>
<tx:method name="find*" propagation="NEVER"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="txBusinessMethods" expression="execution(* com.ztgame.blog.business.*BusinessImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txBusinessMethods"/>
</aop:config>

<!--annotation TX-->
<tx:annotation-driven proxy-target-class="true" transaction-manager="txManager"/>



Spring编程式事物
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="TransactionManager"/>
</property>
</bean>
<bean id="courseService" class="com.test.CourseService">
<property name="transactionTemplate">
<ref bean="transactionTemplate"/>
</property>
</bean>


private TransactionTemplate transactionTemplate;
public void enrollStudent()...{
transactionTemplate.execute(new TransactionCallback()...{
public Object doInTransaction(TransactionStatus ts)...{
try ...{
// 需要事务控制的方法代码
} catch (Exception e) ...{
ts.setRollbackOnly(); //回滚
}
return null; //事务提交
}
});
}
}



7个事务策略:
1、 PROPAGATION_REQUIRED -- 支持当前的事务,如果不存在就创建一个新的。这是最常用的选择。
2 、 PROPAGATION_SUPPORTS -- 支持当前的事务,如果不存在就不使用事务。
3 、 PROPAGATION_MANDATORY -- 支持当前的事务,如果不存在就抛出异常。
4 、 PROPAGATION_REQUIRES_NEW -- 创建一个新的事务,并暂停当前的事务(如果存在)。
5 、 PROPAGATION_NOT_SUPPORTED -- 不使用事务,并暂停当前的事务(如果存在)。
6 、 PROPAGATION_NEVER -- 不使用事务,如果当前存在事务就抛出异常。
7 、 PROPAGATION_NESTED -- 如果当前存在事务就作为嵌入事务执行,否则与 PROPAGATION_REQUIRED 类似。


5个隔离策略:
ISOLATION_DEFAULT
ISOLATION_READ_UNCOMMITED
ISOLATION_COMMITED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
● 未授权读取(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
● 授权读取(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
● 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
● 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。


● 更新丢失(Lost update):两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
● 脏读取(Dirty Reads):一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。
● 不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。
● 两次更新问题(Second lost updates problem):无法重复读取的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。
● 虚读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。

Dirty reads non-repeatable reads phantom reads
SERIALIZABLE 不会 不会 不会
REPEATABLE READ 不会 不会 会
READ COMMITTED 不会 会 会
READ UNCOMMITTED 会 会 会
本文的讨论也很精彩,浏览讨论>>


JavaEye推荐



xwork源代码--Configuration


作者: 1998a  链接: http://1998a.javaeye.com/blog/228577  发表时间: 2008年08月16日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

1.com.opensymphony.xwork2.config.entities.PackageConfig
PackageConfig中Constructor为protected,所有实例的初始化都要通过内部内Builder,例如:

PackageConfig pack = new PackageConfig.Builder("test").build();为何?
Builder的文档为:
/**
     * The builder for this object.  An instance of this object is the only way to

construct a new instance.  The
     * purpose is to enforce the immutability of the object.  The methods are structured in

a way to support chaining.
     * After setting any values you need, call the {@link #build()} method to create the

object.
     */
Build方法如下:
public PackageConfig build() {
            target.actionConfigs = Collections.unmodifiableMap(target.actionConfigs);
            target.globalResultConfigs = Collections.unmodifiableMap

(target.globalResultConfigs);
            target.interceptorConfigs = Collections.unmodifiableMap

(target.interceptorConfigs);
            target.resultTypeConfigs = Collections.unmodifiableMap

(target.resultTypeConfigs);
            target.globalExceptionMappingConfigs = Collections.unmodifiableList

(target.globalExceptionMappingConfigs);
            target.parents = Collections.unmodifiableList(target.parents);

            PackageConfig result = target;
            target = new PackageConfig(result);
            return result;
        }
最后为何在在原来target基础上新new一个PackageConfig呢?
我的想法是这样做的目的是保证PackageConfig是个immutable对象,就是说客户端得到的PackageConfig

对象都是不可变的,所有的可变性操作都是通过PackageConfig.Builder实现的。同样,ActionConfig、

ResultTypeConfig等Config类型对象的immutable也是通过这种方式实现的。

2.com.opensymphony.xwork2.config.Configuration
这个Configuration对象让我想起来Topcoder的ConfigurationAPI组件,一个DTO对象,用于向其他组件

提供对配置文件内容的映射。具体关系如下图:
 
与之不同的是,这里的Configuration还包括了一个reload方法,用于告诉Configuration加载配置文件

。当然,采用了Strategy模式,所有的配置文件读取细节都delegate给ConfigurationProvider,这样一

来便于更改配置文件细节,达到了与核心解耦的目的。

com.opensymphony.xwork2.config.providers.XmlConfigurationProvider便是读取XML配置文件的

provider。

3、com.opensymphony.xwork2.config.ConfigurationManager
这才是configuration最核心的地方。所有上面说到的Configuration或是ConfigurationProvider都被聚

合到ConfigurationManager里,通过getConfiguration这个方法得到Configuration对象。
public synchronized Configuration getConfiguration() {
        if (configuration == null) {
            setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
            try {
                configuration.reloadContainer(getContainerProviders());
            } catch (ConfigurationException e) {
                setConfiguration(null);
                throw new ConfigurationException("Unable to load configuration.", e);
            }
        } else {
            conditionalReload();
        }

        return configuration;
}


本文的讨论也很精彩,浏览讨论>>


JavaEye推荐



struts2+spring+hibernate项目总结


作者: coolworm  链接: http://coolworm.javaeye.com/blog/269196  发表时间: 2008年11月18日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

1、struts 2.0中的<s:textfield>标签
  
<s:textfield label="%{getText('label.login.userName')}"
       name="userName"
       tooltip="%{getText('label.login.userName.toolTip')}"
       id="userName" />

label属性就指明了文本框前面的说明文本,没必要重新加入文字说明


2、在struts 2.0的标签中,国际化信息要用"%{getText('label.login.userName')}"这种形式


3、<s:head/>放置在<head></head>之间

将会引入struts tag用到的一些css和js文件
需要注意的是,如果任何ui tag或者ajax tag的theme属性值是ajax
那么<s:head>必须有theme属性 并且它的值是ajax
这将会额外地引入与ajax相关的js文件,比如dojo.js

如果没有引入,则会出现dojo不存在的错误
同时引入之后,可以在写javascript脚本时使用dojo框架,例如dojo,byId()


4、如果要自定义布局struts 2.0中的ui tag,则要设置ui tag必须加上theme="simple"才能进行自定义布局


5、在结合dwr的过程中,在页面要导入的javascript脚本是
  <script type='text/javascript' src='/tmsot/dwr/interface/login.js'></script>
  <script type='text/javascript' src='/tmsot/dwr/engine.js'></script>
  <script type='text/javascript' src='/tmsot/dwr/util.js'></script>
但是engine.js、util.js文件没必要copy


6、当进行用户名是否存在验证的时候,如果多次输入错误,则会出现多条错误提示,同时,即使输入的是正确的也不能进入,

<bean id="loginAction" class="com.lsxy.tmsoft.web.action.Login"
  abstract="false" lazy-init="default"
  autowire="default" dependency-check="default" scope="prototype">
  <property name="userService">
   <ref bean="userService" />
  </property>
 </bean>

如图配置后就表明每次从spring容器中获取action.Login的实例的时候就会new一个新对象,即我们所说的原型,spring中scope默认的是单态(singleton),当然针对web应用程序,还可以配置为request、session等范围。至于什么时候使用什么权限范围就要看应用程序的使用了,比如在多线程程序中,单态是否会对程序有所影响就需要考虑了。

  
7、对struts.xml代码著一备注

<struts>
 <constant name="objectFactory" value="spring" />
 <constant name="struts.custom.i18n.resources"
  value="globalMessages" />
 <package name="default" extends="struts-default">
  <action name="login" class="loginAction">   
   <result name="SUCCESS">/admin/index.jsp</result>
   <result name="input">/login.jsp</result>
  </action>
 </package>
</struts>

要和spring结合请加<constant name="objectFactory" value="spring" />
要国际化 <constant name="struts.custom.i18n.resources"
  value="globalMessages" />





已有 0 人发表留言,猛击->> 这里<<-参与讨论


JavaEye推荐



互联网相关内容:
spring源代码学习(2.0.5)一(BeanFactory) (2008年07月06日)
源代码解读Spring+Hibernate(JPA)的LazyLoadException异常 (2008年10月11日)
Spring配置总结 (2008年07月02日)
xwork源代码--Configuration (2008年08月16日)
struts2+spring+hibernate项目总结 (2008年11月18日)
mysql源代码安装 (2007年09月16日)
java 源代码地址 (2007年09月25日)
开放源代码 - Wikipedia (2007年09月26日)
.NET FX库源代码 (2007年10月05日)