2010年1月4日

(转贴)数据库连接(内连接,外连接,交叉连接)

数据库连接分为:内连接,外连接(左、右连接,全连接),交叉连接
文章地址 : http://www.zxbc.cn/html/20080527/51189.html
转载 
内连接:把两个表中数据对应的数据查出来 
外连接:以某个表为基础把对应数据查出来(全连接是以多个表为基础) 
student表 
no name 
1     a 
2     b 
3     c 
4     d 
grade表 
no grade 
1     90 
2     98 
3     95 
内连接 inner join(查找条件中对应的数据,no4没有数据不列出来) 
语法:select * from student inner join grade on student.no = grade.no 
结果 
student.no name grade.no grade 
1             a             1         90 
2             b             2         98 
3             c             3         95 
左连接(左表中所有数据,右表中对应数据) 
语法:select * from student left join grade on student.no = grade.no 
结果: 
student.no name grade.no grade 
1                 a         1         90 
2                 b         2         98 
3                 c         3         95 
4                 d     
右连接(右表中所有数据,左表中对应数据) 
语法:select * from student right join grade on student.no = grade.no 
结果: 
student.no name grade.no grade 
1                 a         1         90 
2                 b         2         98 
3                 c         3         95 
全连接 
语法:select * from student full join grade on student.no = grade.no 
结果: 
no name grade 
1     a     90 
2     b     98 
3     c     95 
4     d 
1     a     90 
2     b     98 
3     c     95 
注:access 中不能直接使用full join ,需要使用union all 将左连接和右连接合并后才可以

交叉连接
将两个表所有行组合,连接后的行数为两个表行数的乘积(笛卡尔积)
语法,借用上面的例子应该是
select * from student cross join grade

行数应该为12行 :
no name grade 
1     a     90 
2     b     98 
3     c     95 
4     d  
1     a     90 
2     b     98 
3     c     95 
4     d 
1     a     90 
2     b     98 
3     c     95 
4     d 

posted @ 2011-11-30 17:24 AK47 阅读(475) | 评论 (0)编辑 收藏

JAXB向Xml非根节点添加一个或多个属性

JAXB 向Xml非根节点添加一个或多个属性,直接上代码,关于JAXB的相关注解可查阅JAVA API。

原创文章,转载请注明出处。http://www.blogjava.net/kangdy/archive/2011/11/23/364635.html

code1: colors类  根节点
code1
package com.kangdy.test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "Colors")
@XmlAccessorType(XmlAccessType.FIELD)
public class Colors {
    
    @XmlElement(name = "red",nillable=true)
    private Red red;
    
    @XmlElement(name = "blue",nillable=true)
    private Blue blue;

    public Red getRed() {
        return red;
    }

    public Blue getBlue() {
        return blue;
    }

    public void setRed(Red red) {
        this.red = red;
    }

    public void setBlue(Blue blue) {
        this.blue = blue;
    }
}

code2:  Red类  子节点
code2package com.kangdy.test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "red")
@XmlAccessorType(XmlAccessType.FIELD)
public class Red {
    
    private String value;
    
    @XmlAttribute(name = "att1")
    private String att;
    
    public String getValue() {
        return value;
    }
    
    public void setValue(String value) {
        this.value = value;
    }

    public String getAtt() {
        return att;
    }

    public void setAtt(String att) {
        this.att = att;
    }
    
}


code3:  类 Blue 子节点
code3
package com.kangdy.test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "blue")
@XmlAccessorType(XmlAccessType.FIELD)
public class Blue {
    private String value;
    
    @XmlAttribute(name = "att2")
    private String att2;
    
    @XmlAttribute(name = "att1")
    private String att;
    
    public String getAtt() {
        return att;
    }

    public void setAtt(String att) {
        this.att = att;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getAtt2() {
        return att2;
    }

    public void setAtt2(String att2) {
        this.att2 = att2;
    }
}

code4: main类
code4
package com.kangdy.test;

import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Jaxbtest {
    public static void main(String[] args) throws Exception {

        StringWriter writer = new StringWriter();
        JAXBContext jc = JAXBContext.newInstance(Colors.class);
        Marshaller ma = jc.createMarshaller();
        ma.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        
        Colors colors = new Colors();
        Red red = new Red();
        red.setAtt("att-red");
        red.setValue("red");
        Blue blue = new Blue();
        blue.setValue("blue");
        blue.setAtt("att-blue");
        blue.setAtt2("blue-att2");
        colors.setRed(red);
        colors.setBlue(blue);
        
        ma.marshal(colors, writer);
        System.out.println(writer.toString());

    }
}

运行结果:
结果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Colors>
    <red att1="att-red">
        <value>red</value>
    </red>
    <blue att1="att-blue" att2="blue-att2">
        <value>blue</value>
    </blue>
</Colors>

posted @ 2011-11-23 14:33 AK47 阅读(10080) | 评论 (4)编辑 收藏

(转载)关于paramsPrepareParamsStack

原帖地址:
http://hi.baidu.com/%CC%AB%C6%BD%D1%F31986/blog/item/110b13b1384e805e08230259.html
转贴

paramsPrepareParamsStack在Struts 2.0中是一个很奇妙的interceptor stack,以至于很多人疑问为何不将其设置为默认的interceptor stack。paramsPrepareParamsStack主要解决了ModelDriven和Preparable的配合问题,从字面上理解来说, 这个stack的拦截器调用的顺序为:首先params,然后prepare,接下来modelDriven,最后再params。Struts 2.0的设计上要求modelDriven在params之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。流程如下:
   1. params拦截器首先给action中的相关参数赋值,如id  
   2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
   3. modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare中创建的
   4. params拦截器再将参数赋值给model对象
   5. action的业务逻辑执行 依据此stack,一个action的代码通常如下

public class UserAction extends ActionSupport implements ModelDriven, Preparable {
    private User user;
    private int id;
    private UserService service; // user business service

    public void setId(int id) {
        this.id = id;
    }

    /**
     * create a new user if none exists, otherwise load the user with the
     * specified id
     */
    public void prepare() throws Exception {
        if (id == 0) {
            user = new User();
        } else {
            user = service.findUserById(id);
        }
    }

    public Object getModel() {
        return user;
    }

    /**
     * create or update the user and then view the created user
     */
    public String update() {
        if (id == 0) {
            service.create(user);
        } else {
            service.update(user);
        }
        return "redirect";
    }

    /**
     * delete the user and go to a default home page
     */
    public String delete() {
        service.deleteById(id);
        return "home";
    }

    /**
     * show the page allowing the user to view the existing data
     */
    public String view() {
        return "view";
    }

    /**
     * show the page allowing the user to view the existing data and change the
     * values
     */
    public String edit() {
        return "input";
    }

在上述代码中,edit和view都不需要根据id再为界面准备数据,因为prepare方法已经准备好了model,这些方法很简单。对于update 方法,prepare首先会从数据库中加载数据,然后params拦截器会将参数值付给model,在update直接更新就可以,不会出现数据被乱更新 的情况。象Hibernate框架,会判断哪些字段更新了,然后进行更新,性能也不会损失。
通过paramsPrepareParamsStack可以让流程更明确,代码更简洁,也更利于大家的交流。

posted @ 2011-11-16 15:39 AK47 阅读(425) | 评论 (0)编辑 收藏

(转载) Struts 2杂谈(1):ValueStack对象的传送带机制

Struts 2杂谈(1):ValueStack对象的传送带机
作者:nokiaguy  原文地址:http://blog.csdn.net/nokiaguy/article/details/4684750
转贴
   众所周知,Strut 2的Action类通过属性可以获得所有相关的值,如请求参数、Action配置参数、向其他Action传递属性值(通过chain结果)等等。要获得 这些参数值,我们要做的唯一一件事就是在Action类中声明与参数同名的属性,在Struts 2调用Action类的Action方法(默认是execute方法)之前,就会为相应的Action属性赋值。
    要完成这个功能,有很大程度上,Struts 2要依赖于ValueStack对象。这个对象贯穿整个Action的生命周期(每个Action类的对象实例会拥有一个ValueStack对象)。当 Struts 2接收到一个.action的请求后,会先建立Action类的对象实例,并且将Action类的对象实例压入ValueStack对象中(实际 上,ValueStack对于相当一个栈),而ValueStack类的setValue和findValue方法可以设置和获得Action对象的属性 值。Struts 2中的某些拦截器正是通过ValueStack类的setValue方法来修改Action类的属性值的。如params拦截器用于将请求参数值映射到相 应成Action类的属性值。在params拦截器中在获得请求参数值后,会使用setValue方法设置相应的Action类的属性。
    从这一点可以看出,ValueStack对象就象一个传送带,当客户端请求.action时,Struts 2在创建相应用Action对象后就将Action对象放到了ValueStack传送带上,然后ValueStack传送带会带着Action对象经过 若干拦截器,在每一拦截器中都可以通过ValueStack对象设置和获得Action对象中的属性值。实际上,这些拦截器就相当于流水线作业。如果要对 Action对象进行某项加工,再加一个拦截器即可,当不需要进行这项工作时,直接将该拦截器去掉即可。
    下面我们使用一个例子来演示这个过程。在这个例子中实现了一个拦截器,该拦截器的功能是将一个属性文件中的key-value对映射成相应的属性的值。如下面是一个属性文件的内容:

    name = 超人
    price = 10000

    我们可以在Action类中定义name和price属性,在Action中引用这个拦截器后,就会自动为属性赋值。
    在使用该拦截器有如下规则:
    1.  拦截器读取的属性文件路径由path参数指定。
    2.  属性文件的编码格式由encoding参数指定,默认值是UTF-8。
    3.  如果某个key中包含有“.”(该符号不能出现在标识符中),则有如下处理方法:
    (1)将Action类的属性名定义为去掉“.”的key。例如,key为person.name,而属性名可定义为personname。
    (2)将Action类的属性名定义为将“.”替换成其他字符的表示符号。例如,key为person.name,而属性名可定义为person_name,其中“_”由separator参数指定。
    4.  如果key太长,也可以直接使用Action参数进行映射,例如,key为country.person.name,可做如下映射:
      <param name="countrypersonname">name</param>
      要注意的是,name属性值不能包含“.”,因此,应将key值中的“.”去掉。现在就可以直接在Action类中定义名为name的属性的,name属性的值会与key值相同。
    5.  上面所有的规则可以同时使用。

拦截器的源代码:

package interceptors;

import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.io.InputStream;
import java.io.FileInputStream;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.util.ValueStack;

public class PropertyInterceptor extends AbstractInterceptor
{
    
private static final String DEFAULT_PATH_KEY = "path";
    
private static final String DEFAULT_ENCODING_KEY = "encoding";
    
private static final String DEFAULT_SEPARATOR_KEY = "separator";

    
protected String pathKey = DEFAULT_PATH_KEY;
    
protected String encodingKey = DEFAULT_ENCODING_KEY;
    
protected String separatorKey = DEFAULT_SEPARATOR_KEY;

    
public void setPathKey(String pathKey) 
    {
        
this.pathKey = pathKey;
    }

    
public void setEncodingKey(String encodingKey)
    {
        
this.encodingKey = encodingKey;
    }

    
public void setSeparatorKey(String separatorKey)
    {
        
this.separatorKey = separatorKey;
    }

