AOP(Aspect Oriented Programming),即面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件系统开发中的一个热点,也是spring框架的一个重点。利用AOP可以实现业务逻辑各个部分的隔离,从而使得业务逻辑各个部分的耦合性降低,提高程序的可重用性,同时提高开发效率。
AOP原理:
作用:在程序运行期间,不修改源码对已有方法进行增强,扩展功能。
优势:减少重复代码、提高开发效率、维护方便
原理: 动态代理技术
说明:在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
如果目标对象实现接口,使用JDK代理。
目标对象没有实现接口使用CGLIB代理。
动态代理:代理类在程序运行时创建的代理方式被成为动态代理。
JDK动态代理有以下特点:
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们提供目标对象所实现的接口的字节码对象)
动态代理要求目标对象一定要实现接口,所以也叫做接口代理!
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 作用:生成一个代理对象 | |
---|---|
loader | 和目标对象一类的类加载器 |
interfaces | 真实对象所有实现的接口 |
h | 是一个接口,传一个匿名内部类做为实现类,并且重写其中的方法来实现代理的功能 |
返回值 | 返回代理对象 |
Object invoke(Object proxy, Method method, Object[] args) 作用:这个接口中的方法会调用多次,每个方法都会调用一次,用来实现代理方法的功能 | |
---|---|
proxy | 代表生成的代理对象,不建议在方法中直接调用,不然会出现递归调用。 |
method | 代理的方法对象 |
args | 调用方法时传递的参数数组 |
返回 | 返回当前这个方法调用的返回值 |
接口:
interface Service{
//接口
void save();
void update();
void delete();
}
目标对象:
class ServiceImpl implements Service{
//目标对象(被代理对象)
@Override
public void save() {
System.out.println("保存数据");
}
@Override
public void update() {
System.out.println("修改数据");
}
@Override
public void delete() {
System.out.println("删除数据");
}
}
public class Demo {
public static void main(String[] args) throws Exception {
/*
最终返回一个代理对象,此代理对象对目标对象的所有方法都进行了代理
最终执行代理对象的任何方法都会执行InvocationHandler中的invoke方法
*/
Service o = (Service) Proxy.newProxyInstance(
/*
传入目标对象的类加载器
*/
ServiceImpl.class.getClassLoader(),
/*
传入目标对象的所有实现接口的字节码对象
代理对象就是根据此接口字节码对象来指定代理方法,实现动态方法代理
要不然目标对象增加一个方法,动态代理需要手动添加一个代理方法
*/
ServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
//proxy是代理对象
//method是目标对象的方法
//args是目标对象方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强的代码");
/*
因为method.invoke执行的是目标对象的具体方法,因此必须传入目标对象
*/
Object invoke = method.invoke(new ServiceImpl());
System.out.println("增强的代码");
return invoke;
}
}
);
//代理对象执行save方法,最终会执行InvocationHandler中的invoke方法
o.save();
}
}
小结:JDK提供的动态代理是基于接口的,把目标对象所实现的所有接口的字节码对象传入方法中,JDK能在内存中动态的帮我们创建一个对象,该对象实现的目标对象实现的所有方法。即保证目标对象的所有方法代理对象都能代理到
JDK动态代理,要求目标对象一定要实现接口,否则不能用动态代理。
如果目标对象就是没有实现接口,还想对目标对象某个方法进行扩展,怎么办?看CGLIB代理!
动态代理模式要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Spring-Core核心已经包含CGLIB功能。
代码:
Service类:
package com.dfbz.entity;
public class Service {
public void save(){
System.out.println("保存");
}
public void update(){
System.out.println("修改");
}
public void delete(){
System.out.println("删除");
}
}
package com.itdfbz.spring;
import com.dfbz.entity.Service;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Demo1 {
@Test
public void test1() {
//返回一个service的子类
Service serviceProxy = (Service) Enhancer.create(Service.class, new MethodInterceptor() {
/*
*
* proxy:代理对象
* method:要执行的方法对象
* args:要执行的方法的参数
* methodProxy:代理对象的方法对象
* */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("增强的代码");
// Object invoke = method.invoke(new Service());
/*
methodProxy:
代理对象的方法类,因为cglib代理对目标对象进行继承派生出子类,因此代理对象是目标对象的子类
此代理对象方法类可以执行本代理对象方法,也可以执行
*/
Object invoke = methodProxy.invoke(new Service(), args);
System.out.println("增强的代码");
return invoke;
}
});
serviceProxy.save();
}
}
使用CGLIB子类代理:
通过动态代理模式的实现后,我们可以定义AOP其实就是用于通过规则设置来拦截方法,加入可以统一处理的代码。
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
AOP配置后 Spring容器返回的目标对象,实质上是 Spring利用动态代理技术生成一个代理类型。代理类重写了原目标组件方法的功能,在代理类中调用方面对象功能和目标对象功能
Spring框架采用了两种动态代理实现:
利用cglib工具包
目标没有接口时采用CGLIB代理,代理类是利用继承方法生成一个目标子类
利用 JDK Proxy API
目标有接口时采用JDK动态代理,代理类是用实现目标接口方法生成一个类
在程序执行过程中,需要拦截的方法
根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点。如:UserService中的save()方法就是连接点
将通知应用到连接点形成切点的过程
一个类被AOP织入增强后,就产生一个结果代理类
使用spring的aop,需要加入IOC基础框架包和spring的AOP支持包。
aspectj 开源的面向切面编程的组织,SpringAop在此基础上实现的切面编程。
需求:编写一个切面类,在执行save,delete,update方法,分别在方法执行前、方法之后、异常出现后、分别方法执行前后调用编写统一处理的代码,
引入依赖(context依赖中依赖有aop包,所有我们只要引入context依赖就ok):
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
StudentService接口
package com.dfbz.service;
public class UserService {
public void save(){
System.out.println("保存");
}
}
package com.dfbz.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void before(){
System.out.println("前置通知");
}
public void afterReturning(){
System.out.println("后置通知");
}
public Object around(ProceedingJoinPoint around) throws Throwable {
//环绕通知
System.out.println("环绕通知之前");
Object proceed = around.proceed(); //执行目标方法
System.out.println("环绕通知之后");
return proceed; //将目标方法的返回值返回
}
public void after(){
System.out.println("最终通知");
}
public void afterThrowing(){
System.out.println("异常抛出通知");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.dfbz.service.UserService">bean>
<bean id="myAspect" class="com.dfbz.aspect.MyAspect">bean>
<aop:config>
<aop:pointcut id="myPoint" expression="execution(public void com.dfbz.service.UserService.save(..))"/>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut-ref="myPoint"/>
<aop:around method="around" pointcut-ref="myPoint"/>
<aop:after-returning method="afterReturning" pointcut-ref="myPoint"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="myPoint"/>
<aop:after method="after" pointcut-ref="myPoint"/>
aop:aspect>
aop:config>
beans>
package com.dfbz.spring;
import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Demo1 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.save();
}
}
测试结果:
在我们开发中通知顺序应该为:
但是如果在application.xml配置顺序如果不一样则会导致不同的情况,因此必须严格控制在spring配置文件中配置的通知顺序
匹配方法的执行(常用)
execution(表达式)表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
全匹配方式:
public void com.dfbz.service.UserService.save(..)
访问修饰符可以省略
void com.dfbz.service.UserService.save()
返回值可以使用**号,表示任意返回值*
* com.dfbz.service.UserService.save()
包名可以使用*号,表示任意包,但是有几级包,需要写几个
* *.*.*.*.UserService.save()
使用…来表示当前包,及其子包
* com..UserService.save()
方法名匹配UserService的所有方法都能找到(但是方法必须无参)
* com..UserService.*();
方法参数匹配,Userservice所有方法都能找到,不管方法是否有参
* com..UserService.*(..)
类名可以使用 * 号,表示任意类*
* com..*.save()
按类匹配:
被匹配的类中的所有方法作为目标方法,使用within来实现
语法within(包名.类名)
匹配到具体类
匹配到包下的类(不包含子包)
com.dfbz.service.*
找所有的service包下的类匹配到包和子包下的类
within(com..*)
注意所有的该包和子包的类都会切入切面
*:通配的是任何的字符串
… :如果通配包的时候,就是匹配包以及它子包的类,如果通配是方法参数,就是不限定任何参数
作用:aop的核心配置标签
作用:用于配置切面。
属性:
id:给切面提供一个唯一标识。
ref:引用配置好的通知类bean的id。
作用:
用于配置切入点表达式
属性:
expression:用于定义切入点表达式。
id:用于给切入点表达式提供一个唯一标识。
作用:用于配置前置通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
作用:用于配置后置通知,如果出了异常就一定不会调用切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
作用:用于配置异常通知,只有出了异常才会调用切面对应的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
作用:用于配置最终通知,不管出不出异常,调用的切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
作用:用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
引入依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
UserService
package com.dfbz.service;
public class UserService {
public void save(){
System.out.println("保存");
}
}
package com.dfbz.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //标注此类是个注解类
@Component
public class AnnoMyAspect {
@Pointcut("execution(* com.dfbz.service.UserService.save())")
public void pt(){
}
// @Before("execution(* com.dfbz.service.UserService.save())") //单独定义切点
@Before("pt()") //引用切点
public void before(){
System.out.println("前置通知");
}
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("后置通知");
}
@Around("pt()")
public Object around(ProceedingJoinPoint around) throws Throwable {
//环绕通知
System.out.println("环绕通知之前");
Object proceed = around.proceed(); //执行目标方法
System.out.println("环绕通知之后");
return proceed; //将目标方法的返回值返回
}
@After("pt()")
public void after(){
System.out.println("最终通知");
}
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("异常抛出通知");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.dfbz" />
<bean id="userService" class="com.dfbz.service.UserService" />
<aop:aspectj-autoproxy />
beans>
package com.dfbz.spring;
import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Demo1 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.save();
}
}
@Aspect
作用:把当前类声明为切面类。
@Before
作用:把当前方法看成是前置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterReturning
作用:把当前方法看成是后置通知。报异常,就不执行
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterThrowing
作用:把当前方法看成是异常通知。只有报异常才执行
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@After
作用:把当前方法看成是最终通知。不管报不报异常都执行
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Around
作用:把当前方法看成是环绕通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Pointcut
作用:指定切入点表达式
属性:
value:指定表达式的内容
使用配置类来替换配置文件
package com.dfbz.app;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration //标注此类是个
@ComponentScan("com.dfbz") //包扫描
@EnableAspectJAutoProxy //开启aop注解支持
public class Application {
}
package com.dfbz.spring;
import com.dfbz.app.Application;
import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class)
public class Demo2 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.save();
}
}
A.导入log4j的jar包和配置文件
B.设置配置文件的日志输出配置
C.在spring的切面组件类中实现记录日志功能
log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
在实际应用中,要使Log4j在系统中运行须事先设定配置文件。配置文件事实上也就是对Logger、Appender及Layout进行相应设定。Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties属性文件。下面以properties属性文件为例介绍log4j.properties的配置。
配置根Logger:
log4j.rootLogger = [ level ] , appenderName1, appenderName2, …
level :设定日志记录的最低级别,可设的值有OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别,Log4j建议只使用中间四个级别。通过在这里设定级别,您可以控制应用程序中相应级别的日志信息的开关,比如在这里设定了INFO级别,则应用程序中所有DEBUG级别的日志信息将不会被打印出来。
ERROR、WARN、INFO、DEBUG
ERROR 为严重错误 主要是程序的错误
WARN 为一般警告,比如session丢失
INFO 为一般要显示的信息,比如登录登出
DEBUG 为程序的调试信息
appenderName:就是指定日志信息要输出到哪里。可以同时指定多个输出目的地,用逗号隔开。
例如:log4j.rootLogger=INFO,A1,B2,C3
log4j.appender.appenderName = appender类全限定名
appender类全限定名可以取值如下:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
控制台ConsoleAppender选项
Threshold=DEBUG:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
Target=System.err:默认情况下是:System.out,指定输出控制台
FileAppender 选项
Threshold=DEBUF:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=D:/logs/mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
RollingFileAppender 选项
Threshold=DEBUG:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来的内容移到mylog.log.1文件。
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
DailyRollingFileAppender选项:
Threshold=WARN:指定日志信息的最低输出级别,默认为DEBUG。
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true。
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认值是true。
File=D:/logs/logging.log4j:指定当前消息输出到logging.log4j文件中。
DatePattern=’.'yyyy-MM:每月滚动一次日志文件,即每月产生一个新的日志文件。当前月的日志文件名为logging.log4j,前一个月的日志文件名为logging.log4j.yyyy-MM。
另外,也可以指定按周、天、时、分等来滚动日志文件,对应的格式如下:
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:每分钟
日志信息格式中几个符号所代表的含义:(了解)
-X号: 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"输出日志信息换行
可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。
如:
%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。
练习:
将当前程序添加日志系统,将info级别的信息输出到e盘,并且以info.log命名,并同时以网页形式输出到e盘,以info.html命名
配置步骤
把log4j-*.*jar引入项目中
编写log4j.properties文件,并放入src/main/resources下
Logger调用
在每一个要产生日志的类上面加上这句,MonitorMessageController为该类的名字。
private final Logger log= Logger.getLogger(MonitorMessageController.class);
在需要记录日志信息的位置:
log.info(“日志信息”):记录info级别信息
log.error(“日志信息”):记录error级别信息
O8601,也可以在其后指定格式,比如:%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"输出日志信息换行
可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。
如:
%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。
练习:
将当前程序添加日志系统,将info级别的信息输出到e盘,并且以info.log命名,并同时以网页形式输出到e盘,以info.html命名
配置步骤
把log4j-*.*jar引入项目中
编写log4j.properties文件,并放入src/main/resources下
Logger调用
在每一个要产生日志的类上面加上这句,MonitorMessageController为该类的名字。
private final Logger log= Logger.getLogger(MonitorMessageController.class);
在需要记录日志信息的位置:
log.info(“日志信息”):记录info级别信息
log.error(“日志信息”):记录error级别信息
通过以上的配置,即可在运行的过程中在日志输出。