<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>jiwenke</title>
    <description></description>
    <link>http://jiwenke.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Spring源代码解析（十）：Spring Acegi框架授权的实现</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/113436" style="color:red;">http://jiwenke.javaeye.com/blog/113436</a>&nbsp;
          发表时间: 2007年08月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我们从FilterSecurityInterceptor我们从入手看看怎样进行授权的：<br /><pre name="code" class="java">
//这里是拦截器拦截HTTP请求的入口
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }
//这是具体的拦截调用
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
            && observeOncePerRequest) {
           //在第一次进行过安全检查之后就不会再做了
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
            //这是第一次收到相应的请求，需要做安全检测，同时把标志为设置好 -  FILTER_APPLIED，下次就再有请求就不会作相同的安全检查了
            if (fi.getRequest() != null) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
            //这里是做安全检查的地方
            InterceptorStatusToken token = super.beforeInvocation(fi);
            //接着向拦截器链执行
            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.afterInvocation(token, null);
            }
        }
    }
</pre><br />我们看看在AbstractSecurityInterceptor是怎样对HTTP请求作安全检测的：<br /><pre name="code" class="java">
    protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");

        if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
            throw new IllegalArgumentException("Security invocation attempted for object "
                + object.getClass().getName()
                + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                + getSecureObjectClass());
        }
        //这里读取配置FilterSecurityInterceptor的ObjectDefinitionSource属性，这些属性配置了资源的安全设置
        ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);

        if (attr == null) {
            if(rejectPublicInvocations) {
                throw new IllegalArgumentException(
                      "No public invocations are allowed via this AbstractSecurityInterceptor. "
                    + "This indicates a configuration error because the "
                    + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Public object - authentication not attempted");
            }

            publishEvent(new PublicInvocationEvent(object));

            return null; // no further work post-invocation
        }


        if (logger.isDebugEnabled()) {
            logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());
        }
        //这里从SecurityContextHolder中去取Authentication对象，一般在登录时会放到SecurityContextHolder中去
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                    "An Authentication object was not found in the SecurityContext"), object, attr);
        }

        // 如果前面没有处理鉴权，这里需要对鉴权进行处理
        Authentication authenticated;

        if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
            try {//调用配置好的AuthenticationManager处理鉴权，如果鉴权不成功，抛出异常结束处理
                authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
                                                                                             .getAuthentication());
            } catch (AuthenticationException authenticationException) {
                throw authenticationException;
            }

            // We don't authenticated.setAuthentication(true), because each provider should do that
            if (logger.isDebugEnabled()) {
                logger.debug("Successfully Authenticated: " + authenticated.toString());
            }
            //这里把鉴权成功后得到的Authentication保存到SecurityContextHolder中供下次使用
            SecurityContextHolder.getContext().setAuthentication(authenticated);
        } else {//这里处理前面已经通过鉴权的请求，先从SecurityContextHolder中去取得Authentication
            authenticated = SecurityContextHolder.getContext().getAuthentication();

            if (logger.isDebugEnabled()) {
                logger.debug("Previously Authenticated: " + authenticated.toString());
            }
        }

        // 这是处理授权的过程
        try {
            //调用配置好的AccessDecisionManager来进行授权
            this.accessDecisionManager.decide(authenticated, object, attr);
        } catch (AccessDeniedException accessDeniedException) {
            //授权不成功向外发布事件
            AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
                    accessDeniedException);
            publishEvent(event);

            throw accessDeniedException;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Authorization successful");
        }

        AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
        publishEvent(event);

        // 这里构建一个RunAsManager来替代当前的Authentication对象，默认情况下使用的是NullRunAsManager会把SecurityContextHolder中的Authentication对象清空
        Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);

        if (runAs == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("RunAsManager did not change Authentication object");
            }

            // no further work post-invocation
            return new InterceptorStatusToken(authenticated, false, attr, object);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Switching to RunAs Authentication: " + runAs.toString());
            }

            SecurityContextHolder.getContext().setAuthentication(runAs);

            // revert to token.Authenticated post-invocation
            return new InterceptorStatusToken(authenticated, true, attr, object);
        }
    }
</pre><br />到这里我们假设配置AffirmativeBased作为AccessDecisionManager：<br /><pre name="code" class="java">
//这里定义了决策机制，需要全票才能通过
    public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
        throws AccessDeniedException {
        //这里取得配置好的迭代器集合
        Iterator iter = this.getDecisionVoters().iterator();
        int deny = 0;
        //依次使用各个投票器进行投票，并对投票结果进行计票
        while (iter.hasNext()) {
            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
            int result = voter.vote(authentication, object, config);
            //这是对投票结果进行处理，如果遇到其中一票通过，那就授权通过，如果是弃权或者反对，那就继续投票
            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED:
                return;

            case AccessDecisionVoter.ACCESS_DENIED:
            //这里对反对票进行计数
                deny++;

                break;

            default:
                break;
            }
        }
        //如果有反对票，抛出异常，整个授权不通过
        if (deny > 0) {
            throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
                    "Access is denied"));
        }

        // 这里对弃权票进行处理，看看是全是弃权票的决定情况，默认是不通过，由allowIfAllAbstainDecisions变量控制
        checkAllowIfAllAbstainDecisions();
    }
具体的投票由投票器进行，我们这里配置了RoleVoter来进行投票：
    public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
        int result = ACCESS_ABSTAIN;
        //这里取得资源的安全配置
        Iterator iter = config.getConfigAttributes();

        while (iter.hasNext()) {
            ConfigAttribute attribute = (ConfigAttribute) iter.next();
            
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;

                // 这里对资源配置的安全授权级别进行判断，也就是匹配ROLE为前缀的角色配置
                // 遍历每个配置属性，如果其中一个匹配该主体持有的GrantedAuthority,则访问被允许。
                for (int i = 0; i &lt; authentication.getAuthorities().length; i++) {
                    if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }
</pre><br />上面就是对整个授权过程的一个分析，从FilterSecurityInterceptor拦截Http请求入手，然后读取对资源的安全配置以后，把这些信息交由AccessDecisionManager来进行决策，Spring为我们提供了若干决策器来使用，在决策器中我们可以配置投票器来完成投票，我们在上面具体分析了角色投票器的使用过程。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/113436#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 17 Aug 2007 11:01:51 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/113436</link>
        <guid>http://jiwenke.javaeye.com/blog/113436</guid>
      </item>
      <item>
        <title>Spring源代码解析（九）：Spring Acegi框架鉴权的实现</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/112979" style="color:red;">http://jiwenke.javaeye.com/blog/112979</a>&nbsp;
          发表时间: 2007年08月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          简单分析一下Spring Acegi的源代码实现：<br />Servlet.Filter的实现AuthenticationProcessingFilter启动Web页面的验证过程 - 在AbstractProcessingFilter定义了整个验证过程的模板：<br /><pre name="code" class="java">
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        //这里检验是不是符合ServletRequest/SevletResponse的要求
        if (!(request instanceof HttpServletRequest)) {
            throw new ServletException("Can only process HttpServletRequest");
        }

        if (!(response instanceof HttpServletResponse)) {
            throw new ServletException("Can only process HttpServletResponse");
        }

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        //根据HttpServletRequest和HttpServletResponse来进行验证
        if (requiresAuthentication(httpRequest, httpResponse)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Request is to process authentication");
            }
            //这里定义Acegi中的Authentication对象来持有相关的用户验证信息
            Authentication authResult;

            try {
                onPreAuthentication(httpRequest, httpResponse);
                //这里的具体验证过程委托给子类完成，比如AuthenticationProcessingFilter来完成基于Web页面的用户验证
                authResult = attemptAuthentication(httpRequest);
            } catch (AuthenticationException failed) {
                // Authentication failed
                unsuccessfulAuthentication(httpRequest, httpResponse, failed);

                return;
            }

            // Authentication success
            if (continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }
            //完成验证后的后续工作，比如跳转到相应的页面
            successfulAuthentication(httpRequest, httpResponse, authResult);

            return;
        }

        chain.doFilter(request, response);
    }
</pre><br />在AuthenticationProcessingFilter中的具体验证过程是这样的：<br /><pre name="code" class="java">
    public Authentication attemptAuthentication(HttpServletRequest request)
        throws AuthenticationException {
        //这里从HttpServletRequest中得到用户验证的用户名和密码
        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }
        //这里根据得到的用户名和密码去构造一个Authentication对象提供给AuthenticationManager进行验证，里面包含了用户的用户名和密码信息
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Place the last username attempted into HttpSession for views
        request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        //这里启动AuthenticationManager进行验证过程
        return this.getAuthenticationManager().authenticate(authRequest);
    }
</pre><br />在Acegi框架中，进行验证管理的主要类是AuthenticationManager,我们看看它是怎样进行验证管理的 - 验证的调用入口是authenticate在AbstractAuthenticationManager的实现中：<br />//这是进行验证的函数，返回一个Authentication对象来记录验证的结果，其中包含了用户的验证信息，权限配置等，同时这个Authentication会以后被授权模块使用<br /><pre name="code" class="java">
//如果验证失败，那么在验证过程中会直接抛出异常
    public final Authentication authenticate(Authentication authRequest)
        throws AuthenticationException {
        try {//这里是实际的验证处理，我们下面使用ProviderManager来说明具体的验证过程，传入的参数authRequest里面已经包含了从HttpServletRequest中得到的用户输入的用户名和密码
            Authentication authResult = doAuthentication(authRequest);
            copyDetails(authRequest, authResult);

            return authResult;
        } catch (AuthenticationException e) {
            e.setAuthentication(authRequest);
            throw e;
        }
    }
