0
点赞
收藏
分享

微信扫一扫

SpringAOP案例

钟罗敏 2022-05-01 阅读 45

文章目录

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

异常信息收集【异常消息】

在业务方法执行时,如果有异常抛出,则根据异常信息记录日志

在这里插入图片描述
log4j.properties文件

log4j.rootLogger = info,stdout,D,E

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%m%n

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = H://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =H://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern =%-d{yyyy-MM-dd HH\:mm\:ss} [ %t\:%r ] - [ %p ] %m%n

统计业务方法执行的时间

统计所有业务方法执行时的耗时
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

通过aop进行权限控制

在这里插入图片描述

自定义注解

在这里插入图片描述

import cn.tedu.enums.Role;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义注解---保留到运行时期,只能在方法上出现
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Priv {
    Role[] value();
}

枚举类(创建角色)

在这里插入图片描述

//代表角色
public enum Role {
    //游客,使用者,管理员
    VISITOR,USER,ADMIN;
}

Web层

在这里插入图片描述

import cn.tedu.enums.Role;
import cn.tedu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserServlet {
    /**
     * 当前角色
     * */
    public static Role role=Role.ADMIN;
    @Autowired
    private UserService userService=null;
    public void test(){
        userService.updateUser();
    }
}

service层

在这里插入图片描述

public interface UserService {
    public void addUser();
    public void updateUser();
    public void queryUser();
    public void delUser();
}

在这里插入图片描述

import cn.tedu.anno.Priv;
import cn.tedu.enums.Role;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

    @Override
    public void addUser() {
        System.out.println("新增用户");
    }
    @Priv({Role.USER,Role.ADMIN})
    @Override
    public void updateUser() {
        System.out.println("修改用户");
    }
    @Priv({Role.USER,Role.ADMIN})
    @Override
    public void queryUser() {
        System.out.println("查询用户");
    }
    @Priv(Role.ADMIN)
    @Override
    public void delUser() {
        System.out.println("删除用户");
    }
}

切面类【方法一】

在这里插入图片描述

import cn.tedu.anno.Priv;
import cn.tedu.enums.Role;
import cn.tedu.web.UserServlet;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Component
@Aspect
public class PrivAspect {
    // 方法一:
    @Around("execution(* cn.tedu.service..*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标方法
        MethodSignature m = (MethodSignature) pjp.getSignature();
        Method infmethod = m.getMethod();//这个获取到的是UserService接口中的方法   而目标方法应该在UserServicImpl类里的方法
        //获取目标对象---UserServicImpl
        Object target = pjp.getTarget();
        Class<?> clz = target.getClass();
        //获取目标对象类中目标方法(目标方法和之前获取的UserService接口中的方法的方法签名一致)
        //infmethod.getName():获取方法名
        //infmethod.getParameterTypes() :获取所有的参数
        Method insMethod = clz.getMethod(infmethod.getName(), infmethod.getParameterTypes());

        //判断目标方法身上是否有指定注解---@Priv
        if (insMethod.isAnnotationPresent(Priv.class)) {
            //获取用户角色
            Role role = UserServlet.role;
            //获取目标方法身上需要角色
            Priv priv = insMethod.getAnnotation(Priv.class);
            Role[] roles = priv.value();
            //检查是否匹配
            //把数组转成List集合,调用集合中contains来判断集合中是否有包含指定内容
            if (Arrays.asList(roles).contains(role)) {
                //匹配执行目标方法
                return pjp.proceed();
            } else {
                //不匹配就抛出异常
                throw new RuntimeException("权限不足,无法访问!!!");
            }
        } else {
            //目标方法上没有注解直接执行目标方法
            return pjp.proceed();
        }
    }
} 

切面类【方法二】