    @Override
    
public String intercept(ActionInvocation invocation) throws Exception
    {
        ActionConfig config 
= invocation.getProxy().getConfig();

        Map
<String, String> parameters = config.getParams();
        
if (parameters.containsKey(pathKey))
        {
            String path 
= parameters.get(pathKey);
            String encoding 
= parameters.get(encodingKey);
            String separator 
= parameters.get(separatorKey);
            
if (encoding == null)
                encoding 
= "UTF-8";
            
if (separator == null)
                separator 
= "";
            path 
= invocation.getAction().getClass().getResource(path)
                    .getPath();
            Properties properties 
= new Properties();
            InputStream is 
= new FileInputStream(path);
            java.io.Reader reader 
= new java.io.InputStreamReader(is, encoding);
            
            properties.load(reader);
            ActionContext ac 
= invocation.getInvocationContext();
            ValueStack stack 
= ac.getValueStack();
            System.out.println(stack.hashCode());
            Enumeration names 
= properties.propertyNames();
            
while (names.hasMoreElements())
            {
                
//  下面会使用setValue方法修改ValueStack对象中的相应属性值
                String name = names.nextElement().toString();
                
if (!name.contains("."))
                    stack.setValue(name, properties.get(name)); 

                String newName 
= null;
                newName 
= parameters.get(name.replaceAll("//."""));
                
if (newName != null)
                    stack.setValue(newName, properties.get(name));

                
if (!separator.equals(""))
                {
                    newName 
= name.replaceAll("//.""");
                    stack.setValue(newName, properties.get(name));
                }               
                newName 
= name.replaceAll("//.", separator);
                stack.setValue(newName, properties.get(name));
            } 
        }
        
return invocation.invoke();
    }
}

用于测试的Action类的源代码:

package actions;

public class MyAction
{
    
private String name;
    
private Integer price;
    
private String log4jappenderstdout;
    
private String log4j_rootLogger;
    
private String conversionPattern;

    
public String getName()
    {
        
return name;
    }

    
public void setName(String name)
    {
        
this.name = name;
    }

    
public Integer getPrice()
    {
        
return price;
    }

    
public void setPrice(Integer price)
    {
        
this.price = price;
    }

    
public String getLog4jappenderstdout()
    {
        
return log4jappenderstdout;
    }

    
public void setLog4jappenderstdout(String log4jappenderstdout)
    {
        
this.log4jappenderstdout = log4jappenderstdout;
    }

    
public String getLog4j_rootLogger()
    {
        
return log4j_rootLogger;
    }

    
public void setLog4j_rootLogger(String log4j_rootLogger)
    {
        
this.log4j_rootLogger = log4j_rootLogger;
    }

    
public String getConversionPattern()
    {
        
return conversionPattern;
    }

    
public void setConversionPattern(String conversionPattern)
    {
        
this.conversionPattern = conversionPattern;
    }

    
public String execute()
    {
        System.out.println(
"name:" + name);
        System.out.println(
"price:" + price);
        System.out.println(
"log4jappenderstdout:" + log4jappenderstdout);
        System.out.println(
"log4j_rootLogger:" + log4j_rootLogger);
        System.out.println(
"conversionPattern:" + conversionPattern);
        
return null;
    }
}

Action类的配置代码如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
    "http://struts.apache.org/dtds/struts-2.1.dtd"
>
<struts>
    
<package name="struts" extends="struts-default">

        
<interceptors>
            
<interceptor name="property"
                class
="interceptors.PropertyInterceptor" />
            
<interceptor-stack name="myStack">
                
<interceptor-ref name="defaultStack" />
                
<interceptor-ref name="property" />
            
</interceptor-stack>
        
</interceptors>
        
<action name="test" class="actions.MyAction">
            
<interceptor-ref name="myStack" />
            
<param name="path">/log4j.properties</param>
            
<param name="encoding">UTF-8</param>
            
<param name="separator">_</param>
            
<param name="log4jappenderstdoutlayoutConversionPattern">
                conversionPattern
            
</param>

        
</action>
    
</package>
</struts>

  请将log4j.properties文件复制到WEB-INF/classes目录,并在该文件中加入name和price属性。

测试结果:

name:中国
price:
34
log4jappenderstdout:org.apache.log4j.ConsoleAppender
log4j_rootLogger:error
, stdout
conversionPattern:%d{ABSOLUTE} %5p %c{
1}:%L - %m%n

    由于property拦截器在defaultStack后引用,因此,在该拦截器中设置的属性值是最终结果,如果将property拦截器放在 defaultStack前面(将两个<interceptor-ref>元素掉换一下),就可以通过同名胜Action配置参数或请求参数 来干预最终究输出结果了。

posted @ 2011-11-11 17:21 AK47 阅读(350) | 评论 (0)编辑 收藏

(转贴)Struts2数据传输的背后机制:ValueStack(值栈)

     摘要: (转)Struts2数据传输的背后机制:ValueStack(值栈)原文地址 :http://blog.csdn.net/li_tengfei/article/details/6098134转载 1.     数据传输背后机制:ValueStack(值栈)   在这一切的背后,是因为有了ValueStack(值栈)!   Valu...  阅读全文

posted @ 2011-11-11 16:19 AK47 阅读(806) | 评论 (0)编辑 收藏

structs2配置UrlRewriteFilter

转载每个网页或请求都是一个url地址,一般,这个地址可能是.do,.page,.action之类的并加上'?'号、'&'号查询串等构成的一个长长的的url。很urgly。

一般的url----------------------------------------------------------较好的url
http://www.xxx.net/user/profile.do?id=20001   ====> http://www.xxx.net/user/20001
http://www.xxx.net/forum/board.do?name=java   ====> http://www.xxx.net/forum/java
http://www.xxx.net/forum/thread.do?id=29923   ====> http://www.xxx.net/thread/29923

后者明显较为直观和漂亮。

使用url rewrite可以很好的改善这个状况。网站url rewrite应用是非常广泛的,良好的url设计给用户带来的非常好的体验,同时也能吸引搜索引擎的注意。
原文地址:http://www.iteye.com/topic/53834
使用方式:
1 配置web.xml文件
样例:
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>osivFilter</filter-name>
        <filter-class>
            org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <filter-mapping>
        <filter-name>osivFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--配置UrlRewriteFilter过滤器-->
    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>*.html</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>
    <filter>
        <filter-name>struts-prepare</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter</filter-class>
        <init-param>
            <param-name>actionPackages</param-name>
            <param-value>com.secneo.action.*.*</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>

    <filter>
        <filter-name>struts-execute</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter</filter-class>
    </filter>
    <filter>
        <filter-name>struts-cleanup</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    <!--在structs2中使用UrlRewriteFilter过滤器-->
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.action</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.tld</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.tag</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>struts-prepare</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-prepare</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-prepare</filter-name>
        <url-pattern>*.tld</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>struts-execute</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-execute</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-execute</filter-name>
        <url-pattern>*.tld</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>struts-cleanup</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-cleanup</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-cleanup</filter-name>
        <url-pattern>*.tld</url-pattern>
    </filter-mapping>
    <listener>
        <listener-class>
            org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    </listener>
2  在WEB-INF目录下添加urlrewrite.xml 文件,根据具体需要写规则
样例:
<?xml version="1.0" encoding="utf-8"?>
<urlrewrite>
    <rule>
        <from>^/(.*).html$</from>
        <to type="forward">/$1.action</to>
    </rule>
    <rule>
        <from>^/(.*).html?(.*)$</from>
        <to type="forward">/$1.action?$2</to>
    </rule>
</urlrewrite>

posted @ 2011-11-09 17:22 AK47 阅读(1762) | 评论 (0)编辑 收藏

structs2 filter的执行顺序

根据servlet2.3规范filter执行是按照web.xml配置的filter-mapping先后顺序进行执行。
所以自己配置的过滤器放在structs2的过滤器之前。

posted @ 2011-11-09 15:44 AK47 阅读(353) | 评论 (0)编辑 收藏

structs2拦截器

深入struct2拦截器  这篇文章很好,细致讲解了structs2和拦截器的原理。
http://zhanghong.iteye.com/blog/452465
转载在每次对你的 Action的 execute()方法请求时,系统会生成一个 ActionInvocation对象,这个对象保存了 action和你所配置的所有的拦截器以及一些状态信息。比如你的应用使用的是 defaultStack,系统将会以拦截器栈配置的顺序将每个拦截器包装成一个个 InterceptorMapping(包含拦截器名字和对应的拦截器对象 )组成一个 Iterator保存在 ActionInvocation中。在执行 ActionInvocation的 invoke()方法时会对这个 Iterator进行迭代,每次取出一个 InterceptorMapping,然后执行对应 Interceptor的 intercept(ActionInVocation inv)方法,而 intercept(ActionInInvocation inv)方法又包含当前的 ActionInInvcation对象作为参数,而在每个拦截器中又会调用 inv的 invoke()方法,这样就会进入下一个拦截器执行了,这样直到最后一个拦截器执行完,然后执行 Action的 execute()方法 (假设你没有配置访问方法,默认执行 Action的 execute()方法 )。在执行完 execute()方法取得了 result后又以相反的顺序走出拦截器栈,这时可以做些清理工作。最后系统得到了一个 result,然后根据 result的类型做进一步操作。

配置拦截器:Struts2中提供了大量的拦截器,多个拦截器可以组成一个拦截器栈,系统配置了一个默认的拦截器栈 defaultStack,具体包括那些拦截器以及顺序可以在struts-default.xml中找到。
1)
<package name="default" extends="struts-default">
   <interceptors>
       <interceptor name="timer" class=".."/>
       <interceptor name="logger" class=".."/>
   </interceptors>

   <action name="login"
      class="tutorial.Login">
        <interceptor-ref name="timer"/>
        <interceptor-ref name="logger"/>
         <result name="input">login.jsp</result>
         <result name="success"
            type="redirectAction">/secure/home</result>
   </action>
</package>

2)
<package name="default" extends="struts-default">
   <interceptors>
        <interceptor name="timer" class=".."/>
        <interceptor name="logger" class=".."/>
        <interceptor-stack name="myStack">
           <interceptor-ref name="timer"/>
           <interceptor-ref name="logger"/>
       <interceptor-ref name="defaultStack"/>    
        </interceptor-stack>
    </interceptors>

<action name="login"
     class="tutuorial.Login">
         <interceptor-ref name="myStack"/>
         <result name="input">login.jsp</result>
         <result name="success"
             type="redirectAction">/secure/home</result>
</action>
</package>

拦截器执行顺序:
<interceptor-stack name="xaStack">
  <interceptor-ref name="thisWillRunFirstInterceptor"/>
  <interceptor-ref name="thisWillRunNextInterceptor"/>
  <interceptor-ref name="followedByThisInterceptor"/>
  <interceptor-ref name="thisWillRunLastInterceptor"/>
</interceptor-stack>

执行顺序:
thisWillRunFirstInterceptor
  thisWillRunNextInterceptor
    followedByThisInterceptor
      thisWillRunLastInterceptor
        MyAction1
        MyAction2 (chain)
        MyPreResultListener
        MyResult (result)
      thisWillRunLastInterceptor
    followedByThisInterceptor
  thisWillRunNextInterceptor
thisWillRunFirstInterceptor


自定义拦截器:必须实现 com.opensymphony.xwork2.interceptor.Interceptor 也可以继承 AbstractInterceptor

拦截器要保证线程安全。因为structs2中拦截器会在请求间共享

posted @ 2011-11-08 18:35 AK47 阅读(1429) | 评论 (0)编辑 收藏

(转贴)struts2 工作原理图

     摘要: 原贴地址:http://blog.csdn.net/qjyong/article/details/1795833转贴 最近学习struts2,其实它就是webwork2.2的升级版,现附上原理图 上图来源于Struts2官方站点,是Struts 2 的整体结构。一个请求在Struts2框架中的处理大概分为以下几个步骤1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求2 ...  阅读全文

posted @ 2011-11-08 15:10 AK47 阅读(1622) | 评论 (0)编辑 收藏

重新认识Java finally

关于java finally 网上有2篇文章个人认为相当不错
以下是转贴内容:

1 . JAVA finally字句的异常丢失和返回值覆盖解析
原帖地址 :
http://blog.csdn.net/sureyonder/article/details/5560538
转贴
Java虚拟机在每个try语句块和与其相关的catch子句的结尾 处都会“调用”finally子句的子例程。实际上,finally子句在方法内部的表现很象“微型子例程”。finally子句正常结束后-指的是finally子句中最后一条语句正常执行完毕,不包括抛出异常,或执行return、continue、break等情况,隶属于这个finally子句的微型子例程执行“返回”操作。程序在第一次调用微型子例程的地方继续执行后面的语句。

finally“微型子例程”不等同于方法函数的调用,finally子句都是在同一个栈内执行的,微型子例程的“返回”操作也不会涉及到方法退栈,仅仅是使程序计数器pc跳转到同一个方法的一个不同的位置继续执行。
一 异常丢失
    public static void exceptionLost()  
     {  
       try  
       {  
         try  
         {  
           throw new Exception( "exception in try" );  
         }  
         finally  
         {  
           throw new Exception( "exception in finally" );  
         }  
       }  
       catch( Exception e )  
       {  
         System.out.println( e );  
       }  
     }  

exceptionLost()的输出结果是“exception in finally”,而不是try块中抛出的异常,这是JAVA异常机制的一个瑕疵-异常丢失。

在字节码中,throw语句不是原子性操作。在较老的JDK中,exceptionLost()中try块的throw语句分解为几步操作:
1) 把Exception("exception in try")对象引用存储到一个局部变量中
  astore_2  // pop the reference to the thrown exception, store into local variable 2
2) 调用finally微型子程序
3) 把局部变量中的Exception("exception in try")对象引用push到操作数栈顶,然后抛出异常
  aload_2  // push the reference to the thrown exception from local variable 2

  athrow   // throw the exception

如果finally通过break、return、continue,或者抛出异常而退出,那么上面的第3步就不会执行。

在JDK1.6中,通过字节码我们可以看到,finally子句作为一种特殊的catch来实现的,下面是exceptionLost()方法的异常表:

Exception table:
  from   to   target  type
   0     10    10     any
 0     21    21     Class java/lang/Exception

finally可以捕获从0行到9行之间抛出的任何类型(any)的异常,并重新抛出捕获的异常,或者抛出一个自己构造的新异常,这个新异常就会覆盖try语句块中的异常。
二 返回值覆盖

    public static int getValue()  
     {  
       int value = 0;  
         
       try  
       {  
         value = 100;  
           
         return value;  
       }  
       finally  
       {  
         value = 200;  
       }  
     }  