</pre><br />在ProviderManager中进行实际的验证工作，假设这里使用数据库来存取用户信息：<br /><pre name="code" class="java">
    public Authentication doAuthentication(Authentication authentication)
        throws AuthenticationException {
        //这里取得配置好的provider链的迭代器，在配置的时候可以配置多个provider,这里我们配置的是DaoAuthenticationProvider来说明, 它使用数据库来保存用户的用户名和密码信息。
        Iterator iter = providers.iterator();

        Class toTest = authentication.getClass();

        AuthenticationException lastException = null;

        while (iter.hasNext()) {
            AuthenticationProvider provider = (AuthenticationProvider) iter.next();

            if (provider.supports(toTest)) {
                logger.debug("Authentication attempt using " + provider.getClass().getName());
                //这个result包含了验证中得到的结果信息
                Authentication result = null;

                try {//这里是provider进行验证处理的过程
                    result = provider.authenticate(authentication);
                    sessionController.checkAuthenticationAllowed(result);
                } catch (AuthenticationException ae) {
                    lastException = ae;
                    result = null;
                }

                if (result != null) {
                    sessionController.registerSuccessfulAuthentication(result);
                    publishEvent(new AuthenticationSuccessEvent(result));

                    return result;
                }
            }
        }

        if (lastException == null) {
            lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
                        new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
        }

        // 这里发布事件来通知上下文的监听器
        String className = exceptionMappings.getProperty(lastException.getClass().getName());
        AbstractAuthenticationEvent event = null;

        if (className != null) {
            try {
                Class clazz = getClass().getClassLoader().loadClass(className);
                Constructor constructor = clazz.getConstructor(new Class[] {
                            Authentication.class, AuthenticationException.class
                        });
                Object obj = constructor.newInstance(new Object[] {authentication, lastException});
                Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent");
                event = (AbstractAuthenticationEvent) obj;
            } catch (ClassNotFoundException ignored) {}
            catch (NoSuchMethodException ignored) {}
            catch (IllegalAccessException ignored) {}
            catch (InstantiationException ignored) {}
            catch (InvocationTargetException ignored) {}
        }

        if (event != null) {
            publishEvent(event);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("No event was found for the exception " + lastException.getClass().getName());
            }
        }

        // Throw the exception
        throw lastException;
    }
</pre><br />我们下面看看在DaoAuthenticationProvider是怎样从数据库中取出对应的验证信息进行用户验证的，在它的基类AbstractUserDetailsAuthenticationProvider定义了验证的处理模板：<br /><pre name="code" class="java">
    public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
            messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
                "Only UsernamePasswordAuthenticationToken is supported"));

        // 这里取得用户输入的用户名
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
        // 如果配置了缓存，从缓存中去取以前存入的用户验证信息 - 这里是UserDetail，是服务器端存在数据库里的用户信息，这样就不用每次都去数据库中取了
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        //没有取到，设置标志位，下面会把这次取到的服务器端用户信息存入缓存中去
        if (user == null) {
            cacheWasUsed = false;

            try {//这里是调用UserDetailService去取用户数据库里信息的地方
                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            } catch (UsernameNotFoundException notFound) {
                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                } else {
                    throw notFound;
                }
            }

            Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
        }

        if (!user.isAccountNonLocked()) {
            throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",
                    "User account is locked"));
        }

        if (!user.isEnabled()) {
            throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled",
                    "User is disabled"));
        }

        if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired",
                    "User account has expired"));
        }

        // This check must come here, as we don't want to tell users
        // about account status unless they presented the correct credentials
        try {//这里是验证过程，在retrieveUser中从数据库中得到用户的信息，在additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息
              //如果验证通过，那么构造一个Authentication对象来让以后的授权使用，如果验证不通过，直接抛出异常结束鉴权过程
            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
        } catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                // There was a problem, so try again after checking
                // we're using latest data (ie not from the cache)
                cacheWasUsed = false;
                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
                additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
            } else {
                throw exception;
            }
        }

        if (!user.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
        }
        //根据前面的缓存结果决定是不是要把当前的用户信息存入缓存以供下次验证使用
        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;

        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }
        //最后返回Authentication记录了验证结果供以后的授权使用
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }
    //这是是调用UserDetailService去加载服务器端用户信息的地方，从什么地方加载要看设置，这里我们假设由JdbcDaoImp来从数据中进行加载
    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
        throws AuthenticationException {
        UserDetails loadedUser;
        //这里调用UserDetailService去从数据库中加载用户验证信息，同时返回从数据库中返回的信息，这些信息放到了UserDetails对象中去了
        try {
            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        } catch (DataAccessException repositoryProblem) {
            throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
        }

        if (loadedUser == null) {
            throw new AuthenticationServiceException(
                "UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }
</pre><br />下面我们重点分析一下JdbcDaoImp这个类来看看具体是怎样从数据库中得到用户信息的：<br /><pre name="code" class="java">
public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
    //~ Static fields/initializers =====================================================================================
    //这里是预定义好的对查询语句，对应于默认的数据库表结构，也可以自己定义查询语句对应特定的用户数据库验证表的设计
    public static final String DEF_USERS_BY_USERNAME_QUERY =
            "SELECT username,password,enabled FROM users WHERE username = ?";
    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
            "SELECT username,authority FROM authorities WHERE username = ?";

    //~ Instance fields ================================================================================================
    //这里使用Spring JDBC来进行数据库操作
    protected MappingSqlQuery authoritiesByUsernameMapping;
    protected MappingSqlQuery usersByUsernameMapping;
    private String authoritiesByUsernameQuery;
    private String rolePrefix = "";
    private String usersByUsernameQuery;
    private boolean usernameBasedPrimaryKey = true;

    //~ Constructors ===================================================================================================
    //在初始化函数中把查询语句设置为预定义的SQL语句
    public JdbcDaoImpl() {
        usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
        authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
    }

    //~ Methods ========================================================================================================

    protected void addCustomAuthorities(String username, List authorities) {}

    public String getAuthoritiesByUsernameQuery() {
        return authoritiesByUsernameQuery;
    }

    public String getRolePrefix() {
        return rolePrefix;
    }

    public String getUsersByUsernameQuery() {
        return usersByUsernameQuery;
    }

    protected void initDao() throws ApplicationContextException {
        initMappingSqlQueries();
    }

    /**
     * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass
     */
    protected void initMappingSqlQueries() {
        this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());
        this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
    }

    public boolean isUsernameBasedPrimaryKey() {
        return usernameBasedPrimaryKey;
    }
    //这里是取得数据库用户信息的具体过程
    public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException, DataAccessException {
        //根据用户名在用户表中得到用户信息，包括用户名，密码和用户是否有效的信息
        List users = usersByUsernameMapping.execute(username);

        if (users.size() == 0) {
            throw new UsernameNotFoundException("User not found");
        }
        //取集合中的第一个作为有效的用户对象
        UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]
        //这里在权限表中去取得用户的权限信息，同样的返回一个权限集合对应于这个用户
        List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());

        addCustomAuthorities(user.getUsername(), dbAuths);

        if (dbAuths.size() == 0) {
            throw new UsernameNotFoundException("User has no GrantedAuthority");
        }
        //这里根据得到的权限集合来配置返回的User对象供以后使用
        GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);

        String returnUsername = user.getUsername();

        if (!usernameBasedPrimaryKey) {
            returnUsername = username;
        }

        return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);
    }

    public void setAuthoritiesByUsernameQuery(String queryString) {
        authoritiesByUsernameQuery = queryString;
    }

    public void setRolePrefix(String rolePrefix) {
        this.rolePrefix = rolePrefix;
    }

    public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
        this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
    }

    public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
        this.usersByUsernameQuery = usersByUsernameQueryString;
    }

    //~ Inner Classes ==================================================================================================

    /**
     * 这里是调用Spring JDBC的数据库操作，具体可以参考对JDBC的分析，这个类的作用是把数据库查询得到的记录集合转换为对象集合 - 一个很简单的O/R实现
     */
    protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {
        protected AuthoritiesByUsernameMapping(DataSource ds) {
            super(ds, authoritiesByUsernameQuery);
            declareParameter(new SqlParameter(Types.VARCHAR));
            compile();
        }

        protected Object mapRow(ResultSet rs, int rownum)
            throws SQLException {
            String roleName = rolePrefix + rs.getString(2);
            GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);

            return authority;
        }
    }

    /**
     * Query object to look up a user.
     */
    protected class UsersByUsernameMapping extends MappingSqlQuery {
        protected UsersByUsernameMapping(DataSource ds) {
            super(ds, usersByUsernameQuery);
            declareParameter(new SqlParameter(Types.VARCHAR));
            compile();
        }

        protected Object mapRow(ResultSet rs, int rownum)
            throws SQLException {
            String username = rs.getString(1);
            String password = rs.getString(2);
            boolean enabled = rs.getBoolean(3);
            UserDetails user = new User(username, password, enabled, true, true, true,
                    new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});

            return user;
        }
    }
}
</pre><br />从数据库中得到用户信息后，就是一个比对用户输入的信息和这个数据库用户信息的比对过程，这个比对过程在DaoAuthenticationProvider:<br /><pre name="code" class="java">
//这个UserDetail是从数据库中查询到的，这个authentication是从用户输入中得到的
    protected void additionalAuthenticationChecks(UserDetails userDetails,
        UsernamePasswordAuthenticationToken authentication)
        throws AuthenticationException {
        Object salt = null;

        if (this.saltSource != null) {
            salt = this.saltSource.getSalt(userDetails);
        }
        //如果用户没有输入密码，直接抛出异常
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
                    includeDetailsObject ? userDetails : null);
        }
        //这里取得用户输入的密码
        String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();
        //这里判断用户输入的密码是不是和数据库里的密码相同，这里可以使用passwordEncoder来对数据库里的密码加解密
        // 如果不相同，抛出异常，如果相同则鉴权成功
        if (!passwordEncoder.isPasswordValid(
                userDetails.getPassword(), presentedPassword, salt)) {
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
                    includeDetailsObject ? userDetails : null);
        }
    }
