精华内容
下载资源
问答
  • spring切面注解失效

    2019-09-12 17:31:06
    在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下: 切面注解: package com.t3.ts.driver.resume.aspect; import java.lang.annotation.*; /...

    在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下:

    切面注解:

    package com.t3.ts.driver.resume.aspect;
    
    import java.lang.annotation.*;
    
    /**
     * @Description: 数据脱敏注解 Filed
     * @Date: 2019/9/10
     * @Author: wm yu
     */
    @Inherited
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EncryptField {
    }
    
    package com.t3.ts.driver.resume.aspect;
    
    import java.lang.annotation.*;
    
    /**
     * @Description: 数据脱敏注解 Method
     * @Date: 2019/9/10
     * @Author: wm yu
     */
    @Inherited
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EncryptMethod {
    }
    

    切面类:

    package com.t3.ts.driver.resume.aspect;
    
    import com.alibaba.fastjson.JSON;
    import com.t3.ts.driver.resume.utils.MD5Util;
    import com.t3.ts.driver.resume.utils.StringUtils;
    import com.t3.ts.driver.resume.utils.excel.FieldReflectionUtil;
    import com.t3.ts.result.PageResult;
    import com.t3.ts.result.Response;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Field;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collectors;
    
    /**
     * @class_name: DecryptAspect
     * @description:
     * @author: wm_yu
     * @create: 2019/09/10
     **/
    @Aspect
    @Component
    @Order(-1)
    public class EncryptAspect {
    
        private final static Logger log = LoggerFactory.getLogger(EncryptAspect.class);
        /**
         * 身份证脱敏正则 保留前后四位
         */
        private final static String IDENTITY_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";
        /**
         *银行卡脱敏正则  保留前后四位
         */
        private final static String BLANK_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";
        /**
         * 手机号脱敏正则 保留前三后四位
         */
        private final static String MOBILE_DESENSITIZATION = "(?<=\\d{3})\\d(?=\\d{4})";
        /**
         * 定义其他字段脱敏长度
         */
        private final static Integer OTHER_DESENSITIZATION_LENGTH = 3;
    
        private final static Integer IDENTITY_CARD_LENGTH_18 = 18;
        private final static Integer IDENTITY_CARD_LENGTH_15 = 15;
        private final static Integer MOBILE_LENGTH = 11;
    
        @Pointcut("@annotation(com.t3.ts.driver.resume.aspect.EncryptMethod)")
        public void pointCut(){}
    
        /**
         * 注明切点
         */
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object responseObj = null;
            try {
                responseObj = joinPoint.proceed();
                 //数据脱敏
                handleEncrypt(responseObj);
            } catch (Throwable throwable) {
               log.error("数据脱敏异常:{}", JSON.toJSONString(responseObj),throwable);
            }
            return responseObj;
        }
    
        /**
         * 处理加密
         *
         * @param responseObj
         */
        private void handleEncrypt(Object responseObj) throws IllegalAccessException {
            if (!Optional.ofNullable(responseObj).isPresent()) {
                return;
            }
            Object var = null;
            if(responseObj instanceof List){
                var = responseObj;
            }else{
                Response response = (Response) responseObj;
                 var = response.getData();
            }
            if(!Optional.ofNullable(var).isPresent()){
                return;
            }
            this.dealDateByType(var);
        }
    
        /**
         * 类型判断处理
         * @param var
         * @throws IllegalAccessException
         */
        private void dealDateByType(Object var) throws IllegalAccessException {
            Field[] fields = {};
            if(var instanceof PageResult){
                //分页列表数据
                PageResult pageResult = (PageResult) var;
                List list = pageResult.getList();
                List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());
                for (Object o : filterList) {
                    fields = FieldReflectionUtil.getAllFields(o.getClass());
                    dealPrecisionField(fields,o);
                }
            }
            if(var instanceof List){
                List list = (List) var;
                List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());
                for (Object o : filterList) {
                    fields = FieldReflectionUtil.getAllFields(o.getClass());
                    dealPrecisionField(fields,o);
                }
            }else{
                //详情页面等 --- 数据加密处理,需要前端配合解密展示
                fields = FieldReflectionUtil.getAllFields(var.getClass());
                dealEncryptField(fields,var);
            }
        }
    
        /**
         * 处理数据加密 前端配合解密处理
         * @param fields
         * @param var
         */
        private void dealEncryptField(Field[] fields,Object var) throws IllegalAccessException {
            for (Field field : fields) {
                if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){
                    continue;
                }
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                Object o = field.get(var);
                if(!Optional.ofNullable(o).isPresent()){
                    continue;
                }
                if(!(o instanceof String)){
                    //递归处理
                    Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());
                    this.dealEncryptField(allFields,o);
                }else{
                     String value = encryptField((String) o);
                    field.set(var,value);
                }
            }
        }
    
        /**
         * 字段加密处理
         * @return
         */
        private String encryptField(String source){
            if(StringUtils.isEmpty(source)){
                return source;
            }
            String encryptValue = MD5Util.MD5Encode(source).toUpperCase();
            return encryptValue;
        }
    
        /**
         * 处理数据脱敏
         * @param fields
         * @param var
         * @throws IllegalAccessException
         */
        private void dealPrecisionField(Field[] fields,Object var) throws IllegalAccessException {
            for (Field field : fields) {
                if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){
                    continue;
                }
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                Object o = field.get(var);
                if(!Optional.ofNullable(o).isPresent()){
                    continue;
                }
                if(!(o instanceof String) && !(o instanceof Integer)){
                    //递归处理
                    Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());
                    this.dealPrecisionField(allFields,o);
                }else{
                    Object value = null;
                    if(o instanceof String){
                        value = dealFieldValue(o);
                    }
                    if(o instanceof Integer){
                        value = dealFieldValue( o);
                    }
                     field.set(var,value);
                }
            }
        }
    
        /**
         * 字段数据脱敏
         * @param obj
         * @return
         */
        private Object dealFieldValue(Object obj){
            //integer类型枚举的直接返回
            if(obj instanceof Integer){
                return null;
            }
            String value = (String) obj;
            if(StringUtils.isEmpty(value)){
                return value;
            }
            if(value.length() == IDENTITY_CARD_LENGTH_18 || value.length() == IDENTITY_CARD_LENGTH_15){
                value = idCardReplace(value);
            }
            if(value.length() == MOBILE_LENGTH){
                value = mobileReplace(value);
            }
            if(value.length() <= OTHER_DESENSITIZATION_LENGTH){
                value = dealLessField(value);
            }
            return value;
        }
    
    
        private String dealLessField(String value){
            if(StringUtils.isEmpty(value) || value.length() > 3){
                return value;
            }
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < value.length(); i++) {
                builder.append("*");
            }
            return builder.toString();
        }
    
        /**
         * 身份证号脱敏,保留前四位和后四位
         * @param idCard 身份证号
         * @return
         */
        public  String idCardReplace(String idCard) {
            if (StringUtils.isEmpty(idCard)) {
                return null;
            }
            return replaceAction(idCard, IDENTITY_CARD_DESENSITIZATION);
        }
    
    
        /**
         * 银行卡替换,保留后四位
         * @param bankCard 银行卡号
         * @return
         */
        public  String bankCardReplace(String bankCard) {
            if (StringUtils.isEmpty(bankCard)) {
                return null;
            }
             return replaceAction(bankCard, BLANK_CARD_DESENSITIZATION);
        }
    
        /**
         *手机号脱敏,保留前三后四位
         * @param mobile
         * @return
         */
        public String mobileReplace(String mobile){
            if(StringUtils.isEmpty(mobile)){
                return mobile;
            }
            return replaceAction(mobile,MOBILE_DESENSITIZATION);
        }
    
        /**
         * 脱敏操作
         * @param source
         * @param regular  正则
         * @return
         */
        private  String replaceAction(String source, String regular) {
            return source.replaceAll(regular, "*");
        }
    
    
    }
    

     

    业务调用的方法:

    public void export(ChargingSubsidiesReq chargingSubsidiesReq, HttpServletResponse servletResponse) {
            if (null == chargingSubsidiesReq) {
                chargingSubsidiesReq = new ChargingSubsidiesReq();
            }
            chargingSubsidiesReq.setPageSize(1);
            chargingSubsidiesReq.setCurrPage(1);
            ChargingSubsidiesReqDto reqDto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);
            this.setCondition(chargingSubsidiesReq,reqDto);
            Response<PageResult<ChargingSubsidiesResDto>> response = chargingSubsidiesService.queryChargingSubsidies(reqDto);
            if (response.isSuccess() && Optional.ofNullable(response.getData()).isPresent()) {
                PageResult<ChargingSubsidiesResDto> pageResult = response.getData();
                ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);
                List<ChargingSubsidiesResVo> voList = proxyObj.getExcelData(pageResult, reqDto);
                String excelTitle = StringUtils.isEmpty(chargingSubsidiesReq.getExcelTitle()) ? DriverEnum.DRIVER_CHARGE_SUBSIDIES_EXCEL_TITLE.getMsg() : chargingSubsidiesReq.getExcelTitle();
                List<String> headList = new ArrayList<>();
                CommonUtil.setHeadList(ChargingSubsidiesResVo.class, headList);
                ExcelUtil.downloadExcelFile(excelTitle, headList, voList, servletResponse);
            }
        }
    
    
     @EncryptMethod
        public List<ChargingSubsidiesResVo> getExcelData(PageResult pageResult,ChargingSubsidiesReqDto reqDto){
            ThreadPoolExecutor poolExecutor = BussinessThreadPool.getThreadPoolExecutor();
            int threadCount = CommonUtil.getThreadCount(pageResult);
            List<Future<List<ChargingSubsidiesResDto>>> futureList = new ArrayList<>();
            //多线程查询
            for (int i = 1; i <= threadCount; i++) {
                ChargingSubsidiesReqDto dto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);
                BeanUtils.copyProperties(reqDto,dto);
                Future<List<ChargingSubsidiesResDto>> submit = poolExecutor.submit(new ChargingSubsidiesTask(i, ValidateConstant.EXCEL_EXPORT_DEFAULT_SIZE, dto, chargingSubsidiesService));
                futureList.add(submit);
            }
            List<ChargingSubsidiesResVo> voList = new ArrayList<>();
            List<ChargingSubsidiesResDto> tempList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(futureList)) {
                for (Future<List<ChargingSubsidiesResDto>> future : futureList) {
                    try {
                        List<ChargingSubsidiesResDto> dtoList = future.get();
                        tempList.addAll(dtoList);
                    } catch (InterruptedException | ExecutionException e) {
                        future.cancel(true);
                        log.error("获取线程数据异常{}:", e.getMessage(), e);
                    }
                }
                tempList.stream().forEach(var -> {
                    ChargingSubsidiesResVo vo = ObjectCheckUtil.createClass(ChargingSubsidiesResVo.class);
                    BeanUtils.copyProperties(var, vo);
                    vo.setIdentityCard(var.getIdNumber());
                    vo.setMobile(var.getDriverMobile());
                    voList.add(vo);
                });
            }
            return voList;
        }

     

    该业务是使用多线程获取excel导出的数据,在再使用多线程填充excel,具体见我的另外一篇博客:

    https://blog.csdn.net/qq_42151769/article/details/100674862

     

    如果你在导出中使用:

    this.getExcelData()的方法,那么不好意思,切面是无效的,原因是this是真实对象,不是一个代理对象,而aop切面必须是代理对象才能生效的,那么,我们就需要想办法获取到代理对象,因为spring IOC中存放的就是代理类对象,所以我们需要拿到它

    如下: 注意别忘记了打上注解@Component

    package com.t3.ts.driver.resume.context;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.aop.framework.AdvisedSupport;
    import org.springframework.aop.support.AopUtils;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Field;
    
    /**
     * @class_name: SpringContextUtil
     * @description: 获取spring ioc中的bean
     * @author: wm_yu
     * @create: 2019/09/12
     **/
    @Component
    public class SpringContextUtil implements ApplicationContextAware {
    
        private final static Logger log = LoggerFactory.getLogger(SpringContextUtil.class);
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContextParam) throws BeansException {
            applicationContext = applicationContextParam;
        }
    
        public static Object getObject(String id) {
            Object object = null;
            object = applicationContext.getBean(id);
            return object;
        }
        /**
         * 类路径获取
         * @param tClass
         * @return
         */
        public static Object getBean(String tClass) {
            return applicationContext.getBean(tClass);
        }
    
        /**
         * 字节码对象获取
         * @param tClass
         * @param <T> 代理对象
         * @return
         */
        public static <T> T getBean(Class<T> tClass) {
            return applicationContext.getBean(tClass);
        }
    
    
        /**
         * 根据传入获取真实对象
         * @param beanInstance
         * @return
         */
        public static<T> T getTarget(T beanInstance) {
            if (!AopUtils.isAopProxy(beanInstance)) {
                return beanInstance;
            } else if (AopUtils.isCglibProxy(beanInstance)) {
                try {
                    Field h = beanInstance.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                    h.setAccessible(true);
                    Object dynamicAdvisedInterceptor = h.get(beanInstance);
                    Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
                    advised.setAccessible(true);
                    T target = (T)((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
                    return target;
                } catch (Exception e) {
                    log.error("获取真实对象异常:{}",e.getMessage(),e);
                }
            }
            return null;
        }
    
    
    
    
    
    }
    

    通过这个类,我们就能拿到代理对象了:

    在代码中引用他:

    ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);
     List<ChargingSubsidiesResVo> voList = proxyObj.getExcelData(pageResult, reqDto);

     

    我们可以debug调试下:

    可以看到获取到了本类的代理对象,再看下用this调用也就是不获取代理对象的情况:

    可以看到this是真实对象

    这里看到的代理对象是cglib的,原因是我没有定义接口,所以spring使用的是cglib代理的

    好了,完美解决了,aop切面失效的原因

     

     

     

     

     

     

     

    展开全文
  • Spring 自定义切面注解总的来说分为三步: 一、对自定义注解进行声明; 二、对自定义切面进行声明,在自定义切面类中对注解切入时执行的方法进行业务编写; 三、在需要使用注解切入的地方进行注解声明使用; ...

    Spring 自定义切面注解总的来说分为三步:

    一、对自定义注解进行声明;

    二、对自定义切面进行声明,在自定义切面类中对注解切入时执行的方法进行业务编写;

    三、在需要使用注解切入的地方进行注解声明使用;

     

    具体的操作说明如下:

    1、原测试工程很简单,一个pojo实体类,一个dao类,一个service类,一个controller类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
    
        private Integer id;
    
        private String name;
    
    }
    

     

    @Component
    public class PersonDao {
    
        public Person findPersonById(Integer id) {
            if(id > 10) {
                return null;
            }
            return new Person(id, "person-" + id);
        }
    }
    
    

     

    @Service
    public class PersonService {
    
        private final PersonDao personDao;
    
        public PersonService(PersonDao personDao) {
            this.personDao = personDao;
        }
    
        public Person findPersonById(Integer id) {
            return personDao.findPersonById(id);
        }
    }
    
    
    

     

    @RestController
    public class PersonController {
    
        private final PersonService personService;
    
        public PersonController(PersonService personService) {
            this.personService = personService;
        }
    
        @RequestMapping("person/{id}")
        public Person findPerson(@PathVariable("id") Integer id) {
            return personService.findPersonById(id);
        }
    }
    
    
    

    直接访问localhost:8080/person/1后,显然会得到一个如下的json串 

    现在要在findPerson方法上增加自定义的注解,这个注解只做简单的日志输出

    1、新建一个annotation包,包里新建一个类YMLog

    package com.example.demo.annotation;
    
    import java.lang.annotation.*;
    
    @Documented   //该注解表示支持javaDoc文档导出
    @Retention(RetentionPolicy.RUNTIME) //该注解表示生命周期
    @Target(ElementType.METHOD)  //该注解表示自定义的注解可以使用的对象
    public @interface YMLog {
    
        String value() default "";
    }
    
    
    

    2、新建一个切面类,

    @Component
    @Aspect
    public class YMLogAspect {
        private static Logger logger = LoggerFactory.getLogger(MyAuthorizationHandler.class);
    
        
        @Pointcut("@annotation(com.example.demo.annotation.YMLog)")
        private void pointcut() {}
        
        @Before("pointcut() && @annotation(logger)")
        public void advice(JoinPoint joinPoint, YMLog logger) {
            logger.debug("注解作用的方法名: " + joinPoint.getSignature().getName());
            
            logger.debug("所在类的简单类名: " +         
            joinPoint.getSignature().getDeclaringType().getSimpleName());
            
            logger.debug("所在类的完整类名: " + 
            joinPoint.getSignature().getDeclaringType());
            
            logger.debug("目标方法的声明类型: " + 
            Modifier.toString(joinPoint.getSignature().getModifiers()));
    
            logger.debug("--- 日志的内容为" + logger.value() + " ---");
        }
    
    
        //这里还可以省略pointcut()方法,使用下面的写法
        @Before(value="@annotation(logger)")
        public void advice(JoinPoint joinPoint, YMLog logger) {
            logger.debug("注解作用的方法名: " + joinPoint.getSignature().getName());
            
            logger.debug("所在类的简单类名: " +         
            joinPoint.getSignature().getDeclaringType().getSimpleName());
            
            logger.debug("所在类的完整类名: " + 
            joinPoint.getSignature().getDeclaringType());
            
            logger.debug("目标方法的声明类型: " + 
            Modifier.toString(joinPoint.getSignature().getModifiers()));
    
            logger.debug("--- 日志的内容为" + logger.value() + " ---");
        }
    
    }
    
    
    
    
    

    @Pointcut声明了切点(这里的切点是我们自定义的注解类),@Before声明了通知内容(@Before, @After, @Around),在具体的通知中,我们通过@annotation(logger)拿到了自定义的注解对象,所以就能够获取我们在使用注解时赋予的值了。如果这里不明白的,可以学习Spring基础知识,了解什么是AOP;

    3、在需要使用的地方进行注解声明使用

    @RestController
    public class PersonController {
    
        private final PersonService personService;
    
        public PersonController(PersonService personService) {
            this.personService = personService;
        }
    
        @YMLog("这是自定义日志切入")
        @RequestMapping("person/{id}")
        public Person findPerson(@PathVariable("id") Integer id) {
            return personService.findPersonById(id);
        }
    }
    
    
    

    使用注解还可以修改前台传给后端的参数和后端返回给前台的返回值,具体操作如下:

    @Around("pointcut() && @annotation(logger)")
    public Object advice(ProceedingJoinPoint joinPoint, YMLog logger) {
       System.out.println("["
                    + joinPoint.getSignature().getDeclaringType().getSimpleName()
                    + "][" + joinPoint.getSignature().getName()
                    + "]-日志内容-[" + logger.value() + "]");
    
            Object result = null;
            
            Object[] args = joinPoint.getArgs();
            //对前端传入的参数做处理
            for (int i = 0; i < args.length; i++) {
                if(args[i] instanceof Integer) {
                    args[i] = (Integer)args[i] - 1;
                    break;
                }
            }
    
            try {
                //这里是执行真正的原方法
                result = joinPoint.proceed(args);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            
            //这里是在返回之前对返回结果进行处理
            if(result instanceof Person) {
                Person person= (Person) result;
                person.setName("tom");
                return person;
            }
            return result;
    }
    
    
    


     

     

     

    展开全文
  • Springboot(二十一)@Aspect 切面注解使用

    万次阅读 多人点赞 2018-09-08 17:36:11
    Spring AOP面向切面编程,可以用来配置事务、做日志、权限验证、在用户请求时...首先定义一个切面类,加上@Component @Aspect这两个注解 @Component @Aspect public class LogAspect { private static final Lo...

           Spring AOP面向切面编程,可以用来配置事务、做日志、权限验证、在用户请求时做一些处理等等。用@Aspect做一个切面,就可以直接实现。

        1.首先定义一个切面类,加上@Component  @Aspect这两个注解   

    @Component
    @Aspect
    public class LogAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    
    
    }

         2.定义切点     

        private final String POINT_CUT = "execution(public * com.xhx.springboot.controller.*.*(..))";
    
        @Pointcut(POINT_CUT)
        public void pointCut(){}

        切点表达式中,..两个点表明多个,*代表一个,  上面表达式代表切入com.xhx.springboot.controller包下的所有类的所有方法,方法参数不限,返回类型不限。  其中访问修饰符可以不写,不能用*,,第一个*代表返回类型不限,第二个*表示所有类,第三个*表示所有方法,..两个点表示方法里的参数不限。  然后用@Pointcut切点注解,想在一个空方法上面,一会儿在Advice通知中,直接调用这个空方法就行了,也可以把切点表达式卸载Advice通知中的,单独定义出来主要是为了好管理。

     

       3.Advice,通知增强,主要包括五个注解Before,After,AfterReturning,AfterThrowing,Around,下面代码中关键地方都有注释,我都列出来了。

       @Before  在切点方法之前执行

       @After  在切点方法之后执行

        @AfterReturning 切点方法返回后执行

       @AfterThrowing 切点方法抛异常执行

      @Around 属于环绕增强,能控制切点执行前,执行后,,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解

          

    package com.xhx.springboot.config;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.SourceLocation;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Arrays;
    
    @Component
    @Aspect
    public class LogAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    
        private final String POINT_CUT = "execution(public * com.xhx.springboot.controller.*.*(..))";
    
        @Pointcut(POINT_CUT)
        public void pointCut(){}
    
       @Before(value = "pointCut()")
        public void before(JoinPoint joinPoint){
            logger.info("@Before通知执行");
            //获取目标方法参数信息
            Object[] args = joinPoint.getArgs();
            Arrays.stream(args).forEach(arg->{  // 大大
                try {
                    logger.info(OBJECT_MAPPER.writeValueAsString(arg));
                } catch (JsonProcessingException e) {
                    logger.info(arg.toString());
                }
            });
    
    
            //aop代理对象
            Object aThis = joinPoint.getThis();
            logger.info(aThis.toString()); //com.xhx.springboot.controller.HelloController@69fbbcdd
    
            //被代理对象
            Object target = joinPoint.getTarget();
            logger.info(target.toString()); //com.xhx.springboot.controller.HelloController@69fbbcdd
    
            //获取连接点的方法签名对象
            Signature signature = joinPoint.getSignature();
            logger.info(signature.toLongString()); //public java.lang.String com.xhx.springboot.controller.HelloController.getName(java.lang.String)
            logger.info(signature.toShortString()); //HelloController.getName(..)
            logger.info(signature.toString()); //String com.xhx.springboot.controller.HelloController.getName(String)
            //获取方法名
            logger.info(signature.getName()); //getName
            //获取声明类型名
            logger.info(signature.getDeclaringTypeName()); //com.xhx.springboot.controller.HelloController
            //获取声明类型  方法所在类的class对象
            logger.info(signature.getDeclaringType().toString()); //class com.xhx.springboot.controller.HelloController
            //和getDeclaringTypeName()一样
            logger.info(signature.getDeclaringType().getName());//com.xhx.springboot.controller.HelloController
    
            //连接点类型
            String kind = joinPoint.getKind();
            logger.info(kind);//method-execution
    
            //返回连接点方法所在类文件中的位置  打印报异常
            SourceLocation sourceLocation = joinPoint.getSourceLocation();
            logger.info(sourceLocation.toString());
            //logger.info(sourceLocation.getFileName());
            //logger.info(sourceLocation.getLine()+"");
            //logger.info(sourceLocation.getWithinType().toString()); //class com.xhx.springboot.controller.HelloController
    
            ///返回连接点静态部分
            JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();
            logger.info(staticPart.toLongString());  //execution(public java.lang.String com.xhx.springboot.controller.HelloController.getName(java.lang.String))
    
    
            //attributes可以获取request信息 session信息等
            ServletRequestAttributes attributes =
                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            logger.info(request.getRequestURL().toString()); //http://127.0.0.1:8080/hello/getName
            logger.info(request.getRemoteAddr()); //127.0.0.1
            logger.info(request.getMethod()); //GET
    
           logger.info("before通知执行结束");
        }
    
    
        /**
         * 后置返回
         *      如果第一个参数为JoinPoint,则第二个参数为返回值的信息
         *      如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
         * returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行,
         *            参数为Object类型将匹配任何目标返回值
         */
        @AfterReturning(value = POINT_CUT,returning = "result")
        public void doAfterReturningAdvice1(JoinPoint joinPoint,Object result){
            logger.info("第一个后置返回通知的返回值:"+result);
        }
    
        @AfterReturning(value = POINT_CUT,returning = "result",argNames = "result")
        public void doAfterReturningAdvice2(String result){
            logger.info("第二个后置返回通知的返回值:"+result);
        }
        //第一个后置返回通知的返回值:姓名是大大
        //第二个后置返回通知的返回值:姓名是大大
        //第一个后置返回通知的返回值:{name=小小, id=1}
    
    
        /**
         * 后置异常通知
         *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
         *  throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
         *            对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
         * @param joinPoint
         * @param exception
         */
        @AfterThrowing(value = POINT_CUT,throwing = "exception")
        public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
            logger.info(joinPoint.getSignature().getName());
            if(exception instanceof NullPointerException){
                logger.info("发生了空指针异常!!!!!");
            }
        }
    
        @After(value = POINT_CUT)
        public void doAfterAdvice(JoinPoint joinPoint){
            logger.info("后置通知执行了!");
        }
    
        /**
         * 环绕通知:
         *   注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用
         *
         *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
         *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
         */
        @Around(value = POINT_CUT)
        public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
            logger.info("@Around环绕通知:"+proceedingJoinPoint.getSignature().toString());
            Object obj = null;
            try {
                obj = proceedingJoinPoint.proceed(); //可以加参数
                logger.info(obj.toString());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            logger.info("@Around环绕通知执行结束");
            return obj;
        }
    }
    

            执行前后顺序是这样

     : @Around环绕通知
     : @Before通知执行
     : @Before通知执行结束
     : @Around环绕通知执行结束
     : @After后置通知执行了!
     : @AfterReturning第一个后置返回通知的返回值:18

        

    org.aspectj.lang.JoinPoint :  方法中的参数JoinPoint为连接点对象,它可以获取当前切入的方法的参数、代理类等信息,因此可以记录一些信息,验证一些信息等。

    org.aspectj.lang.ProceedingJoinPoint: 为JoinPoint的子类,多了两个方法

    public Object proceed() throws Throwable;  //调用下一个advice或者执行目标方法,返回值为目标方法返回值,因此可以通过更改返回值,修改方法的返回值
    public Object proceed(Object[] args) throws Throwable;   //参数为目标方法的参数  因此可以通过修改参数改变方法入参

     

    可以用下面的方式获取request、session等对象

    //attributes可以获取request信息 session信息等
    ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

     

    下面介绍一下切点表达式:

       1.execution(方法修饰符 返回类型 方法全限定名(参数))         主要用来匹配整个方法签名和返回值的

    "execution(public * com.xhx.springboot.controller.*.*(..))"

        *只能匹配一级路径  

        ..可以匹配多级,可以是包路径,也可以匹配多个参数

        + 只能放在类后面,表明本类及所有子类

       还可以按下面这么玩,所有get开头的,第一个参数是Long类型的

    @Pointcut("execution(* *..get*(Long,..))")

      2. within(类路径)   用来限定类,同样可以使用匹配符

           下面用来表示com.xhx.springboot包及其子包下的所有类方法

    "within(com.xhx.springboot..*)"

     

    3. this与target

         this与target在用法上有些重合,理解上有对比性。

          this表示当前切入点表达式所指代的方法的对象的实例,即代理对象是否满足this类型

          target表示当前切入点表达式所指代的方法的目标对象的实例   即是否是为target类做的代理

        如果当前对象生成的代理对象符合this指定的类型,则进行切面,target是匹配业务对象为指定类型的类,则进行切面。

         生成代理对象时会有两种方法,一个是CGLIB一个是jdk动态代理。

          用下面三个例子进行说明:     

    • this(SomeInterface)或target(SomeInterface):这种情况下,无论是对于Jdk代理还是Cglib代理,其目标对象和代理对象都是实现SomeInterface接口的(Cglib生成的目标对象的子类也是实现了SomeInterface接口的),因而this和target语义都是符合的,此时这两个表达式的效果一样;
    • this(SomeObject)或target(SomeObject),这里SomeObject没实现任何接口:这种情况下,Spring会使用Cglib代理生成SomeObject的代理类对象,由于代理类是SomeObject的子类,子类的对象也是符合SomeObject类型的,因而this将会被匹配,而对于target,由于目标对象本身就是SomeObject类型,因而这两个表达式的效果一样;
    • this(SomeObject)或target(SomeObject),这里SomeObject实现了某个接口:对于这种情况,虽然表达式中指定的是一种具体的对象类型,但由于其实现了某个接口,因而Spring默认会使用Jdk代理为其生成代理对象,Jdk代理生成的代理对象与目标对象实现的是同一个接口,但代理对象与目标对象还是不同的对象,由于代理对象不是SomeObject类型的,因而此时是不符合this语义的,而由于目标对象就是SomeObject类型,因而target语义是符合的,此时this和target的效果就产生了区别;这里如果强制Spring使用Cglib代理,因而生成的代理对象都是SomeObject子类的对象,其是SomeObject类型的,因而this和target的语义都符合,其效果就是一致的。

     

      4.args(paramType)

           args无论其类路径或者是方法名是什么,表达式的作用是匹配指定参数类型和指定参数数量的方法,类型用全路径     

    args(java.lang.String,..,java.lang.Integer)

      5.@within(annotationType) 匹配带有指定注解的类,,within为配置指定类型的类实例

           下面匹配含有 @Component注解的类

    "@within(org.springframework.stereotype.Component)"

      6.@annotation(annotationType) 匹配带有指定注解的方法

     

      7.@args(annotationType)

                @args表示使用指定注解标注的类作为某个方法的参数时该方法将会被匹配

     

    可以使用&&、||、!、三种运算符来组合切点表达式,表示与或非的关系。

    @Around(value = "pointcut1() || pointcut2()")

     

      

    参考文献:

        Spring AOP切点表达式用法总结

     

    实时内容请关注微信公众号,公众号与博客同时更新:程序员星星

    实时内容请关注微信公众号,公众号与博客同时更新

     

    展开全文
  • 1、需要切面首先切面注解(DataSourceTypeAnno) 2、数据源枚举类(表示数据源的bean)(DataSourceEnum) 3、Aspect切面对象 (DataSourceAspect) 4、线程上下文对象,以保证线程间安全,不同线程使用不同数据源...

    利用切面自动切换数据源,出现多数据源情况

    1、需要切面首先切面注解(DataSourceTypeAnno)

    2、数据源枚举类(表示数据源的bean)(DataSourceEnum)

    3、Aspect切面对象 (DataSourceAspect)

    4、线程上下文对象,以保证线程间安全,不同线程使用不同数据源(DataSourceContextHolder)

    5、实现spring一个接口AbstractRoutingDataSource 的 dermineCurrentLookupKey()方法(也就是在spring创建连接对象时候会先调用这个方法来替换数据源)(DynamicDataSource)

    6、具体配置多个bean的数据源config配置文件(MyBatisDefaultConfig)

    1、DataSourceTypeAnno

    /**
     * @author liangchen
     * @date 2020/12/8
     */
    // 在运行时可见
    @Retention(RetentionPolicy.RUNTIME)
    // 注解可以用在方法上
    @Target({ElementType.METHOD})
    public @interface DataSourceTypeAnno {
        DataSourceEnum value() default DataSourceEnum.DEFUALT;
    }
    

    2、DataSourceEnum

    /**
     * @author liangchen
     * @date 2020/12/8
     */
    public enum DataSourceEnum {
        /**
         * 默认数据
         */
        DEFUALT,
      	/** 账户数据源*/
        ACCOUNT;
    }
    
    

    3、DataSourceAspect

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 数据源连接器切面
     * @author liangchen
     * @date 2020/12/8
     */
    @Component
    @Aspect
    @Log4j
    public class DataSourceAspect {
        
    //注解类路径  @Pointcut("@annotation(com.jack.base.annotations.DataSourceTypeAnno)")
        public void dataSourcePointcut() {
        }
    
        @Around("dataSourcePointcut()")
        public Object doAround(ProceedingJoinPoint pjp) {
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            DataSourceTypeAnno typeAnno = method.getAnnotation(DataSourceTypeAnno.class);
            DataSourceEnum sourceEnum = typeAnno.value();
    				// 设置数据源标志,spring获取这些标志之后就会获取对象的数据源,其实就bean的实例名称
            if (sourceEnum == DataSourceEnum.DEFUALT) {
                DataSourceContextHolder.setDataSourceType(DataSourceEnum.DEFUALT);
            } else if (sourceEnum == DataSourceEnum.ACCOUNT) {
                DataSourceContextHolder.setDataSourceType(DataSourceEnum.ACCOUNT);
            }
    
            Object result = null;
            try {
                result = pjp.proceed();
            } catch (Throwable throwable) {
                log.error("切换数据源发生异常",throwable);
            } finally {
                DataSourceContextHolder.resetDataSourceType();
            }
    
            return result;
        }
    }
    

    4、DataSourceContextHolder

    /**
     * @author liangchen
     * @date 2020/12/8
     */
    public class DataSourceContextHolder {
        private static final ThreadLocal<DataSourceEnum> CONTEXT_HOLDER = new ThreadLocal<DataSourceEnum>() {
    
            @Override
            protected DataSourceEnum initialValue() {
               // 初始化线程的数据源是 DEFAULT
                return DataSourceEnum.DEFUALT;
            }
        };
    
    
        public static void setDataSourceType(DataSourceEnum type) {
            CONTEXT_HOLDER.set(type);
        }
    
        public static DataSourceEnum getDataSourceType() {
            return CONTEXT_HOLDER.get();
        }
    
        public static void resetDataSourceType() {
            CONTEXT_HOLDER.set(DataSourceEnum.DEFUALT);
        }
    }
    

    5、DynamicDataSource

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * @author liangchen
     * @date 2020/12/8
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
      // spring在使用connection时候需要取那个数据源
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceContextHolder.getDataSourceType();
        }
    }
    
    

    6、MyBatisDefaultConfig

    //配置数据源
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author liangchen
     * @date 2020/12/8
     */
    @Configuration
    @MapperScan(basePackages = "com.jack.bo.*.dao")
    public class MyBatisConfig {
        static final String CONFIG_LOCATION = "classpath:mybatis-config.xml";
        static final String MAPPER_LOCATION = "classpath:mybatis/default/*.xml";
        static final String SQL_SESSION_FACTORY = "defaultSqlSessionFactory";
        static final String TRANSACTION_MANAGER = "defaultTransactionManager";
        /**
         * @return
         * @throws Exception
         * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
         */
    
        @Primary
        @Bean("defautDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.default")
        public DataSource defautDataSource() throws Exception {
            return DataSourceBuilder.create().build();
        }
    
        @Bean("accountDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.account")
        public DataSource accountDataSource() throws Exception {
            return DataSourceBuilder.create().build();
        }
    
        /**
         * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
         */
        @Bean("dynamicDataSource")
        public DynamicDataSource dynamicDataSource(@Qualifier("defaultDataSource") DataSource defautDataSource,
                                                   @Qualifier("accountDataSource") DataSource accountDataSource) {
            Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
            targetDataSources.put(DataSourceEnum.ACCOUNT, accountDataSource);
            targetDataSources.put(DataSourceEnum.DEFUALT, defautDataSource);
    
            DynamicDataSource dataSource = new DynamicDataSource();
            // 该方法是AbstractRoutingDataSource的方法
            dataSource.setTargetDataSources(targetDataSources);
            // 默认的datasource设置为myTestDbDataSource
            dataSource.setDefaultTargetDataSource(defautDataSource);
    
            return dataSource;
        }
       
        //配置一个jdbcTemplate
        @Bean
        public JdbcTemplate jdbcTemplate(@Qualifier("dynamicDataSource") DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
        /**
         * 根据数据源创建SqlSessionFactory
         */
        @Bean
        public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource,
                                                   @Value("mybatis.typeAliasesPackage") String typeAliasesPackage,
                                                   @Value("mybatis.mapperLocations") String mapperLocations) throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dynamicDataSource);// 指定数据源(这个必须有,否则报错)
            // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
    //        factoryBean.setTypeAliasesPackage(typeAliasesPackage);// 指定基包
    //        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));//
            factoryBean.setConfigLocation(new PathMatchingResourcePatternResolver()
                    .getResource(CONFIG_LOCATION));
            factoryBean.setVfs(SpringBootVFS.class);
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                    .getResources(MAPPER_LOCATION));
            return factoryBean.getObject();
        }
    
        /**
         * 配置事务管理器
         */
        @Bean
        public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
            return new DataSourceTransactionManager(dataSource);
        }
    }
    
    • @ConfigurationProperties(prefix = “spring.datasource.default”) 需要在yml文件或配置中心配置一下数据源,表示加载以spring.datasource.default开头的属性进行对象的属性字段填充

    ###7、切点位置

    • 可以controller,service,dao,最好还是放在service层的方法上,因为如果放到controller,如果其他类直接调用service不会切换数据源, 放在dao,控制有点细了,一个service方法可能有几个操作数据库的方法,连接池只是一个,只需处理一次切面切换,而不需要处理多次数据源切换【虽然一样】

    image-20201208205545907

    8、源码

    • 切换源代码图示

      • 
        /** 这一段就数据源切换代码
        	 * Retrieve the current target DataSource. Determines the
        	 * {@link #determineCurrentLookupKey() current lookup key}, performs
        	 * a lookup in the {@link #setTargetDataSources targetDataSources} map,
        	 * falls back to the specified
        	 * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
        	 * @see #determineCurrentLookupKey()
        	 */
        protected DataSource determineTargetDataSource() {
        		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            // 获取自己定义key(枚举类)
        		Object lookupKey = determineCurrentLookupKey();
            // resolvedDataSources 是加载所有数据源(当前是两个)
        		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
           // 如果没有就用默认的数据源
        		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        			dataSource = this.resolvedDefaultDataSource;
        		}
        		if (dataSource == null) {
        			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        		}
        		return dataSource;
        	}
        
    • 那么 resolvedDataSources 其实是一个map, 它是怎么生成呢

      -

      • 核心代码

        • @Override
          	public void afterPropertiesSet() {
              // 在@Bean初始化进去的两个
          		if (this.targetDataSources == null) {
          			throw new IllegalArgumentException("Property 'targetDataSources' is required");
          		}
          		this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
              // 设置数据源
          		for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
          			Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
          			DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
          			this.resolvedDataSources.put(lookupKey, dataSource);
          		}
          		if (this.defaultTargetDataSource != null) {
          			this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
          		}
          	}
          

    9、注意点

    1、 jdbctemplate必须调用有注解的服务层或者dao,不能单独写sql语句,不然就不生效了。

    10、参考

    Springboot MyBatis多数据源切换

    展开全文
  • AOP AspectJ 切面注解中五种通知注解: @Before: 前置通知, 在方法执行之前执行 @After: 后置通知, 在方法执行之后执行 。 @AfterRunning: 返回通知, 在方法返回结果之后执行 @AfterThrowing: 异常通知, 在方法抛出...
  • 切面注解执行顺序

    2019-01-14 09:59:43
    try { try { // @Before mothod.invoke(); } finally { // @After } // @AfterReturning catch() { // @AfterThrowing } ...另@Around中包含方法调用,可手动调节方法调用和切面代码逻辑...
  • aop切面 注解、参数获取方法

    万次阅读 2019-08-25 22:26:54
    在工作中会经常使用aop,这里将aop使用...1、定义需要切面注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AnnDemo { String value(); boolean isA...
  • 切面注解@Pointcut(“execution()”)写法 [权限修饰符] [包路径].[类(全部为*)].[方法](参数[所有..]) 包路径内点与点之间也可以使用*代表所有 示例 * com.main.demo.*.*(..)
  • spring中aop的注解实现方式简单实例  上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢。前面已经讲过aop的简单...在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类。 ...
  • JAVA实现简单的切面注解

    万次阅读 2019-08-22 00:31:07
    背景 今天在分析同事遇到一个springboot的注解和方法锁一起用而导致的问题(@...本文介绍java中其中一种(InvocationHandler)利用动态代理的方式实现的代理的方法,从而类似的机制我们推测出spring的切面...
  • 把spring从4.0升级到4.3.29后,自定义一个注解,至于方法上,切面中定义的规则是注解代理,升级后工程运行不走代理方法而是走注解下的方法。 2.探究 Spring开启注解的配置是通过...
  • sping AOP切面注解使用中的坑–同一个类中嵌套方法注解不生效     在开发过程中,监控方法运行时间,然后获取程序运行的瓶颈,是一个常见的优化步骤。写一个spring切面实现的注解来实现函数运行...
  • 博主的问题是 用this无法生效切面 public class ServiceImpl implements Service { @Override public T getAssetDetail() { xxxxxx; } @Override @TranslateRemoteField public T ...
  • 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例....在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ 支持 5 种类型的
  • <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <scope>test</scope> <...d...
  • 在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行一些方法,提高系统的执行效率。今天我们来探讨下spring是如何完成这个功能的。1、spring 在扫描bean的时候会扫描方法上是否包含@async的...
  • 故我们可以定义切面,来进行处理日志的记录;简化开发; 下面的案例是自定义注解来记入日志;可以根据自己的需求来改变自己的切面实现和连接点, 下面只是提供一个案例参考。 一、注解SaveLog @Target({ElementType....
  • Java-切面注解拦截器

    2020-05-08 16:59:16
    创建切面类,启用注解@Aspect、@Component、@Order(-99);通过@Pointcut("@annotation(具体注解类路径)")指定注解切入点;最后通过环绕通知(@Around(“pointCut()”))实现拦截效果,在此方法里进行判断,最终通过...
  • 自己实现建了一个切面注解@Mypoint 注解有一个key属性 ``` String key(); ``` 在使用注解时 ``` @Mypoint(key = "abc-${env}") ``` 在properties配置文件中有 ``` env=dev ``` 这样通过配置文件可以...
  • 本来我是使用@annotation() 直接切面指定注解的,结果方法内部调用的方法无法切入,因此我使用了一下方式替代。切面Action下所有类所有方法,同时方法上有ExecuteTask注解的才进行切面 @Component @Aspect public ...
  • SpringBoot项目 Spring AOP切面注解实现

    千次阅读 2019-03-30 01:58:46
    //切面拦截器,拦截订单接口请求中是否能查询到token(类似SpringAOP) @Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" + "&& !execution(public * ...
  • Spring AOP 切面 注解,拦截方法失效

    千次阅读 2019-02-26 21:30:07
    问题 使用 aop 环绕拦截对应的方法,远程断点发现没有拦截到。 解决方法 发现拦截的方法并非直接从 controller -&... service 直接调用过来,而是在 service 层通过其他方法调用的,无法被拦截到;...
  • AspectJ 支持 5 种类型的通知注解: @Before: 前置通知, 在方法执行之前执行 ...配置一个切面(要使用两个注解,1.@Aspect声明该类是一个切面,Spring官方文档解释,单单这个注解不能把该类注册成bean 。所以需要
  • 所以这边直接循环Class的所有方法拿到Aspectj注解注释的方法 @Override public List < Advisor > getAdvisors ( MetadataAwareAspectInstanceFactory aspectInstanceFactory ) { //...........略 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 84,167
精华内容 33,666
关键字:

切面注解