这个方法的返回值是100还是200?结果是100。
在字节码中,return语句不是原子性操作,它会把getValue()中的return语句分解为几步操作:
1) 把value值存储到一个局部变量(这里命名为temp)中:
   iload_0   // push local variable 0 - the 100
   istore_2   //  pop an int (the 100), store into local varaible 2
2) 调用finally微型子程序
3) 把局部变量(指temp)的值push到操作数栈顶,然后返回到调用方法
     iload_2  // push local varaible 2 - the 100
   ireturn      // return int on top of the stack - the 100: return 100

由于return语句在返回之前会把返回值保存到一个临时的局部变量中,所以在finally子句内对value重新赋值不会影响返回值。

了解finally子句内在的一些知识,我们能够了解finally能够做什么和不能够做什么,这样会帮助我们正确使用finally子句。

2 . 关于 Java 中 finally 语句块的深度辨析
原帖地址 :
http://www.ibm.com/developerworks/cn/java/j-lo-finally/index.html?ca=drs-
转贴
关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《 The JavaTM Virtual Machine Specification, Second Edition 》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine 不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。请注意,前文中我们曾经提到过 return、throw 和 break、continue 的区别,对于这条规则(保留返回值),只适用于 return 和 throw 语句,不适用于 break 和 continue 语句,因为它们根本就没有返回值。

posted @ 2011-11-01 16:56 AK47 阅读(816) | 评论 (0)编辑 收藏

(转贴) jqGrid整理

原帖地址:
http://www.cnblogs.com/mycoding/archive/2011/07/07/2099878.html

一、 jqGrid的加载。

1.引用相关头文件

引入CSS:

<link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />

<link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />

引入JS:

<script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>

<script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>

<script src="Scripts/grid.locale-en.js" type="text/javascript"></script>

<script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>

因为jqGrid3.6及以后的版本集成了jQuery UI,所以,此处需要导入UI相关js和css。另外grid.locale-en.js这个语言文件必须在jquery.jqGrid.min.js之前加载,否则会出问题。

2.将jqgrid加入页面中

根据jqGrid的文档,要想生成一个jqGrid,最直接的方法就是:

$("#list").jqGrid(options);

其中list是页面上的一个table:<table id="list"></table>

下面是一个简单的例子:

<script type="text/javascript">
 