</pre><br />上面分析了整个Acegi进行验证的过程，从AuthenticationProcessingFilter中拦截Http请求得到用户输入的用户名和密码，这些用户输入的验证信息会被放到Authentication对象中持有并传递给AuthenticatioManager来对比在服务端的用户信息来完成整个鉴权。这个鉴权完成以后会把有效的用户信息放在一个Authentication中供以后的授权模块使用。在具体的鉴权过程中，使用了我们配置好的各种Provider以及对应的UserDetailService和Encoder类来完成相应的获取服务器端用户数据以及与用户输入的验证信息的比对工作。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/112979#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 16 Aug 2007 11:36:05 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/112979</link>
        <guid>http://jiwenke.javaeye.com/blog/112979</guid>
      </item>
      <item>
        <title>Spring源代码解析（八）：Spring驱动Hibernate的实现</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/110801" style="color:red;">http://jiwenke.javaeye.com/blog/110801</a>&nbsp;
          发表时间: 2007年08月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          O/R工具出现之后，简化了许多复杂的信息持久化的开发。Spring应用开发者可以通过Spring提供的O/R方案更方便的使用各种持久化工具，比如Hibernate；下面我们就Spring+Hibernate中的Spring实现做一个简单的剖析。<br />Spring对Hinberanate的配置是通过LocalSessionFactoryBean来完成的，这是一个工厂Bean的实现，在基类AbstractSessionFactoryBean中：<br /><pre name="code" class="java">
    /**
     * 这是FactoryBean需要实现的接口方法，直接取得当前的sessionFactory的值
     */
    public Object getObject() {
        return this.sessionFactory;
    }
</pre><br />这个值在afterPropertySet中定义：<br /><pre name="code" class="java">
    public void afterPropertiesSet() throws Exception {
        //这个buildSessionFactory是通过配置信息得到SessionFactory的地方
        SessionFactory rawSf = buildSessionFactory();
        //这里使用了Proxy方法插入对getCurrentSession的拦截，得到和事务相关的session
        this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);
    }
</pre><br />我们先看看SessionFactory是怎样创建的，这个方法很长，包含了创建Hibernate的SessionFactory的详尽步骤：<br /><pre name="code" class="java">
    protected SessionFactory buildSessionFactory() throws Exception {
        SessionFactory sf = null;

        // Create Configuration instance.
        Configuration config = newConfiguration();

        //这里配置数据源，事务管理器，LobHander到Holder中，这个Holder是一个ThreadLocal变量,这样这些资源就和线程绑定了
        if (this.dataSource != null) {
            // Make given DataSource available for SessionFactory configuration.
            configTimeDataSourceHolder.set(this.dataSource);
        }

        if (this.jtaTransactionManager != null) {
            // Make Spring-provided JTA TransactionManager available.
            configTimeTransactionManagerHolder.set(this.jtaTransactionManager);
        }

        if (this.lobHandler != null) {
            // Make given LobHandler available for SessionFactory configuration.
            // Do early because because mapping resource might refer to custom types.
            configTimeLobHandlerHolder.set(this.lobHandler);
        }

        //这里是使用Hibernate的各个属性的配置，这里使用了Configuration类来抽象这些数据
        try {
            // Set connection release mode "on_close" as default.
            // This was the case for Hibernate 3.0; Hibernate 3.1 changed
            // it to "auto" (i.e. "after_statement" or "after_transaction").
            // However, for Spring's resource management (in particular for
            // HibernateTransactionManager), "on_close" is the better default.
            config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString());

            if (!isExposeTransactionAwareSessionFactory()) {
                // Not exposing a SessionFactory proxy with transaction-aware
                // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext
                // implementation instead, providing the Spring-managed Session that way.
                // Can be overridden by a custom value for corresponding Hibernate property.
                config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS,
                        "org.springframework.orm.hibernate3.SpringSessionContext");
            }

            if (this.entityInterceptor != null) {
                // Set given entity interceptor at SessionFactory level.
                config.setInterceptor(this.entityInterceptor);
            }

            if (this.namingStrategy != null) {
                // Pass given naming strategy to Hibernate Configuration.
                config.setNamingStrategy(this.namingStrategy);
            }

            if (this.typeDefinitions != null) {
                // Register specified Hibernate type definitions.
                Mappings mappings = config.createMappings();
                for (int i = 0; i &lt; this.typeDefinitions.length; i++) {
                    TypeDefinitionBean typeDef = this.typeDefinitions[i];
                    mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());
                }
            }

            if (this.filterDefinitions != null) {
                // Register specified Hibernate FilterDefinitions.
                for (int i = 0; i &lt; this.filterDefinitions.length; i++) {
                    config.addFilterDefinition(this.filterDefinitions[i]);
                }
            }

            if (this.configLocations != null) {
                for (int i = 0; i &lt; this.configLocations.length; i++) {
                    // Load Hibernate configuration from given location.
                    config.configure(this.configLocations[i].getURL());
                }
            }

            if (this.hibernateProperties != null) {
                // Add given Hibernate properties to Configuration.
                config.addProperties(this.hibernateProperties);
            }

            if (this.dataSource != null) {
                boolean actuallyTransactionAware =
                        (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy);
                // Set Spring-provided DataSource as Hibernate ConnectionProvider.
                config.setProperty(Environment.CONNECTION_PROVIDER,
                        actuallyTransactionAware ?
                        TransactionAwareDataSourceConnectionProvider.class.getName() :
                        LocalDataSourceConnectionProvider.class.getName());
            }

            if (this.jtaTransactionManager != null) {
                // Set Spring-provided JTA TransactionManager as Hibernate property.
                config.setProperty(
                        Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
            }

            if (this.mappingLocations != null) {
                // Register given Hibernate mapping definitions, contained in resource files.
                for (int i = 0; i &lt; this.mappingLocations.length; i++) {
                    config.addInputStream(this.mappingLocations[i].getInputStream());
                }
            }

            if (this.cacheableMappingLocations != null) {
                // Register given cacheable Hibernate mapping definitions, read from the file system.
                for (int i = 0; i &lt; this.cacheableMappingLocations.length; i++) {
                    config.addCacheableFile(this.cacheableMappingLocations[i].getFile());
                }
            }

            if (this.mappingJarLocations != null) {
                // Register given Hibernate mapping definitions, contained in jar files.
                for (int i = 0; i &lt; this.mappingJarLocations.length; i++) {
                    Resource resource = this.mappingJarLocations[i];
                    config.addJar(resource.getFile());
                }
            }

            if (this.mappingDirectoryLocations != null) {
                // Register all Hibernate mapping definitions in the given directories.
                for (int i = 0; i &lt; this.mappingDirectoryLocations.length; i++) {
                    File file = this.mappingDirectoryLocations[i].getFile();
                    if (!file.isDirectory()) {
                        throw new IllegalArgumentException(
                                "Mapping directory location [" + this.mappingDirectoryLocations[i] +
                                "] does not denote a directory");
                    }
                    config.addDirectory(file);
                }
            }

            if (this.entityCacheStrategies != null) {
                // Register cache strategies for mapped entities.
                for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {
                    String className = (String) classNames.nextElement();
                    String[] strategyAndRegion =
                            StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className));
                    if (strategyAndRegion.length > 1) {
                        config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);
                    }
                    else if (strategyAndRegion.length > 0) {
                        config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);
                    }
                }
            }

            if (this.collectionCacheStrategies != null) {
                // Register cache strategies for mapped collections.
                for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {
                    String collRole = (String) collRoles.nextElement();
                    String[] strategyAndRegion =
                            StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole));
                    if (strategyAndRegion.length > 1) {
                        config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]);
                    }
                    else if (strategyAndRegion.length > 0) {
                        config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]);
                    }
                }
            }

            if (this.eventListeners != null) {
                // Register specified Hibernate event listeners.
                for (Iterator it = this.eventListeners.entrySet().iterator(); it.hasNext();) {
                    Map.Entry entry = (Map.Entry) it.next();
                    Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String");
                    String listenerType = (String) entry.getKey();
                    Object listenerObject = entry.getValue();
                    if (listenerObject instanceof Collection) {
                        Collection listeners = (Collection) listenerObject;
                        EventListeners listenerRegistry = config.getEventListeners();
                        Object[] listenerArray =
                                (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size());
                        listenerArray = listeners.toArray(listenerArray);
                        config.setListeners(listenerType, listenerArray);
                    }
                    else {
                        config.setListener(listenerType, listenerObject);
                    }
                }
            }

            // Perform custom post-processing in subclasses.
            postProcessConfiguration(config);

            // 这里是根据Configuration配置创建SessionFactory的地方
            logger.info("Building new Hibernate SessionFactory");
            this.configuration = config;
            sf = newSessionFactory(config);
        }
        //最后把和线程绑定的资源清空
        finally {
            if (this.dataSource != null) {
                // Reset DataSource holder.
                configTimeDataSourceHolder.set(null);
            }

            if (this.jtaTransactionManager != null) {
                // Reset TransactionManager holder.
                configTimeTransactionManagerHolder.set(null);
            }

            if (this.lobHandler != null) {
                // Reset LobHandler holder.
                configTimeLobHandlerHolder.set(null);
            }
        }

        // Execute schema update if requested.
        if (this.schemaUpdate) {
            updateDatabaseSchema();
        }

        return sf;
    }
