项目启动时Spring aop不运行 [英] Spring aop doesn't run when project starts

查看:38
本文介绍了项目启动时Spring aop不运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了一个 spring-boot aop 演示并且它运行良好,但是当我想在项目启动时使用它来加载一些资源时,它以某种方式不起作用

I'v implemented a spring-boot aop demo and it runs well, but when I want to use it to load some resource when the project starts, it doesn't work somehow

奥普:

package com.neo.mysql;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * Created by li_weia on 2017/7/6.
 */
@Aspect
@Component
public class DynamicDataSourceAspect {

    @Before("@annotation(VendorSource)")
    public void beforeSwitchDS(JoinPoint point){

        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();

        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);

            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(VendorSource.class)) {
                VendorSource annotation = method.getAnnotation(VendorSource.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);

    }


    @After("@annotation(VendorSource)")
    public void afterSwitchDS(JoinPoint point){

        DataSourceContextHolder.clearDB();

    }
}

VendorSource 注释:

The VendorSource annotation:

package com.neo.mysql;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by li_weia on 2017/7/6.
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface VendorSource {
    String value() default "vendor-master";
}

这里运行良好,我可以通过注解成功更改数据源:

It runs well here, I can successfully change datasource by annotation:

package com.neo.web;

import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    private final ClassMappingDao siteMapper;

    @Autowired(required = false)
    public UserController(ClassMappingDao siteMapper) {
        this.siteMapper = siteMapper;
    }

    @RequestMapping("/getSites")
    @VendorSource("vendor-read")
    public List<SiteEntity> getUsers() {
        return siteMapper.getAllSite();
    }
}

但它在这里不起作用,根本没有调用aop方法:

but it doesn't work here, the aop method is not invoked at all:

package com.neo.component;

import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * Created by li_weia on 2017/7/7.
 */
@Component
public class TestComponent{
    private final ClassMappingDao userMapper;

    @Autowired(required = false)
    public TestComponent(ClassMappingDao userMapper) {
        this.userMapper = userMapper;
        init();
    }

    @VendorSource("vendor-read")
    public void init() {
        List<SiteEntity> sites = userMapper.getAllSite();
        for(SiteEntity site: sites){
            System.out.println(site.getSite());
        }
    }
}

推荐答案

您需要完全限定注释,如下所示:

You need to fully qualify the annotation, like so:

@Before("execution(public * *(..)) && @annotation(com.neo.mysql.VendorSource)")
private void whatever() {}

另外,正如我在上面的评论中提到的,你需要在类路径上有 spring-boot-starter-aop.也许你已经做了,但既然你没有说,那就值得一提.

Also, as mentioned in my comment above, you need to have spring-boot-starter-aop on classpath. Maybe you already do, but since you didn't say, it's worth mentioning.

编辑:

我之前没有注意到真正的问题,我没有注意.

I didn't notice the real problem before, I wasn't paying attention.

  1. Spring AOP 仅在您从另一个类进行调用时触发.这是因为 Spring 需要能够拦截调用并运行切入点.从构造函数调用方法不会做任何事情.

  1. Spring AOP only triggers if you make calls from another class. This is because Spring needs to be able to intercept the call and run the pointcut. Calling the method from constructor is not going to do anything.

你可以做一个骇人听闻的解决方法.在您的类(不是构造函数)中创建一个 @PostConstruct void postConstruct() {} 方法,自动装配 ApplicationContext,然后执行 MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class)postConstruct 方法中.然后调用 myClass 上的方法,AOP 就会启动.

You can do a hackish workaround. Create a @PostConstruct void postConstruct() {} method in your class (not constructor), autowire ApplicationContext, and then do MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class) in the postConstruct method. Then call the method on myClass, and AOP will kick in.

坦率地说,我之前没有检查您在切入点中在做什么,这是一个糟糕的主意.当多个线程运行时,它们将覆盖静态上下文,并创建一个竞争条件,然后您将为其创建另一个问题.不要这样做!改为使用工厂模式,并将 DataSourceFactory 注入现在具有注释的类中.

Frankly, I didn't previously check what you are doing in your pointcut, and it's a terrible idea. When multiple threads run, they are going to overwrite the static context, and create a race-condition that you'll then create another question for. Don't do it! Use the factory pattern instead, and inject the DataSourceFactory in the classes that now have the annotation.

这篇关于项目启动时Spring aop不运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