Spring 项目启动时 aop 没有 运行
Spring aop doesn't run when project starts
我实现了一个spring-boot aop demo,运行的很好,但是当我想在项目启动的时候用它来加载一些资源的时候,怎么就不行了
奥普:
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 注释:
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";
}
这里运行良好,我可以通过注释成功更改数据源:
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方法:
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());
}
}
}
您需要完全限定注释,如下所示:
@Before("execution(public * *(..)) && @annotation(com.neo.mysql.VendorSource)")
private void whatever() {}
此外,正如我在上面的评论中提到的,您需要在 class 路径上有 spring-boot-starter-aop
。也许你已经这样做了,但因为你没有说,所以值得一提。
编辑:
我之前没有注意到真正的问题,我没有注意。
Spring AOP 仅在您从另一个 class 发出调用时触发。这是因为 Spring 需要能够拦截调用和 运行 切入点。从构造函数调用方法不会做任何事情。
你可以做一个 hackish 的解决方法。在 class(不是构造函数)中创建一个 @PostConstruct void postConstruct() {}
方法,自动装配 ApplicationContext
,然后在 postConstruct
方法中执行 MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class)
。然后调用 myClass
上的方法,AOP 将启动。
坦率地说,我之前没有检查过你在切入点中做了什么,这是一个糟糕的主意。当多个线程 运行 时,它们将覆盖静态上下文,并创建一个竞争条件,然后您将为其创建另一个问题。 不要这样做!改用工厂模式,然后将 DataSourceFactory
注入现在具有注释的 class 中。
我实现了一个spring-boot aop demo,运行的很好,但是当我想在项目启动的时候用它来加载一些资源的时候,怎么就不行了
奥普:
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 注释:
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";
}
这里运行良好,我可以通过注释成功更改数据源:
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方法:
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());
}
}
}
您需要完全限定注释,如下所示:
@Before("execution(public * *(..)) && @annotation(com.neo.mysql.VendorSource)")
private void whatever() {}
此外,正如我在上面的评论中提到的,您需要在 class 路径上有 spring-boot-starter-aop
。也许你已经这样做了,但因为你没有说,所以值得一提。
编辑:
我之前没有注意到真正的问题,我没有注意。
Spring AOP 仅在您从另一个 class 发出调用时触发。这是因为 Spring 需要能够拦截调用和 运行 切入点。从构造函数调用方法不会做任何事情。
你可以做一个 hackish 的解决方法。在 class(不是构造函数)中创建一个
@PostConstruct void postConstruct() {}
方法,自动装配ApplicationContext
,然后在postConstruct
方法中执行MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class)
。然后调用myClass
上的方法,AOP 将启动。
坦率地说,我之前没有检查过你在切入点中做了什么,这是一个糟糕的主意。当多个线程 运行 时,它们将覆盖静态上下文,并创建一个竞争条件,然后您将为其创建另一个问题。 不要这样做!改用工厂模式,然后将 DataSourceFactory
注入现在具有注释的 class 中。