</pre><br />而直接调用org.hibernate.cfg.Configuration来得到需要的SessionFactory:<br /><pre name="code" class="java">
    protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
        return config.buildSessionFactory();
    }
</pre><br />所以我们这里看到LocalSessionFactory大致起到的一个读取资源配置然后生成SessionFactory的作用；当然这里在得到 SessionFactory之后，还需要对session的事务管理作一些处理 - 使用了一个Proxy模式对getCurrentSession方法进行了拦截；<br /><pre name="code" class="java">
//这里先根据当前的SessionFactory的类型得到Proxy，然后插入Spring定义好的getCurrentSession拦截器
    protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) {
        Class sfInterface = SessionFactory.class;
        if (target instanceof SessionFactoryImplementor) {
            sfInterface = SessionFactoryImplementor.class;
        }
        return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(),
                new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target));
    }
</pre><br />拦截器的实现如下：<br /><pre name="code" class="java">
    private static class TransactionAwareInvocationHandler implements InvocationHandler {

        private final SessionFactory target;

        public TransactionAwareInvocationHandler(SessionFactory target) {
            this.target = target;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // Invocation on SessionFactory/SessionFactoryImplementor interface coming in...
            // 这里对getCurrentSession方法进行拦截，得到一个和当前事务绑定的session交给用户
            if (method.getName().equals("getCurrentSession")) {
                // Handle getCurrentSession method: return transactional Session, if any.
                try {
                    return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);
                }
                catch (IllegalStateException ex) {
                    throw new HibernateException(ex.getMessage());
                }
            }
            else if (method.getName().equals("equals")) {
                // Only consider equal when proxies are identical.
                return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
            }
            else if (method.getName().equals("hashCode")) {
                // Use hashCode of SessionFactory proxy.
                return new Integer(hashCode());
            }

            // 这里是需要运行的SessionFactory的目标方法
            try {
                return method.invoke(this.target, args);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }
</pre><br />我们看看getCurrentSession的实现，在SessionFactoryUtils中： <br /><pre name="code" class="java">
private static Session doGetSession(
            SessionFactory sessionFactory, Interceptor entityInterceptor,
            SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
            throws HibernateException, IllegalStateException {

        Assert.notNull(sessionFactory, "No SessionFactory specified");

        //这个TransactionSynchronizationManager的Resource是一个ThreadLocal变量,sessionFactory是一个单例，但ThreadLocal是和线程绑定的
        //这样就实现了Hiberante中常用的通过ThreadLocal的session管理机制
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
        if (sessionHolder != null && !sessionHolder.isEmpty()) {
            // pre-bound Hibernate Session
            Session session = null;
            if (TransactionSynchronizationManager.isSynchronizationActive() &&
                    sessionHolder.doesNotHoldNonDefaultSession()) {
                // Spring transaction management is active ->
                // register pre-bound Session with it for transactional flushing.
                session = sessionHolder.getValidatedSession();
                if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
                    logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
                    TransactionSynchronizationManager.registerSynchronization(
                            new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
                    sessionHolder.setSynchronizedWithTransaction(true);
                    // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
                    // with FlushMode.NEVER, which needs to allow flushing within the transaction.
                    FlushMode flushMode = session.getFlushMode();
                    if (flushMode.lessThan(FlushMode.COMMIT) &&
                            !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                        session.setFlushMode(FlushMode.AUTO);
                        sessionHolder.setPreviousFlushMode(flushMode);
                    }
                }
            }
            else {
                // No Spring transaction management active -> try JTA transaction synchronization.
                session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
            }
            if (session != null) {
                return session;
            }
        }
        //这里直接打开一个Session
        logger.debug("Opening Hibernate Session");
        Session session = (entityInterceptor != null ?
                sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());

        // Use same Session for further Hibernate actions within the transaction.
        // Thread object will get removed by synchronization at transaction completion.
        // 把新打开的Session放到SessionHolder,然后放到ThreadLocal里面去和线程绑定起来，这个ThreadLocal是在 TransactionSynchronizationManager中配置好的，可以根据sessionFactory来索取
        // 同时根据事务处理的状态来配置session的属性,比如把FlushMode设置为Never，同时把session和事务处理关联起来
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
            logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
            SessionHolder holderToUse = sessionHolder;
            if (holderToUse == null) {
                holderToUse = new SessionHolder(session);
            }
            else {
                holderToUse.addSession(session);
            }
            if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                session.setFlushMode(FlushMode.NEVER);
            }
            TransactionSynchronizationManager.registerSynchronization(
                    new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
            holderToUse.setSynchronizedWithTransaction(true);
            if (holderToUse != sessionHolder) {
                TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
            }
        }
        else {
            // No Spring transaction management active -> try JTA transaction synchronization.
            registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
        }

        // Check whether we are allowed to return the Session.
        if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
            closeSession(session);
            throw new IllegalStateException("No Hibernate Session bound to thread, " +
                "and configuration does not allow creation of non-transactional one here");
        }

        return session;
    }
</pre><br />这里就是在Spring中为使用Hiberante的SessionFactory以及Session做的准备工作，在这个基础上，用户可以通过使用 HibernateTemplate来使用Hibernate的O/R功能，和以前看到的一样这是一个execute的回调：<br /><pre name="code" class="java">
    public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
        //这里得到配置好的Hibernate的Session
        Session session = getSession();
        boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());
        if (existingTransaction) {
            logger.debug("Found thread-bound Session for HibernateTemplate");
        }

        FlushMode previousFlushMode = null;
        try {
            previousFlushMode = applyFlushMode(session, existingTransaction);
            enableFilters(session);
            Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
            //这里是回调的入口
            Object result = action.doInHibernate(sessionToExpose);
            flushIfNecessary(session, existingTransaction);
            return result;
        }
        catch (HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
        catch (SQLException ex) {
            throw convertJdbcAccessException(ex);
        }
        catch (RuntimeException ex) {
            // Callback code threw application exception...
            throw ex;
        }
        finally {
            //如果这个调用的方法在一个事务当中，
            if (existingTransaction) {
                logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
                disableFilters(session);
                if (previousFlushMode != null) {
                    session.setFlushMode(previousFlushMode);
                }
            } //否则把Session关闭
            else {
                // Never use deferred close for an explicitly new Session.
                if (isAlwaysUseNewSession()) {
                    SessionFactoryUtils.closeSession(session);
                }
                else {
                    SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
                }
            }
        }
    }