$(document).ready(function () {
 
jQuery("#list").jqGrid({
 
url: 'Handler.ashx',
 
datatype: "json",
 
mtype: 'GET',
 
colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
 
colModel: [
 
{ name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
 
{ name: 'Name', index: 'Name', width: 100, align: "center" },
 
{ name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
 
{ name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
 
],
 
rowList: [10, 20, 30],
 
sortname: 'SalesReasonID',
 
viewrecords: true,
 
sortorder: "desc",
 
jsonReader: {
 
root: "griddata",
 
total: "totalpages",
 
page: "currpage",
 
records: "totalrecords",
 
repeatitems: false
 
},
 
pager: jQuery('#pager'),
 
rowNum: 5,
 
altclass: 'altRowsColour',
 
//width: 'auto',
 
width: '500',
 
height: 'auto',
 
caption: "DemoGrid"
 
}).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
 
})

二、 jqgrid的重要选项

具体的options参考,可以访问jqGrid文档关于option的章节(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有几个是比较常用的,重点介绍一下:

  • url :jqGrid控件通过这个参数得到需要显示的数据,具体的返回值可以使XML也可以是Json。
  • datatype :这个参数用于设定将要得到的数据类型。类型包括:json 、xml、xmlstring、local、javascript、function。
  • mtype : 定义使用哪种方法发起请求,GET或者POST。
  • height :Grid的高度,可以接受数字、%值、auto,默认值为150。
  • width :Grid的宽度,如果未设置,则宽度应为所有列宽的之和;如果设置了宽度,则每列的宽度将会根据shrinkToFit选项的设置,进行设置。
  • shrinkToFit :此选项用于根据width计算每列宽度的算法。默认值为true。如果shrinkToFit为true且设置了width值,则每列宽度会根据 width成比例缩放;如果shrinkToFit为false且设置了width值,则每列的宽度不会成比例缩放,而是保持原有设置,而Grid将会有 水平滚动条。
  • autowidth :默认值为false。如果设为true,则Grid的宽度会根据父容器的宽度自动重算。重算仅发生在Grid初始化的阶段;如果当父容器尺寸变化了,同时也需要变化Grid的尺寸的话,则需要在自己的代码中调用setGridWidth方法来完成。
  • pager :定义页码控制条Page Bar,在上面的例子中是用一个div(<div id=”pager”></div>)来放置的。
  • sortname :指定默认的排序列,可以是列名也可以是数字。此参数会在被传递到Server端。
  • viewrecords :设置是否在Pager Bar显示所有记录的总数。
  • caption :设置Grid表格的标题,如果未设置,则标题区域不显示。
  • rowNum :用于设置Grid中一次显示的行数,默认值为20。正是这个选项将参数rows(prmNames中设置的)通过url选项设置的链接传递到Server。注意如果Server返回的数据行数超过了rowNum的设定,则Grid也只显示rowNum设定的行数。
  • rowList :一个数组,用于设置Grid可以接受的rowNum值。例如[10,20,30]。
  • colNames :字符串数组,用于指定各列的题头文本,与列的顺序是对应的。
  • colModel :最重要的数组之一,用于设定各列的参数。(稍后详述)
  • prmNames :这是一个数组,用于设置jqGrid将要向Server传递的参数名称。(稍后详述)
  • jsonReader :这又是一个数组,用来设定如何解析从Server端发回来的json数据。(稍后详述)

2.1 prmNames选项

prmNames是jqGrid的一个重要选项,用于设置jqGrid将要向Server传递的参数名称。其默认值为:

prmNames : {

page:"page", // 表示请求页码的参数名称

rows:"rows", // 表示请求行数的参数名称

sort: "sidx", // 表示用于排序的列名的参数名称

order: "sord", // 表示采用的排序方式的参数名称

search:"_search", // 表示是否是搜索请求的参数名称

nd:"nd", // 表示已经发送请求的次数的参数名称

id:"id", // 表示当在编辑数据模块中发送数据时,使用的id的名称

oper:"oper", // operation参数名称

editoper:"edit", // 当在edit模式中提交数据时,操作的名称

addoper:"add", // 当在add模式中提交数据时,操作的名称

deloper:"del", // 当在delete模式中提交数据时,操作的名称

subgridid:"id", // 当点击以载入数据到子表时,传递的数据名称

npage: null,

totalrows:"totalrows" // 表示需从Server得到总共多少行数据的参数名称,参见jqGrid选项中的rowTotal

}

2.2 jsonReader选项

jsonReader是jqGrid的一个重要选项,用于设置如何解析从Server端发回来的json数据,如果Server返回的是xml数据,则对应的使用xmlReader来解析。jsonReader的默认值为:

jsonReader : {

root: "rows", // json中代表实际模型数据的入口

page: "page", // json中代表当前页码的数据

total: "total", // json中代表页码总数的数据

records: "records", // json中代表数据行总数的数据

repeatitems: true, // 如果设为false,则jqGrid在解析json时,会根据name来搜索对应的数据元素(即可以json中元素可以不按顺序);而所使用的name是来自于colModel中的name设定。

cell: "cell",

id: "id",

userdata: "userdata",

subgrid: {

root:"rows",

repeatitems: true,

cell:"cell"

}

}

假如有下面一个json字符串:

{"totalpages":"3","currpage":"1","totalrecords":"11","griddata": [{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998 年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998 年6月1日"}]}

其对应的jsonReader为:jsonReader: {

root: "griddata",

total: "totalpages",

page: "currpage",

records: "totalrecords",

repeatitems: false

}

注:cell、id在repeatitems为true时可以用到,即每一个记录是由一对id和cell组合而成,即可以适用另一种json结构。援引文档中的例子:

repeatitems为true时:

jQuery("#gridid").jqGrid({  

     ...  

     jsonReader : {  

         root:"invdata",  

         page: "currpage",  

         total: "totalpages",  

         records: "totalrecords"

     },  

     ...  

});  

json结构为:

{   

"totalpages": "xxx",   

"currpage": "yyy",  

"totalrecords": "zzz",  

"invdata" : [  

                  {"id" :"1", "cell" :["cell11", "cell12", "cell13"]},   // cell中不需要各列的name,只要值就OK了,但是需要保持对应

                  {"id" :"2", "cell" :["cell21", "cell22", "cell23"]},  

                  ...  

     ]  

}  

repeatitems为false时:

jQuery("#gridid").jqGrid({  

     ...  

     jsonReader : {  

         root:"invdata",  

         page: "currpage",  

         total: "totalpages",  

         records: "totalrecords",  

         repeatitems: false,  

         id: "0"

     },  

     ...  

});  

json结构为:

{   

"totalpages" : "xxx",   

"currpage" : "yyy",  

"totalrecords" : "zzz",  

"invdata" : [  

                 {"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 数据中需要各列的name,但是可以不按列的顺序

                  {"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},  

                  ...  

     ]  

}  

2.3 colModel的重要选项

colModel也有许多非常重要的选项,在使用搜索、排序等方面都会用到。这里先只说说最基本的。

  • name :为Grid中的每个列设置唯一的名称,这是一个必需选项,其中保留字包括subgrid、cb、rn。
  • index :设置排序时所使用的索引名称,这个index名称会作为sidx参数(prmNames中设置的)传递到Server。
  • label :当jqGrid的colNames选项数组为空时,为各列指定题头。如果colNames和此项都为空时,则name选项值会成为题头。
  • width :设置列的宽度,目前只能接受以px为单位的数值,默认为150。
  • sortable :设置该列是否可以排序,默认为true。
  • search :设置该列是否可以被列为搜索条件,默认为true。
  • resizable :设置列是否可以变更尺寸,默认为true。
  • hidden :设置此列初始化时是否为隐藏状态,默认为false。
  • formatter :预设类型或用来格式化该列的自定义函数名。常用预设格式有:integer、date、currency、number等(具体参见文档 )。

三、 注意事项

1. 动态改变Add Form或者Edit Form中的select的内容,如:改变下图中的Comparator下拉中的内容。

clip_image002

$("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},

{

checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,

beforeInitData:function(formid){

initComparator();

},

beforeShowForm: function(formid){

$("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});

$('#tr_Name', formid).hide();

}

},//edit

{},//add

{}//del

beforeInitData, beforeShowForm在每次点击编辑的时候都会执行。initComparator的作用是通过ajax获取数据,然后利 用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });来设置Comparator下拉中的内容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。键值之间用冒号隔开,2项之间用分号隔开。注意:把recreateForm设为true,否则'setColProp'只在第一次调用时有效。

2. var rowNum = parseInt($(this).getGridParam("records"), 10); 得到数据条数。

3. jQuery("#list_d").clearGridData();清空数据。

4. jQuery("#list").getCell(ids,"Key");获取第ids行的key列。

5. $("#list").jqGrid('setSelection', "1");选中第一行。放在loadComplete:中在gird加载完成的时候自动选中第一行。 loadComplete:function(data){$("#list").jqGrid('setSelection', "1");

}

6. 对于像1中的可编辑的字段,可以设定rule,参见http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules

7. 修改Option,以URL为例

jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');


复杂的表格可以参考jquery grid demo网站 :




posted @ 2011-11-01 14:23 AK47 阅读(2308) | 评论 (0)编辑 收藏

(转载)Spring 注解@Component,@Service,@Controller,@Repository

Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。

在 一个稍大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找以及维护起来也不太方便。 Spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了 @Component,@Service,@Controller,@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用 和在xml文件中使用bean节点配置组件时一样的。要使用自动扫描机制,我们需要打开以下配置信息: 
Java代码

1. <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-2.5.xsd"  
2. >  
3.   
4. <context:component-scan base-package=”com.eric.spring”>   
5. </beans>   
   /*其中base-package为需要扫描的包(含所有子包)

     @Service用于标注业务层组件,

     @Controller用于标注控制层组件(如struts中的action),

     @Repository用于标注数据访问组件,即DAO组件,

     @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

    */   


6. @Service public class VentorServiceImpl implements iVentorService {   
7. } @Repository public class VentorDaoImpl implements iVentorDao {  
8. }

/*getBean的默认名称是类名(头字母小 写),如果想自定义,可以@Service(“aaaaa”)这样来指定,这种bean默认是单例的,如果想改变,可以使用 @Service(“beanName”) @Scope(“prototype”)来改变。可以使用以下方式指定初始化方法和销毁方法(方法名任意): @PostConstruct public void init() {  

*/
9. }  
10. @PreDestroy public void destory() {  
11. } 

注入方式:

把 DAO实现类注入到service实现类中,把service的接口(注意不要是service的实现类)注入到action中,注入时不要new 这个注入的类,因为spring会自动注入,如果手动再new的话会出现错误,然后属性加上@Autowired后不需要getter()和 setter()方法,Spring也会自动注入。至于更具体的内容,等对注入的方式更加熟练后会做个完整的例子上来。

注解:

在 spring的配置文件里面只需要加上<context:annotation-config/> 和<context:component-scan base-package="需要实现注入的类所在包"/>,可以使用base-package="*"表示全部的类。   

<context:component-scan base-package=”com.eric.spring”> 

其中base-package为需要扫描的包(含所有子包)

在接口前面标上@Autowired和@Qualifier注释使得接口可以被容器注入,当接口存在两个实现类的时候必须指定其中一个来注入,使用实现类首字母小写的字符串来注入,如:

  1.     @Autowired     
  2.   
  3.     @Qualifier("chinese")      
  4.   
  5.     private Man man;   

否则可以省略,只写@Autowired   。 

@Service服务层组件,用于标注业务层组件,表示定义一个bean,自动根据bean的类名实例化一个首写字母为小写的bean,例如Chinese实例化为chinese,如果需要自己改名字则:@Service("你自己改的bean名")。   

@Controller用于标注控制层组件(如struts中的action)

@Repository持久层组件,用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 


@Service 
public class VentorServiceImpl implements iVentorService { 
}

@Repository 
public class VentorDaoImpl implements iVentorDao { 


getBean 的默认名称是类名(头字母小写),如果想自定义,可以@Service(“aaaaa”) 这样来指定,这种

bean默认是单例的,如果想改变,可以使用@Service(“beanName”) @Scope(“prototype”)来改变。

可以使用以下方式指定初始化方法和销毁方法(方法名任意):

@PostConstruct

public void init() { 



@PreDestroy

public void destory() { 

}

posted @ 2011-10-10 16:46 AK47 阅读(49698) | 评论 (3)编辑 收藏

(转贴)使用 Spring 2.5 注释驱动的 IoC 功能

原帖地址
http://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/

概述

注释配置相对于 XML 配置具有很多的优势:

  • 它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO 属性名、类型都一致,您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
  • 注释和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。

因此在很多情况下,注释配置比 XML 配置更受欢迎,注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类,现在您已经可以使用注释配置完成大部分 XML 配置的功能。在这篇文章里,我们将向您讲述使用注释进行 Bean 定义和依赖注入的内容。

 
原来我们是怎么做的      
在使用注释配置之前,先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。下面是 3 个类,它们分别是 Office、Car 和 Boss,这 3 个类需要在 Spring 容器中配置为 Bean:    
   
Office 仅有一个属性:    
     
清单 1. Office.java    
                    
package com.baobaotao;    
public class Office {    
    private String officeNo =”001”;    
   
    //省略 get/setter    
   
    @Override   
    public String toString() {    
        return "officeNo:" + officeNo;    
    }    
}    
       
Car 拥有两个属性:    
     
清单 2. Car.java 
                     
package com.baobaotao;    
   
public class Car {    
    private String brand;    
    private double price;    
   
    // 省略 get/setter    
   
    @Override   
    public String toString() {    
        return "brand:" + brand + "," + "price:" + price;    
    }    
}    
      
Boss 拥有 Office 和 Car 类型的两个属性:    
  
清单 3. Boss.java    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
   
    // 省略 get/setter    
   
    @Override   
    public String toString() {    
        return "car:" + car + "\n" + "office:" + office;    
    }    
}    
    
我们在 Spring 容器中将 Office 和 Car 声明为 Bean,并注入到 Boss Bean 中:下面是使用传统 XML 完成这个工作的配置文件 beans.xml:    
    
清单 4. beans.xml 将以上三个类配置成 Bean    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
    <bean id="boss" class="com.baobaotao.Boss">    
        <property name="car" ref="car"/>    
        <property name="office" ref="office" />    
    </bean>    
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="002"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
当我们运行以下代码时,控制台将正确打出 boss 的信息:    
  
清单 5. 测试类:AnnoIoCTest.java    
                    
import org.springframework.context.ApplicationContext;    
import org.springframework.context.support.ClassPathXmlApplicationContext;    
public class AnnoIoCTest {    
   
    public static void main(String[] args) {    
        String[] locations = {"beans.xml"};    
        ApplicationContext ctx =     
            new ClassPathXmlApplicationContext(locations);    
        Boss boss = (Boss) ctx.getBean("boss");    
        System.out.println(boss);    
    }    
}    
    
这说明 Spring 容器已经正确完成了 Bean 创建和装配的工作。    
     
使用 @Autowired 注释    
   
Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。来看一下使用 @Autowired 进行成员变量自动注入的代码:    
  
清单 6. 使用 @Autowired 注释的 Boss.java    
                    
package com.baobaotao;    
import org.springframework.beans.factory.annotation.Autowired;    
   
public class Boss {    
   
    @Autowired   
    private Car car;    
   
    @Autowired   
    private Office office;    
   
    …    
}    
       
Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。   

清单 7. 让 @Autowired 注释工作起来    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
   
    <!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->    
    <bean class="AutowiredAnnotationBeanPostProcessor  
        org.springframework.beans.factory.annotation.  "/>    
   
    <!-- 移除 boss Bean 的属性注入配置的信息 -->    
    <bean id="boss" class="com.baobaotao.Boss"/>    
     
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="001"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
    
这 样,当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。    
   
按 照上面的配置,Spring 将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后,您大可将它们的 setter 方法(setCar() 和 setOffice())从 Boss 中删除。    
   
当然,您也可以通过 @Autowired 对方法或构造函数进行标注,来看下面的代码:    
    
清单 8. 将 @Autowired 注释标注在 Setter 方法上    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
   
     @Autowired   
    public void setCar(Car car) {    
        this.car = car;    
    }    
     
    @Autowired   
    public void setOffice(Office office) {    
        this.office = office;    
    }    
    …    
}    
     
这时,@Autowired 将查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注:    
    
清单 9. 将 @Autowired 注释标注在构造函数上    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
     
    @Autowired   
    public Boss(Car car ,Office office){    
        this.car = car;    
        this.office = office ;    
    }    
     
    …    
}    
       
由于 Boss() 构造函数有两个入参,分别是 car 和 office,@Autowired 将分别寻找和它们类型匹配的 Bean,将它们作为 Boss(Car car ,Office office) 的入参来创建 Boss Bean。    
     
当候选 Bean 数目不为 1 时的应对方法    
   
在 默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验:    
   
   
清单 10. 候选 Bean 数目为 0 时    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
     xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">    
     
    <bean class="AutowiredAnnotationBeanPostProcessor  
        org.springframework.beans.factory.annotation.  "/>     
   
    <bean id="boss" class="com.baobaotao.Boss"/>    
   
    <!-- 将 office Bean 注释掉 -->    
    <!-- <bean id="office" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
    </bean>-->    
   
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
由于 office Bean 被注释掉了,所以 Spring 容器中将没有类型为 Office 的 Bean 了,而 Boss 的 office 属性标注了 @Autowired,当启动 Spring 容器时,异常就产生了。    
   
当 不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。来看一下具体的例子:    
   
   
清单 11. 使用 @Autowired(required = false)    
                    
package com.baobaotao;    
   
import org.springframework.beans.factory.annotation.Autowired;    
import org.springframework.beans.factory.annotation.Required;    
   
public class Boss {    
   
    private Car car;    
    private Office office;    
   
    @Autowired   
    public void setCar(Car car) {    
        this.car = car;    
    }    
    @Autowired(required = false)    
    public void setOffice(Office office) {    
        this.office = office;    
    }    
    …    
}    
    
当 然,一般情况下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到(如为了快速启动 Spring 容器,仅引入一些模块的 Spring 配置文件),所以 @Autowired(required = false) 会很少用到。    
   
和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子:    
    
清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean    
                    
…     
<bean id="office" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
</bean>    
<bean id="office2" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
</bean>    
…    
     
我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean,当对 Boss 的 office 成员变量进行自动注入时,Spring 容器将无法确定到底要用哪一个 Bean,因此异常发生了。    
   
Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了,可以通过下面的方法解决异常:    
  
清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称    
                    
@Autowired   
public void setOffice(@Qualifier("office")Office office) {    
    this.office = office;    
}    
    
 
@Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码:    
   
对成员变量进行注释:    
  
清单 14. 对成员变量使用 @Qualifier 注释    
                    
public class Boss {    
    @Autowired   
    private Car car;    
     
    @Autowired   
    @Qualifier("office")    
    private Office office;    
    …    
}    
     
    
对构造函数入参进行注释:    
    
清单 15. 对构造函数变量使用 @Qualifier 注释    
                    
public class Boss {    
    private Car car;    
    private Office office;    
   
    @Autowired   
    public Boss(Car car , @Qualifier("office")Office office){    
        this.car = car;    
        this.office = office ;    
    }    
}    
     
@Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。    
    
   
使用 JSR-250 的注释    
   
Spring 不但支持自己定义的 @Autowired 的注释,还支持几个由 JSR-250 规范定义的注释,它们分别是 @Resource、@PostConstruct 以及 @PreDestroy。    
   
@Resource   
   
@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。    
   
Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中,因此在使用之前必须将其加入到项目的类库中。来看一个使用 @Resource 的例子:    
   
清单 16. 使用 @Resource 注释的 Boss.java    
                    
package com.baobaotao;    
   
import javax.annotation.Resource;    
   
public class Boss {    
    // 自动注入类型为 Car 的 Bean    
    @Resource   
    private Car car;    
   
    // 自动注入 bean 名称为 office 的 Bean    
    @Resource(name = "office")    
    private Office office;    
}    
     
一般情况下,我们无需使用类似于 @Resource(type=Car.class) 的注释方式,因为 Bean 的类型信息可以通过 Java 反射从代码中获取。    
   
要让 JSR-250 的注释生效,除了在 Bean 类中标注这些注释外,还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor:    
   
<bean     
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>    
     
  
CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口,它负责扫描使用了 JSR-250 注释的 Bean,并对它们进行相应的操作。    
   
@PostConstruct 和 @PreDestroy   
   
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期,笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述,有兴趣的读者可以查阅。    
   
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。    
  
清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java    
                    
package com.baobaotao;    
   
import javax.annotation.Resource;    
import javax.annotation.PostConstruct;    
import javax.annotation.PreDestroy;    
   
public class Boss {    
    @Resource   
    private Car car;    
   
    @Resource(name = "office")    
    private Office office;    
   
    @PostConstruct   
    public void postConstruct1(){    
        System.out.println("postConstruct1");    
    }    
   
    @PreDestroy   
    public void preDestroy1(){    
        System.out.println("preDestroy1");     
    }    
    …    
}    
     
您只需要在方法前标注 @PostConstruct 或 @PreDestroy,这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。    
   
我 们知道,不管是通过实现 InitializingBean/DisposableBean 接口,还是通过 <bean> 元素的 init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct 或 @PreDestroy 注释的方法都会在初始化 / 销毁时被执行。    
   
通过以下的测试代码,您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的:    
  
清单 18. 测试类代码    
                    
package com.baobaotao;    
   
import org.springframework.context.support.ClassPathXmlApplicationContext;    
   
public class AnnoIoCTest {    
   
    public static void main(String[] args) {    
        String[] locations = {"beans.xml"};    
        ClassPathXmlApplicationContext ctx =     
            new ClassPathXmlApplicationContext(locations);    
        Boss boss = (Boss) ctx.getBean("boss");    
        System.out.println(boss);    
        ctx.destroy();// 关闭 Spring 容器,以触发 Bean 销毁方法的执行    
    }    
}    
     
   
这 时,您将看到标注了 @PostConstruct 的 postConstruct1() 方法将在 Spring 容器启动时,创建 Boss Bean 的时候被触发执行,而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。    
       
使用 <context:annotation-config/> 简化配置    
   
Spring 2.1 添加了一个新的 context 的 Schema 命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数 据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。     
   
而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式,这就是 <context:annotation-config/>。请看下面的配置:    
     
清单 19. 调整 beans.xml 配置文件    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
     xmlns:context="http://www.springframework.org/schema/context"   
     xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
 http://www.springframework.org/schema/context     
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
     
    <context:annotation-config/>     
   
    <bean id="boss" class="com.baobaotao.Boss"/>    
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="001"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
      
<context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。    
   
在配置文件中使用 context 命名空间之前,必须在 <beans> 元素中声明 context 命名空间。    
   
    
使用 @Component   
   
虽 然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义 —— 也就是说,在 XML 配置文件中定义 Bean,通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。    
   
下面,我们完全使用注释定义 Bean 并完成 Bean 之间装配:    
   
   
清单 20. 使用 @Component 注释的 Car.java    
                    
package com.baobaotao;    
   
import org.springframework.stereotype.Component;    
   
@Component   
public class Car {    
    …    
}    
     
     
仅需要在类定义处,使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个 Bean:    
    
清单 21. 使用 @Component 注释的 Office.java    
                    
package com.baobaotao;    
   
import org.springframework.stereotype.Component;    
   
@Component   
public class Office {    
    private String officeNo = "001";    
    …    
}    
     
这样,我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了。    
   
清单 22. 使用 @Component 注释的 Boss.java    
                    
package com.baobaotao;    
   
import org.springframework.beans.factory.annotation.Autowired;    
import org.springframework.beans.factory.annotation.Required;    
import org.springframework.beans.factory.annotation.Qualifier;    
import org.springframework.stereotype.Component;    
   
@Component("boss")    
public class Boss {    
    @Autowired   
    private Car car;    
   
    @Autowired   
    private Office office;    
    …    
}    
    
@Component 有一个可选的入参,用于指定 Bean 的名称,在 Boss 中,我们就将 Bean 名称定义为“boss”。一般情况下,Bean 都是 singleton 的,需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了,所以大可不必指定 Bean 的名称。    
   
在使用 @Component 注释后,Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:    
    
清单 23. 简化版的 beans.xml    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:context="http://www.springframework.org/schema/context"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
 http://www.springframework.org/schema/context     
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
    <context:component-scan base-package="com.baobaotao"/>    
</beans>    
      
这 里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。    
   
<context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式,通过下表说明:    
   
表 1. 扫描过滤方式    
过滤器类型 说明     
注释 假如 com.baobaotao.SomeAnnotation 是一个注释类,我们可以将使用该注释的类过滤出来。     
类名指定 通过全限定类名进行过滤,如您可以指定将 com.baobaotao.Boss 纳入扫描,而将 com.baobaotao.Car 排除在外。     
正则表达式 通过正则表达式定义过滤的类,如下所示: com\.baobaotao\.Default.*     
AspectJ 表达式 通过 AspectJ 表达式定义过滤的类,如下所示: com. baobaotao..*Service+     
   
下面是一个简单的例子:    
   
<context:component-scan base-package="com.baobaotao">    
    <context:include-filter type="regex"     
        expression="com\.baobaotao\.service\..*"/>    
    <context:exclude-filter type="aspectj"     
        expression="com.baobaotao.util..*"/>    
</context:component-scan>    
      
值 得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。    
   
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标,如以下代码所示:    
   
清单 24. 通过 @Scope 指定 Bean 的作用范围    
                    
package com.baobaotao;    
import org.springframework.context.annotation.Scope;    
…    
@Scope("prototype")    
@Component("boss")    
public class Boss {    
    …    
}    
     
这样,当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了。    
      
采用具有特殊语义的注释    
   
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。    
        
注释配置和 XML 配置的适用场合    
   
是否有了这些 IOC 注释,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:    
   
注 释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注释配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注释配置,因为注释是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。     
如果 Bean 不是自己编写的类(如 JdbcTemplate、SessionFactoryBean 等),注释配置将无法实施,此时 XML 配置是唯一可用的方式。     
注释配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释,使用 aop/tx 命名空间的事务配置更加灵活和简单。     
所 以在实现应用中,我们往往需要同时使用注释配置和 XML 配置,对于类级别且不会发生变动的配置可以优先考虑注释配置;而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。    
       
小结    
   
Spring 在 2.1 以后对注释配置提供了强力的支持,注释配置功能成为 Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配置,可以有效减少配置的工作量,提高程序的内聚性。但是这并不意味着传统 XML 配置将走向消亡,在第三方类 Bean 的配置,以及那些诸如数据源、缓存池、持久层操作模板类、事务管理等内容的配置上,XML 配置依然拥有不可替代的地位。

posted @ 2011-10-10 15:49 AK47 阅读(318) | 评论 (0)编辑 收藏

(转贴)数据库三范式经典实例解析

数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
     设计范式是不是很难懂呢?非也,大学教材上给我们一堆数学公式我们当然看不懂,也记不住。所以我们很多人就根本不按照范式来设计数据库。
     实质上,设计范式用很形象、很简洁的话语就能说清楚,道明白。本文将对范式进行通俗地说明,并以笔者曾经设计的一个简单论坛的数据库为例来讲解怎样将这些范式应用于实际工程。

范式说明
     第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
     例如,如下的数据库表是符合第一范式:

字段1 字段2 字段3 字段4
? ? ? ?
 而这样的数据库表是不符合第一范式的:
字段1 字段2 字段3 字段4
? ? 字段3.1 字段3.2 ?

 

     很显然,在当前的任何关系数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。
     第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
     假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分),关键字为组合关键字(学号, 课程名称),因为存在如下决定关系:
     (学号, 课程名称) → (姓名, 年龄, 成绩, 学分)
     这个数据库表不满足第二范式,因为存在如下决定关系:
     (课程名称) → (学分)
     (学号) → (姓名, 年龄)
即存在组合关键字中的字段决定非关键字的情况。
     由于不符合2NF,这个选课关系表会存在如下问题:
     (1) 数据冗余:
     同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
     (2) 更新异常:
     若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
     (3) 插入异常:
     假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
     (4) 删除异常:
     假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。

     把选课关系表SelectCourse改为如下三个表:
     学生:Student(学号, 姓名, 年龄);
     课程:Course(课程名称, 学分);
     选课关系:SelectCourse(学号, 课程名称, 成绩)。
     这样的数据库表是符合第二范式的,消除了数据冗余、更新异常、插入异常和删除异常。
     另外,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字。
     第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:
     关键字段 → 非关键字段x → 非关键字段y
     假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字"学号",因为存在如下决定关系:
     (学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)
这个数据库是符合2NF的,但是不符合3NF,因为存在如下决定关系:
     (学号) → (所在学院) → (学院地点, 学院电话)
即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖。
     它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知。
     把学生关系表分为如下两个表:
     学生:(学号, 姓名, 年龄, 所在学院);
     学院:(学院, 地点, 电话)。
这样的数据库表是符合第三范式的,消除了数据冗余、更新异常、插入异常和删除异常。
     鲍依斯-科得范式(BCNF):在第三范式的基础上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合第三范式。
     假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:
     (仓库ID, 存储物品ID) →(管理员ID, 数量)
     (管理员ID, 存储物品ID) → (仓库ID, 数量)
     所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:
     (仓库ID) → (管理员ID)
     (管理员ID) → (仓库ID)
即存在关键字段决定关键字段的情况,所以其不符合 BCNF范式。它会出现如下异常情况:
     (1) 删除异常:
     当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。
     (2) 插入异常:
     当仓库没有存储任何物品时,无法给仓库分配管理员。
     (3) 更新异常:
     如果仓库换了管理员,则表中所有行的管理员ID都要修改。
     把仓库管理关系表分解为二个关系表:
     仓库管理:StorehouseManage(仓库ID, 管理员ID);
     仓库:Storehouse(仓库ID, 存储物品ID, 数量)。
     这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。


原帖地址: http://www.cublog.cn/u/23975/showart.php?id=391210

posted @ 2011-02-21 14:45 AK47 阅读(309) | 评论 (0)编辑 收藏

Hibernate 实体对象的生命周期汇总

本帖汇总了网上几篇关于hibernate的生命周期的帖子。

转载:

实体对象的生命周期在Hibernate应用中是一个很关键的概念,正确的理解实体对象的生命周期将对我们应用Hibernate做持久层设计起到很大的作用.而所谓的实体对象的生命周期就是指实体对象由产生到被GC回收的一段过程.在这过程中我们需要理解的就是实体对象生命周期中的三种状态.

1. 自由状态(Transient)
所谓的Transient状态,即实体对象在内存中自由存在,与数据库中的记录无关,通常是我们的J2EE中 VO,并没有被纳入Hibernate的实体管理容器.

1    Test test = new Test();
2        test.setName("energykk");
3        //此时的test对象处于Transient(自由状态)并没有被Hibernate框架所管理
4        

2.持久状态(Persistent)
何谓 Persistent? 即实体对象已经处于被Hibernate实体管理容器容器所管理的状态.这种状态下这个实体对象的引用将被纳入Hibernate实体管理容器容器所管理.
处于Persistent状态的实体对象,对它的变更也将被固化到数据库中.
在J2EE中通常指的是一个PO.
Transaction tr = session.beginTransaction();
        session.save(test);
        
//此时的test对象已经处于Persistent(持久状态)它被Hibernate 纳入实体管理容器
        tr.commit();
        Transaction tr2 
= session.beginTransaction();
        test.setName(
"xukai");
        
//在这个事务中我们并没有显示的调用save()方法但是由于Persistent状态的对象将会自动的固化到
        
//数据库中,因此此时正处在Persistent状态的test对象的变化也将自动被同步到数据库中
        tr2.commit();

处于Persistent状态的实体可以简单的理解为:如果一个实体对象与session发生了关联,并且处于session的有效期内,那么这个实体对象就处于Persistent状态.

3.游离状态(Detached)
处于Persistent状态的实体对象,其对应的session关闭以后,那么这个实体就处于 Detached状态.
我们可以认为session对象就是一个Persistent的宿主,一旦这个宿主失效,那么这个实体就处于 Detached状态.

session.close();
        
//与test对象关联的session被关闭,因此此时的test对象进入 Detached(游离状态)
        
        session2 
= HibernateSessionFactory.getSession();
        Transaction tr3 
= session2.beginTransaction();
        session2.update(test);
        
//此时正处于Detached状态的test对象由于再次借助与session2被纳入到Hibernate的实体管理容器所以此时的
        
//test对象恢复到Persistent状态
        test.setName("jjjj");
        tr3.commit();
        
        session2.close();

既然Transient状态的实体与Detached状态的实体都与Hibernate的实体管理容器没有关系,那他们到底存在哪些差异?
差异就在于处于Transient状态的只有一个Name的属性.此时的test对象所包含的数据信息仅限于此,他与数据库中的记录没有任何瓜葛.
但是处于Detached状态的实体已经不止包含Name这个属性,还被赋予了主键也就是通常POJO里的id属性,由于id是主键,他可以确定数据库表中的一条
唯一的记录,那么自然的处于Detached状态的实体就能与数据库表中拥有相同id的记录相关联.
这就是他们之间所存在的差异, 简而言之,Transient状态的实体缺乏与数据库表记录之间的联系,而Detached状态的试题恰恰相反.只不过是脱离了session这个数据库操作平台而已.
原帖地址 : http://www.blogjava.net/energykk/archive/2007/05/08/115927.html

 生命周期图:
原图地址:http://hi.baidu.com/quest2run/blog/item/39e1d08c7dbd45f4503d9222.html
 

persistence context



生命周期特征总结 :
原帖地址 : http://blog.csdn.net/hgd250/archive/2008/08/06/2775943.aspx
Transient:

    与数据库中的记录没有任何关系,即没有与其相关联的数据库记录.
    与session没有任何关系.即没有通过session对象的实例对其进行任何持久化的操作
Persistent:
    每个persistent状态的实体对象都与一个session对象的实例相关联
    处于 Persistent状态的实体对象是与数据库中的记录相关联的.
    Hibernate会依据persistent状态的实体对象的属性变化而改变数据库中相对应的记录
.
Detached:
    游离态是由持久态实体对象转变而来的.
    游离态实体不再与session对象相关联.
    游离态实体对象与数据库中的记录没有直接联系,对其所做的任何修改将不会影响到到数据库中的数据.
    游离态实体对象在数据库有相对应的数据记录,如果没有被其他事务删除.

posted @ 2011-02-14 14:26 AK47 阅读(323) | 评论 (0)编辑 收藏

(转贴)BigInteger 和 BigDecimal

高精度数字
Java 提供了两个类专门用于进行高精度运算BigInteger 和 BigDecimal ,尽管它们可大致划分到与封装器相同的类别里,但两者都没有对应的主类型;这两个类都有自己的一系列方法,类似于我们针对主类型执行的操作,也就是说能用 int 或float 做的事情,用BigInteger和BigDecimal 一样可以做,只是必须换用方法调用,而不是使用运算符。此外由于牵涉更多,所以运算速度会慢一点总之我们牺牲了速度,但换来了精度。

高精度浮点数BigDecimal

一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。
    从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数:BigDecimal。BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10-scale。
用于加、减、乘和除的方法给  BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销,BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。
如浮点类型一样,BigDecimal 也有一些令人奇怪的行为。尤其在使用 equals() 方法来检测数值之间是否相等时要小心。equals() 方法认为,两个表示同一个数但换算值不同(例如,100.00 和  100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法会认为这两个数是相等的,所以在从数值上比较两个  BigDecimal 值时,应该使用 compareTo() 而不是 equals()。
另外还有一些情形,任意精度的小数运算仍不能表示精确结果。例如,1 除以 9 会产生无限循环的小数 .111111...。出于这个原因,在进行除法运算时,BigDecimal 可以让您显式地控制舍入。movePointLeft() 方法支持 10 的幂次方的精确除法。
对于 BigDecimal,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用  BigDecimal(double) 构造函数, 因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。
如果使用 BigDecimal(double) 构造函数不恰当,在传递给 JDBC setBigDecimal() 方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字 0.01 存储到小数字段:
  PreparedStatement ps =connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
  ps.setString(1, "penny");
  ps.setBigDecimal(2, new BigDecimal(0.01));
  ps.executeUpdate();
     在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01 的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC 驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 构造 BigDecimal 来避免这类问题, 因为这两种方法都可以精确地表示小数。
 

code :

import java.math.BigDecimal;
/** * *
* <p>Title: 开源,开放</p>
* * <p>Description: opeansource</p>
* * <p>Copyright: Copyright (c) 2004</p>
* * <p>Company: ?海棠</p>
* * @author HaiTang Ming
* * @version 1.0 */
public class BigDecimalUtil { 
//默认除法运算精度,及即保留小数点多少位 
private static final int DEF_DIV_SCALE = 2; 
//这个类不能实例化 
private BigDecimalUtil (){   } 
/**   
  * * 提供精确的加法运算。   
  * * @param v1 被加数   
  * * @param v2 加数   
  * * @return 两个参数的和   
  * */ 
public static double add(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.add(b2)).doubleValue(); 

/**

  *提供精确的减法运算。 
  * * @param v1 被减数 
  * * @param v2 减数 
  * * @return 两个参数的差
  **/ 
public static double sub(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.subtract(b2)).doubleValue(); 

/**   
  * * 提供精确的乘法运算。   
  * * @param v1 被乘数   
  * * @param v2 乘数   
  * * @return 两个参数的积   
  * */
public static double mul(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.multiply(b2)).doubleValue(); 

/**   
  * * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到   
  * * 小数点以后多少位,以后的数字四舍五入。   
  * * @param v1 被除数   
  * * @param v2 除数   
  * * @return 两个参数的商   
  * */ 
public static double div(double v1,double v2){   
  return div(v1,v2,DEF_DIV_SCALE); 

/**   
  * * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指   
  * * 定精度,以后的数字四舍五入。   
  * * @param v1 被除数 
  * @param v2 除数   
  * * @param scale 表示表示需要精确到小数点以后几位。   
  * * @return 两个参数的商   
  * */ 
public static double div(double v1,double v2,int scale){   
  if(scale<0){     
   throw new IllegalArgumentException("The scale must be a positive integer or zero");   
  }   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 

/**   
  * * 提供精确的小数位四舍五入处理。   
  * * @param v 需要四舍五入的数字   
  * * @param scale 小数点后保留几位   
  * * @return 四舍五入后的结果   
  * */ 
public static double round(double v,int scale){ 
  if(scale<0){     
   throw new IllegalArgumentException("The scale must be a positive integer or zero");
  }   
  BigDecimal b = new BigDecimal(Double.toString(v));   
  BigDecimal one = new BigDecimal("1");   
  return (b.divide(one,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 
}   
public static void main(String[] args){   
  System.out.println(add(234.44,534.90));
 
  double a = 123.345678;   
  double d = round(a,2);   
  System.out.println("round("+a+",2)--->"+d); 
}
}

高精度整数BigInteger

BigInteger支持任意精度的整数,也就是说我们可精确表示任意大小的整数值;同时在运算过程中不会丢失任何信息;
在BigInteger类中有所有的基本算术运算方法,如加、减、乘、除,以及可能会用到的位运算如或、异或、非、左移、右移等。下面是一些方法的例子:当然,如果要有更多的使用方法,可以查阅java api 。

code :public class BigIntegerTest { 
public BigIntegerTest() {   } 
/**   
  * * 测试BigInteger
  * */ 
public static void testBigInteger() {   
  BigInteger bi = new BigInteger("888");   
  //multiply :乘法   
  BigInteger result = bi.multiply(new BigInteger("2"));   
  System.out.println(result);   
  //divide : 除法   
  result = bi.divide(new BigInteger("2"));   
  System.out.println(result);   
  //add : 加法   
  result = bi.add(new BigInteger("232"));   
  System.out.println(result);   
  //subtract :减法   
  result = bi.subtract(new BigInteger("23122"));   
  System.out.println(result);   
  result = bi.shiftRight(2);   
  System.out.println(result); 
}   
public static void main(String[] args) {   
  testBigInteger(); 
}
}
原贴地址http://dev.firnow.com/course/3_program/java/javaxl/2008914 /142796_2.html

posted @ 2010-12-10 14:16 AK47 阅读(1014) | 评论 (0)编辑 收藏

(转贴) 超大整数相加,超过了long的范围,你要怎么做!

 

引用:

 这个只能够用字符串的形式来处理了,因为计算机能够处理的最大是long型,本文以字符串的形式来进行超大数据的相加,理论上只要你的内存允许,相加多大的数都可以。

/**

 * 超大整数相加:

 * 题目要求:如果系统要使用超大整数(超过long的范围),请你设计一个数据结构来存储这种

 * 超大型数字以及设计一种算法来实现超大整数的加法运算

 * @author Administrator

 *

 */

public class VeryBigNumAdd {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       /*

       String a="1223232";

       for(int i=a.length()-1;i>=0;i--)

       {

           System.out.print(a.charAt(i));

       }

       */

       VeryBigNumAdd vbn=new VeryBigNumAdd();

       String a="123453243455535634535252345234677576252241234123523453664563634";

       String b="123453243455535634535252345234677576252241234123523453664563634";

       String result=vbn.doAdd(a,b);

       System.out.println("result:"+result);

    }

    /**

     *

     * @param a 加数字符串1

     * @param b 加数字符串2

     * @return 结果字符串

     * 分析:

     * 1、取得两个字符串的长度

     * 2、把两个的长度做比较,并得出较长的长度,及较短的长度

     * 3、把长度较短的加数字符串,在左面补0,使之与较长的字符串一样长

     * 4、从最高位,一个个数的取出来相加,当然首先得转换为整型

     * 5、设置进位,如果两个数相加及加上进位大于等于10,并且这不是最左边一个字符相加,相加结果等于

     *    (取出1+取出2+进位)-10,并把进位设为1;如果没有大于10,就把进位设为0,如些循环,把

     *    相加的结果以字符串的形式结合起来,就得到最后的结果

     */

    String doAdd(String a,String b)

    {

       String str="";

       int lenA=a.length();

       int lenB=b.length();

       int maxLen=(lenA>lenB) ? lenA : lenB;

       int minLen=(lenA<lenB) ? lenA : lenB;

       String strTmp="";

       for(int i=maxLen-minLen;i>0;i--)

       {

           strTmp+="0";

       }

       //把长度调整到相同

       if(maxLen==lenA)

       {

           b=strTmp+b;

       }else

           a=strTmp+a;

       int JW=0;//进位

       for(int i=maxLen-1;i>=0;i--)

       {        

           int tempA=Integer.parseInt(String.valueOf(a.charAt(i)));

           int tempB=Integer.parseInt(String.valueOf(b.charAt(i)));

           int temp;

           if(tempA+tempB+JW>=10 && i!=0)

           {

              temp=tempA+tempB+JW-10;

              JW=1;

           }

           else

           {

              temp=tempA+tempB+JW;

              JW=0;

           }        

           str=String.valueOf(temp)+str;        

       }

       return str;

    }

 

}

 

原帖地址: http://blog.csdn.net/fenglibing/archive/2007/08/23/1756773.aspx

    
    其实java 本身也提供了api ,java.math.BigInteger;import java.math.BigDecimal; 也可以实现。

code :

package com.kangdy.test;

import java.math.BigInteger;
import java.math.BigDecimal;

public class NumberTest {
 public static void main(String args[]){
  BigInteger b1= new BigInteger("2222222222222222222222222");
  BigInteger b2= new BigInteger("8888888888888888888888888");
  BigDecimal b3 = new BigDecimal("66666666666666666666666666");
  BigDecimal b4 = new BigDecimal("9999999999999999999999999999");
  System.out.println(b1.add(b2).toString());
  System.out.println(b3.add(b4).toString());
 }
}

这里只是给出简单的例子。



posted @ 2010-12-10 14:06 AK47 阅读(910) | 评论 (0)编辑 收藏

(转贴)java回调函数

原帖地址: http://ayzw001.blog.163.com/blog/static/1134114222009420112538726/

引用:

       所谓回调,就是客户程序C调用服务程序S中的某个方法a,然后S又在某个时候反过来调用C中的某个方法b,对于C来说,这个b便叫做回调函数。

一般说来,C不会自己调用b,C提供b的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的b叫甚名谁,所以S会约定b的接口规范(函数原型),然后由C提前通过S的一个函数r告诉S自己将要使用b函数,这个过程称为回调函数的注册,r称为注册函数。

下面举个通俗的例子:

某天,我打电话向你请教问题,当然是个难题,:),你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。

这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。

 

如果你还不太清楚看看这段描述合和代码:

声明一个接口,另外一个类有方法里面有个参数以是这个接口类型的,而后在另外类中实现这个接口(java中多用的是匿名内部类),而且以这个匿名的类生成的对象为参数传到上面提到类中,而后实现回调.......这种用法可以参考java里面常用到的数据库操作所用到的几个接口.....

//声明一个接口
public interface ICallBack {
    void postExec();
}

 

//另外一个类有方法里面有个参数以是这个接口类型的
public class FooBar {
    private ICallBack callBack;
    public void setCallBack(ICallBack callBack) {
        this.callBack = callBack;
    }
    public void doSth() {
        callBack.postExec();
    }
}
---------------------------------------
回调的实现
public class Test {
    public static void main(String[] args) {
        FooBar foo = new FooBar();
        foo.setCallBack(new ICallBack() {
            public void postExec() {
                System.out.println("method executed.");
            }
        });
        foo.doSth();//调用函数
    }
}

posted @ 2010-12-10 11:22 AK47 阅读(610) | 评论 (0)编辑 收藏

(转贴) 真正理解面向接口编程

面向对象设计里有一点大家已基本形成共识,就是面向接口编程,我想大多数人对这个是没有什么觉得需要怀疑的。

问题是在实际的项目开发中我们是怎么体现的呢? 难道就是每一个实现都提供一个接口就了事了?反过来说,你有时候有没有觉得接口是多余的事? 又或者,你仅仅是觉得现在类似spring这样的框架已习惯用接口这种方式而心存当然。

设计模式解析里提到了面向对象设计考虑的几个视角,一个是概念层,一个是规约层,一个是实现层。我如果没有猜错的话,实际上我们大多数人的眼睛一直是盯着实现层的,而这正是面向对象设计所极力避免的,即你不要在一开始就关注这些细节,你要关注的是规约(接口).

对于实际项目开发来说,如果我们把实现的过程分为多个阶段的话我们不妨这么划分,第一阶段,根据client端的需要去设计我们的规约(interface),在这个阶段任何实现都没有,所有的任务就是定义接口所需要的职责,以及所需要的一些po,vo;第二阶段,实现前面定义的规约。而以前我是怎么做的呢? 我是交叉作的,即假模假样的定义一个接口(其实我心里在想这个东西有屁用),然后定义了一个方法,然后就立即去实现这个方法,再然后我又定义一个方法,继续去实现,我现在终于想通了,这样好累,效率很低,最重要的是,这不属于真正的设计。
现在我是怎么做的呢?比如一个list.jsp里需要查询,列表,然后看明细信息,然后增加信息,我会第一步在接口里定义完(这个过程会有整体设计的意识),毫不关心底层实现(数据库、事务),我的目标就是"我想要这个功能,我想要那个功能",至于那个功能怎么实现在第一阶段我认为那不是我的事情(尽管这个事情最终还是由我来做) .大家看这个过程和前面的过程有什么本质的不同呢? 就是分层的概念更加明显,你的工作更有层次,每次都有先设计再实现的步骤,而前面那个过程很容易就让你不知不觉地陷入纯实现的陷阱中。

一点感想,欢迎大家拍砖。

原帖地址: http://www.blogjava.net/alex/archive/2007/03/12/103185.html

posted @ 2010-11-05 14:09 AK47 阅读(298) | 评论 (0)编辑 收藏

数字金额的中文大小写转化

曾经去过一家公司面试。笔试题的最后一题是一个数字金额大小写转化的问题。
当时没想那么多,仅仅想到应该把数拆开然后添加单位的一个大致的设计思路。
而那个面试官一个劲儿的问我用啥算法。那个题最后也没答上,回来后比较郁闷,
在网上搜了一下。这个答案还真不少。不过觉得有一种设计还比较靠谱。
大概是这样:
 * 先将整数与小数部分分开,计算小数部分,角分并保存
 * 整数部分长度不足12位,前面加0补足。
 * 将整数部分分割3部分。高4位代表亿,中间的是万,其余分别代表千,百,十,个
 * 定一个方法拼出每一部分串。
 * 最后整数与小数部分合成。

自己实现了一下,以下是代码。
code :

package com.kangdy.test;
/**
 * 数字金额转化成大写
 * 先将整数与小数部分分开,计算小数部分,角分并保存
 * 整数部分长度不足12位,前面加0补足。
 * 将整数部分分割3部分。高4位代表亿,中间的是万,其余分别代表千,百,十,个
 * 定一个方法拼出每一部分串。
 * 最后整数与小数部分合成。
 * @author dkang
 *
 */
public class NumberToString {

 String numberStr;

 public static final String unit[] = { "", "十", "百", "千", "万", "亿" };

 public static final String unit2[] = { "元", "角", "分" };

 public static final String numStr[] = { "零", "壹", "贰", "叁", "肆", "伍", "陆",
   "柒", "捌", "玖" };

 /**
  * 字符串长度不足12位用0补足
  *
  * @param str
  * @return
  */
 private String additionalZero(String str) {
  StringBuffer strb = new StringBuffer();
  if (str.length() < 12) {
   int size = 12 - str.length();
   for (int i = 0; i < size; i++) {
    strb.append("0");
   }
  }
  return strb.append(str).toString();
 }

 /**
  * 递归拆分数字成字符串
  *
  * @param value
  * @param strBuffer
  * @return
  */
 private String decomposeNumberToString(int value, StringBuffer strBuffer) {
  int quotient = 0;
  quotient = value / 10;
  if (quotient != 0) {
   decomposeNumberToString(quotient, strBuffer);
  }
  int remaider = value % 10;
  strBuffer.append(remaider + ",");
  return strBuffer.toString().substring(0,
    strBuffer.toString().length() - 1);
 }

 /**
  * 使用循环拆分数字成字符串
  *
  * @param value
  * @return
  */
 private String decomposeNumberToString2(int value) {
  StringBuilder strBuilder = new StringBuilder();
  int quotient = value;
  int remaider = 0;
  while (quotient != 0) {
   remaider = quotient % 10;
   strBuilder.append(remaider + ",");
   quotient = quotient / 10;
  }
  strBuilder.deleteCharAt(strBuilder.lastIndexOf(","));
  return strBuilder.reverse().toString();
 }

 /**
  * 添加单位
  *
  * @param temp
  * @return
  */
 private String addUnits(String temp) {
  StringBuffer sb = new StringBuffer();
  String str[] = temp.split(",");
  String tempStr = temp.replace(",", "");
  if (tempStr.contains("000")) {
   return sb.append(resplaceNumToStr(str[0]) + unit[3]).toString();
  } else if (tempStr.contains("00")) {
   if (tempStr.charAt(3) == '0') {
    return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
      resplaceNumToStr(str[1]) + unit[2]).toString();
   } else {
    return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
      numStr[0]).append(resplaceNumToStr(str[3])).toString();
   }
  } else {
   for (int i = 0; i < str.length; i++) {
    sb.append(resplaceNumToStr(str[i]));
    if (!str[i].equals("0")) {
     sb.append(unit[str.length - (i + 1)]);
    }
   }
  }
  return sb.toString();
 }

 /**
  * 数字替换
  *
  * @param str
  * @return
  */
 private String resplaceNumToStr(String str) {
  try {
   int num = Integer.parseInt(str);
   return numStr[num];
  } catch (Exception e) {
   e.printStackTrace();
  }
  return "";
 }

 /**
  * 把4位长度的数字转化成字符串
  *
  * @param number
  * @param i
  * @return
  */
 private String transformNumberToString(String number, int i) {
  StringBuffer strBuffer = new StringBuffer();
  StringBuilder strBuilder = new StringBuilder();
  try {
   int num = Integer.parseInt(number);
   if (num != 0) {
    String s1 = decomposeNumberToString(num, strBuffer);
    strBuilder.append(addUnits(s1));
    if (i == 1) {
     strBuilder.append(unit[5]);
    } else if (i == 2)
     strBuilder.append(unit[4]);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strBuilder.toString();
 }

 /**
  * 得到最终结果
  *
  * @param str
  * @return
  */
 public String IntegrationResultString(String str) {
  StringBuffer strBuffer = new StringBuffer();
  String numStr[] = null;
  if (str.indexOf(".") != -1) {
   numStr = str.split("\\.");
  } else {
   return strBuffer.append(createIntegerPartsResult(str)).toString();
  }
  String fractionalStr = createFractionalPartsResult(numStr[1]);
  String integerStr = createIntegerPartsResult(numStr[0]);
  return strBuffer.append(integerStr).append(fractionalStr).toString();
 }

 private String createIntegerPartsResult(String integer) {
  StringBuffer strBuffer = new StringBuffer();
  String temp = additionalZero(integer);
  String str1 = temp.substring(0, 4);
  strBuffer.append(transformNumberToString(str1, 1));
  String str2 = temp.substring(4, 8);
  strBuffer.append(transformNumberToString(str2, 2));
  String str3 = temp.substring(8, temp.length());
  strBuffer.append(transformNumberToString(str3, 3) + unit2[0]);
  return strBuffer.toString();
 }

 private String createFractionalPartsResult(String fractionalStr) {
  StringBuilder strB = new StringBuilder();
  String s1 = fractionalStr.substring(0, 1);
  String s2 = fractionalStr.substring(1, fractionalStr.length());
  if (!s1.equals("0")) {
   strB.append(resplaceNumToStr(s1) + unit2[1]);
  }
  if (!s2.equals("0")) {
   strB.append(resplaceNumToStr(s2) + unit2[2]);
  }
  return strB.toString();
 }

 public static void main(String args[]) {
  NumberToString test = new NumberToString();
  String str = "200123004054.11";
  System.out.println(test.IntegrationResultString(str));
 }
}



posted @ 2010-11-02 14:59 AK47 阅读(583) | 评论 (0)编辑 收藏

类和对象的初始化

类的生命周期:分为装载,链接,初始化
如图:


1)装载:查找并装载类型的二进制数据
2)连接:执行验证,准备,和解析(可选)
         a) 验证:确保导入类型正确
         b) 准备:为类变量分配内存,并将其初始化为默认值
         c) 解析:把类型中的符号引用转换成直接引用
3)初始化:把类变量初始化为默认初值


      随着Java虚拟机装载了一个类,并执行了一些它选择进行的验证之后,类就可以进入准备阶
段了。在准备阶段,Java虚拟机为类变量分配内存,设置默认初始值:但在到达初始化阶段之前,
类变量都没有被初始化为真正的初始值。(在准备阶段是不会执行Java代码的。)在准备阶段,虚
拟机把给类变量新分配的内存根据类型设置为默认值。

 为了准备让一个类或者接口被"首次主动"使用,最后一个步骤就是初始化,也就是为类变量      
赋予正确的初始值。这里的”正确”初始值指的是程序员希望这个类变量所具备的起始值。正
确的初始值是和在准备阶段赋予的默认初始值对比而言的。前面说过,根据类型的不同,类变
量已经被赋予了默认初始值。而正确的初始值是根据程序员制定的主观计划面生成的。


在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化语句给出的。
 1)一个类变量初始化语句是变量声明后面的等号和表达式:
 2)静态初始化语句是一个以static开头的程序块
 example : 
    public class Example1 {
     
     // 类变量初始化语句
     static int value = (int) (Math.random()*6.0);
     
     // 静态初始化语句
     static{
      System.out.println("this is example");
     }
    }
所有的类变量初始化语句和类型的静态初始化器都被Java编译器收集在—起,放到——个特殊
的方法中。对于类来说,这个方法被称作类初始化方法;对于接口来说,它被称为接口初始化
方法。在类和接口的Javaclass文件中,这个方法被称为”<clinit>”。通常的Java程序方法是无法
调用这个<clinit>方法的。这种方法只能被Java虚拟机调用

clinit>()方法
    前面说过,Java编译器把类变量初始化语句和静态初始化浯句的代码都放到class文件的
<clinit>()方法中,顺序就按照它们在类或者接门声明中出现的顺序。
 example:
  public class Example1 {
    static int width;
    static int height = (int) (Math.random()*6.0);

    static{
     width = (int) (Math.random()*3.0);
    }
 }
java 编译器生成下面<clinit>方法:
0 invokestatic java.lang.Math.random
3 ldc2_w 6.0 (double)
6 dmul
7 d2i
8 putstatic Example1.height
11 invokestatic java.lang.Math.random
14 ldc2_w 3.0 (double) 17 dmul
18 d2i
19 putstatic Example1.width
22 return

clinit 方法首先执行唯一的类变量初始化语句初始化heght,然后在静态初始化语句中
初始化width(虽然它声明在height之前,但那仅仅是声明了类变量而不是类变量初始化语句).

 

除接口以外,初始化一个类之前必须保证其直接超类已被初始化,并且该初始化过程是由 Jvm 保证线程安全的。
另外,并非所有的类都会拥有一个 <clinit>() 方法。
1)如果类没有声明任何类变量,也没有静态初始化语句,那么它不会有<clinit>()方法。
2)如果声明了类变量但是没有使用类变量初始化语句或者静态初始化语句初始它们,那么类不会有<clinit>()方法。 
   example:
      public class example{
       static int val;
      }
    
