如题,如果想在AOP中拦截某个具体子类中继承自父类的方法,需要按照如下两种方式完成
public abstract class GenericDao<T> {
protected int insert(T entity)throws Exception{
System.out.println("插入记录==" + entity.getClass().getSimpleName());
return 0;
}
protected int deleteById()throws Exception{
System.out.println("删除了数据=");
return 0;
}
}
@Repository
public class DeptDao extends GenericDao<DeptInfo> {
@Override
public int deleteById() throws Exception {
return super.deleteById();
}
}
@Aspect
@Component
public class SubClassInterCeptor {
@After(value = "execution(* com.example.aopdemo.dao.DeptDao.deleteById(..))")
public void afterDeptDelete(){
System.out.println("进入dept后置拦截");
}
}
注意:com.example.aopdemo.dao.DeptDao.deleteById ,表示拦截具体子类DeptDao对应的方法
@Repository
public class UserDao extends GenericDao<UserInfo> {
}
@Aspect
@Component
public class SubClassInterceptor {
@After(value = "execution(* com.example.aopdemo.dao.GenericDao+.insert(..)) && target(com.example.aopdemo.dao.UserDao)")
public void afterUserInsert(JoinPoint joinPoint){
System.out.println("进入user后置拦截");
}
}
关键点:
com.example.aopdemo.dao.GenericDao+.insert(…)) GenericDao+,表示拦截GenericDao及其子类
target(com.example.aopdemo.dao.UserDao) target表示具体执行目标类,必须为类的全限定名
@Aspect
@Component
public class SubClassInterceptor {
@After(value = "execution(* com.example.aopdemo.dao.GenericDao+.insert(..)) && target(bean)")
public void afterUserInsert(JoinPoint joinPoint, UserDao bean){
System.out.println("进入user后置拦截");
}
}
在Spring项目开发过程中,经常会遇到各种基类(super class),比如各种GenericDao ,BaseDao,模板模式类
中定义的方法,这些基类有若干个子类实现,若想通过AOP方式关注某个具体子类执行某个基类方法的执行,
则需要采取正确的姿势来定义pointcut,否则会无法正确拦截,因为Spring AOP 默认忽略继承自父类的方法
近日接到产品一个需求,需要在用户注册完成后向其推荐人发送小程序推送消息,由于用户注册服务早已开发测试完毕,且用户注册来自多个渠道,但是最终都会调用BaseDao中的insert方法进行入库,直接覆盖insert方法添加发送逻辑,在此处耦合了非insert本身的逻辑,于是便想到了采用AOP方式,关注user insert执行,基于执行结果判定是否需要发送消息,而且对业务无侵入,快速插拔。于是乎coding走起
-编写AOP代码
@Aspect
@Component
public class SubClassInterceptor {
@After(value = "execution(* com.example.aopdemo.dao.UserDao.insert(..))")
public void afterUserInsert(JoinPoint joinPoint){
System.out.println("进入user后置拦截");
}
}
由于只关注user的 insert方法执行,想当然配置了
@After(value = “execution(* com.example.aopdemo.dao.UserDao.insert(…))”)
兴奋的跑起测试用例
@SpringBootTest
class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
void testInsert(){
try {
userDao.insert(new UserInfo());
} catch (Exception e) {
e.printStackTrace();
}
}
}
然而并没有如期看到 “进入user后置拦截”的控制台输出,蒙圈中…明明是执行的userDao.insert方法啊
于是乎找出Spring Framework Reference Documentation ,查看 11.2 @AspectJ support ,定位到
Declaring a pointcut
• execution - for matching method execution join points, this is the primary pointcut designator you will
use when working with Spring AOP
• within - limits matching to join points within certain types (simply the execution of a method declared
within a matching type when using Spring AOP)
• this - limits matching to join points (the execution of methods when using Spring AOP) where the bean
reference (Spring AOP proxy) is an instance of the given type
• target - limits matching to join points (the execution of methods when using Spring AOP) where the
target object (application object being proxied) is an instance of the given type
• args - limits matching to join points (the execution of methods when using Spring AOP) where the
arguments are instances of the given types
平时开发过程中用到的最多的可能是execution ,这里
target 用于限定执行目标方法的对象的类型
官方示例寻找端倪
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code and bean
}
英文不太好,于是乎参考
链接: Spring AOP 所有切入点指示符详解(execution,within,this,target,args,@within,@target,@args,@annotation).
总结出以上解决方案,若您有更好的解决方案欢迎留言哦~
public class UserInfo {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public class DeptInfo {
private Long id;
private String deptName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public abstract class GenericDao<T> {
protected int insert(T entity)throws Exception{
System.out.println("插入记录==" + entity.getClass().getSimpleName());
return 0;
}
protected int deleteById()throws Exception{
System.out.println("删除了数据=");
return 0;
}
}
@Repository
public class UserDao extends GenericDao<UserInfo> {
}
@Repository
public class DeptDao extends GenericDao<DeptInfo> {
@Override
public int deleteById() throws Exception {
return super.deleteById();
}
}
@Aspect
@Component
public class SubClassInterceptor {
/**
* 用户信息插入后执行的业务逻辑
* @param joinPoint
* @param bean
*/
//@After(value = "execution(* com.example.aopdemo.dao.GenericDao+.insert(..)) && target(com.example.aopdemo.dao.UserDao)")
@After(value = "execution(* com.example.aopdemo.dao.GenericDao+.insert(..)) && target(bean)")
public void afterUserInsert(JoinPoint joinPoint, UserDao bean){
System.out.println("进入user后置拦截");
}
/**
* 只关注 dept删除方法执行
*/
@After(value = "execution(* com.example.aopdemo.dao.DeptDao.deleteById(..))")
public void afterDeptDelete(JoinPoint joinPoint){
System.out.println("进入dept后置拦截");
}
}
@SpringBootTest
class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
void testInsert(){
try {
userDao.insert(new UserInfo());
} catch (Exception e) {
e.printStackTrace();
}
}
}
@SpringBootTest
class DeptDaoTest {
@Autowired
private DeptDao deptDao;
@Test
void testDeptInsert(){
try {
deptDao.insert(new DeptInfo());
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testDeptDelete(){
try {
deptDao.deleteById();
} catch (Exception e) {
e.printStackTrace();
}
}
}