想必用过
Spring的程序员们都有这样的感觉,Spring把逻辑层封装的太完美了(个人感觉View层封装的不是很好)。以至于有的初学者都不知道Spring配置文件的意思,就拿来用了。所以今天我给大家详细解释一下Spring的applicationContext.xml
文件。Ok,我还是通过代码加注释的方式为大家演示:
以下是详解Spring的applicationContext.xml文件代码:
<!-- 头文件,主要注意一下编码 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 建立数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 数据库驱动,我这里使用的是Mysql数据库 -->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<!-- 数据库地址,这里也要注意一下编码,不然乱码可是很郁闷的哦! -->
<property name="url">
<value>
jdbc:mysql://localhost:3306/tie?useUnicode=true&characterEncoding=utf-8
</value>
</property>
<!-- 数据库的用户名 -->
<property name="username">
<value>root</value>
</property>
<!-- 数据库的密码 -->
<property name="password">
<value>123</value>
</property>
</bean>
<!-- 把数据源注入给Session工厂 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<!-- 配置映射文件 -->
<property name="mappingResources">
<list>
<value>com/alonely/vo/User.hbm.xml</value>
</list>
</property>
</bean>
<!-- 把Session工厂注入给hibernateTemplate -->
<!-- 解释一下hibernateTemplate:hibernateTemplate提供了很多方便的方法,在执行时自动建立 HibernateCallback 对象,例如:load()、get()、save、delete()等方法。 -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<constructor-arg>
<ref local="sessionFactory" />
</constructor-arg>
</bean>
<!-- 把DAO注入给Session工厂 -->
<bean id="userDAO" class="com.alonely.dao.UserDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 把Service注入给DAO -->
<bean id="userService" class="com.alonely.service.UserService">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
<!-- 把Action注入给Service -->
<bean name="/user" class="com.alonely.struts.action.UserAction">
<property name="userService">
<ref bean="userService" />
</property>
</bean>
</beans> |
以上Spring的applicationContext.xml文件我是用的SSH架构,如果您用Spring的
MVC架构,其原理也是一样的。
posted @
2008-06-07 13:48 xzc 阅读(18077) |
评论 (3) |
编辑 收藏
一:理解多线程
多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。
多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。
二:在Java中实现多线程
我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!
真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。
那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是 run() ,它为Thread 类的方法 start() 所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!
方法一:继承 Thread 类,覆盖方法 run()
我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。
下面是一个例子:
public class MyThread extends Thread {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new MyThread(i+1).start();
}
}
这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?
我们不妨来探索一种新的方法:我们不创建 Thread 类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)
Java 提供了接口 java.lang.Runnable 来支持这种方法。
方法二:实现 Runnable 接口
Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。
但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数
public Thread(Runnable target);来实现。
下面是一个例子:
public class MyThread implements Runnable {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start();
}
}
严格地说,创建 Thread 子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我
们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。
使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。
综上所述,两种方法各有千秋,大家可以灵活运用。
下面让我们一起来研究一下多线程使用中的一些问题。
三:线程的四种状态
1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。
2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。
3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。
4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。
四:线程的优先级
线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。
你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。
五:线程的同步
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方
法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
六:线程的阻塞
为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。
1. sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。
典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。
4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。
初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。
上述的核心区别导致了一系列的细节上的区别。
首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。
wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。
关于 wait() 和 notify() 方法最后再说明两点:
第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。
以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。
七:守护线程
守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。
可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程,也可以调用方法 setDaemon() 来将一个线程设为守护线程。
八:线程组
线程组是一个 Java 特有的概念,在 Java 中,线程组是类ThreadGroup 的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为 system 的系统线程组。
在 Java 中,除了预建的系统线程组外,所有线程组都必须显式创建。
在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。
Java 允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。
Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。
九:总结
在这一讲中,我们一起学习了 Java 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?
多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。
假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。
posted @
2008-06-07 13:42 xzc 阅读(244) |
评论 (0) |
编辑 收藏
1、使用JdbcTemplate的execute()方法执行SQL语句
- jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
2、如果是UPDATE或INSERT,可以用update()方法。
- jdbcTemplate.update("INSERT INTO USER VALUES('"
- + user.getId() + "', '"
- + user.getName() + "', '"
- + user.getSex() + "', '"
- + user.getAge() + "')");
jdbcTemplate.update("INSERT INTO USER VALUES('"
+ user.getId() + "', '"
+ user.getName() + "', '"
+ user.getSex() + "', '"
+ user.getAge() + "')");
3、带参数的更新
- jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
- jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
4、使用JdbcTemplate进行查询时,使用queryForXXX()等方法
- int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
- String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
- List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
- List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
- Iterator it = rows.iterator();
- while(it.hasNext()) {
- Map userMap = (Map) it.next();
- System.out.print(userMap.get("user_id") + "\t");
- System.out.print(userMap.get("name") + "\t");
- System.out.print(userMap.get("sex") + "\t");
- System.out.println(userMap.get("age") + "\t");
- }
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
Iterator it = rows.iterator();
while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.print(userMap.get("user_id") + "\t");
System.out.print(userMap.get("name") + "\t");
System.out.print(userMap.get("sex") + "\t");
System.out.println(userMap.get("age") + "\t");
}
JdbcTemplate将我们使用的JDBC的流程封装起来,包括了异常的捕捉、SQL的执行、查询结果的转换等等。spring大量使用Template Method模式来封装固定流程的动作,XXXTemplate等类别都是基于这种方式的实现。
除了大量使用Template Method来封装一些底层的操作细节,spring也大量使用callback方式类回调相关类别的方法以提供JDBC相关类别的功能,使传统的JDBC的使用者也能清楚了解spring所提供的相关封装类别方法的使用。
JDBC的PreparedStatement
- final String id = user.getId();
- final String name = user.getName();
- final String sex = user.getSex() + "";
- final int age = user.getAge();
-
- jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
- new PreparedStatementSetter() {
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setString(1, id);
- ps.setString(2, name);
- ps.setString(3, sex);
- ps.setInt(4, age);
- }
- });
final String id = user.getId();
final String name = user.getName();
final String sex = user.getSex() + "";
final int age = user.getAge();
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
});
- final User user = new User();
- jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
- new Object[] {id},
- new RowCallbackHandler() {
- public void processRow(ResultSet rs) throws SQLException {
- user.setId(rs.getString("user_id"));
- user.setName(rs.getString("name"));
- user.setSex(rs.getString("sex").charAt(0));
- user.setAge(rs.getInt("age"));
- }
- });
final User user = new User();
jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
}
});
- class UserRowMapper implements RowMapper {
- public Object mapRow(ResultSet rs, int index) throws SQLException {
- User user = new User();
-
- user.setId(rs.getString("user_id"));
- user.setName(rs.getString("name"));
- user.setSex(rs.getString("sex").charAt(0));
- user.setAge(rs.getInt("age"));
-
- return user;
- }
- }
-
- public List findAllByRowMapperResultReader() {
- String sql = "SELECT * FROM USER";
- return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
- }
class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
return user;
}
}
public List findAllByRowMapperResultReader() {
String sql = "SELECT * FROM USER";
return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
}
在getUser(id)里面使用UserRowMapper
- public User getUser(final String id) throws DataAccessException {
- String sql = "SELECT * FROM USER WHERE user_id=?";
- final Object[] params = new Object[] { id };
- List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
-
- return (User) list.get(0);
- }
public User getUser(final String id) throws DataAccessException {
String sql = "SELECT * FROM USER WHERE user_id=?";
final Object[] params = new Object[] { id };
List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
return (User) list.get(0);
}
网上收集
org.springframework.jdbc.core.PreparedStatementCreator 返回预编译SQL 不能于Object[]一起用
- public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
- return con.prepareStatement(sql);
- }
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(sql);
}
1.增删改
org.springframework.jdbc.core.JdbcTemplate 类(必须指定数据源dataSource)
- template.update("insert into web_person values(?,?,?)",Object[]);
template.update("insert into web_person values(?,?,?)",Object[]);
或
- template.update("insert into web_person values(?,?,?)",new PreparedStatementSetter(){ 匿名内部类 只能访问外部最终局部变量
-
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setInt(index++,3);
- });
template.update("insert into web_person values(?,?,?)",new PreparedStatementSetter(){ 匿名内部类 只能访问外部最终局部变量
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
});
org.springframework.jdbc.core.PreparedStatementSetter 接口 处理预编译SQL
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setInt(index++,3);
- }
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
}
2.查询JdbcTemplate.query(String,[Object[]/PreparedStatementSetter],RowMapper/RowCallbackHandler)
org.springframework.jdbc.core.RowMapper 记录映射接口 处理结果集
- public Object mapRow(ResultSet rs, int arg1) throws SQLException { int表当前行数
- person.setId(rs.getInt("id"));
- }
- List template.query("select * from web_person where id=?",Object[],RowMapper);
public Object mapRow(ResultSet rs, int arg1) throws SQLException { int表当前行数
person.setId(rs.getInt("id"));
}
List template.query("select * from web_person where id=?",Object[],RowMapper);
org.springframework.jdbc.core.RowCallbackHandler 记录回调管理器接口 处理结果集
- template.query("select * from web_person where id=?",Object[],new RowCallbackHandler(){
- public void processRow(ResultSet rs) throws SQLException {
- person.setId(rs.getInt("id"));
- });
posted @
2008-06-07 13:37 xzc 阅读(459) |
评论 (0) |
编辑 收藏
在现实工作中,我们常常需要保存一些系统配置信息,大家一般都会选择配置文件来完成,本文根据笔者工作中用到的读取配置文件的方法小小总结一下,主要叙述的是spring读取配置文件的方法。
一.读取xml配置文件
(一)新建一个java bean(HelloBean.java)
java 代码
- package chb.demo.vo;
-
- public class HelloBean {
- private String helloWorld;
-
- public String getHelloWorld() {
- return helloWorld;
- }
-
- public void setHelloWorld(String helloWorld) {
- this.helloWorld = helloWorld;
- }
- }
-
(二)构造一个配置文件(beanConfig.xml)
xml 代码
- xml version="1.0" encoding="UTF-8"?>
- >
- <beans>
- <bean id="helloBean" class="chb.demo.vo.HelloBean">
- <property name="helloWorld">
- <value>Hello!chb!value>
- property>
- bean>
- beans>
(三)读取xml文件
1.利用ClassPathXmlApplicationContext
java 代码
- ApplicationContext context = new ClassPathXmlApplicationContext("beanConfig.xml");
- HelloBean helloBean = (HelloBean)context.getBean("helloBean");
- System.out.println(helloBean.getHelloWorld());
2.利用FileSystemResource读取
java 代码
- Resource rs = new FileSystemResource("D:/software/tomcat/webapps/springWebDemo/WEB-INF/classes/beanConfig.xml");
- BeanFactory factory = new XmlBeanFactory(rs);
- HelloBean helloBean = (HelloBean)factory.getBean("helloBean");\
- System.out.println(helloBean.getHelloWorld());
值得注意的是:利用FileSystemResource,则配置文件必须放在project直接目录下,或者写明绝对路径,否则就会抛出找不到文件的异常
二.读取properties配置文件
这里介绍两种技术:利用spring读取properties 文件和利用java.util.Properties读取
(一)利用spring读取properties 文件
我们还利用上面的HelloBean.java文件,构造如下beanConfig.properties文件:
properties 代码
- helloBean.class=chb.demo.vo.HelloBean
- helloBean.helloWorld=Hello!chb!
属性文件中的"helloBean"名称即是Bean的别名设定,.class用于指定类来源。
然后利用org.springframework.beans.factory.support.PropertiesBeanDefinitionReader来读取属性文件
java 代码
- BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
- PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(reg);
- reader.loadBeanDefinitions(new ClassPathResource("beanConfig.properties"));
- BeanFactory factory = (BeanFactory)reg;
- HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
- System.out.println(helloBean.getHelloWorld());
(二)利用java.util.Properties读取属性文件
比如,我们构造一个ipConfig.properties来保存服务器ip地址和端口,如:
properties 代码
则,我们可以用如下程序来获得服务器配置信息:
java 代码
- InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("ipConfig.properties");
- Properties p = new Properties();
- try {
- p.load(inputStream);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- System.out.println("ip:"+p.getProperty("ip")+",port:"+p.getProperty("port"));
本文只介绍了一些简单操作,不当之处希望大家多多指教
posted @
2008-06-06 15:56 xzc 阅读(1069) |
评论 (0) |
编辑 收藏
一、数据库名
数据库名是数据库的“身份证号码”,用于标示一个数据库。在参数文件中用DB_NAME表示。
数据库名是在安装数据库、创建新的数据库、创建数据库控制文件、修改数据库结构、备份与恢复数据库时都需要使用到的。
如何查看数据库名呢?方式有三:
·使用SQL语句:select name from v$database;
·使用show命令:show parameter db_name;
·查看参数文件:查看init.ora文件
二、数据库实例名:
数据库实例名是用于和操作系统进行联系的标识,也就是说数据库和操作系统之间的交互使用的是数据库实例名。
实例名也被写入参数文件中,该参数为instance_name,在winnt平台中,实例名同时也被写入注册表。
数据库名和实例名可以相同也可以不同。在一般情况下,数据库名和实例名是一对一的关系,但如果在oracle并行服务器架构(即oracle实时应用集群)中,数据库名和实例名是一对多的关系。
如何查看当前数据库实例名呢?方式有三:
·使用SQL语句:select instance_name from v$instance;
·使用show命令:show parameter instance
·查看参数文件:查看init.ora文件
数据库实例名与ORACLE_SID两者都表示oracle实例,但是有区别的。instance_name是oracle数据库参数。而ORACLE_SID是操作系统的环境变量。ORACLD_SID用于与操作系统交互,也就是说,从操作系统的角度访问实例名,必须通过ORACLE_SID。
ORACLE_SID必须与instance_name的值一致。否则,你将会收到一个错误。在unix平台,是“ORACLE not available”,在winnt平台,是“TNS:协议适配器错误”。
三、数据库域名与全局数据库名
随着由多个数据库构成的分布式数据库的普及,这种命令数据库的方法给数据库的管理造成一定的负担,因为各个数据库的名字可能一样,造成管理上的混乱。
为了解决这种情况,引入了Db_domain参数,这样在数据库的标识是由Db_name(数据库名)和 Db_domain(数据库域名)两个参数共同决定的,避免了因为数据库重名而造成管理上的混乱。这类似于互连网上的机器名的管理。
我们将Db_name和 Db_domain两个参数用‘.’连接起来,表示一个数据库,并将该数据库的名称称为Global_name(全局数据库名),即它扩展了Db_name。Db_name参数只能由字母、数字、’_’、’#’、’$’组成,而且最多8个字符。
对一个网络数据库(Oracle database)的唯一标识,oracle建议用此种方法命令数据库。该值是在创建数据库是决定的,缺省值为Db_name. Db_domain。在以后对参数文件中Db_name与Db_domain参数的任何修改不影响Global_name的值,如果要修改 Global_name,只能用ALTER DATABASE RENAME GLOBAL_NAME TO 命令进行修改,然后修改相应参数。
如何查询数据库域名呢?方法有三:
·使用SQL命令:select value from v$parameter where name = ´db_domain´;
·使用show命令:show parameter domain
·查看参数文件:在参数文件中查询。
四、数据库服务名
该参数是oracle8i新引进的。在8i以前,我们用SID来表示标识数据库的一个实例,但是在Oracle的并行环境中,一个数据库对应多个实例,这样就需要多个网络服务名,设置繁琐。为了方便并行环境中的设置,引进了Service_name参数。该参数对应一个数据库,而不是一个实例。
该参数的缺省值为Db_name. Db_domain,即等于Global_name。如果数据库有域名,则数据库服务名就是全局数据库名;否则,数据库服务名与数据库名相同。
如何查询数据库服务名呢?方法有三:
·使用SQL语句:select value from v$parameter where name = ´service_name´;
·使用show命令:show parameter service_name
·查看参数文件:在参数文件中查询。
从oracle8i开如的oracle网络组件,数据库与客户端的连接主机串使用数据库服务名。之前用的是ORACLE_SID,即数据库实例名。
五、网络服务名
网络服务名,又可以称为数据库别名(database alias)。是客户端程序访问数据库时所需要,屏蔽了客户端如何连接到服务器端的细节,实现了数据库的位置透明的特性。网络服务名被记录在tnsnames.ora文件中。
网络服务名是从客户端的角度出发,当客户端连接远程数据库或其他服务时,可以指定Net服务名。因此需要使用一个或多个命名方法将此Net服务名解析为连接数据库或其他服务的连接描述符。
通常选择的是[本地]-将存储在本地客户机的tnsnames.ora文件中的网络服务名解析为连接描述符。
[Oracle Names]-由Oracle名字服务器提供为网络上的每个Oracle Net服务提供解析方法
[主机名]-通过TCP/IP环境中的主机别名连接到Oracle数据库服务
[Sun NIS]/[DCE CDS]-专用系统用的,在Windows 2000系统环境下不适用
六、总结
Oracle中各种命名的比较名称查询方式
名称
|
查询方式
|
DB_NAME |
select name from v$database |
INSTANCE_NAME |
select instance_name from v$instance |
ORACLE_SID |
值和INSTANCE_NAME相同 |
DB_DOMAIN |
select value from v$parameter where name="db_domain" |
GLOBAL_NAME |
DB_NAME.DB_DOMAIN |
SERVICE_NAME |
select value from v$parameter where name="service_name" |
NET_SERVICE_NAME |
检查tnsnames.ora文件 |
posted @
2008-05-31 15:18 xzc 阅读(539) |
评论 (2) |
编辑 收藏
统一建模语言(Unified Modeling Language,UML)
一些术语:
系统(system)指的是硬件和软件的结合体,它能提供业务问题的解决方案。
系统开发(system development)是为客户建立一个系统的过程。
客户(client)是需要解决问题的人。
系统分析员(analyst)将客户所要解决的问题编制成文档,并将该文档转交给开发人员。
开发人员(developer)是为了及决客户的问题而构造软件并在计算机硬件上实施该软件的程序员。
UML的组成
UML包括了一些可以相互组合图表的图形元素。
1.类图
一个类(class)是一类或一组具有类似属性和共同行为的事物。
矩形方框代表类的图标,它被分成3个区域。最上面的区域中是类名,中间区域是类的属性,最下面区域里列的是类的操作。
举一个例子,属于洗衣机(washing machine)类的事物都具有诸如品牌(brand name)、型号(model name)、序列号(serial number)和容量(capacity)等属性。这类事物的行为包括“加衣物(add clothes)”、“加洗涤剂(add detergent)”、“开机(turn on)”和“取出衣物(remove clothes)”等操作。
2.对象图
对象(object)是一个类的实例,是具有具体属性值和行为的一个具体事物。例如,洗衣机的品牌可能是“Laundatorium”,型号为“Washmeister”,序列号为“GL57774”,一次最多可以洗涤重量为16磅的衣物。
对象的图标也是一个矩形,和类的图标一样,但是对象名下面要带下划线。具体实例的名字位于冒号的左边而该实例所属的类名位于冒号的右边。
3.用例图
用例(use case)是从用户的观点对系统行为的一个描述。
例,一个人使用一台洗衣机,显然是为了洗衣服(wash clothes)。
代表洗衣机用户的智力小人形被称为参与者(actor)。椭圆形代表用例。
参与者(它是发起用例的实体)可以是一个人也可以是另一个系统。
4.状态图
一台洗衣机可以处于浸泡(soak)、洗涤(Wash)、漂洗(Rinse)、脱水(Spin)或者关机(off)状态。
最顶端的符号代表起始状态,最底端的符号表示终止状态。
5.顺序图
类图和对象图标大的实系统的静态结构。在一个运行的系统中,对象之间要发生交互,并且这些交互要经历一定的时间。UML顺序图所表达的正式这种基于时间的动态交互。
例,洗衣机的构件包括一个注水的进水管(Water Pipe)、一个用来装衣物的洗涤缸(Drum)以一个排水管(Drain)。假设已经完成了“加衣物”、“加洗涤剂”和“开机”操作。洗衣服这个用例被执行时按照如下顺序进行:
1通过进水管想洗涤缸中注水。
2洗涤缸保持5分钟静止状态。
3水注满,停止注水。
4洗涤缸往返旋转15分钟。
5通过排水管排掉洗涤后的脏水。
6重新开始注水。
7洗涤缸继续往返旋转洗涤。
8停止向洗衣机中注水。
9通过排水管排掉漂洗衣物的水。
10洗涤缸加快速度单方向旋转5分钟。
11洗涤缸停止旋转,洗衣过程结束。
图中,对象之间发送的消息有:注入新水(Send fresh water)、保持静止(Remain stationary)、停止注水(Stop)、往返旋转(Ratate back and forth)、排掉洗涤后的脏水(Send soapy water)、排掉漂洗过的水(Send rinse water)等。
6.活动图
用例和对象的行为中的各个活动之间通常具有时间顺序。
7.协作图
系统的工作目标是由系统中各组成元素相互协作完成的。例子中洗衣机构件的类集中又增加了一个内部计时器(Internal Timer)。在经过一段时间后,定时器停止注水,然后启动洗涤缸往返旋转。图中的序号代表命令消息的发送顺序。
8.构件图
构件图和部署图和整个计算机系统密切相关。
9.部署图
UML部署图显示了基于计算机系统的物理体系结构。它可以描述计算机和设备,展示它们之间的连接,以及驻留在每台机器中的软件。每台计算机用一个立方体来表示,立方体之间的连线表示这些计算机之间的通信关系。
如果需要将图中的组织元素分组,或者在图中说明一些类或构件是某个特定子系统的一部分,可以通过将这些元素组织成包(package)来达到此目的。包用一边突起的公文夹形图标来表示。
注释(note)的图标是一个带折角的矩形,矩形框中是解释性文字。注释和被注释的图元素之间用一条虚线连接。
构造型(stereotype)能够使用现有的UML元素来定制新的元素。构造型用尖对括号括起来的一个名称来表示,这个括号叫双尖括号(guillemets)。
v
posted @
2008-05-30 21:03 xzc 阅读(413) |
评论 (0) |
编辑 收藏
是特定事件出现的时候,自动执行的代码块。类似于存储过程,但是用户不能直接调用他们。
功能:
1、 允许/限制对表的修改
2、 自动生成派生列,比如自增字段
3、 强制数据一致性
4、 提供审计和日志记录
5、 防止无效的事务处理
6、 启用复杂的业务逻辑
开始
create trigger biufer_employees_department_id
before insert or update
of department_id
on employees
referencing old as old_value
new as new_value
for each row
when (new_value.department_id<>80 )
begin
:new_value.commission_pct :=0;
end;
/
触发器的组成部分:
1、 触发器名称
2、 触发语句
3、 触发器限制
4、 触发操作
1、 触发器名称
create trigger biufer_employees_department_id
命名习惯:
biufer(before insert update for each row)
employees 表名
department_id 列名
2、 触发语句
比如:
表或视图上的DML语句
DDL语句
数据库关闭或启动,startup shutdown 等等
before insert or update
of department_id
on employees
referencing old as old_value
new as new_value
for each row
说明:
1、 无论是否规定了department_id ,对employees表进行insert的时候
2、 对employees表的department_id列进行update的时候
3、 触发器限制
when (new_value.department_id<>80 )
限制不是必须的。此例表示如果列department_id不等于80的时候,触发器就会执行。
其中的new_value是代表更新之后的值。
4、 触发操作
是触发器的主体
begin
:new_value.commission_pct :=0;
end;
主体很简单,就是将更新后的commission_pct列置为0
触发:
insert into employees(employee_id,
last_name,first_name,hire_date,job_id,email,department_id,salary,commission_pct )
values( 12345,’Chen’,’Donny’, sysdate, 12,
‘donny@hotmail.com’,60,10000,.25);
select commission_pct from employees where employee_id=12345;
触发器不会通知用户,便改变了用户的输入值。
触发器类型:
1、 语句触发器
2、 行触发器
3、 INSTEAD OF 触发器
4、 系统条件触发器
5、 用户事件触发器
1、 语句触发器
是在表上或者某些情况下的视图上执行的特定语句或者语句组上的触发器。能够与INSERT、UPDATE、DELETE或者组合上进行关联。但是无论使用什么样的组合,各个语句触发器都只会针对指定语句激活一次。比如,无论update多少行,也只会调用一次update语句触发器。
例子:
需要对在表上进行DML操作的用户进行安全检查,看是否具有合适的特权。
Create table foo(a number);
Create trigger biud_foo
Before insert or update or delete
On foo
Begin
If user not in (‘DONNY’) then
Raise_application_error(-20001, ‘You don’t have
access to modify this table.’);
End if;
End;
/
即使SYS,SYSTEM用户也不能修改foo表
[试验]
对修改表的时间、人物进行日志记录。
1、 建立试验表
create table employees_copy as select *from hr.employees
2、 建立日志表
create table employees_log(
who varchar2(30),
when date);
3、 在employees_copy表上建立语句触发器,在触发器中填充employees_log 表。
Create or replace trigger biud_employee_copy
Before insert or update or delete
On employees_copy
Begin
Insert into employees_log(
Who,when)
Values( user, sysdate);
End;
/
4、 测试
update employees_copy set salary= salary*1.1;
select *from employess_log;
5、 确定是哪个语句起作用?
即是INSERT/UPDATE/DELETE中的哪一个触发了触发器?
可以在触发器中使用INSERTING / UPDATING / DELETING 条件谓词,作判断:
begin
if inserting then
-----
elsif updating then
-----
elsif deleting then
------
end if;
end;
if updating(‘COL1’) or updating(‘COL2’) then
------
end if;
[试验]
1、 修改日志表
alter table employees_log
add (action varchar2(20));
2、 修改触发器,以便记录语句类型。
Create or replace trigger biud_employee_copy
Before insert or update or delete
On employees_copy
Declare
L_action employees_log.action%type;
Begin
if inserting then
l_action:=’Insert’;
elsif updating then
l_action:=’Update’;
elsif deleting then
l_action:=’Delete’;
else
raise_application_error(-20001,’You should never ever get this error.’);
Insert into employees_log(
Who,action,when)
Values( user, l_action,sysdate);
End;
/
3、 测试
insert into employees_copy( employee_id, last_name, email, hire_date, job_id)
values(12345,’Chen’,’Donny@hotmail’,sysdate,12);
select *from employees_log
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1711633
posted @
2008-05-27 15:22 xzc 阅读(894) |
评论 (1) |
编辑 收藏
下面就是例子程序
--明细表打印予处理 通用报表:
procedure mx_print_common(pd_id in mx_pd_syn.pd_id%type,
p_pd_mxb_id IN mx_pd_mxb_syn.p_mxb_id%type,
p_dept_no IN sc_mxk.dept_code%type,
p1 sc_bz_syn.bz_code%type,
p2 sc_cjjc_syn.cjjc_code%type,
p3 sc_mxk.warehouse_num%type)
is
sql2 varchar2(500); --存储查询语句
sql3 varchar2(500); --存储查询条件
str1 sc_print_syn.a%type; --存储车间进程
str2 sc_print_syn.b%type; --存储班组(工艺、工序)进程
s_ip sc_print_syn.ip%type;
type cursor_type is ref cursor;
c1 cursor_type;
type record_type is record(
pbom_id sc_mxk.pbom_id%type
);
r_c1 record_type;
/*
注意上面红色的两行和蓝色的两行
红色的两行定义一个游标
蓝色的两行定义一个游标中将要返回的数据的数据结构
*/
cursor c2(p_pbom_id sc_mxk.pbom_id%type) is
select a.dd_count,b.gx_name,c.bz_name,d.cjjc_name
from sc_p_gx_syn a,sc_gx_syn b,sc_bz_syn c,sc_cjjc_syn d
where pbom_id = p_pbom_id
and a.gx_code=b.gx_code(+) and b.dept_code=p_dept_no
and a.bz_code=c.bz_code(+) and b.dept_code=p_dept_no
and a.cjjc_code=d.cjjc_code(+) and b.dept_code=p_dept_no;
r_c2 c2%rowtype;
BEGIN
s_ip :=sys_context('USERENV','IP_ADDRESS');
delete from sc_print_syn where ip=s_ip and p_id=pd_id;
commit;
--下面开始构造查询语句
sql2:='select distinct a.pbom_id from sc_mxk a';
sql3:=' where a.p_id=' || pd_id || ' and a.dept_code= ''' || p_dept_no || '''';
if p_pd_mxb_id >0 then
sql2:=sql3 || ',mxk c ';
sql3:=sql3 || ' and c.m_mxb_id= ' || p_pd_mxb_id || ' and a.mxb_id = c.mxb_id';
end if;
if p1 is not null then
sql2:=sql2 || ',sc_p_gx_syn b';
sql3:=sql3 || ' and a.pbom_id=b.pbom_id and b.bz_code = ''' || p1 || '''';
end if;
if p2 is not null then
sql2:=sql2 || ',sc_p_gx_syn b';
sql3:=sql3 || ' and a.pbom_id=b.pbom_id and b.cjjc_code = ''' || p2 || '''';
end if;
if p3 is not null then
sql3:=sql3 || ' and a.warehouse_num = ''' || p3 || '''';
end if;
sql2:=sql2 || sql3;
--打开动态游标,再往下就都一样了
open c1 for sql2;
loop
fetch c1 into r_c1;
exit when c1%notfound;
str1:='';
str2:='';
--打开工序表进行处理
open c2(r_c1.pbom_id);
loop
fetch c2 into r_c2;
exit when c2%notfound; --没有记录退出
if r_c2.cjjc_name is not null then
str1 :=str1 || to_char(r_c2.cjjc_name);
end if;
if r_c2.bz_name is not null then
str2 := str2 || r_c2.bz_name || to_char(r_c2.dd_count);
elsif r_c2.gx_name is not null then
str2 := str2 || to_char(r_c2.gx_name) || to_char(r_c2.dd_count);
end if;
end loop;
close c2;
insert into sc_print_syn(a,b,ip,p_id,r_id)
values(str1,str2,s_ip,pd_id,r_c1.pbom_id);
COMMIT;
end loop;
close c1;
END mx_print_common;
当然,实现的方法一定很多,甚至可以用隐式游标。但是隐式游标中用动态查询语句也要费一些周折的。
作者:Northsnow
电子邮件:northsnow@163.com
blog:http://blog.csdn.net/precipitant
posted @
2008-05-27 09:17 xzc 阅读(10537) |
评论 (3) |
编辑 收藏
Log4j有三个主要的组件:Loggers,Appenders和Layouts,这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松的记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置。下面对三个组件分别进行说明:
1、 Loggers
Loggers组件在此系统中被分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,明白这一点很重要,这里Log4j有一个规则:假设Loggers级别为P,如果在Loggers中发生了一个级别Q比P高,则可以启动,否则屏蔽掉。
Java程序举例来说:
//建立Logger的一个实例,命名为“com.foo”
Logger logger = Logger.getLogger("com.foo");
//设置logger的级别。通常不在程序中设置logger的级别。一般在配置文件中设置。
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
//下面这个请求可用,因为WARN >= INFO
logger.warn("Low fuel level.");
//下面这个请求不可用,因为DEBUG < INFO
logger.debug("Starting search for nearest gas station.");
//命名为“com.foo.bar”的实例barlogger会继承实例“com.foo”的级别。因此,下面这个请求可用,因为INFO >= INFO
barlogger.info("Located nearest gas station.");
//下面这个请求不可用,因为DEBUG < INFO
barlogger.debug("Exiting gas station search");
这里“是否可用”的意思是能否输出Logger信息。
在对Logger实例进行命名时,没有限制,可以取任意自己感兴趣的名字。一般情况下建议以类的所在位置来命名Logger实例,这是目前来讲比较有效的Logger命名方式。这样可以使得每个类建立自己的日志信息,便于管理。比如:
static Logger logger = Logger.getLogger(ClientWithLog4j.class.getName());
2、 Appenders
禁用与使用日志请求只是Log4j其中的一个小小的地方,Log4j日志系统允许把日志输出到不同的地方,如控制台(Console)、文件(Files)、根据天数或者文件大小产生新的文件、以流的形式发送到其它地方等等。
其语法表示为:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置时使用方式为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
这样就为日志的输出提供了相当大的便利。
3、 Layouts
有时用户希望根据自己的喜好格式化自己的日志输出。Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供了四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式等等。
其语法表示为:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
配置时使用方式为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
以上是从原理方面说明Log4j的使用方法,在具体Java编程使用Log4j可以参照以下示例:
1、 建立Logger实例:
语法表示:public static Logger getLogger( String name)
实际使用:static Logger logger = Logger.getLogger (ServerWithLog4j.class.getName ()) ;
2、 读取配置文件:
获得了Logger的实例之后,接下来将配置Log4j使用环境:
语法表示:
BasicConfigurator.configure():自动快速地使用缺省Log4j环境。
PropertyConfigurator.configure(String configFilename):读取使用Java的特性文件编写的配置文件。
DOMConfigurator.configure(String filename):读取XML形式的配置文件。
实际使用:PropertyConfigurator.configure("ServerWithLog4j.properties");
3、 插入日志信息
完成了以上连个步骤以后,下面就可以按日志的不同级别插入到你要记录日志的任何地方了。
语法表示:
Logger.debug(Object message);
Logger.info(Object message);
Logger.warn(Object message);
Logger.error(Object message);
实际使用:logger.info("ServerSocket before accept: " + server);
在实际编程时,要使Log4j真正在系统中运行事先还要对配置文件进行定义。定义步骤就是对Logger、Appender及Layout的分别使用,具体如下:
1、 配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
这里level指Logger的优先级,appenderName是日志信息的输出地,可以同时指定多个输出地。如:log4j.rootLogger= INFO,A1,A2
2、 配置日志信息输出目的地,其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
可以指定上面所述五个目的地中的一个。
3、 配置日志信息的格式,其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
这里上面三个步骤是对前面Log4j组件说明的一个简化;下面给出一个具体配置例子,在程序中可以参照执行:
log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=
%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
这里需要说明的就是日志信息格式中几个符号所代表的含义:
-X号: X信息输出时左对齐;
%p: 日志信息级别
%d{}: 日志信息产生时间
%c: 日志信息所在地(类名)
%m: 产生的日志具体信息
%n: 输出日志信息换行
根据上面的日志格式,某一个程序的输出结果如下:
0 INFO 2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT
posted @
2008-05-21 10:36 xzc 阅读(425) |
评论 (0) |
编辑 收藏
在
项目的开发过程中,系统对日志的要求很细,而且要求分类清楚。所以还是采用了Log4J。
在强调可重用组件开发的今天,除了自己从头到尾开发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j。
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
此外,通过Log4j其他语言接口,您可以在C、php、C++、.Net、PL/SQL程序中使用Log4j,其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。
Log4j配置文件详细说明(*.properties和*.xml)
属性文件Properties
properties属性文件
编号 配置项 配置项描述 示例
1 log4j.threshold 阈值项
log4j.threshold = error
2 log4j.rootLogger 根日志属性项
log4j.rootLogger = info,stdout1,stdout2
3 log4j.category. 子日志属性项(旧)
log4j.category.com.eos = NULL,stdout1
4 log4j.logger. 子日志属性项(新)
log4j.logger.com.eos.log = debug,stdout2
5 log4j.additivity. appender是否继承设置
log4j.additivity.com.eos = false
6 log4j.appender. 输出目的地定义项
log4j.appender.stdout2 = org.apache.log4j.ConsoleAppender
7 log4j.appender.A.layout 输出格式定义项
log4j.appender.stdout2.layout = org.apache.log4j.PatternLayout
xml文件
编号 配置项 配置项描述 示例
1 threshold 阈值项
2 root 根日志属性项
3 priority 级别项(旧)
4 level 级别项(新)
5 category 子日志属性项(旧)
6 logger 子日志属性项(新)
7 appender-ref 输出端控制项
8 additivity appender是否继承设置
9 appender 输出目的地定义项
10 layout 输出格式定义项
详细说明(只针对Log4j常用的,用户可以自定义)Appender
Appender继承关系
Appender基本种类
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
· ConsoleAppender选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
Target=System.err:默认情况下是:System.out,指定输出控制台
· FileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
· DailyRollingFileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
DatePattern='.'yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。当然也可以指定按月、周、
天、时和分。即对应的格式如下:
1)'.'yyyy-MM: 每月
2)'.'yyyy-ww: 每周
3)'.'yyyy-MM-dd: 每天
4)'.'yyyy-MM-dd-a: 每天两次
5)'.'yyyy-MM-dd-HH: 每小时
6)'.'yyyy-MM-dd-HH-mm: 每分钟
n RollingFileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来
的内容移到mylog.log.1文件。
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。
详细说明(只针对Log4j,用户可以自定义)Layout
Log4j的Layout基本种类
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
· HTMLLayout选项
LocationInfo=true:默认值是false,输出java文件名称和行号
Title=my app file: 默认值是 Log4J Log Messages.
n PatternLayout 选项
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
这里需要说明的就是日志信息格式中几个符号所代表的含义:
%X: 信息输出时左对齐;
%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%r: 输出自应用启动到输出该log信息耗费的毫秒数
%c: 输出日志信息所属的类目,通常就是所在类的全名
%t: 输出产生该日志事件的线程名
%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行,可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
1)%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
2)%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
3)%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
4)%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符, 就从左边交远销出的字符截掉。
· XMLLayout 选项
LocationInfo=true:默认值是false,输出java文件和行号
日志配置文件内容范例
log4j.properties
#注意:在属性配置文件中,属性值的第一个一定是级别,输出端可有可无,以逗号分割。(而xml文件格式没有这种限制)
log4j.xml
- < xml version="1.0" encoding="UTF-8" >
- <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
- <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false" threshold="null">
-
-
- <appender class="org.apache.log4j.ConsoleAppender" name="CONSOLE">
- <param name="Target" value="System.out"/>
- <param name="Threshold" value="INFO"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- <filter class="org.apache.log4j.varia.DenyAllFilter"/>
- <errorHandler class="org.apache.log4j.varia. FallbackErrorHandler"/>
- </appender>
-
- <appender class="org.apache.log4j.FileAppender" name="FILE">
- <param name="File" value="file.log"/>
- <param name="Append" value="false"/>
- <param name="Threshold" value="INFO"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- </appender>
-
- <appender class="org.apache.log4j.RollingFileAppender" name="ROLLING_FILE">
- <param name="Threshold" value="INFO"/>
- <param name="File" value="rolling.log"/>
- <param name="Append" value="false"/>
- <param name="MaxFileSize" value="10KB"/>
- <param name="MaxBackupIndex" value="1"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- </appender>
-
- <logger additivity="false" name="com.eos">
- <level value="info"/>
- <appender-ref ref="CONSOLE"/>
- </logger>
-
- <category additivity="true" name="com.eos.log">
- <priority value="warn"/>
- </category>
-
- <root>
- <priority value="info"/>
- <appender-ref ref="CONSOLE"/>
- </root>
- </log4j:configuration>
posted @
2008-05-21 10:35 xzc 阅读(3983) |
评论 (1) |
编辑 收藏