3)如果类仅包含静态 final 变量的类变量初始化语句,并且类变量初始化语句是编译时常量表达式,类不会有<clinit>()方法。
    example:
    public class Example {
     static final String str ="abc";
     static final int value = 100;
    }
这种情况java编译器把 str 和 value 被看做是常量,jvm会直接使用该类的常量池或者在字节码中直接存放常量值。该类不会被加载。
 
如果接口不包含在编译时解析成常量的字段初始化语句,接口中就包含一个<clinit>()方法。
example:
 interface Example{
  int i =5;
  int hoursOfSleep = (int) (Math.random()*3.0);
  
 }
字段hoursOfSleep会被放在<clinit>()方法中(比较诡异???它被看作类变量了),而字段i被看作是编译时常量特殊处理(JAVA语法规定,接口中的变量默认自动隐含是public static final)。
 java 编译器生成下面<clinit>方法:
0 invokestatic java.lang.Math.random
3 ldc2_w 3.0 (double)
6 dmul
7 d2i
8 putstatic Example.hoursOfSleep
11 return

主动使用和被动使用
    在前面讲过,Java虚拟机在首次主动使用类型时初始化它们。只有6种活动被认为是主动使
用:
 1)创建类的新实例,
 2)调用类中声明的静态方法,
 3)操作类或者接口中声明的非常量静态字段,
 4)调用JavaAPI中特定的反射方法
 5)初始化一个类的子类;
 6)以及指定一个类作为Java虚拟机启动时的初始化类。
 
   使用一个非常量的静态字段只有当类或者接口的确声明了这个字段时才是主动使用、比如,
