前置通知
在目标方法前切入追加功能。采用@Before,方法定义格式如下:
@Before("切入点表达式")
public void xxx(){...}
后置通知
在目标方法后切入追加功能,如果目标方法抛异常不会切入功能。采用@AfterReturning
@AfterReturning("切入点表达式")
public void xxx(Object retval){...}
最终通知
在目标方法后切入追加功能,目标方法有无异常都会切入功能。采用@After
@After("切入点表达式")
public void xxx(){...}
异常通知
在目标方法抛出异常后切入追加功能。采用@AfterThrowing
@AfterThrowing(...)
public void xxx(Exception e){}
环绕通知
在目标方法前和后切入追加功能。采用@Around
public Object xxx(ProccedingJoinPoint pjp){
//前面切入
Object obj = pjp.proceed();//调用目标方法
//后面切入
return obj;
}
需求:当服务调用中发生异常,将异常记录到日志文件中。
切面
将异常信息记录到日志文件FileWriter、PrintWriter或log4j
切入点
给所有controller方法within(cn.xdl.controller..*)
通知
发生异常后,异常通知 @AfterThrowing
实现过程
编写切面组件,追加异常通知方法
@Component
@Aspect
public class ExceptionBean {
@AfterThrowing(throwing="ex",pointcut="within(cn.xdl.controller..*)")
public void handle(Exception ex){
try {
FileWriter fw = new FileWriter("E:\\spring_07_error.log", true);
PrintWriter out = new PrintWriter(fw);
out.println("-----------发生了异常------------");
out.println("-异常类型:"+ex);
out.println("-发生时间:"+new Date());
out.println("-异常详情:");
StackTraceElement[] stes = ex.getStackTrace();
for(StackTraceElement s : stes){
if(s.toString().contains("cn.xdl")){
out.println(s.toString());
}
}
out.flush();
out.close();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
追加@Component、@Aspect、@AfterThrowing标记
原理:Spring容器配置AOP,容器返回的BookContorller对象实际是一个动态代理技术生成子类对象(CGLIB技术)。动态代理类将BookController对象所有方法重写,在重写时加入BookController功能和切入新功能(切面组件)。
扩展:CGLIB技术使用方法(了解)
根据现有组件创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());//将bean类型做父类
enhancer.setCallback(this);//注册回调函数
return enhancer.create();
每次调用代理对象方法,都会采用回调机制调用原组件方法,可以在回调方法中做扩展操作
public class CglibProxyFactoryBean implements MethodInterceptor{
public Object intercept(
Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
//切入目标方法前面执行的逻辑
System.out.println("回首2017");
Object retval = method.invoke(target, args);//调用目标组件对象的method方法
//切入目标方法后面执行的逻辑
System.out.println("展望2018");
return retval;
}
}
MyBatis前身是iBatis.MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
Mybatis框架可以简化数据库访问操作,对以前JDBC技术做了封装操作。主要封装功能如下:
搭建mybatis环境
sqlmap-config.xml代码如下:
编写实体类User.java
public class User implements Serializable{
private Integer id;
private String login_name;
private String nick_name;
private String real_name;
private Integer grade_id;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLogin_name() {
return login_name;
}
public void setLogin_name(String login_name) {
this.login_name = login_name;
}
public String getNick_name() {
return nick_name;
}
public void setNick_name(String nick_name) {
this.nick_name = nick_name;
}
public String getReal_name() {
return real_name;
}
public void setReal_name(String real_name) {
this.real_name = real_name;
}
public Integer getGrade_id() {
return grade_id;
}
public void setGrade_id(Integer grade_id) {
this.grade_id = grade_id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
编写SQL定义文件UserMapper.xml
delete from xdl_user where id=#{id}
insert into xdl_user
(ID,LOGIN_NAME,NICK_NAME,REAL_NAME,GRADE_ID,PASSWORD)
values
(#{id},#{login_name},#{nick_name},#{real_name},#{grade_id},#{password})
update xdl_user set nick_name=#{nick} where id=#{id}
获取SqlSession对象
public class MyBatisUtil {
public static SqlSession openSession() throws IOException{
//SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//SqlSessionFactory
//加载了主配置文件中的连接参数和sql定义文件信息
Reader reader = Resources.getResourceAsReader("sqlmap-config.xml");
SqlSessionFactory factory = builder.build(reader);
//SqlSession
SqlSession sqlSession = factory.openSession();
return sqlSession;
}
}
使用sqlSession调用sql语句
public class TestUser {
@Test
public void test1() throws IOException{
SqlSession sqlSession = MyBatisUtil.openSession();
List list = sqlSession.selectList("user.findAll");
for(User user:list){
System.out.println(user.getId()+" "
+user.getLogin_name()+" "+user.getNick_name());
}
sqlSession.close();
}
@Test
public void test2() throws IOException{
SqlSession sqlSession = MyBatisUtil.openSession();
User user = new User();
user.setId(1);
user.setLogin_name("tom");
user.setNick_name("tom");
user.setPassword("123");
user.setReal_name("同名");
user.setGrade_id(1);
sqlSession.insert("user.save",user);
sqlSession.commit();
sqlSession.close();
}
@Test
public void test3() throws IOException{
SqlSession sqlSession = MyBatisUtil.openSession();
int rows = sqlSession.delete("user.delete",1);
System.out.println("删除记录数:"+rows);
sqlSession.commit();
sqlSession.close();
}
@Test
public void test4() throws IOException{
SqlSession sqlSession = MyBatisUtil.openSession();
User user = sqlSession.selectOne("user.findById",37);
if(user != null){
System.out.println(user.getId()+" "+user.getLogin_name());
}else{
System.out.println("未找到记录");
}
sqlSession.close();
}
@Test
public void test5() throws IOException{
SqlSession sqlSession = MyBatisUtil.openSession();
Map map = new HashMap();
map.put("id", 37);//#{id}
map.put("nick", "SPRING");//#{nick}
sqlSession.update("user.updateNickname",map);
sqlSession.commit();
sqlSession.close();
}
}
定义类型别名
在sqlmap-config.xml中定义< typeAliases>
这样在定义SQL时,parameterType或resultType属性可以指定为user,简化类型定义.
无效列类型:111
原因是:SQL参数使用#{}时,遇到null值。
解决方法:#{key,jdbcType=xxx}
insert into xdl_user
(ID,LOGIN_NAME,NICK_NAME,REAL_NAME,GRADE_ID,PASSWORD)
values
(#{id},
#{login_name,jdbcType=VARCHAR},
#{nick_name,jdbcType=VARCHAR},
#{real_name,jdbcType=VARCHAR},
#{grade_id,jdbcType=NUMERIC},
#{password,jdbcType=VARCHAR})
参考下表:
Mapper映射器
Mapper映射器是一个接口规范,按规则定义接口后,MyBatis可以自动生成接口的实现对象。(原理:动态代理技术JDK Proxy)
规则如下:
例如SQL定义
对应的接口方法
public User findById(int id);