Spring 控制器中的 AOP 切入点
Spring AOP pointcut in a controller
我已经定义了这个 class 对服务工作正常的方面(不允许访问,除非用户具有 MANAGER 角色,但是控制器没有限制(?)
@Aspect
public class DeviceAspect extends ServiceSupport {
@Pointcut("execution(* fo.belecam.services.client.ManageLicenseService.*(..))")
public void manage() {
}
@Pointcut("execution(* fo.belecam.services.client.AwardService.*(..))")
public void award() {
}
@Pointcut("execution(* fo.belecam.services.client.DeviceService.*(..))")
public void handleDeviceServiceMethod() {
}
@Pointcut("execution(* fo.belecam.controller.manage.ImportController.*(..))")
public void handleImportController() {
}
@Before("fo.belecam.services.aop.DeviceAspect.handleImportController() || fo.belecam.services.aop.DeviceAspect.handleDeviceServiceMethod() || fo.belecam.services.aop.DeviceAspect.manage() || fo.belecam.services.aop.DeviceAspect.award()")
@After ("fo.belecam.services.aop.DeviceAspect.handleImportController() || fo.belecam.services.aop.DeviceAspect.handleDeviceServiceMethod() || fo.belecam.services.aop.DeviceAspect.manage() || fo.belecam.services.aop.DeviceAspect.award()")
public void before(JoinPoint _jp) {
User user = getUser();
if(user == null || user.getUserRole() != UserRole.MANAGER) {
throw new NoSufficientRoleException(user == null ? null : user.getUserRole(), UserRole.MANAGER);
}
}
}
和 ImportController:
@SuppressWarnings("deprecation")
public class ImportController extends AbstractFormController {
private String view;
private String successView;
@Autowired
protected UserService userService;
@Autowired
private ManageDeviceService manageDeviceService;
public String getView() {
return view;
}
public void setView(String view) {
this.view = view;
}
public String getSuccessView() {
return successView;
}
public void setSuccessView(String successView) {
this.successView = successView;
}
@Override
public ModelAndView processFormSubmission(final HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
final ModelAndView mav = new ModelAndView(getView());
FileUploadCommand file = (FileUploadCommand)command;
MultipartFile multipartFile = file.getFile();
if(multipartFile!=null && multipartFile.getSize()>0) {
Workbook workbook = Workbook.getWorkbook(multipartFile.getInputStream());
DataCollector dataCollector = new XLSDataCollector(workbook, true);
final List<Application> applications = manageDeviceService.loadApplications (dataCollector.getDataCollection());
List<ApplicationImporterError> importationErrors = manageDeviceService.validateApplications(applications);
savedApplications.add(manageDeviceService.getApplicationById(application.getId(), true));
}
return mav;
}
@Override
public ModelAndView showForm(HttpServletRequest request, HttpServletResponse arg1, BindException errors)
throws Exception {
return new ModelAndView(getView());
}
}
/**
* @param applications
* @param competentBody
* @return
* @throws Exception
*/
private List<Application> saveApplications(List<Application> applications,User user) throws Exception {
return manageDeviceService.saveImportedApplications (applications, user);
}
/**
* @param session
* @return
*/
public User getUser(HttpSession session) {
User user = (User) session.getAttribute(Const.SESSION_USER);
if (user == null) {
user = new User();
}
return user;
}
}
当我以UserRole.MANAGER登录时调用之前的方法(JoinPoint _jp),否则不会
我明白了,当我在浏览器中粘贴 URL 时,方法 before(JoinPoint _jp) 没有被调用.... http://127.0.0.1:7001/devices/manage/import.do
Spring 基于动态代理的 AOP 的一个限制是它只能建议 public 个连接点——来自文档:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
您的许多控制器方法是 protected
,因此尽管您的控制器注册为 Spring bean(大概)- 不建议使用非 public 方法。
问题可能在于控制器对象本身调用了您试图拦截的方法(毕竟它们最初是受保护的),这是从抽象控制器直接调用其覆盖的方法之一。 Spring AOP 的工作方式是它在您的对象周围创建一个代理来拦截方法,但只有在通过 Spring 注入的依赖项从外部调用该方法时才有效。这里不是这种情况——因为该方法是从它自己的对象中调用的,所以该调用会绕过任何为 "outside world".
包装对象的代理。
我遇到了同样的问题,对 Repository 的建议有效,但对 Controller 的建议无效。最后我找到了解决办法。简而言之,您需要确保您的 AOP 定义是在 Servlet 上下文中加载的,而不是其他上下文。
在我的例子中,我的 Spring AOP 定义是在 tools-config.xml
中定义的。从这里移动后
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/tools-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
到这里,
<servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/mvc-core-config.xml, classpath:spring/tools-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Controller 的建议有效。
我已经定义了这个 class 对服务工作正常的方面(不允许访问,除非用户具有 MANAGER 角色,但是控制器没有限制(?)
@Aspect
public class DeviceAspect extends ServiceSupport {
@Pointcut("execution(* fo.belecam.services.client.ManageLicenseService.*(..))")
public void manage() {
}
@Pointcut("execution(* fo.belecam.services.client.AwardService.*(..))")
public void award() {
}
@Pointcut("execution(* fo.belecam.services.client.DeviceService.*(..))")
public void handleDeviceServiceMethod() {
}
@Pointcut("execution(* fo.belecam.controller.manage.ImportController.*(..))")
public void handleImportController() {
}
@Before("fo.belecam.services.aop.DeviceAspect.handleImportController() || fo.belecam.services.aop.DeviceAspect.handleDeviceServiceMethod() || fo.belecam.services.aop.DeviceAspect.manage() || fo.belecam.services.aop.DeviceAspect.award()")
@After ("fo.belecam.services.aop.DeviceAspect.handleImportController() || fo.belecam.services.aop.DeviceAspect.handleDeviceServiceMethod() || fo.belecam.services.aop.DeviceAspect.manage() || fo.belecam.services.aop.DeviceAspect.award()")
public void before(JoinPoint _jp) {
User user = getUser();
if(user == null || user.getUserRole() != UserRole.MANAGER) {
throw new NoSufficientRoleException(user == null ? null : user.getUserRole(), UserRole.MANAGER);
}
}
}
和 ImportController:
@SuppressWarnings("deprecation")
public class ImportController extends AbstractFormController {
private String view;
private String successView;
@Autowired
protected UserService userService;
@Autowired
private ManageDeviceService manageDeviceService;
public String getView() {
return view;
}
public void setView(String view) {
this.view = view;
}
public String getSuccessView() {
return successView;
}
public void setSuccessView(String successView) {
this.successView = successView;
}
@Override
public ModelAndView processFormSubmission(final HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
final ModelAndView mav = new ModelAndView(getView());
FileUploadCommand file = (FileUploadCommand)command;
MultipartFile multipartFile = file.getFile();
if(multipartFile!=null && multipartFile.getSize()>0) {
Workbook workbook = Workbook.getWorkbook(multipartFile.getInputStream());
DataCollector dataCollector = new XLSDataCollector(workbook, true);
final List<Application> applications = manageDeviceService.loadApplications (dataCollector.getDataCollection());
List<ApplicationImporterError> importationErrors = manageDeviceService.validateApplications(applications);
savedApplications.add(manageDeviceService.getApplicationById(application.getId(), true));
}
return mav;
}
@Override
public ModelAndView showForm(HttpServletRequest request, HttpServletResponse arg1, BindException errors)
throws Exception {
return new ModelAndView(getView());
}
}
/**
* @param applications
* @param competentBody
* @return
* @throws Exception
*/
private List<Application> saveApplications(List<Application> applications,User user) throws Exception {
return manageDeviceService.saveImportedApplications (applications, user);
}
/**
* @param session
* @return
*/
public User getUser(HttpSession session) {
User user = (User) session.getAttribute(Const.SESSION_USER);
if (user == null) {
user = new User();
}
return user;
}
}
当我以UserRole.MANAGER登录时调用之前的方法(JoinPoint _jp),否则不会
我明白了,当我在浏览器中粘贴 URL 时,方法 before(JoinPoint _jp) 没有被调用.... http://127.0.0.1:7001/devices/manage/import.do
Spring 基于动态代理的 AOP 的一个限制是它只能建议 public 个连接点——来自文档:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only! If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
您的许多控制器方法是 protected
,因此尽管您的控制器注册为 Spring bean(大概)- 不建议使用非 public 方法。
问题可能在于控制器对象本身调用了您试图拦截的方法(毕竟它们最初是受保护的),这是从抽象控制器直接调用其覆盖的方法之一。 Spring AOP 的工作方式是它在您的对象周围创建一个代理来拦截方法,但只有在通过 Spring 注入的依赖项从外部调用该方法时才有效。这里不是这种情况——因为该方法是从它自己的对象中调用的,所以该调用会绕过任何为 "outside world".
包装对象的代理。我遇到了同样的问题,对 Repository 的建议有效,但对 Controller 的建议无效。最后我找到了解决办法。简而言之,您需要确保您的 AOP 定义是在 Servlet 上下文中加载的,而不是其他上下文。
在我的例子中,我的 Spring AOP 定义是在 tools-config.xml
中定义的。从这里移动后
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/tools-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
到这里,
<servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/mvc-core-config.xml, classpath:spring/tools-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Controller 的建议有效。