在我们的应用程序中,我们有几个(实际上很多,大约30个)Web服务.每个Web服务都驻留在自己的WAR文件中,并且具有自己的Spring上下文,该上下文在应用程序启动时初始化.
我们还有许多应用于Web服务类的注释驱动的方面类.在开始时,poincut表达式看起来像这样:
@Pointcut("execution(public * my.package.service.business.*BusinessServiceImpl.*(..))")
public void methodsToBeLogged() {
}
Run Code Online (Sandbox Code Playgroud)
并且通过配置输入启用了AOP服务.
但是当网络服务的数量增加时,我们开始OutOfMemoryException在服务器上体验s.在进行一些分析和分析之后,看起来内存是由AspectJExpressionPointcut类的实例保存的缓存占用的.
每个实例的缓存大约是5 MB.由于我们有3个方面和30个服务,因此共有90个实例共存储了450MB的数据.
在检查了缓存的内容后,我们意识到它包含了WAR中存在的所有类的Java反射方法实例,即使是那些不属于my.package.service.business包的类.修改了切点表达式后还有附加within条款:
@Pointcut("execution(public * my.package.service.business.*BusinessServiceImpl.*(..)) &&
within(my.package.service.business..*)")
public void methodsToBeLogged() {
}
Run Code Online (Sandbox Code Playgroud)
内存使用量再次降至正常水平.并且所有AspectJExpressionPointcut实例总共花费不到1MB.
有人可以解释为什么会这样吗?为什么第一点切割表达还不够?为什么AspectJExpressionPointcut不共享缓存?
我现在有一个要求,就是当使用mybatis(特别是那些批处理执行sql)时,先检查参数,如果参数为null或为空,则只返回,不要继续,如果返回类型是List,例如.
List<User> getByIds(List<Long> idList)
Run Code Online (Sandbox Code Playgroud)
返回空ArrayList,如果返回类型为void:
void batchInsert(List<User>)
Run Code Online (Sandbox Code Playgroud)
返回null.目的是避免这种情况,例如.
select * from user where id in ()
insert into user(name,email) values ()
Run Code Online (Sandbox Code Playgroud)
但是从joinPoint我无法获得返回类型,只能得到args.
Object[] args = joinPoint.getArgs();
if(args!=null&&args.length=1){
if(args[0] instanceof List){
if(((List) obj).isEmpty()){
if(returnType.equals("java.util.List"))
return new ArrayList();
else if(returnType.equals("void"))
return null;
}
}
return joinPoint.proceed();
Run Code Online (Sandbox Code Playgroud)
那么如何在aop中得到返回类型:around?
我想在建议之前在Spring AOP中获取响应对象.如果会话无效,我想重定向到登录页面,但无法在Before advice方法中获取HttpServletResponse对象.
尝试以下方式.
@Autowired
private HttpServletResponse response;
public void setResponse(HttpServletResponse response) {
this.response = response;
}
Run Code Online (Sandbox Code Playgroud)
堆栈跟踪:
caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.servlet.http.HttpServletResponse com.****.****.aspect.LogProvider.response; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.http.HttpServletResponse] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:506)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
... 33 more
Run Code Online (Sandbox Code Playgroud)
任何帮助将不胜感激.
我在这里检查了测试项目:https://github.com/loesak/spring-aop-injection-bug
给出以下pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.loesoft</groupId>
<artifactId>spring-aop-injection-bug</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<spring.framework.bom.version>4.2.0.RELEASE</spring.framework.bom.version>
<spring.retry.version>1.1.2.RELEASE</spring.retry.version>
<aspectj.aspectjweaver>1.8.7</aspectj.aspectjweaver>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.framework.bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>${spring.retry.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.aspectjweaver}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
</project>
Run Code Online (Sandbox Code Playgroud)
和Spring配置:
package com.loesoft.spring.aop.injection.bug;
import org.springframework.beans.factory.annotation.Autowired;
import …Run Code Online (Sandbox Code Playgroud) 我有这个服务/控制器方法:
public ResponseEntity<PolicyDTO> addPolicy(@Valid @RequestBody PolicyDTO policy)
throws InternalServerException, BadRequestException {
log.debug("Adding a new policy");
}
Run Code Online (Sandbox Code Playgroud)
注意方法参数中的@Valid批注
对于此控制器,我们定义了以下方面类:
@Aspect
@Component
public class BackwardsCompatibilityHandler {
@Around("execution(* com.company.PolicyController.addPolicy(..))")
public Object ControllerMethod(JoinPoint jp)
{
// DO Code before and after the method call
}
Run Code Online (Sandbox Code Playgroud)
这方面/代理背后的主要原因是拦截方法调用,并对输入参数/返回的结果进行一些前/后处理。
我们现在面临的主要问题是@Valid批注在实际执行代码之前和之后的方面之前进行处理。
关于如何使验证检查在AOP代码中运行的任何想法?我知道有一种方法可以通过手动调用方法内部的验证器来做到这一点,但是我不想过多地触摸现有代码...因此,除此以外,还有其他任何想法...
现在执行的顺序是:
我们希望有更多类似的东西(首先是我们的代码,然后是Valid注释):
请跳过编译器错误等,该代码仅用于演示目的,不是真正的代码:)
我创建了一个名为BaseCron的bean,它有一个executeBefore()在spring的下面配置中配置的方法,用于拦截Crons类的所有方法调用并在它们之前执行.
该executeBefore()方法有一些验证.我之前验证了某些条件,如果它们是假的,我就是抛出异常.抛出异常会导致方法失败,因此Crons类中的方法不会执行.
它工作正常.
你可以建议一些其他的方法,我可以停止执行Crons类而不抛出异常.我试过回来但是没用.
<bean id="baseCronBean" class="com.myapp.cron.Jkl">
</bean>
<aop:config>
<aop:aspect id="cron" ref="baseCronBean">
<aop:pointcut id="runBefore" expression="execution(* com.myapp.cron.Abc.*.*(..)) " />
<aop:before pointcut-ref="runBefore" method="executeBefore" />
</aop:aspect>
</aop:config>
Run Code Online (Sandbox Code Playgroud)
Abc课程:
public class Abc {
public void checkCronExecution() {
log.info("Test Executed");
log.info("Test Executed");
}
}
Run Code Online (Sandbox Code Playgroud)
Jkl课程:
public class Jkl {
public void executeBefore() {
//certain validations
}
}
Run Code Online (Sandbox Code Playgroud) 我尝试将 AspectJ 添加到我的 Spring Boot 项目之一但失败了。我尝试创建非常简单的 Spring Boot 项目,但它也不起作用。我添加了 @EnableAspectJAutoProxy 注释等,但仍然相同。我究竟做错了什么?
这是这个简单项目的代码:
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Aspect
@Component
public class MessageAspect {
@Before("execution(* com.example.demo.MessageController.mainMethod())")
public void beforeMethod() {
System.out.println("before method");
}
}
@RestController
public class MessageController {
@RequestMapping(value = "/")
public String mainMethod() {
return "result from mainMethod";
}
}
Run Code Online (Sandbox Code Playgroud)
Maven 依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud) 我有一个自定义记录器@Aspect,我希望它始终在最后运行,这样无论控制器返回什么响应,它都会被记录到数据库中(所以我@Order(1)在这方面添加了一个)。我还编写了一个错误处理程序,用于@ControllerAdvice处理所有意外异常并返回500自定义响应正文,并且我希望记录器也记录它,因此我@Order(2)在其上添加了一个,但是看起来放置@Order注释并没有安排Spring Aspect 和 Spring ControllerAdvice 之间的命令,那么如何让我的错误处理程序始终在记录器之前运行?(当然,无需将我的错误处理程序转换为另一个 Spring Aspect)
据我了解,SpringAutowiredAnnotationBeanPostProcessor在postProcessBeforeInitialization舞台上管理自动装配机制。但它如何注入应该在postProcessAfterInitialization舞台上创建的代理呢?
假设我有这个 Spring 配置
@Service
class RegularBean {
// injected on postProcessBeforeInitialization stage
@Autowired
private TransactionBean tBean;
// invoked in between of postProcessBeforeInitialization and postProcessAfterInitialization
@PostConstruct
void init() {
tBean.transactionMethod();
}
}
@Service
class TransactionBean {
// transactional proxy is created on postProcessAfterInitialization stage
@Transactional
public void transactionMethod() { ... }
}
Run Code Online (Sandbox Code Playgroud)
事务代理是在postProcessAfterInitialization舞台上创建的。但@PostConstruct在它之前被调用。注入是否tBean包含事务代理?如果是这样,那为什么呢?因为不应该这样。如果不包装,那么以后的交易将如何处理?
假设我用构造函数注入替换字段注入。它会以某种方式改变行为吗?
据我了解,Spring AOP一般涉及:
为什么不一直使用 CGLIB 代理呢?我期望获得以下收益:
spring-aop ×10
spring ×9
aop ×3
java ×3
aspectj ×2
spring-boot ×2
spring-mvc ×2
cglib ×1
expression ×1
pointcut ×1
servlets ×1
validation ×1