Tôi có một ứng dụng đang chạy với Spring và tôi đang sử dụng AOP ở một số nơi. Vì tôi muốn sử dụng chú giải @Transactional ở cấp độ giao diện, tôi phải cho phép Spring tạo các proxy JDK. Vì vậy, tôi không đặt thuộc tính proxy-target-class thành true. Mặt khác, tôi không muốn tạo một giao diện cho mọi lớp đơn lẻ mà tôi muốn: nếu giao diện không có ý nghĩa, tôi chỉ muốn thực hiện, và Spring nên tạo một proxy CGLIB.Trộn các proxy JDK và CGLIB trong Spring
Mọi thứ hoạt động hoàn hảo, giống như tôi đã mô tả. Nhưng tôi muốn có một số chú thích khác (do tôi tạo) đi vào các giao diện và được "kế thừa" bởi các lớp thực hiện (giống như lớp @Transactional). Hóa ra là tôi không thể làm điều đó với sự hỗ trợ tích hợp cho AOP trong Spring (ít nhất tôi không thể tìm ra cách để thực hiện nó sau một số nghiên cứu. Chú thích trong giao diện không hiển thị trong lớp thực hiện, và do đó lớp học đó không được thông báo).
Vì vậy, tôi quyết định thực hiện của riêng tôi pointcut và chặn, cho phép chú thích phương pháp khác để đi trên giao diện. Về cơ bản, con trỏ của tôi tìm chú thích trên phương thức và, cho đến khi không tìm thấy, trong cùng một phương thức (cùng tên và kiểu tham số) của các giao diện mà lớp hoặc các lớp siêu thực hiện của nó.
Vấn đề là: khi tôi khai báo một hạt DefaultAdvisorAutoProxyCreator, sẽ áp dụng đúng cách điểm cắt/chặn này, hành vi của các lớp cố vấn không có giao diện bị hỏng. Rõ ràng có điều gì đó sai và Spring cố gắng ủy nhiệm các lớp của tôi hai lần, một lần với CGLIB và sau đó với JDK.
Đây là tập tin cấu hình của tôi:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250's @Resource. -->
<context:annotation-config />
<context:component-scan base-package="mypackage" />
<!-- Instruct Spring to perform declarative transaction management automatically
on annotated classes. -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="logger.advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<constructor-arg>
<bean class="mypackage.MethodAnnotationPointcut">
<constructor-arg value="mypackage.Trace" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="mypackage.TraceInterceptor" />
</constructor-arg>
</bean>
</beans>
Đây là lớp học Tôi muốn được proxy, không có giao diện:
@Component
public class ServiceExecutorImpl
{
@Transactional
public Object execute(...)
{
...
}
}
Khi tôi cố gắng autowire nó trong một số đậu khác , như:
public class BaseService {
@Autowired
private ServiceExecutorImpl serviceExecutorImpl;
...
}
Tôi nhận được ngoại lệ sau:
java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26
Đây là một số dòng sản lượng mùa xuân:
13:51:12,672 [main] DEBUG [org.springframework.aop.framework.Cglib2AopProxy] - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [[email protected]]
...
13:51:12,782 [main] DEBUG [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'serviceExecutorImpl' with 0 common interceptors and 1 specific interceptors
13:51:12,783 [main] DEBUG [org.springframework.aop.framework.JdkDynamicAopProxy] - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [mypackage.Servi[email protected]]
tôi có thể cung cấp đầy đủ các đầu ra nếu ai đó nghĩ nó sẽ giúp. Tôi không biết tại sao Spring lại cố gắng "nhân đôi" lớp của tôi, và tại sao điều này chỉ xảy ra khi tôi tuyên bố bean DefaultAdvisorAutoProxyCreator.
Tôi đã phải vật lộn với điều này một thời gian, và mọi trợ giúp hoặc ý tưởng sẽ được đánh giá rất nhiều.
EDIT:
Đây là mã nguồn chặn của tôi, theo yêu cầu. Về cơ bản nó đăng nhập thực hiện phương thức (chỉ các phương thức được chú thích bằng @Trace mới bị chặn). Nếu phương thức được chú thích bằng @Trace (sai), quá trình ghi sẽ bị tạm dừng cho đến khi phương thức trả về.
public class TraceInterceptor
implements
MethodInterceptor
{
@Override
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
if(ThreadExecutionContext.getCurrentContext().isLogSuspended()) {
return invocation.proceed();
}
Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(),
invocation.getThis().getClass());
Trace traceAnnotation = method.getAnnotation(Trace.class);
if(traceAnnotation != null && traceAnnotation.value() == false) {
ThreadExecutionContext.getCurrentContext().suspendLogging();
Object result = invocation.proceed();
ThreadExecutionContext.getCurrentContext().resumeLogging();
return result;
}
ThreadExecutionContext.startNestedLevel();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss.SSS");
Logger.log("Timestamp: " + dateFormat.format(new Date()));
String toString = invocation.getThis().toString();
Logger.log("Class: " + toString.substring(0, toString.lastIndexOf('@')));
Logger.log("Method: " + getMethodName(method));
Logger.log("Parameters: ");
for(Object arg : invocation.getArguments()) {
Logger.log(arg);
}
long before = System.currentTimeMillis();
try {
Object result = invocation.proceed();
Logger.log("Return: ");
Logger.log(result);
return result;
} finally {
long after = System.currentTimeMillis();
Logger.log("Total execution time (ms): " + (after - before));
ThreadExecutionContext.endNestedLevel();
}
}
// Just formats a method name, with parameter and return types
private String getMethodName(
Method method)
{
StringBuffer methodName = new StringBuffer(method.getReturnType().getSimpleName() + " "
+ method.getName() + "(");
Class<?>[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length == 0) {
methodName.append(")");
} else {
int index;
for(index = 0; index < (parameterTypes.length - 1); index++) {
methodName.append(parameterTypes[ index ].getSimpleName() + ", ");
}
methodName.append(parameterTypes[ index ].getSimpleName() + ")");
}
return methodName.toString();
}
}
Cảm ơn!
Vâng, tôi đoán proxy CGLIB triển khai một số (các) giao diện. Nhưng tôi nghĩ điều này nằm ngoài tầm kiểm soát của tôi, đúng không? Lớp gốc, mà tôi đã viết, không thực hiện bất kỳ giao diện nào, hoặc thậm chí mở rộng bất kỳ lớp nào (ngoại trừ Object, dĩ nhiên). Tôi không biết liệu việc kiểm tra Tư vấn có thể giúp tôi hay không, vì lớp học này đang được Spring khuyên dùng (do @Transactional), và không phải do bất kỳ điểm nào tôi viết. Tôi sẽ xem để xem liệu nó có thể giúp tôi không. Cảm ơn! –
bạn có thể hiển thị mã chặn không? – Bozho
Tôi đã chỉnh sửa câu hỏi của mình với nguồn cho máy đánh chặn. Bất kỳ đầu mối? –