</pre><br />我们看看怎样得到对应的Session的，仍然使用了SessionFactoryUtils的方法doGetSession:<br /><pre name="code" class="java">
    protected Session getSession() {
        if (isAlwaysUseNewSession()) {
            return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
        }
        else if (!isAllowCreate()) {
            return SessionFactoryUtils.getSession(getSessionFactory(), false);
        }
        else {
            return SessionFactoryUtils.getSession(
                    getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
        }
    }
</pre><br />这样我们就可以和其他的Template那样使用Hibernate的基本功能了，使用的时候Spring已经为我们对Session的获取和关闭，事务处理的绑定做好了封装 - 从这个角度看也大大方便了用户的使用。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/110801#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 10 Aug 2007 10:51:05 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/110801</link>
        <guid>http://jiwenke.javaeye.com/blog/110801</guid>
      </item>
      <item>
        <title>Re: 学习Acegi-认证(authentication)</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/108159" style="color:red;">http://jiwenke.javaeye.com/blog/108159</a>&nbsp;
          发表时间: 2007年08月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          对acegi不是很熟悉但很感兴趣，请问如果通过了认证以后，相关的应用代码怎样获得当前主体的信息？比如登录的用户名什么的，因为有时候应用代码需要的到这些相关的信息来进行相关的处理。只看到说这些信息会存在ContextHolder里面，但具体怎么取出来使用呢？看了一下手册，是不是这样使用： Inside the SecurityContextHolder we store details of the principal currently interacting with the application. Acegi Security uses an Authentication object to represent this information. Whilst you won't normally need to create an Authentication object yourself, it is fairly common for users to query the Authentication  object. You can use the following code block - from anywhere in your application - to do this: <br />
[code] <br />
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  <br />
if (obj instanceof UserDetails) {   String username = ((UserDetails)obj).getUsername(); } else {   String username = obj.toString(); } <br />
[/code]
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/108159#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 02 Aug 2007 13:56:47 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/108159</link>
        <guid>http://jiwenke.javaeye.com/blog/108159</guid>
      </item>
      <item>
        <title>Spring源代码解析（七）：Spring AOP中对拦截器调用的实现</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/107785" style="color:red;">http://jiwenke.javaeye.com/blog/107785</a>&nbsp;
          发表时间: 2007年08月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          前面我们分析了Spring AOP实现中得到Proxy对象的过程，下面我们看看在Spring AOP中拦截器链是怎样被调用的，也就是Proxy模式是怎样起作用的，或者说Spring是怎样为我们提供AOP功能的；<br />在JdkDynamicAopProxy中生成Proxy对象的时候：<br /><pre name="code" class="java">
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
</pre><br />这里的this参数对应的是InvocationHandler对象,这里我们的JdkDynamicAopProxy实现了这个接口，也就是说当Proxy对象的函数被调用的时候，这个InvocationHandler的invoke方法会被作为回调函数调用，下面我们看看这个方法的实现：<br /><pre name="code" class="java">
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation = null;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class targetClass = null;
        Object target = null;

        try {
            // Try special rules for equals() method and implementation of the
            // Advised AOP configuration interface.

            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // What if equals throws exception!?
                // This class implements the equals(Object) method itself.
                return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
            }
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // This class implements the hashCode() method itself.
                return new Integer(hashCode());
            }
            if (Advised.class == method.getDeclaringClass()) {
                // service invocations on ProxyConfig with the proxy config
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal = null;

            if (this.advised.exposeProxy) {
                // make invocation available if necessary
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // May be &lt;code>null&lt;/code>. Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            // 这里是得到目标对象的地方，当然这个目标对象可能来自于一个实例池或者是一个简单的JAVA对象
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            // get the interception chain for this method
            // 这里获得定义好的拦截器链
            List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this.advised, proxy, method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            // 如果没有设定拦截器，那么我们就直接调用目标的对应方法
            if (chain.isEmpty()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }
            else {
                // We need to create a method invocation...
                // invocation = advised.getMethodInvocationFactory().getMethodInvocation(
                //         proxy, method, targetClass, target, args, chain, advised);
                // 如果有拦截器的设定，那么需要调用拦截器之后才调用目标对象的相应方法
                // 这里通过构造一个ReflectiveMethodInvocation来实现，下面我们会看这个ReflectiveMethodInvocation类
                invocation = new ReflectiveMethodInvocation(
                        proxy, target, method, args, targetClass, chain);

                // proceed to the joinpoint through the interceptor chain
                // 这里通过ReflectiveMethodInvocation来调用拦截器链和相应的目标方法
                retVal = invocation.proceed();
            }

            // massage return value if necessary
            if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {
                // Special case: it returned "this" and the return type of the method is type-compatible
                // Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // must have come from TargetSource
                targetSource.releaseTarget(target);
            }

            if (setProxyContext) {
                // restore old proxy
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
</pre><br />我们先看看目标对象方法的调用，这里是通过AopUtils的方法调用 - 使用反射机制来对目标对象的方法进行调用：<br /><pre name="code" class="java">
    public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
        throws Throwable {

        // Use reflection to invoke the method.
        // 利用放射机制得到相应的方法，并且调用invoke
        try {
            if (!Modifier.isPublic(method.getModifiers()) ||
                    !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
                method.setAccessible(true);
            }
            return method.invoke(target, args);
        }
        catch (InvocationTargetException ex) {
            // Invoked method threw a checked exception.
            // We must rethrow it. The client won't see the interceptor.
            throw ex.getTargetException();
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
                    method + "] on target [" + target + "]", ex);
        }
        catch (IllegalAccessException ex) {
            throw new AopInvocationException("Couldn't access method: " + method, ex);
        }
    }
</pre><br />对拦截器链的调用处理是在ReflectiveMethodInvocation里实现的：<br /><pre name="code" class="java">
    public Object proceed() throws Throwable {
        //    We start with an index of -1 and increment early.
        // 这里直接调用目标对象的方法，没有拦截器的调用或者拦截器已经调用完了，这个currentInterceptorIndex的初始值是0
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            // 这里获得相应的拦截器，如果拦截器可以匹配的上的话，那就调用拦截器的invoke方法
            InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(nextInvocation());
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                // 如果拦截器匹配不上，那就调用下一个拦截器，这个时候拦截器链的位置指示后移并迭代调用当前的proceed方法
                this.currentInterceptorIndex++;
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());
        }
    }
</pre><br />这里把当前的拦截器链以及在拦截器链的位置标志都clone到一个MethodInvocation对象了，作用是当前的拦截器执行完之后，会继续沿着得到这个拦截器链执行下面的拦截行为，也就是会迭代的调用上面这个proceed:<br /><pre name="code" class="java">
    private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {
        ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();
        invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;
        invocation.parent = this;
        return invocation;
    }