在这里插入图片描述

    // 方法二:
    //符合切入点表达式的连接点的方法并且要求方法上面有@Priv类型的注解
    @Around("execution(* cn.tedu.service..*(..))&&@annotation(p)")
    public Object around(ProceedingJoinPoint pjp, Priv p) throws Throwable {
        //进了环绕通知表明是符号要求的切入点的方法并且此方法的上面有@Priv类型的注解
        //获取用户角色
        Role role = UserServlet.role;
        //获取方法需要的角色
        Role[] roles = p.value();
        //检查是否匹配
        //把数组转成List集合,调用集合中contains来判断集合中是否有包含指定内容
        if (Arrays.asList(roles).contains(role)) {
            //匹配执行目标方法
            return pjp.proceed();
        } else {
            //不匹配就抛出异常
            throw new RuntimeException("权限不足,无法访问!!!");
        }
    }

配置文件【applicationContext.xml】

在这里插入图片描述

<?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"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置包扫描-->
    <context:component-scan base-package="cn.tedu"/>
    <!--注解方式的DI-->
    <context:annotation-config/>
    <!--注解方式的AOP-->
    <aop:aspectj-autoproxy/>
</beans>

测试类

在这里插入图片描述

import cn.tedu.web.UserServlet;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
    @Test
    public void test01(){
        //容器初始化
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        UserServlet userServlet = context.getBean(UserServlet.class);
        userServlet.test();
        //容器关闭
        ((ClassPathXmlApplicationContext)context).close();
    }
}

实现事务控制

在这里插入图片描述

配置文件

在这里插入图片描述

    <context:component-scan base-package="cn.tedu"/>
    <!--配置注解方式的DI-->
    <context:annotation-config/>
    <!--配置注解方式的AOP-->
    <aop:aspectj-autoproxy/>

User类

在这里插入图片描述

public class User {
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

工具类 事务管理器

在这里插入图片描述

import java.sql.Connection;
import java.sql.SQLException;

//工具类---事务管理器
public class TransactionMnanager {
    private TransactionMnanager(){}

   /* private static Connection conn;

    static {
        try {
            conn = C3P0Utiles.getConnection();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }*/

    //可以直接把当前线程对象当作键
    private static ThreadLocal<Connection> tl=new ThreadLocal<Connection>(){
        //给定匿名内部类来继承ThreadLocal去重写initialValue()这个方法来给定初始值
        @Override
        protected Connection initialValue() {
            try {
                //给定的初始值就是数据库连接对象
                return C3P0Utiles.getConnection();
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                throw new RuntimeException();
            }
        }
    };

    /*
    * 开启事务
    * */
    public static void startTran(){
        try {
            //conn.setAutoCommit(false);
            tl.get().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    /*

    提交事务*/
    public static void commitTran(){
        try {
            //conn.commit();
            tl.get().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }


    /*
    * 回滚事务
    *
    * */
    public static void rollbackTran(){
        try {
            //conn.rollback();
            tl.get().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    /*
    *
    * 获取连接
    * */
    public static Connection getConn(){
        //return conn;
        return tl.get();
    }


    /*
    * 释放资源
    *
    * */
    public static void release(){
        //C3P0Utiles.close(conn,null,null);
        C3P0Utiles.close(tl.get(),null,null);
        tl.remove();
    }
}

工具类 c3p0类

在这里插入图片描述

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

//配合c3p0连接池使用的工具类
public class C3P0Utiles {
    //私有化构造方法
    private C3P0Utiles() {
    }

    //创建c3p0连接池对象(全局统一)
    //配置文件名称为c3p0才能默认去读取配置文件的内容
    private static ComboPooledDataSource source = new ComboPooledDataSource();

    //定义静态方法---注册驱动以及获取数据库连接
    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        //获取连接(连接池默认注册数据库驱动)
        return source.getConnection();
    }

    //定义静态方法---关闭资源
    public static void close(Connection conn, Statement stat, ResultSet rs) {
        if (rs != null)
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                rs = null;
            }
        if (stat != null)
            try {
                stat.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                stat = null;
            }
        if (conn != null)
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                conn = null;
            }
    }
}

C3P0配置文件

在这里插入图片描述

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/sdb
c3p0.user=root
c3p0.password=root

Web层

在这里插入图片描述

import cn.tedu.domain.User;
import cn.tedu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserServlet {
    @Autowired
    private UserService userService = null;
    public void addUser(User user) {
        userService.addUser(user);
    }
    public void test() {
        userService.addUser(new User(0, "iii", 8));
    }
}

service层

在这里插入图片描述

public interface UserService {
    public void addUser(User user);
    public void delUser(int id);
}

在这里插入图片描述

package cn.tedu.service;

import cn.tedu.anno.Trans;
import cn.tedu.dao.UserDao;
import cn.tedu.domain.User;
import cn.tedu.utils.TransactionMnanager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao = null;

    @Override
    @Trans
    public void addUser(User user) {
       /* try {
            TransactionMnanager.startTran();//开启事务
            userDao.addUser(user);
            int i = 1 / 0;
            TransactionMnanager.commitTran();//提交事务
        }catch (Exception e){
            TransactionMnanager.rollbackTran();//回滚事务
            e.printStackTrace();
        }finally {
            TransactionMnanager.release();//释放资源
        }*/

        userDao.addUser(user);
        //int i = 1 / 0;
    }

    @Override
    @Trans
    public void delUser(int id) {
        userDao.delUser(id);
    }
}

dao层

在这里插入图片描述

public interface UserDao {
    public void addUser(User user);
    public void delUser(int id);
}

在这里插入图片描述

package cn.tedu.dao;

import cn.tedu.domain.User;
import cn.tedu.utils.C3P0Utiles;
import cn.tedu.utils.TransactionMnanager;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

@Repository
public class UserDaoImpl implements UserDao{
    @Override
    public void addUser(User user) {
        Connection conn=null;
        PreparedStatement ps=null;
        try {
            conn = TransactionMnanager.getConn();
            ps = conn.prepareStatement("insert into user values(null,?,?)");
            ps.setString(1,user.getName());
            ps.setInt(2,user.getAge());
            ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            C3P0Utiles.close(null,ps,null);
        }
    }

