spring關於面向切面程式設計AOP的操作
阿新 • • 發佈:2019-01-03
首先說明:用一個要在進行增刪改操作的時候進行日誌資訊新增的操作來介紹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;
}
}