</pre><br />这里的nextInvocation就已经包含了当前的拦截链的基本信息，我们看到在Interceptor中的实现比如TransactionInterceptor的实现中：<br /><pre name="code" class="java">
    public Object invoke(final MethodInvocation invocation) throws Throwable {
       ......//这里是TransactionInterceptor插入的事务处理代码，我们会在后面分析事务处理实现的时候进行分析
            try {
                //这里是对配置的拦截器链进行迭代处理的调用
                retVal = invocation.proceed();
            }
       ......//省略了和事务处理的异常处理代码 ，也是TransactionInterceptor插入的处理
          else {
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
                        new TransactionCallback() {
                            public Object doInTransaction(TransactionStatus status) {
                                 //这里是TransactionInterceptor插入对事务处理的代码
                                TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
                                //这里是对配置的拦截器链进行迭代处理的调用，接着顺着拦截器进行处理
                                try {                        
                                    return invocation.proceed();
                                }
       ......//省略了和事务处理的异常处理代码 ，也是TransactionInterceptor插入的处理
       }
</pre><br />从上面的分析我们看到了Spring AOP的基本实现，比如Spring怎样得到Proxy,怎样利用JAVA Proxy以及反射机制对用户定义的拦截器链进行处理。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/107785#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 01 Aug 2007 14:56:44 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/107785</link>
        <guid>http://jiwenke.javaeye.com/blog/107785</guid>
      </item>
      <item>
        <title>Re: Spring源代码解析(二)：IoC容器在Web容器中的启动</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/98109" style="color:red;">http://jiwenke.javaeye.com/blog/98109</a>&nbsp;
          发表时间: 2007年07月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          下面就是我们要讨论的问题： 在web应用根上下文建立的时候，是可以对它设置父上下文的，在ContextLoader中： [code]     protected ApplicationContext loadParentContext(ServletContext servletContext)             throws BeansException {          ApplicationContext parentContext = null;         //这里读取在web.xml中配置的参数         String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);         String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);                 //这里得到一个Locator实际上是一个管理IOC容器的容器         if (locatorFactorySelector != null) {             BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);              if (logger.isInfoEnabled()) {                 logger.info(&quot;Getting parent context definition: using parent context key of '&quot; +                         parentContextKey + &quot;' with BeanFactoryLocator&quot;);             }             //这里从Locator容器中得到需要的IOC容器作为根上下文的父上下文，这个IOC容器的取得通过配置LOCATOR_FACTORY_KEY_PARAM属性             this.parentContextRef = locator.useBeanFactory(parentContextKey);             parentContext = (ApplicationContext) this.parentContextRef.getFactory();         }          return parentContext;     } [/code] 问题在这个BeanFactory的作用上，看到在reference guide和Java Doc中的说明： 在reference3.9中提到这个Selector的使用： [i]As another example, in a complex J2EE apps with multiple layers (various JAR files, EJBs, and WAR files packaged as an EAR), with each layer having its own Spring IoC container definition (effectively forming a hierarchy), the preferred approach when there is only one web-app (WAR) in the top hierarchy is to simply create one composite Spring IoC container from the multiple XML definition files from each layer. All of the various Spring IoC container implementations may be constructed from multiple definition files in this fashion. However, if there are multiple sibling web-applications at the root of the hierarchy, it is problematic to create a Spring IoC container for each web-application which consists of mostly identical bean definitions from lower layers, as there may be issues due to increased memory usage, issues with creating multiple copies of beans which take a long time to initialize (e.g. a Hibernate SessionFactory), and possible issues due to side-effects. As an alternative, classes such as ContextSingletonBeanFactoryLocator or SingletonBeanFactoryLocator may be used to demand-load multiple hierarchical (that is one container is the parent of another) Spring IoC container instances in a singleton fashion, which may then be used as the parents of the web-application Spring IoC container instances. The result is that bean definitions for lower layers are loaded only as needed, and loaded only once. [/i] 这里的大致意思是说如果在一个复杂的J2EE应用中怎样简化IOC配置的问题，可以通过这个Locator为多个web应用或者是多层IOC定义一个顶层的IOC容器，这样的话可以只用对多个配置文件读取一次，同时这个顶层IOC容器中管理的bean都是可以共享的 - 而且这个顶层的IOC容器是以单例形式被使用的。在JAVA Doc中给出了使用的例子： 首先配置在顶层IOC需要的定义文件，实际上是配置了一个IOC容器作为这个Locator的bean: [code]        <beans>      <bean class="org.springframework.context.support.ClassPathXmlApplicationContext" id="com.mycompany.myapp">      <constructor-arg>
<list>          <value>com/mycompany/myapp/util/applicationContext.xml</value>          <value>com/mycompany/myapp/dataaccess/applicationContext.xml</value>          <value>com/mycompany/myapp/dataaccess/services.xml</value>        </list>
</constructor-arg>    </bean>    </beans> [/code]  然后就是对这个Locator的使用，如果去看一下ContextLoader的代码也是这样使用的： [code]  BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();   BeanFactoryReference bf = bfl.useBeanFactory(&quot;com.mycompany.myapp&quot;);  // now use some bean from factory   MyClass zed = bf.getFactory().getBean(&quot;mybean&quot;); [/code]  在ContextLoader中的使用是这样的： [code]         if (locatorFactorySelector != null) {              BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);                if (logger.isInfoEnabled()) {                  logger.info(&quot;Getting parent context definition: using parent context key of '&quot; +                          parentContextKey + &quot;' with BeanFactoryLocator&quot;);              }                //这里从Locator容器中得到需要的IOC容器作为根上下文的父上下文，这个IOC容器的取得通过配置LOCATOR_FACTORY_KEY_PARAM属性              this.parentContextRef = locator.useBeanFactory(parentContextKey);              parentContext = (ApplicationContext) this.parentContextRef.getFactory();          } [/code] 从这点看，这个管理IOC容器的容器还是很方便的使用的 - 而且是单例的形式，这样为我们管理那些公用的bean提供了很多便利， 换句话说，如果灵活使用的话，我们可以通过这个管理容器的容器根据需要选择我们的需要到的容器，也就是选择需要的bean - 不一定在顶层去作为根上下文的父上下文。 但经过讨论，我还是不太明白这个Locator怎样能够在跨web应用做到一个单例的使用？因为一个web应用启动时就会维护一个自己的ContextLoader，这里就会有一个自己的Locator，也就是以后在这个应用的范围内使用这个Locator,所以难道这个Locator只能在一个Web应用中使用来管理IOC容器？但是reference guide中的这段话又怎么理解呢？ [i] However, if there are multiple sibling web-applications at the root of the hierarchy, it is problematic to create a Spring IoC container for each web-application which consists of mostly identical bean definitions from lower layers,as there may be issues due to increased memory usage, issues with creating multiple copies of beans which take a long time to initialize (e.g. a Hibernate SessionFactory), and possible issues due to side-effects.[/i] 难道这个Locator的作用范围的确只能在一个web应用中为多层次的IOC定义提供服务？
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/98109#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jul 2007 13:30:17 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/98109</link>
        <guid>http://jiwenke.javaeye.com/blog/98109</guid>
      </item>
      <item>
        <title>Re: Spring源代码解析(二)：IoC容器在Web容器中的启动</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/98108" style="color:red;">http://jiwenke.javaeye.com/blog/98108</a>&nbsp;
          发表时间: 2007年07月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          [quote=&quot;bennyparlo&quot;]又看了遍代码,虽然还没有完全看明白,尤其是那个ContextLoader中的loadParentContext方法.当然,大致思想已经明白了. [/quote] 总的来说，在web容器中启动Spring MVC应用程序的过程主要就是建立一系列上下文的过程，在这里有一个上下文体系被建立起来，具体的说一个web应用对应一个web.xml,这里面定义的 ContextLoader载入这个应用的根上下文 - 这个根上上文也是可以设置自己的父上下文的，只是一般不设而已，这里就是下面要讨论的地方。 然后是对每个DispatchServlet建立自己上下文的过程，每个Servlet的都建立起自己对应的上下文并且把web应用ContextLoader建立的根上下文作为自己上下文的父上下文，业就是说不同的Servlet之间是可以通过共享根上下文来共享bean的使用的 - 只要把这些需要共享的bean用根上下文中定义就好了。值得注意的是这个根上下文对应的是这个web应用，也就是说这些共享bean的共享范围是在这个web应用中的。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/98108#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jul 2007 13:29:48 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/98108</link>
        <guid>http://jiwenke.javaeye.com/blog/98108</guid>
      </item>
      <item>
        <title>Re: Spring源代码解析(一）：IOC容器</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/97624" style="color:red;">http://jiwenke.javaeye.com/blog/97624</a>&nbsp;
          发表时间: 2007年07月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          [quote=&quot;bennyparlo&quot;] 没错,可以参考spring reference中的1段说明:  3.3. 属性，合作者，自动装配和依赖检查 3.3.1. 设置bean的属性和合作者 ................  通常你可以信任Spring做了正确的事情。它会在BeanFactory装载的时候检查出错误，包括对不存在bean的引用和循环引用。它会尽可能晚地设置属性和解决依赖（比如创建那些需要的依赖），也就是在bean真正被创建的时候。这就意味着：就算一个BeanFactory被正确地装载，稍后当你请求一个bean的时候，如果创建那个bean或者它的依赖的时候出现了错误，这个BeanFactory也会抛出一个异常。比如，如果一个bean抛出一个异常作为缺少或非法属性的结果，这样的情况就会发生。这种潜在地推迟一些配置错误可见性的行为正是ApplicationContext默认预实例化singleton bean的原因。以前期的时间和内存为代价在beans真正需要之前创建它们，你就可以在ApplicationContext创建的时候找出配置错误，而不是在后来。如果你愿意，你也可以覆盖这种默认的行为，设置这些singleton bean为lazy-load（不是预实例化的）。      这里是翻译版的原文.应该可以对应你说的这段了.[/quote] 呵呵，把代码找出来，验证一下，这里涉及到一个预实例化的问题，也是BeanFactory和上下文使用的一个区别，也牵涉到一个bean定义属性lazy-init的使用： 我们记得在在上下文初始化的时候会通过refresh来完成 - 在AbstractApplicationContext中： [code]     public void refresh() throws BeansException, IllegalStateException {         synchronized (this.startupShutdownMonitor) {              // 这里是定位，读入，解析和注册bean定义的地方             refreshBeanFactory();             .......               try {                 ........                 // 这里是对bean作预实例化的地方，也是lazy-init属性起作用的地方                 beanFactory.preInstantiateSingletons();                  // Last step: publish corresponding event.                 publishEvent(new ContextRefreshedEvent(this));             }              catch (BeansException ex) {                 // Destroy already created singletons to avoid dangling resources.                 beanFactory.destroySingletons();                 throw ex;             }         }     } [/code] 在DefaultListableBeanFactory中， [code]     public void preInstantiateSingletons() throws BeansException {         if (logger.isInfoEnabled()) {             logger.info(&quot;Pre-instantiating singletons in factory [&quot; + this + &quot;]&quot;);         }          //这里迭代所有的bean定义          for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {             String beanName = (String) it.next();             if (!containsSingleton(beanName) &amp;&amp; containsBeanDefinition(beanName)) {                 RootBeanDefinition bd = getMergedBeanDefinition(beanName, false);                   //预实例化只对singleton和lazy-init设为false的bean起作用                  //而实际的预实例化就是在容器启动的过程就把依赖注入，而不是等到用户要求的时候                  //调用getBean,和用户第一次要求的时候处理是一样的                  if (!bd.isAbstract() &amp;&amp; bd.isSingleton() &amp;&amp; !bd.isLazyInit()) {                     Class beanClass = resolveBeanClass(bd, beanName);                     if (beanClass != null &amp;&amp; FactoryBean.class.isAssignableFrom(beanClass)) {                         getBean(FACTORY_BEAN_PREFIX + beanName);                     }                     else {                         getBean(beanName);                     }                 }             }         }     } [/code] 而这个lazy-init的属性是在BeanDefinitionParserDelegate中设置的，但值得注意的是这里只作设置不做处理，处理要放到预实例化中去作。 [code]     public AbstractBeanDefinition parseBeanDefinitionElement(             Element ele, String beanName, BeanDefinition containingBean) {             ........              //这里取得属性值，在bean定义文件中              String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);              //如果是默认的值并且这个bean是单例，设为false              if (DEFAULT_VALUE.equals(lazyInit) &amp;&amp; bd.isSingleton()) {                 // Just apply default to singletons, as lazy-init has no meaning for prototypes.                 lazyInit = getDefaultLazyInit();             }              //否则设为true              bd.setLazyInit(TRUE_VALUE.equals(lazyInit));              ........      } [/code] 需要注意的是，getBean是BeanFactory的接口方法，而这里的调用都是在上下文中的。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/97624#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 04 Jul 2007 09:58:33 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/97624</link>
        <guid>http://jiwenke.javaeye.com/blog/97624</guid>
      </item>
      <item>
        <title>Google Doc大改版！看来要和微软大干一场了</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/94546" style="color:red;">http://jiwenke.javaeye.com/blog/94546</a>&nbsp;
          发表时间: 2007年06月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天到google doc中去，忽然发现界面变化很大！ 大家进去看看！！
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/94546#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 27 Jun 2007 10:01:07 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/94546</link>
        <guid>http://jiwenke.javaeye.com/blog/94546</guid>
      </item>
      <item>
        <title>Spring和IBM的新闻</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/92693" style="color:red;">http://jiwenke.javaeye.com/blog/92693</a>&nbsp;
          发表时间: 2007年06月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.ibm.com/developerworks/websphere/library/techarticles/0706_johnsonbuck/0706_johnsonbuck.html" target="_blank">http://www.ibm.com/developerworks/websphere/library/techarticles/0706_johnsonbuck/0706_johnsonbuck.html</a><br /><br />With the recent announcement from Interface21 that the Spring Framework has been certified with IBM® WebSphere® Application Server, developerWorks sat down with Rod Johnson, CEO of Interface21, and Paul Buck, Director of WebSphere Open Source at IBM, to find out what this announcement means to developers and customers of Spring and the WebSphere product family.
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/92693#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 21 Jun 2007 09:24:21 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/92693</link>
        <guid>http://jiwenke.javaeye.com/blog/92693</guid>
      </item>
      <item>
        <title>Spring源代码解析(六）：Spring声明式事务处理</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/88189" style="color:red;">http://jiwenke.javaeye.com/blog/88189</a>&nbsp;
          发表时间: 2007年06月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我们看看Spring中的事务处理的代码，使用Spring管理事务有声明式和编程式两种方式，声明式事务处理通过AOP的实现把事物管理代码作为方面封装来横向插入到业务代码中，使得事务管理代码和业务代码解藕。在这种方式我们结合IoC容器和Spirng已有的FactoryBean来对事务管理进行属性配置，比如传播行为，隔离级别等。其中最简单的方式就是通过配置TransactionProxyFactoryBean来实现声明式事物；<br />在整个源代码分析中，我们可以大致可以看到Spring实现声明式事物管理有这么几个部分：<br /><br />    * 对在上下文中配置的属性的处理，这里涉及的类是TransactionAttributeSourceAdvisor，这是一个通知器，用它来对属性值进行处理，属性信息放在TransactionAttribute中来使用,而这些属性的处理往往是和对切入点的处理是结合起来的。对属性的处理放在类TransactionAttributeSource中完成。<br />    * 创建事物的过程，这个过程是委托给具体的事物管理器来创建的，但Spring通过TransactionStatus来传递相关的信息。<br />    * 对事物的处理通过对相关信息的判断来委托给具体的事物管理器完成。<br /><br />我们下面看看具体的实现，在TransactionFactoryBean中：<br /><pre name="code" class="java">
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
        implements FactoryBean, BeanFactoryAware {
//这里是Spring事务处理而使用的AOP拦截器，中间封装了Spring对事务处理的代码来支持声明式事务处理的实现
    private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

    private Pointcut pointcut;

//这里Spring把TransactionManager注入到TransactionInterceptor中去
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionInterceptor.setTransactionManager(transactionManager);
    }

//这里把在bean配置文件中读到的事务管理的属性信息注入到TransactionInterceptor中去
    public void setTransactionAttributes(Properties transactionAttributes) {
        this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
    }

    .........中间省略了其他一些方法.......

    //这里创建Spring AOP对事务处理的Advisor
    protected Object createMainInterceptor() {
        this.transactionInterceptor.afterPropertiesSet();
        if (this.pointcut != null) {
            //这里使用默认的通知器
            return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
        }
        else {
            // 使用上面定义好的TransactionInterceptor作为拦截器，同时使用TransactionAttributeSourceAdvisor
            return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
        }
    }
}
</pre><br />那什么时候Spring的TransactionInterceptor被注入到Spring AOP中成为Advisor中的一部分呢？我们看到在TransactionProxyFactoryBean中，这个方法在IOC初始化bean的时候被执行：<br /><pre name="code" class="java">
    public void afterPropertiesSet() {
        .......
        //TransactionProxyFactoryBean实际上使用ProxyFactory完成AOP的基本功能。
        ProxyFactory proxyFactory = new ProxyFactory();

        if (this.preInterceptors != null) {
            for (int i = 0; i &lt; this.preInterceptors.length; i++) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));
            }
        }

        //这里是Spring加入通知器的地方
        //有两种通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
        //这里把Spring处理声明式事务处理的AOP代码都放到ProxyFactory中去，怎样加入advisor我们可以参考ProxyFactory的父类AdvisedSupport()
        //由它来维护一个advice的链表，通过这个链表的增删改来抽象我们对整个通知器配置的增删改操作。
        proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

        if (this.postInterceptors != null) {
            for (int i = 0; i &lt; this.postInterceptors.length; i++) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));
            }
        }

        proxyFactory.copyFrom(this);
       
        //这里创建AOP的目标源
        TargetSource targetSource = createTargetSource(this.target);
        proxyFactory.setTargetSource(targetSource);

        if (this.proxyInterfaces != null) {
            proxyFactory.setInterfaces(this.proxyInterfaces);
        }
        else if (!isProxyTargetClass()) {
            proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));
        }

        this.proxy = getProxy(proxyFactory);
    }