类中声明的字段可能会被子类引用;接口中声明的字段可能会被子接口或者实现了这个接口的
类引用。对于子类、子接口和实现接口的类来说.这就是被动使用(使用它们并不会触发
它们的初始化)。下面的例子说明了这个原理:

class NewParement{
 static int hoursOfSleep = (int) (Math.random()*3.0);
 
 static{
  System.out.println("new parement is initialized.");
 }
}

class NewbornBaby extends NewParement{
 static int hoursOfCry = (int) (Math.random()*2.0);
 
 static{
  System.out.println("new bornBaby is initialized.");
 }
}


public class Example1 {
 
 public static void main(String[] args){
  int hours = NewbornBaby.hoursOfSleep;
  System.out.println(hours);
 }
 static{
  System.out.println("example1 is initialized.");
 }
 
}
运行结果:
example1 is initialized.
new parement is initialized.
0
NewbornBaby 没有被初始化,也没有被加载。


对象的生命周期

        当java虚拟机创建一个新的类实例时不管明确的还是隐含的,首先要在堆中为保存对象的实例变量分配内存,包含所有在对象类中和它超类中
声明的变量(包括隐藏的实例变量)都要分配内存。其次赋默认初值,最后赋予正确的初始值。

java编译器为每个类都至少生成一个实例初始化方法 "<init>()"与构造方法相对应。

