下载最新稳定版
https://repo.spring.io/release/org/springframework/spring/
1 2 3 4 5 6 7 8 |
<!--配置User 对象创建--> <bean id="user" class="org.alanhou.spring5.User"></bean> //1.加载 Spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //2. 获取配置创建的对象 User user = context.getBean("user", User.class); |
IOC
IOC 控制反转,降低耦合度,把对象创建和对象调用都交给 Spring
IOC 底层原理:XML 解析、工厂模式、反射
Spring 提供 IOC容器的两种实现方式(接口):
- BeanFactory:一般由Spring 内部使用;加载配置文件时不创建对象,使用时创建
- ApplicationContext:BeanFactory的子接口,一般由开发人员使用;加载配置文件时即创建对象
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
Bean管理
- Spring 创建对象
Bean 属性:id(唯一标识)、class(类全路径)、name(标识,可加特殊符号,不常用) - Spring 注入属性
DI 依赖注入,一种注入方式是使用 set 方法,另一种是使用配置文件
12345678<bean id="book" class="org.alanhou.spring5.Book"><!--使用 property 完成属性注入name: 属性名称value: 注入的属性值--><property name="author" value="Conan Doyle"></property><property name="bname" value="Sherlock Holmes"></property></bean>
此外还有有参构造
123456<!--有参构造创建--><bean id="order" class="org.alanhou.spring5.Order"><constructor-arg name="oname" value="iPad"></constructor-arg><constructor-arg name="address" value="Beijing"></constructor-arg><!--<constructor-arg index="0" value=""></constructor-arg>--></bean>
p 名称空间注入,简化xml 配置
12345<beans ...xmlns:p="http://www.springframework.org/schema/p"...><bean id="book" class="org.alanhou.spring5.Book" p:bname="Sherlock Holmes" p:author="Conan Doyle"></bean></beans>
Bean管理XML方式
XML 注入其它类型属性
1 2 3 4 5 6 7 |
<!--设置空值--> <property name="address"> <null/> </property> <property name="address"> <value><![CDATA[<<南京>>]]></value> </property> |
外部 bean
1 2 3 4 5 |
<bean name="userService" class="org.alanhou.spring5.service.UserService"> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean name="userDaoImpl" class="org.alanhou.spring5.dao.UserDaoImpl"></bean> |
内部 bean 和级联赋值
1 2 3 4 5 6 7 8 9 10 11 |
<bean id="emp" class="org.alanhou.spring5.bean.Emp"> <property name="ename" value="张三"></property> <property name="gender" value="男"></property> <property name="dept"> <bean id="dept" class="org.alanhou.spring5.bean.Dept"> <property name="dname" value="财务部"></property> </bean> </property> <!--使用此方式要求在 emp 中要有 dept 的 get 方法--> <!--<property name="dept.dname" value="技术部"></property>--> </bean> |
XML注入集合属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 1.数组类型属性 private String[] courses; // 2.list 集合属性 private List<String> list; // 3. map集合类型属性 private Map<String, String> map; //4 .set 集合类型属性 private Set<String> set; <bean id="stu" class="org.alanhou.spring5.collectiontype.Stu"> <!--数组类型属性注入--> <property name="courses"> <array> <value>Java</value> <value>MySQL</value> </array> </property> <!--list 类型属性注入--> <property name="list"> <list> <value>Jackson</value> <value>Jack</value> </list> </property> <!--map 类型属性注入--> <property name="map"> <map> <entry key="JAVA" value="java"></entry> <entry key="PYTHON" value="python"></entry> </map> </property> <!--set 类型属性注入--> <property name="set"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean> |
注入对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<bean id="stu" class="org.alanhou.spring5.collectiontype.Stu"> .... <!--注入 list 集合类型,值是对象--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean> <!--创建多个 course对象--> <bean id="course1" class="org.alanhou.spring5.collectiontype.Course"> <property name="cname" value="Spring 5"></property> </bean> <bean id="course2" class="org.alanhou.spring5.collectiontype.Course"> <property name="cname" value="MyBatis"></property> </bean> |
集合类型提取和注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<beans ... xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="... http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!--提取list集合类型属性注入--> <util:list id="listList"> <value>Jackson</value> <value>Jack</value> </util:list> <bean id="stu" class="org.alanhou.spring5.collectiontype.Stu"> ... <!--提取list集合类型属性注入的使用--> <property name="list" ref="listList"></property> ... </bean> </beans> |
Spring 有两种 Bean,一种是普通Bean,另一种是 FactoryBean;普通Bean 配置文件中所定义类型(class 属性)即为返回类型,工厂 Bean 可以为不同(通过实现FactoryBean接口并在 getObject 方法中定义,如下例中对 MyBean 返回 Course对象)。
1 2 3 4 5 6 7 8 9 10 11 |
public class MyBean implements FactoryBean<Course> { // 定义返回 bean @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("MySQL"); return course; } ... } |
Bean 的作用域:Bean 可以创建单实例或多实例,默认为单实例,通过 scope 进行设置,单实例为 singleton,多实例为 prototype
1 |
<bean id="book" class="org.alanhou.spring5.Book" scope="prototype"> |
单实例、多实例通过实例化多个对象打印地址进行比对
Bean 的生命周期:
- 通过构造创建Bean 实例(无参构造)
- 对 Bean 的属性设置值或对其它 Bean 的引用
- 调用 Bean 的初始化方法(自行配置:xml 中的init-method属性)
- 使用 Bean
- 关闭容器时,调用销毁方法(自行配置:xml 中的destroy-method属性,并且需要执行 context.close()方法)
第3步Bean 的初始化前(postProcessBeforeInitialization)后(postProcessAfterInitialization)分别有一个后置处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } <bean id="myBeanPost" class="org.alanhou.spring5.bean.MyBeanPost"></bean> |
XML自动装配
根据属性名称(byName)或属性类型(byType)由 Spring 自动匹配进行注入(xml中 bean 标签添加 autowire 属性)
外部属性文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!--外部属性文件jdbc.properties--> prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/userDb prop.userName=root prop.password=root <!--bean 配置文件--> <beans ... xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="... http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean> </beans> |
Bean管理注解方式
格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
- @Component
- @Service
- @Controller
- @Repository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<beans ... xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="... http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启组件扫描 扫描多个包: 1、可使用逗号分隔 2、扫描包所在的上层目录 --> <context:component-scan base-package="org.alanhou.spring5.service,org.alanhou.spring5.dao"></context:component-scan> </beans> // 以下注解等价于<bean id="userService"... // 下面的 value 定义可以省略,默认为类名首字母小写 @Component(value = "userService") public class UserService { ... } |
通过设置use-default-filters为 false 可不使用默认的 filter(全部导入),如下例中只读取 Controller 注解的类,对应的还有context:exclude-filter用于排除规则
1 2 3 |
<context:component-scan base-package="org.alanhou.spring5" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter> </context:component-scan> |
基于注解方式实现属性注入
- @Autowired:根据属性类型进行自动装配
123456789101112131415161718192021//UserDaoImpl.java@Repositorypublic class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("dao add...");}}//UserService.java@Servicepublic class UserService {@Autowiredprivate UserDao userDao;public void add(){System.out.println("add...");userDao.add();}} - @Qualifier:根据属性名称进行自动注入
1234// 在多个实现类或使用了非默认的注解的 value 值时则需指定名称@Autowired@Qualifier(value = "userDaoImpl")private UserDao userDao; - @Resource:可以根据类型,也可以根据名称进行注入
直接使用@Resource 为按类型注入,@Resource(name = “userDaoImpl”)为按名称注入;这种方式由 JAVA 扩展包所提供,因此 Spring 官方更推荐使用上面两种 - @Value 注入普通类型属性
12@Value(value = "Jack")private String name;
完全注解开发
即不使用 XML 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 配置类,如SpringConfig.class @Configuration @ComponentScan(basePackages = {"org.alanhou"}) public class SpringConfig { } // 测试类写法 @Test public void testService(){ ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); ... } |
AOP
面向切面编程,对业务逻辑各个部分进行隔离,降低耦合度,提高程序可重用性。
AOP底层使用动态代理:有接口时使用 JDK 动态代理,无接口使用 CGLIB 动态代理。
- JDK 动态代理
java.lang.reflect.Proxy: newProxyInstance,有三个参数:- 类加载器
- 增强方法所在类实现的接口,支持多个接口
- 实现接口的 InvocationHandler,创建代理对象、增强的方法
1234567891011121314151617181920212223242526272829303132333435363738394041424344public class JDKProxy {public static void main(String[] args) {//创建接口实现类代理对象Class[] interfaces = {UserDao.class};/*// 匿名内部类实现方式Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}});*/UserDaoImpl userDao = new UserDaoImpl();UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));int result = dao.add(1, 2);System.out.println("result: "+result);}}// 创建代理对象代码class UserDaoProxy implements InvocationHandler{// 1、把所创建代理的对象传递过来,可使用有参构造private Object obj;public UserDaoProxy(Object obj){this.obj = obj;}// 增强的逻辑@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法之前System.out.println("方法之前执行..."+method.getName()+":传递的参数..."+ Arrays.toString(args));// 被增强方法Object res = method.invoke(obj, args);// 方法之后System.out.println("方法之后执行..."+obj);return res;}} - CGLIB 动态代理
AOP术语
- 连接点:类中可以被增强的方法
- 切入点:实际被增强了的方法
- 通知(增强):实际增强的逻辑部分,有前置通知(Before)、后置通知(AfterReturning)、环绕通知(Around)、异常通知(AfterThrowing)和最终通知(After)
- 切面:把通知应用到切入点的过程
在 Spring 框架中基于 AspectJ 实现 AOP 操作,可基于 XML配置文件实现,也可基于注解方式实现,通常使用后者。
切入点表达式语法结构:
execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表])),可使用星号 * 表示所有
基于注解方式实现AOP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<beans ... xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="... http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="org.alanhou.spring5.aopannotation"></context:component-scan> <!--开启 Aspect 生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans> @Component @Aspect // 生成代理对象 public class UserProxy { // 相同切入点制取 @Pointcut(value = "execution(* org.alanhou.spring5.aopannotation.User.add(..))") public void pointdemo(){ } @Before(value = "pointdemo()") // 前置通知 // @Before(value = "execution(* org.alanhou.spring5.aopannotation.User.add(..))") System.out.println("before..."); } ... @Around(value = "execution(* org.alanhou.spring5.aopannotation.User.add(..))") // 环绕通知 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕之前..."); proceedingJoinPoint.proceed(); System.out.println("环绕之后..."); } } // 测试类 public class testAop { @Test public void testAopAnnotation(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user = context.getBean("user", User.class); user.add(); } } |
有多个增强类对同一方法进行增强时,可使用@Order(数字类型值)设置优化级
完全使用注解开发
1 2 3 4 5 6 |
@Configuration @ComponentScan(basePackages = {"org.alanhou"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop{ } |
基于 XML配置文件实现AOP
例创建类 Book,包含方法 buy,增强类 BookProxy 包含前置方法 before(名称可自定义),配置文件示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<beans ...> <!--创建对象--> <bean id="book" class="org.alanhou.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="org.alanhou.spring5.aopxml.BookProxy"></bean> <!--配置 AOP增强--> <aop:config> <!--切入点--> <aop:pointcut id="p" expression="execution(* org.alanhou.spring5.aopxml.Book.buy(..))" /> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增强所作用的方法--> <aop:before method="before" pointcut-ref="p" /> </aop:aspect> </aop:config> </beans> |
JdbcTemplate
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!--组件扫描--> <context:component-scan base-package="org.alanhou"></context:component-scan> <!--数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="jdbc:mysql:///xxx" /> <property name="username" value="root" /> <property name="password" value="root" /> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> </bean> <!--JdbcTemplate 对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入 dataSource--> <property name="dataSource" ref="dataSource"></property> </bean> |
Service
1 2 3 4 5 6 7 8 9 10 11 |
@Service public class BookService { // 注入 Dao @Autowired private BookDao bookDao; // 添加方法 public void addBook(Book book){ bookDao.add(book); } } |
Dao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// BookDao public interface BookDao { // 添加方法 void add(Book book); } // BookDaoImpl @Repository public class BookDaoImpl implements BookDao { // 注入 JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; // 添加方法,修改、删除方法实现方式类似 @Override public void add(Book book) { // 创建 sql 语句 String sql = "INSERT INTO t_book VALUES(?,?,?)"; // 调用方法实现 Object[] args = {book.getBookId(), book.getBookname(), book.getStatus()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } } |
Entity
1 2 3 4 5 6 |
public class Book { private String bookId; private String bookname; private String status; // getter, setter 方法 } |
Test
1 2 3 4 5 6 7 8 9 10 |
@Test public void testJdbcTemplate(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); BookService bookService = context.getBean("bookService", BookService.class); Book book = new Book(); book.setBookId("1"); book.setBookname("Java"); book.setStatus("a"); bookService.addBook(book); } |
查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 查询表中的记录数 @Override public int selectCount() { String sql = "SELECT count(*) FROM t_book"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); return count; } // 查询返回对象 @Override public Book findBookInfo(String id) { String sql = "SELECT * FROM t_book WHERE book_id=?"; Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class),id); return book; } // 查询返回集合 @Override public List<Book> findAllBooks() { String sql = "SELECT * FROM t_book"; List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class)); return books; } |
批量操作batchUpdate
事务操作
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
Spring 实现事务:
- 编程式事务管理
- 声明式事务管理(常用)
- 注解(常用)
- XML
注解声明式事务管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<beans ... xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="... http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> ... <!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans> |
@Transational 注解可添加到类上,也可添加到方法上
1 2 3 4 |
@Service @Transactional public class UserService { ... |
声明式事务管理@Transational的主要参数
- propagation:事务传播行为,即多事务之间方法调用如何管理事务
传播行为 含义 REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行,默认传播行为 REQUIRED_NEW 当前方法必须启动新事务,并在自己的事务内运行,如果有事务正在运行,则将它挂起 SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中 NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就会抛出异常 NEVER 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常 NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。 - isolation:事务隔离级别
脏读、不可重复读、虚读/幻读,MySQL中的默认隔离级别为可重复读
隔离级别 脏读 不可重复读 幻读 READ UNCOMMITED
(读未提交)有 有 有 READ COMMITED
(读已提交)无 有 有 REPEATABLE READ
(可重复读)无 无 有 SERIALIZABLE
(串行化)无 无 无 - timeout:超时时间,默认值-1,不超时,单位为秒
- readOnly:是否只读
- rollbackFor:回滚
- noRollbackFor:不回滚
XML 声明式事务管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置通知--> <tx:advice id="txadvice"> <!--配置事务参数--> <tx:attributes> <!--指定的哪种规则的方法上添加事务--> <tx:method name="account*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* org.alanhou.spring5.service.UserService.*(..))" /> <!--配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt" /> </aop:config> |
完全注解声明式事务开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// 配置类 @Configuration @ComponentScan(basePackages = "org.alanhou") @EnableTransactionManagement public class TxConfig { // 创建数据库连接池 @Bean public DruidDataSource getDruidDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///test"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } // 创建 JdbcTemplate 对象 @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(); // 注入 dataSource jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } // 创建事务管理器 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } } // 测试方法 @Test public void testAccount2(){ ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.transferMoney(); } |
Spring5新功能
参见https://cntofu.com/book/95/33-what-new-in-the-spring-framework.md
- 整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除
- Spring 5.0框架自带了通用的日志封装;Spring5已经移除Log4jConfigListener,官方建议使用Log4j2
- 核心容器支持@Nullable注解
- 核心容器支持函数式风格GenericApplicationContext
log4j2.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出--> <configuration status="INFO"> <!--先定义所有的appender--> <appenders> <!--输出日志信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出--> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration> |
手动输出日志
1 2 3 4 5 6 7 8 9 10 |
public class UserLog { private static final Logger log = LoggerFactory.getLogger(UserLog.class); public static void main(String[] args) { log.info("INFO 日志测试"); log.warn("WARN 日志测试"); log.error("ERROR 日志测试"); } } |
测试
1 2 3 4 5 6 7 8 9 |
// JUnit4 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:bean2.xml") // JUnit5 @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:bean2.xml") 或 @SpringJUnitConfig(locations = "classpath:bean2.xml") |