</pre><br />Spring 已经定义了一个transctionInterceptor作为拦截器或者AOP advice的实现，在IOC容器中定义的其他属性比如transactionManager和事务管理的属性都会传到已经定义好的 TransactionInterceptor那里去进行处理。以上反映了基本的Spring AOP的定义过程，其中pointcut和advice都已经定义好，同时也通过通知器配置到ProxyFactory中去了。<br />下面让我们回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎样定义的，这样我们可以理解具体的属性是怎样起作用，这里我们分析一下类TransactionAttributeSourceAdvisor:<br /><pre name="code" class="java">
public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
    //和其他Advisor一样，同样需要定义AOP中的用到的Interceptor和Pointcut
    //Interceptor使用传进来的TransactionInterceptor
    //而对于pointcut,这里定义了一个内部类，参见下面的代码  
    private TransactionInterceptor transactionInterceptor;

    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();
   
    .........
    //定义的PointCut内部类
        private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
       .......
      //方法匹配的实现，使用了TransactionAttributeSource类
        public boolean matches(Method method, Class targetClass) {
            TransactionAttributeSource tas = getTransactionAttributeSource();
            //这里使用TransactionAttributeSource来对配置属性进行处理
            return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);
        }
    ........省略了equal,hashcode,tostring的代码
    }
</pre><br />这里我们看看属性值是怎样被读入的：AbstractFallbackTransactionAttributeSource负责具体的属性读入任务，我们可以有两种读入方式，比如annotation和直接配置.我们下面看看直接配置的读入方式，在Spring中同时对读入的属性值进行了缓存处理，这是一个decorator模式：<br /><pre name="code" class="java">
    public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
        //这里先查一下缓存里有没有事务管理的属性配置，如果有从缓存中取得TransactionAttribute
        Object cacheKey = getCacheKey(method, targetClass);
        Object cached = this.cache.get(cacheKey);
        if (cached != null) {
            if (cached == NULL_TRANSACTION_ATTRIBUTE) {
                return null;
            }
            else {
                return (TransactionAttribute) cached;
            }
        }
        else {
            // 这里通过对方法和目标对象的信息来计算事务缓存属性
            TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
            //把得到的事务缓存属性存到缓存中，下次可以直接从缓存中取得。
            if (txAtt == null) {
                this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
            }
            else {
                ...........
                this.cache.put(cacheKey, txAtt);
            }
            return txAtt;
        }
    }
</pre><br />别急，基本的处理在computeTransactionAttribute（）中：<br /><pre name="code" class="java">
    private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {
        //这里检测是不是public方法
        if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }
       
        Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
       
        // First try is the method in the target class.
        TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));
        if (txAtt != null) {
            return txAtt;
        }

        // Second try is the transaction attribute on the target class.
        txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));
        if (txAtt != null) {
            return txAtt;
        }

        if (specificMethod != method) {
            // Fallback is to look at the original method.
            txAtt = findTransactionAttribute(findAllAttributes(method));
            if (txAtt != null) {
                return txAtt;
            }
            // Last fallback is the class of the original method.
            return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));
        }
        return null;
    }
</pre><br />经过一系列的尝试我们可以通过findTransactionAttribute（）通过调用findAllAttribute()得到TransactionAttribute的对象，如果返回的是null,这说明该方法不是我们需要事务处理的方法。<br />在完成把需要的通知器加到ProxyFactory中去的基础上，我们看看具体的看事务处理代码怎样起作用，在TransactionInterceptor中：<br /><pre name="code" class="java">
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        //这里得到目标对象
        Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

        //这里同样的通过判断是否能够得到TransactionAttribute来决定是否对当前方法进行事务处理，有可能该属性已经被缓存，
        //具体可以参考上面对getTransactionAttribute的分析，同样是通过TransactionAttributeSource
        final TransactionAttribute txAttr =
                getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
        final String joinpointIdentification = methodIdentification(invocation.getMethod());

        //这里判断我们使用了什么TransactionManager
        if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
            // 这里创建事务，同时把创建事务过程中得到的信息放到TransactionInfo中去
            TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
            Object retVal = null;
            try {
                  retVal = invocation.proceed();
            }
            catch (Throwable ex) {
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

        else {
            // 使用的是Spring定义的PlatformTransactionManager同时实现了回调接口,我们通过其回调函数完成事务处理，就像我们使用编程式事务处理一样。
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
                        new TransactionCallback() {
                            public Object doInTransaction(TransactionStatus status) {
                                //同样的需要一个TransactonInfo
                                TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
                                try {
                                    return invocation.proceed();
                                }
                             .....这里省去了异常处理和事务信息的清理代码
                        });
             ...........
        }
    }
</pre><br />这里面涉及到事务的创建，我们可以在TransactionAspectSupport实现的事务管理代码：<br /><pre name="code" class="java">
    protected TransactionInfo createTransactionIfNecessary(
            TransactionAttribute txAttr, final String joinpointIdentification) {

        // If no name specified, apply method identification as transaction name.
        if (txAttr != null && txAttr.getName() == null) {
            txAttr = new DelegatingTransactionAttribute(txAttr) {
                public String getName() {
                    return joinpointIdentification;
                }
            };
        }

        TransactionStatus status = null;
        if (txAttr != null) {
        //这里使用了我们定义好的事务配置信息,有事务管理器来创建事务，同时返回TransactionInfo
            status = getTransactionManager().getTransaction(txAttr);
        }
        return prepareTransactionInfo(txAttr, joinpointIdentification, status);
    }
</pre><br />首先通过TransactionManager得到需要的事务，事务的创建根据我们定义的事务配置决定，在 AbstractTransactionManager中给出一个标准的创建过程，当然创建什么样的事务还是需要具体的 PlatformTransactionManager来决定，但这里给出了创建事务的模板：<br /><pre name="code" class="java">
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();
        ......

        if (definition == null) {
            //如果事务信息没有被配置，我们使用Spring默认的配置方式
            definition = new DefaultTransactionDefinition();
        }

        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        //下面就是使用配置信息来创建我们需要的事务;比如传播属性和同步属性等
        //最后把创建过程中的信息收集起来放到TransactionStatus中返回；  
        if (definition.getTimeout() &lt; TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to behave.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "Transaction propagation 'mandatory' but no existing transaction found");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            //这里是事务管理器创建事务的地方，并将创建过程中得到的信息放到TransactionStatus中去，包括创建出来的事务
            doBegin(transaction, definition);
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
        }
        else {
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);
        }
    }
</pre><br />接着通过调用prepareTransactionInfo完成事务创建的准备，创建过程中得到的信息存储在TransactionInfo对象中进行传递同时把信息和当前线程绑定；<br /><pre name="code" class="java">
    protected TransactionInfo prepareTransactionInfo(
            TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {

        TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);
        if (txAttr != null) {
        .....
            // 同样的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中来。
            txInfo.newTransactionStatus(status);
        }
        else {
        .......
       }

        // 绑定事务创建信息到当前线程
        txInfo.bindToThread();
        return txInfo;
    }