如果构造方法调用同一个类中的另一个构造方法(构造方法重载),它对应的init<>():
1)一个同类init<>()调用。
2)对应构造方法体代码的调用。
如果构造方法不是通过this()调用开始,且对象不是Object 它对应的init<>():
1)一个超类init<>()调用。
2)任意实例变量初始化代码调用。
3)对应构造方法体代码的调用。
如果上述对象是Object,则去掉第一条。如果构造方法明确使用super()首先调用对应超类init<>()其余不变。
下面的例子详细说明了实例变量初始化(摘自Java Language Specification)
class Point{
 int x,y;
 Point(){x=1;y=1;}
}
class ColoredPoint extends Point{
 int color = OxFF00FF;
}
class Test{
 public static void main(String[] args){
  ColoredPoint cp = new ColoredPoint();
  System.out.println(cp.color);
 }
}
首先,为新的ColoredPoint实例分配内存空间,以存储实例变量x,y和color;然后将这些变量初始化成默认值
在这个例子中都是0。
接下来调用无参数的ColoredPoint(),由于ColorPoint没有声明构造方法,java编译器会自动提供如下的构造方
法:ColoredPoint(){super();}。
该构造方法然后调用无参数的Point(),而Point()没有显示的超类,编译器会提供一个对其无参数的构造方法的
隐式调用:Point(){super();x=1;y=1}。
因此将会调用到Object();Object类没有超类,至此递归调用会终止。接下来会调用Object任何实例初始化语句
及任何实例变量初始化语句。
接着执行Object()由于Object类中未声明这样的构造方法。因此编译器会提供默认的构造方法object(){}。
但是执行该构造方法不会产生任何影响,然后返回。
接下来执行Point类实例变量初始化语句。当这个过程发生时,x,y的声明没有提供任何初始化表达式,因此这个
步骤未采取任何动作(x,y 仍为0);
接下来执行Point构造方法体,将x,y赋值为1。
接下来会执行类ColoredPoint的实例变量初始化语句。把color赋值0xFF00FF,最后执行ColoredPoint构造方法体
余下的部分(super()调用之后的部分),碰巧没有任何语句,因此不需要进一步的动作,初始化完成。