    @Override
    public void delUser(int id) {
        Connection conn=null;
        PreparedStatement ps=null;
        try {
            conn=TransactionMnanager.getConn();
            ps = conn.prepareStatement("delete from user where id=?");
            ps.setInt(1,id);
            ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            C3P0Utiles.close(null,ps,null);
        }
    }
}

自定义注解

在这里插入图片描述

//自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trans {
}

切面类

在这里插入图片描述

@Component
@Aspect
public class TransAspect {

    @Around("execution(* cn.tedu.service..*(..))&&@annotation(ta)")
    public Object around(ProceedingJoinPoint pjp, Trans ta) throws Throwable {
        try {
            TransactionMnanager.startTran();//开启事务
            Object proceed = pjp.proceed();//执行目标方法
            TransactionMnanager.commitTran();//提交事务
            return proceed;
        } catch (Throwable throwable) {
            TransactionMnanager.rollbackTran();//回滚事务
            throw throwable;
        }finally {
            TransactionMnanager.release();//释放资源
        }
    }
}

测试类

在这里插入图片描述

import cn.tedu.domain.User;
import cn.tedu.web.UserServlet;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {

    @Test
    public void test01(){
        //容器初始化
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        UserServlet userServlet = context.getBean(UserServlet.class);
        //userServlet.test();
        new Thread(new Runnable() {
            @Override
            public void run() {
                userServlet.addUser(new User(0,"xxx",30));
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                userServlet.addUser(new User(0,"yyy",31));
            }
        }).start();
        while (true);
        //容器关闭
        /*((ClassPathXmlApplicationContext)context).close();*/
    }
}

解释说明

  1. 为了不再Service层使用conn造成耦合,开发TransactionManager专门用于管理事务
  2. 开发切面,实现环绕通知 根据目标方法是否有@Trans决定要不要增强事务控制
  3. 扩展:通过ThreadLocal,保证每个线程各自使用各自的Conn对象
    当在多线程环境下时,所有线程都用统一个conn必然会出现线程安全问题。
    此时可以通过ThreadLocal让每个Thread都具有自己的Conn,保证线程安全。

在这里插入图片描述

举报

相关推荐

SpringAop

SpringAOP

springAOP

SpringAOP学习

【框架】-SpringAOP

SpringAOP总揽

SpringAop实战

SpringAOP原理分析

springAOP增强实现

0 条评论