</pre><br />将创建事务的信息返回，然后看到其他的事务管理代码：<br /><pre name="code" class="java">
    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());
            }
            this.transactionManager.commit(txInfo.getTransactionStatus());
        }
    }
</pre><br />通过transactionManager对事务进行处理，包括异常抛出和正常的提交事务，具体的事务管理器由用户程序设定。<br /><pre name="code" class="java">
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                ......
                try {
                    this.transactionManager.rollback(txInfo.getTransactionStatus());
                }
                ..........
      }
            else {
                .........
                try {
                    this.transactionManager.commit(txInfo.getTransactionStatus());
                }
       ...........
    }

    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (txInfo != null && txInfo.hasTransaction()) {
            ......
            this.transactionManager.commit(txInfo.getTransactionStatus());
        }
    }
</pre><br />Spring通过以上代码对transactionManager进行事务处理的过程进行了AOP包装，到这里我们看到为了方便客户实现声明式的事务处理，Spring还是做了许多工作的。如果说使用编程式事务处理，过程其实比较清楚，我们可以参考书中的例子：<br /><pre name="code" class="java">
    TransactionDefinition td = new DefaultTransactionDefinition();
    TransactionStatus status = transactionManager.getTransaction(td);
    try{
          ......//这里是我们的业务方法
    }catch (ApplicationException e) {
       transactionManager.rollback(status);
       throw e
    }
    transactionManager.commit(status);
     ........
</pre><br />我们看到这里选取了默认的事务配置DefaultTransactionDefinition，同时在创建事物的过程中得到TransactionStatus,然后通过直接调用事务管理器的相关方法就能完成事务处理。<br />声明式事务处理也同样实现了类似的过程，只是因为采用了声明的方法，需要增加对属性的读取处理，并且需要把整个过程整合到Spring AOP框架中和IoC容器中去的过程。<br />下面我们选取一个具体的transactionManager - DataSourceTransactionManager来看看其中事务处理的实现：<br />同样的通过使用AbstractPlatformTransactionManager使用模板方法，这些都体现了对具体平台相关的事务管理器操作的封装，比如commit：<br /><pre name="code" class="java">
    public final void commit(TransactionStatus status) throws TransactionException {
        ......
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        if (defStatus.isLocalRollbackOnly()) {
            ......
            processRollback(defStatus);
            return;
        }
             .......
            processRollback(defStatus);
        ......
        }

        processCommit(defStatus);
    }
</pre><br />通过对TransactionStatus的具体状态的判断，来决定具体的事务处理：<br /><pre name="code" class="java">
    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
        try {
            boolean beforeCompletionInvoked = false;
            try {
                triggerBeforeCommit(status);
                triggerBeforeCompletion(status);
                beforeCompletionInvoked = true;
                boolean globalRollbackOnly = false;
                if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                    globalRollbackOnly = status.isGlobalRollbackOnly();
                }
                if (status.hasSavepoint()) {
                ........
                   status.releaseHeldSavepoint();
                }
                else if (status.isNewTransaction()) {
                ......
                    doCommit(status);
                }
            .........
    }
</pre><br />这些模板方法的实现由具体的transactionManager来实现，比如在DataSourceTransactionManager:<br /><pre name="code" class="java">
    protected void doCommit(DefaultTransactionStatus status) {
        //这里得到存在TransactionInfo中已经创建好的事务
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();

        //这里得到和事务绑定的数据库连接
        Connection con = txObject.getConnectionHolder().getConnection();
        ........
        try {
        //这里通过数据库连接来提交事务
            con.commit();
        }
       .......
    }

    protected void doRollback(DefaultTransactionStatus status) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
        }
        try {
        //这里通过数据库连接来回滚事务
            con.rollback();
        }
        catch (SQLException ex) {
            throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
        }
    }
</pre><br />我们看到在DataSourceTransactionManager中最后还是交给connection来实现事务的提交和rollback。整个声明式事务处理是事务处理在Spring AOP中的应用，我们看到了一个很好的使用Spring AOP的例子，在Spring声明式事务处理的源代码中我们可以看到：<br />1.怎样封装各种不同平台下的事务处理代码<br />2.怎样读取属性值和结合事务处理代码来完成既定的事务处理策略<br />3.怎样灵活的使用SpringAOP框架。<br />如果能够结合前面的Spring AOP的源代码来学习，理解可能会更深刻些。
          <br/>
          <span style="color:red;">
            <a href="http://jiwenke.javaeye.com/blog/88189#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Jun 2007 17:19:36 +0800</pubDate>
        <link>http://jiwenke.javaeye.com/blog/88189</link>
        <guid>http://jiwenke.javaeye.com/blog/88189</guid>
      </item>
      <item>
        <title>Spring源代码解析(五）：Spring AOP获取Proxy</title>
        <author>jiwenke</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jiwenke.javaeye.com">jiwenke</a>&nbsp;
          链接：<a href="http://jiwenke.javaeye.com/blog/88187" style="color:red;">http://jiwenke.javaeye.com/blog/88187</a>&nbsp;
          发表时间: 2007年06月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          下面我们来看看Spring的AOP的一些相关代码是怎么得到Proxy的，让我们我们先看看AOP和Spring AOP的一些基本概念：<br />Advice：<br />    通知，制定在连接点做什么，在Sping中，他主要描述Spring围绕方法调用注入的额外的行为，Spring提供的通知类型有：<br />    before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice，这些都是Spring AOP定义的接口类，具体的动作实现需要用户程序来完成。<br />Pointcut:<br />    切点，其决定一个advice应该应用于哪个连接点，也就是需要插入额外处理的地方的集合，例如，被某个advice作为目标的一组方法。Spring pointcut通常意味着标示方法，可以选择一组方法调用作为pointcut,Spring提供了具体的切点来给用户使用，比如正则表达式切点 JdkRegexpMethodPointcut通过正则表达式对方法名进行匹配，其通过使用 AbstractJdkRegexpMethodPointcut中的对MethodMatcher接口的实现来完成pointcut功能：<br /><pre name="code" class="java">
    public final boolean matches(Method method, Class targetClass) {
        //这里通过放射得到方法的全名
        String patt = method.getDeclaringClass().getName() + "." + method.getName();
        for (int i = 0; i &lt; this.patterns.length; i++) {
            // 这里是判断是否和方法名是否匹配的代码
            boolean matched = matches(patt, i);
            if (matched) {
                for (int j = 0; j &lt; this.excludedPatterns.length; j++) {
                    boolean excluded = matchesExclusion(patt, j);
                    if(excluded) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }
</pre><br />在JDKRegexpMethodPointcut中通过JDK中的正则表达式匹配来完成pointcut的最终确定：<br /><pre name="code" class="java">
    protected boolean matches(String pattern, int patternIndex) {
        Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
        return matcher.matches();
    }
</pre><br />Advisor：<br />当我们完成额外的动作设计（advice)和额外动作插入点的设计（pointcut)以后,我们需要一个对象把他们结合起来，这就是通知器 - advisor,定义应该在哪里应用哪个通知。Advisor的实现有：DefaultPointcutAdvisor他有两个属性advice和 pointcut来让我们配置advice和pointcut。<br />接着我们就可以通过ProxyFactoryBean来配置我们的代理对象和方面行为，在ProxyFactoryBean中有interceptorNames来配置已经定义好的通知器-advisor,虽然这里的名字叫做interceptNames,但实际上是供我们配置advisor的地方，具体的代理实现通过JDK 的Proxy或者CGLIB来完成。因为ProxyFactoryBean是一个FactoryBean,在ProxyFactoryBean中我们通过getObject()可以直接得到代理对象：<br /><pre name="code" class="java">
    public Object getObject() throws BeansException {
        //这里初始化通知器链
        initializeAdvisorChain();
        if (isSingleton()) {
        //根据定义需要生成单件的Proxy
            return getSingletonInstance();
        }
        else {
        .......
            //这里根据定义需要生成Prototype类型的Proxy
            return newPrototypeInstance();
        }
    }
</pre><br />我们看看怎样生成单件的代理对象：<br /><pre name="code" class="java">
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // 这里设置代理对象的接口
                setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
            }
            // Eagerly initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            // 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy
            this.singletonInstance = getProxy(createAopProxy());
            // We must listen to superclass advice change events to recache the singleton
            // instance if necessary.
            addListener(this);
        }
        return this.singletonInstance;
    }

    //使用createAopProxy放回的AopProxy来得到代理对象。
    protected Object getProxy(AopProxy aopProxy) {
        return aopProxy.getProxy(this.beanClassLoader);
    }
</pre><br />ProxyFactoryBean的父类是AdvisedSupport，Spring使用AopProxy接口把AOP代理的实现与框架的其他部分分离开来；在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮助 - 下面我们看到Spring为我们提供的实现，来帮助我们方便的从JDK或者cglib中得到我们想要的代理对象：<br /><pre name="code" class="java">
    protected synchronized AopProxy createAopProxy() {
        if (!this.isActive) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
</pre><br />而在ProxyConfig中对使用的AopProxyFactory做了定义：<br /><pre name="code" class="java">
    //这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方，
    //当然了它包含JDK和Cglib两种实现方式。
    private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
</pre><br />其中在DefaultAopProxyFactory中是这样生成AopProxy的：<br /><pre name="code" class="java">
    public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
        //首先考虑使用cglib来实现代理对象，当然如果同时目标对象不是接口的实现类的话
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
            advisedSupport.getProxiedInterfaces().length == 0) {
            //这里判断如果不存在cglib库，直接抛出异常。
            if (!cglibAvailable) {
                throw new AopConfigException(
                        "Cannot proxy target class because C