与C++不同的是,在创建新的类实例期间,java编程语言不会为方法分派来指定变更的规则。如果调用的方法在被
初始化对象的子类中重写,那么就是用重写的方法。甚至新对象被完全初始化前也是如此。编译和运行下面的例子
class Super{
 Super(){printThree();}
 void printThree{System.out.println("Three");}
}
class Test extends Super{
 int three = (int)Math.PI; // That is 3
 public static void main(String args[]){
  Test t = new Test();
  t.printThree();
 }
 void printThree(){System.out.println(three);}
}
输出:
0
3
这表明Super类中的printThree()没有被执行。而是调用的Test中的printThree()。

 

 
 

posted @ 2010-07-14 16:18 AK47 阅读(882) | 评论 (0)编辑 收藏

Java虚拟机体系结构

Java虚拟机体系结构


方法区
         在Java虚拟机中,被装载类型的信息存储在一个逻辑上被称为方法区的内存中。
 当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,-->读入这个class文件(一个线性的二进制流)->将它传入虚拟机-->
 虚拟机提取类型信息,并将信息存入方法区,类型中的类(静态)变量也存储在方法区.
 方法区特点:
 1)所有线程共享方法区。它是线程安全的。
 2)方法区大小不是固定的。虚拟机根据需要自行调整。
 3)方法区可以被垃圾回收。
 对于每个被装载的类型,虚拟机会在方法区中存储以下信息。
 
 1)类型的基本信息;
     a)类型的全限定名
     b)类型的直接超类全限定名(除非这个类型是java.lang.Objet,它没超类)。
     c)类型是类类型还是接口类型(就是说是一个类还是一个接口)。
     d)类型的访问修饰符(public ,abstract或final的某个子类)
     e)任何直接超接口的全限定名的有序列表。
     
 2)该类型的常量池
   虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,
   包括直接常量(string,integer,floating point常量)和对其他类型、字段和方法的符号引用。
   池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、
   字段和方法的符号引用,所以它在Java程序的动态连接中起着核心的作用。
   
 3)字段信息
   类型中声明的每一个字段,方法区中必须保存下面的信息,字段在类或接口中声明的顺序也必须保存。
   字段名,字段类型,字段修饰符(public private protected static final 等)
   
 4)方法信息
   类型中声明的每一个方法,方法区中必须保存下面的信息,方法在类或接口中声明的顺序也必须保存。
   方法名,返回值类型,参数数量和类型(按声明顺序),方法修饰符(public private protected static final 等)
   如果方法不是抽象的或本地的还必须保存:方法字节码,操作数栈和该方法在栈针中局部变量的大小,异常表。
   
 5)除了常量以外的所有类(静态)变量
   这里主要说下编译时常量:就是那些用final声明以及编译时已知的值初始化的类变量(例如:static final int val =5)
   每个编译时常量的类型都会复制它所有常量到它自己的常量池中或者它的字节码流中(通常情况下编译时直接替换字节码)。

   
 6)一个到类classLoader的引用
   指向ClassLoader类的引用  每个类型被装载的时候,虚拟机必须跟踪它是由启动类装载器
   还是由用户自定义类装载器装载的。如果是用户自定义类装载器装载的,那么虚拟机必须在类
   型信息中存储对该装载器的引用:这是作为方法表中的类型数据的一部分保存的。
   虚拟机会在动态连按期间使用这个信息。当某个类型引用另一个类型的时候,虚拟机会请求装载
   发起引用类型的类装载器来装载被引用的类型。这个动态连接的过程,对于虚拟机分离命名空间
   的方式也是至关重要的。为了能够正确地执行动态连接以及维护多个命名空间,虚拟机需要在方
   法表中得知每个类都是由哪个类装载器装载的。
   
 7)一个到Class类的引用
    指向Class类的引用  对于每一个被装载的类型(不管是类还是接口),虚拟机都会相应地为
    它创建一个java.lang.Class类的实例(Class实例放在内存中的堆区),
而且虚拟机还必须以某种方式把这个实例的引用存储在方法区
    
       为了尽可能提高访问效率,设计者必须仔细设计存储在方法区中的类型信息的数据结构,因此,
除了以上时论的原始类型信息,实现中还可能包括其他数据结构以加快访问原始数据的速度,比如方法表。
虚拟机对每个装载的非抽象类,都生成一个方法表,把它作为类信息的一部分保存在方法区。方法表是一个数组,
它的元素是所有它的实例可能被调用的实例方法的直接引用,包括那些从超类继承过来的实例方法:(对于抽象类和接口,方法表没有什么帮
助,因为程序决不会生成它们的实例。)运行时可以通过方法表快速搜寻在对象中调用的实例方法。
 
方法区使用的例子

 class Lava{
  private int speed = 5;
 
  void flow(){
  
  }
 
 }

 public class Volcano {
  
  public static void main(String args[]){
   
   Lava lava = new Lava();
   
   lava.flow();
  }
 }

  1)虚拟机在方法区查找Volcano这个名字,未果,载入volcano.class文件,并提取相应信息
   存入方法区。
  2)虚拟机开始执行Volcano类中main()方法的字节码的时候,尽管Lava类还没被装载,
  但是和大多数(也许所有)虚拟机实现一样,它不会等到把程序中用到的所有类都装载后才开
  始运行程序。恰好相反,它只在需要时才装载相应的类。
  3)main()的第一条指令告知虚拟机为列在常量池第一项的类分配足够的内存。所以虚拟机
  使用指向Volcano常量池的指针找到第一项,发现它是一个对Lava类的符号引用,然后它就检查
  方法区,看Lava类是否已经被装载了。
  4)当虚拟机发现还没有装载过名为"Lava"的类时,它就开始查找并装载文件“Lava.class”,
  并把从读入的二进制数据中提取的类型信息放在方法区中。
  5)虚拟机以一个直接指向方法区Lava类数据的指针来替换常量池第—项(就是那个
  字符串“Lava”)——以后就可以用这个指针来快速地访问Lava类了。这个替换过程称为常量池
  解析,即把常量池中的符号引用替换为直接引用:这是通过在方法区中搜索被引用的元素实现
  的,在这期间可能又需要装载其他类。在这里,我们替换掉符号引用的“直接引用”是一个本
  地指针。
  6)虚拟机准备为一个新的Lava对象分配内存。此时,它又需要方法区中的信息。还记
  得刚刚放到Volcano类常量池第——项的指针吗?现在虚拟机用它来访问Lava类型信息(此前刚放
  到方法区中的),找出其中记录的这样一个信息:一个Lava对象需要分配多少堆空间。
  7)虚拟机确定一个Lava对象大小后,就在堆上分配空间,并把这个对象实例变量speed初始化为默认初始值0
  8)当把新生成的Lava对象的引用压到栈中,main()方法的第一条指令也完成了,指令通过这个引用
  调用Java代码(该代码把speed变量初始化为正确初始值5).另外用这个引用调用Lava对象引用的flow()方法。


        每个java虚拟机实例都有一个方法区以及一个堆,一个java程序独占一个java虚拟机实例,而每个java程序都有自己的堆空间,它们不会彼此干扰,但同一个java程序的多个线程共享一个堆空间。这种情况下要考虑多线程访问同步问题。
 
Java栈
        一个新线程被创建时,都会得到自己的PC寄存器和一个java栈,虚拟机为每个线程开辟内存区。这些内存区是私有的,任何线程不能访问其他线程的PC寄存器和java栈。java栈总是存储该线程中java方法的调用状态。包括它的局部变量,被调用时传进来的参数,它的返回值,以及运算的中间结果等。java栈是由许多栈帧或者说帧组成,一个栈帧包含一个java方法的调用状态,当线程调用java方法时,虚拟机压入一个新的栈帧到该线程的java栈中。当方法返回时,这个栈帧被从java栈中弹出并抛弃。
.本地方法栈
         任何本地方法接口都会使用某种本地方法饯。当线程调用Java方法时,虚拟机会创建一个新的栈帧井压人Java栈。
然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压人新的帧,虚拟机只是简单地动态连接
并直接调用指定的本地方法。可以把这看做是虚拟机利用本地方法来动态扩展自己。

posted @ 2010-07-06 13:47 AK47 阅读(367) | 评论 (0)编辑 收藏

Eclipse 内置webservice浏览器问题

最近在使用eclipse 内置webservice 浏览器遇到了些问题,无法点开WSDL Main 如下图:


大家遇到过类似的情况没有,目前我只能先使用外部浏览器了。

posted @ 2010-06-10 14:17 AK47 阅读(437) | 评论 (0)编辑 收藏

eclipse 无法启动 JVM terminated. Exit code=-1 的解决办法

转贴 :

这两天,突然无法启动我的MyEclipse6.5了,不知道为什么,提示错误: JVM   terminated. Exit   code=-1。

昨天,我以为是机器运行时间太长,重启一下,果然好了。但是今天又来了。看了一下错误提示,我以为是JVM有问题,就在启动Eclipse里加个JVM的参数,结果还是不行。

后来在网上找了一下,有人说是JAVA环境配置的问题,我想这不可能,因为以前一直用的好好的。有人说是JVM的问题,这个我刚刚换了一个,也不是这个问题,后来看来有人说是:eclipse.ini中内存设置过大的问题,虽然我不以为然,还是试了一下,以前我修改过内存设置,一直都好好的,之前eclipse.ini的配置如下:

-showsplash
com.genuitec.myeclipse.product
--launcher.XXMaxPermSize
512m
-vmargs
-Xms256m
-Xmx512m
-Duser.language=en
-XX:PermSize=256M
-XX:MaxPermSize=512M

现在修改了一下,-Xms256m改成-Xms128m,把Xmx512m  改为 Xmx256m,结果还真的好了,没想到居然是这样的小问题引起来的。

转载自:巴士飞扬-技术BLOG : 链接地址:http://www.busfly.cn/post/eclipse-JVM-terminated-Exit-code-1.html

posted @ 2010-03-05 12:10 AK47 阅读(241) | 评论 (0)编辑 收藏

从追MM谈Java的23种设计模式(转)

这样学习设计模式肯定便于理解:
http://hi.baidu.com/xghzlg/blog/item/3288de589071d7202934f06f.html

引用:

 

从追MM谈Java的23种设计模式
设计模式做为程序员的“内功心法”,越来越受到.net 社区的重视,这种变化是很可喜的,Java社区走在了我们的前面,但这种状况也许有一天会发生改变。

  从追MM谈Java的23种设计模式

  1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯 德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory.

    工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点 是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。

  程序代码

  以下是引用片段:

以下是引用片段:
public class Factory{
public String Boy = "boy" ;
public String Girl = "girl" ;
public People getPeople (String people){
if (people.equals("boy")){
return new Boy();
}else if(people.equals("girl")){
return new Girl();
}
}
}


2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)

   建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得 产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

  3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模 式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

    工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出 具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

  4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是 我的情话prototype了。(100块钱一份,你要不要)

  原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

        5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个 人,那就是我(刚才做了个梦啦,哪有这么好的事)

    单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的 “单一实例”的需求时才可使用。

  以下是引用片段:

以下是引用片段:
public class SingLeton{
private static SingLeton instance = new SingLeton();
public static SingLeton getInstance(){
return instance;
}
}


6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他 作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)

    适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类 能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

  7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好; 碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型, 要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了

  桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的 抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

  8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?

    合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成 模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

  9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

    装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个 对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。

        10、FA?ADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Fa?ade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

    门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用 。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

  11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

    享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

  12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序 做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

    代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

以下是引用片段:
public interface FactoryProxy{
public People createBoy();
public People creteGirl();
}


13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上 “Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

    责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接

    起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

  14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送 COMMAND,就数你最小气,才请我吃面。”,

    命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。


        15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只 要做一个Interpreter,照着上面的脚本执行就可以了。

    解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

  16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。

    Mary:“想要我跟你结婚,得答应我的条件”

    我:“什么条件我都答应,你说吧”

    Mary:“我看上了那个一克拉的钻石”

    我:“我买,我买,还有吗?”

    Mary:“我看上了湖边的那栋别墅”

    我:“我买,我买,还有吗?”

    Mary:“我看上那辆法拉利跑车”

    我脑袋嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?”

    ……

    迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色 变化。

  17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这 里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

    调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

  18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与 哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

    备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一 个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

  19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知 我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦

  观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生 变化时,会通知所有观察者对象,使他们能够自动更新自己。

         20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的 MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

    状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子 类。

  21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目 的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

    策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

  22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

    模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

  23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;  

    访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

 

posted @ 2010-01-04 16:48 AK47 阅读(238) | 评论 (0)编辑 收藏

<2010年1月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

导航

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