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

查看:115
本文介绍了项目启动时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,然后在postConstruct方法中执行MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class).然后在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天全站免登陆