一.注解使用
1.value 注解
如果配置文件中没有 demo.num 配置属性,启动时就会报错,spring 加载不到此属性值。
@value("${demo.num}")
如果配置文件中没有 demo.num 配置属性,取默认值 100。
@value("${demo.num:100}")
二.日志打印
SpringBoot 中 使用 [info] 日志级别打印 mybatis sql 语句
在 Spring Cloud 项目中,生产环境需要打印 mybatis 的 sql 语句日志,但是 mybatis 打印的 sql 的默认日志级别是 [debug],如果生产环境想看到 sql 语句,就必须开启[debug] 级别的日志打印,这样做 debug 日志量过大,显然不可行。
解决思路
Spring Boot 中通过 logback 打印 mybatis 的 sql 语句日志,并自定义日志输出实现将 sql 语句 [debug] 日志级别上升到 [info] 日志级别
解决方案
1.日志原理
常用的 mybatis 日志输出是由 org.apache.ibatis.logging.stdout.StdOutImpl 控制的
根据 StdOutImpl.java 可看出日志都是 System.out.println(s); 的控制台输出,配置及源码如下
#application.xml
mybatis:
type-aliases-package: com.jiafupeng.mapper
mapper-locations: classpath:mapper*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台输出日志
public class StdOutImpl implements Log {
public StdOutImpl(String clazz) {
}
@Override
public boolean isDebugEnabled() {
return true;
}
@Override
public boolean isTraceEnabled() {
return true;
}
@Override
public void error(String s, Throwable e) {
System.err.println(s);
e.printStackTrace(System.err);
}
@Override
public void error(String s) {
System.err.println(s);
}
@Override
public void debug(String s) {
System.out.println(s);
}
@Override
public void trace(String s) {
System.out.println(s);
}
@Override
public void warn(String s) {
System.out.println(s);
}
}
2.自定义 log
要想改变 mybatis sql 语句输出内容级别,则只需自定义 Log 实现类,重写 mybatis sql 打印方式及级别。代码如下
#application.xml
mybatis:
type-aliases-package: com.jiafupeng.mapper
mapper-locations: classpath:mapper*.xml
configuration:
log-impl: com.jiafupeng.util.MySlf4jImpl # mybatis自定义日志输出实现类 并将[debug]日志输出成[info]日志
@Slf4j
public class MySlf4jImpl implements Log {
public MySlf4jImpl(String clazz) {
}
@Override
public boolean isDebugEnabled() {
return log.isInfoEnabled();
}
@Override
public void debug(String s) {
log.info(s);
}
}
3.配置 log 输出
指定 logback 的日志级别为 info,也可在 [info] 级别日志中查看 mybatis 的 sql 语句。
#logback.xml
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="FILE-INFO"/>
</root>
<!-- 如果想将mybatis-sql[info]日志单独输出到一个文件中,就加上如下配置 -->
<!-- <logger name="com.jiafupeng.util.MySlf4jImpl" level="info" additivity="false">-->
<!-- <appender-ref ref="FILE-SQL"/>-->
<!-- </logger>-->
4.设置不打印
生产环境如果不想打印 mybatis sql 则注释掉打印实现类即可,或者使用 NoLoggingImpl.java 作为实现类,具体看源码。
mybatis:
type-aliases-package: com.jiafupeng.mapper
mapper-locations: classpath:mapper*.xml
或
mybatis:
type-aliases-package: com.jiafupeng.mapper
mapper-locations: classpath:mapper*.xml
configuration:
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl # 无日志(默认有debug日志)
三.xml 配置
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--注入user类-->
<bean id="user" class="com.kwan.spring5.User"></bean>
<!--spring方式: set方法注入属性-->
<bean id="book" class="com.kwan.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="Hello"></property>
<property name="bauthor" value="World"></property>
</bean>
<!--(2)spring方式:有参数构造注入属性-->
<bean id="orders" class="com.kwan.spring5.Orders">
<constructor-arg name="oname" value="Hello"></constructor-arg>
<constructor-arg name="address" value="China!"></constructor-arg>
</bean>
</beans>
public class Spring_01_BookTest {
@Test
public void test1() {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring1.xml");
Book book = (Book) ctx.getBean("book");
System.out.println(book);
}
}
四.常见问题
1.事务问题
1.1.事务失效
插入数据
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestDao dao;
@Override
//@Transactional
public void insert() {
this.save();
//throw new RuntimeException();
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save() {
dao.insert("test");
dao.update();
throw new RuntimeException("aaa");
}
}
1.2.未插入数据
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestDao dao;
@Override
@Transactional
public void insert() {
this.save();
//throw new RuntimeException();
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save() {
dao.insert("test");
dao.update();
throw new RuntimeException("aaa");
}
}
1.3.未插入数据
save 方法的事务被 insert 方法的事务一起接管了
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestDao dao;
@Override
@Transactional
public void insert() {
this.save();
throw new RuntimeException();
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save() {
dao.insert("test");
dao.update();
//throw new RuntimeException("aaa");
}
}
2.@Nullable
@Nullable 注解可以使用在方法、属性、参数上,分别表示方法返回可以为空、属性值可以为空、参数值可以为空。
3.Alias标签
在bean标签里边有一个alias属性和name属性,可以指定bean的别名,但是有的场景下,在定义bean的时候就把他的别名都指定好是不适用的.
比如这个Bean在组件A中,想把他叫做componentA,但是在组件B中又想把他叫做componetB,所以还有一个单独的标签:< alias>专门解决上述场景的.
<bean id="myBean" class="com.itpluto.MyBean"></bean>
<alias name="myBean" alias="componetA">
<alias name="myBean" alias="componetB">
4.Import标签
import的使用场景主要是因为项目比较大,配置文件非常多的时候,会进行分模块,这个时候就可以用到import这个标签了.
- 获取resource属性的值
解析路径中的系统属性,比如:${user.dir} - 根据resource的值来判断是绝对路径还是相对路径
- 如果是绝对路径直接加载解析文件即可
- 如果是相对路径要先计算出绝对路径
- 通知监听器,解析完成.
<import resource="customerContext.xml" />
<import resource="systemContext.xml" />
5.读取xml
1.ApplicationContext
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) ctx.getBean("user");
2.BeanFactory
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
User user1 = (User) bf.getBean("user");
3.项目根路径
FileSystemXmlApplicationContext fct =
new FileSystemXmlApplicationContext("/src/main/resources/applicationContext.xml");
User user2 = (User) fct.getBean("user");
4.系统根路径
String rootPath = System.getProperty("user.dir");
BeanFactory bf1 = new XmlBeanFactory(
new FileSystemResource(rootPath+"/src/main/resources/applicationContext.xml"));
User user3 = (User) bf1.getBean("user");
5.监听器
<!-- web启动读取applicationContext.xml -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
6.环绕通知
@Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
{
Object[] args = proceedingJoinPoint.getArgs();
Object result=null;
try {
//前置通知@Before
System.out.println("环绕前置通知");
//目标方法执行
result = proceedingJoinPoint.proceed(args);
//环绕返回通知@AfterReturning
System.out.println("环绕返回通知");
} catch (Throwable throwable) {
//环绕异常通知@AfterThrowing
System.out.println("环绕异常通知");
throw new RuntimeException(throwable);
} finally {
//最终通知@After
System.out.println("环绕最终通知");
}
return result;
}
五.特殊工具
1.发邮件工具类
package com.deepexi.bfp.financial.service.domain.bill.service.impl;
import com.deepexi.bfp.financial.service.domain.bill.config.MailPropertiesConfig;
import com.deepexi.bfp.financial.service.domain.bill.service.IMailService;
import com.deepexi.bfp.financial.service.domain.bill.utils.SuperCsvUtil;
import com.deepexi.util.exception.ApplicationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Properties;
@Slf4j
@Service
public class MailServiceImpl implements IMailService {
@Autowired
private MailPropertiesConfig mailConfig;
@Override
public void send(String email, File file) {
try {
Properties props = new Properties();
props.put("mail.smtp.host ", mailConfig.getHost());
props.put("mail.smtp.auth", mailConfig.getAuth());
Session session = Session.getInstance(props);
Message message = new MimeMessage(session);
InternetAddress from = new InternetAddress(mailConfig.getAddress());
message.setFrom(from);
InternetAddress to = new InternetAddress(email);
message.setRecipient(Message.RecipientType.TO, to);
message.setSubject(MimeUtility.encodeText("交易对帐平台"));
message.setSentDate(new Date());
MimeMultipart msgMultipart = new MimeMultipart("mixed");// 指定为混合关系
message.setContent(msgMultipart);
// 邮件内容
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(
"<body>"
+ "<div>"
+ "交易对帐平台,对账差异信息,请查看附件" + "</div></body>",
"text/html;charset=UTF-8");
//组装的顺序非常重要,一定要先组装文本域,再组装文件
msgMultipart.addBodyPart(htmlPart);
// 组装附件
MimeBodyPart mimeBodyPart = new MimeBodyPart();
FileDataSource file_datasource = new FileDataSource(file.getPath());
DataHandler dh = new DataHandler(file_datasource);
mimeBodyPart.setDataHandler(dh);
// 附件区别内嵌内容的一个特点是有文件名,为防止中文乱码要编码
mimeBodyPart.setFileName(MimeUtility.encodeText(dh.getName()));
msgMultipart.addBodyPart(mimeBodyPart);
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect(mailConfig.getHost(), mailConfig.getPort(), mailConfig.getUsername(), mailConfig.getPassword());
transport.sendMessage(message, message.getAllRecipients());
transport.close();
} catch (MessagingException | UnsupportedEncodingException e) {
throw new ApplicationException("发送邮件失败");
}
log.info("工单邮件发送完毕");
SuperCsvUtil.deleteFile(file.getPath());
}
}