版权声明:本篇文章由IT_CREATE整理 https://blog.csdn.net/IT_CREATE/article/details/85329664
首先说明:用一个要在进行增删改操作的时候进行日志信息添加的操作来介绍AOP
1、首先需要在spring配置文件applicationContext.xml中进行相应的配置
配置<aop:aspectj-autoproxy/>这样一句话
<?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:p="http://www.springframework.org/schema/p"
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-3.0.xsd
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">
<!-- 开启spring容器的自动扫描功能 -->
<context:component-scan base-package="com.gezhi.springaop"></context:component-scan>
<!-- 开启切面的动态代理支持 -->
<aop:aspectj-autoproxy/>
</beans>
2、编写AOP类(切入点定位在某个方法上)这个不是日志操作代码,只是介绍怎样编写
1)该类使用@Aspect注解声明为一个切面对象,同时使用@Component注解将这个类丢进spring容器
2)写一个方法作为切入点,在该方法上写上@Pointcut注解定义切入点在哪
3)设置通知方式,写一个方法,在方法上写上@Before这种类型的通知注解,需要哪些视情况而定。
/**
* 该类主要完成切面功能的演示
* @Aspect 表示将一个Java类定义为一个切面
* 一个切面就是一个业务交叉功能的处理面
* @author Administrator
*
*/
//@Component
//@Aspect
public class DemoAspectJ {
/**
* @Pointcut制定切入点 切面关注对象的标准
*
* execution()主要用来定义 切面上的通知(代码),什么时候可以开启执行
* 语法格式:execution(访问修饰符? 返回类型 包结构.类结构? 方法名(参数列表) 抛出的异常?)
*
* ?代表可以省略
*
* * 表示通配 ..表示0-N个参数
*
*/
@Pointcut("execution(* com.gezhi.springaop.*mag.service.impl.*ServiceImpl.*(..))")
public void pointcut() {}
/**
* 前置通知
* @param point 固定参数 连接点(代表的是:切面 和 正在执行的目标方法 取得连接的一个点)
*/
@Before("pointcut()")
public void beforeAdvice(JoinPoint point) {
Object obj = point.getTarget();//获得目标对象(代理对象)
String method = point.getSignature().getName();//获取正在执行的目标方法
Object[] params = point.getArgs();//获得目标方法获取到的参数
System.out.println("目标对象是" + obj);
System.out.println("目标方法是" + method);
System.out.println("获取到的参数是" + Arrays.toString(params));
//通过反射取得目标对象的类对象
Class<?> cls = obj.getClass();
try {
Field f = cls.getDeclaredField("encoding");
f.setAccessible(true);//设置私有属性更改权限
f.set(obj, "utf-8");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我是前置通知,我执行在" + method + "执行之前!");
}
/**
* @AfterReturning 定义后置返回通知,该通知执行在目标方法 正常执行之后
* @param point 连接点
* @param obj 返回对象
*/
@AfterReturning(pointcut="pointcut()",returning="ret")
public void afterReturningAdvice(JoinPoint point,Object ret) {
String method = point.getSignature().getName();//获取正在执行的目标方法
System.out.println("后置返回通知,我执行在"+method+"正常执行之后,我收到的返回是:" + ret);
}
/**
* @AfterThrowing 定义后置异常通知,该通知执行在目标方法 抛出异常之后
* @param point
* @param e
*/
@AfterThrowing(pointcut="pointcut()",throwing="e")
public void afterThrowingAdvice(JoinPoint point,Exception e) {
System.out.println("我是后置异常通知,我接收的异常是:" + e);
}
/**
* @After 定义后置返回通知 ,该通知对象,不管目标方法是否正常执行成功,该通知都要执行
* 该通知执行在@AfterThrowing || @AfterReturning 之前
* @param point
*/
@After("pointcut()")
public void afterAdvice(JoinPoint point) {
System.out.println("我是后置返回通知,我执行在目标方法返回结果或异常之前!");
}
@Around("pointcut()")
public Object aroundAdive(ProceedingJoinPoint point) {
Object obj = point.getTarget();//获得目标对象(代理对象)
String method = point.getSignature().getName();//获取正在执行的目标方法
Object[] params = point.getArgs();//获得目标方法获取到的参数
params[0] = new UserBean();//可以修改参数
Object ret = null;
try {
ret = point.proceed(params);//还可以控制目标方法的执行
ret = new UserBean();//可以修改返回
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ret;
}
}
2、编写AOP类(切入点定位在某个注解上)这个是关于日志操作的
下面是定位到@MyLog注解上
@Component
@Aspect
public class OptLogAspectJ {
@Resource
private IOptLogService optLogServiceImpl;
@Pointcut("@annotation(com.ali.springaop.anno.MyLog)")
public void pointcut() {}
/**
* @annotation(mylog) 表示将@MyLog 对象作为参数,传递到mylog形参中,使用&& @annotation(mylog)的方式可以更简单的去获取注解的内容
* @param point
* @param mylog
* @param ret
*/
@AfterReturning(pointcut="pointcut() && @annotation(mylog)",returning="ret")
public void afterReturningAdvice(JoinPoint point,MyLog mylog, Object ret) {
Object[] args = point.getArgs();//获得目标方法的参数
OptLogBean log = new OptLogBean();//实例化日志javabean
//从注解中获取注解属性的值
log.setMenuName(mylog.value());
log.setOptType(mylog.type().getValue());
log.setOptTime(new Date());
log.setUserName("");
log.setOptData(Arrays.toString(args));
//日志信息添加
optLogServiceImpl.addOptLogBean(log);
}
}
那么在方法上就要有相应的注解:
/**
* @Service 表示该Java类是一个需要被spring容器管理起来的业务层的组件
* 默认情况下,spring容器扫描到该组件之后,将会将该类的类名 “首字母小写后的字符串”,作为该组件在容器中的ID
* 当然你也可以通过@Service("sb")这种方式去改
* @author Administrator
*
*/
@Service
public class UserServiceImpl implements IUserService {
/**
* @Autowired 代表自动装配,只不过它默认采用的装配方式是byType
* @Qualifier 代表精准装配,改byType为byName
*/
// @Autowired
// @Qualifier("userDaoImpl3")
/**
* @Resource 约== @Autowired + @Qualifier
* 默认情况下,@Resource将先按照byName装配方式进行精准装配,如果装配失败,将回退到byType装配方式
*
* 如果你指定了name="userDaoImpl3" ,那么将严格按照byName的装配方式,不会回退
*/
@Resource
private IUserDao userDaoImpl;
@MyLog(value="用户管理",type=LogEnum.ADD)
@Override
public UserBean saveUserBean(UserBean user) {
// TODO Auto-generated method stub
return userDaoImpl.addUserBean(user);;
}
@MyLog(value="用户管理",type=LogEnum.ADD)
@Override
public int addBatchUserBean(List<UserBean> users) {
// TODO Auto-generated method stub
return userDaoImpl.addBatchUserBean(users);
}
@MyLog(value="用户管理",type=LogEnum.UPDATE)
@Override
public int updateUserBean(UserBean user) {
// TODO Auto-generated method stub
return userDaoImpl.updateUserBean(user);
}
@MyLog(value="用户管理",type=LogEnum.DELETE)
@Override
public int deleteUserBean(UserBean user) {
// TODO Auto-generated method stub
return userDaoImpl.deleteUserBean(user);
}
@MyLog(value="用户管理",type=LogEnum.DELETE)
@Override
public int deleteBatchUserBean(int[] ids) {
// TODO Auto-generated method stub
return userDaoImpl.deleteBatchUserBean(ids);
}
@MyLog(value="用户管理",type=LogEnum.DELETE)
@Override
public int deleteUserBean(Integer id) {
// TODO Auto-generated method stub
return userDaoImpl.deleteUserBean(id);
}
@Override
public UserBean getUserBeanById(Integer id) {
// TODO Auto-generated method stub
return userDaoImpl.getUserBeanById(id);
}
@Override
public Map<String, Object> queryUserBeanById(Integer id) {
// TODO Auto-generated method stub
return userDaoImpl.queryUserBeanById(id);
}
@Override
public List<Map<String, Object>> findUserBeanMapByObject(UserBean user) {
// TODO Auto-generated method stub
return userDaoImpl.findUserBeanMapByObject(user);
}
@Override
public UserBean findUserBeanByLoginNameAndPwd(String loginName, String pwd) {
// TODO Auto-generated method stub
return userDaoImpl.findUserBeanByLoginNameAndPwd(loginName, pwd);
}
@Override
public List<UserBean> findUserBeanByObject(UserBean user) {
// TODO Auto-generated method stub
return userDaoImpl.findUserBeanByObject(user);
}
@Override
public List<UserBean> findUserBeanByMap(Map map) {
// TODO Auto-generated method stub
return userDaoImpl.findUserBeanByMap(map);
}
@Override
public PageBean findUserBeanList2PageBean(PageBean page, UserBean user) {
// TODO Auto-generated method stub
int totalRows = userDaoImpl.countUserBeanList2PageBean(user);
List<?> datas = null;
if(totalRows > 0) {
datas = userDaoImpl.findUserBeanList(page, user);
}
page.setTotalRows(totalRows);
page.setData(datas);
return page;
}
}
这个注解是自己写的,如下:
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
/**
* value 代表着 具体的操作模块
* @return
*/
String value() default "";
/**
* 操作类型 0- 新增,1-修改,2-删除
* @return
*/
LogEnum type();
}
LogEnum枚举也是自己写的,如下:
public enum LogEnum {
ADD(0),UPDATE(1),DELETE(2);
private int value;
private LogEnum(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}