Aspect-Oriented Programming (AOP)

AOP只是一種概念,因此許多人會覺得聽起來非常抽象,很難理解它的意思,原先我們使用OOP時,會發現我們都是由上到下的完成一個流程,但AOP卻會Cross-cutting concern,將整個流程橫切,如安全 (Security)檢查、交易(Transaction)等系統層面的服務(Service),在一些應用程式之中常被見到安插至各個物件的處理流程之 中,這些動作在AOP的術語中被稱之為Cross-cutting concerns。

由上一篇所說,Spring大部分建立在IOC(DI)技術上,並且讓Spring控管這些類別,也只有Spring所控管的類別才可以達到SpringAOP的效果,因此我們不免地要建立一個XML讓Spring可以映射這些類別,並且去管控。

若想看更詳細的AOP介紹可以來此AOP 觀念與術語,不過我這邊會先說明如何使用Spring AOP。

建立User.java
public class User {

    private String account;

    private String nickName;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "User [account=" + account + ", nickName=" + nickName + "]";
    }

}
建立UserDAO.java
@Component
public class UserDAO {

    public User getUserById(int id) {
        System.out.println("Method getUserById() called");
        return new User();
    }

    public int setUser(User user) {
        System.out.println("Method setUser() called");
        return 0;
    }

    public int delUserById(int id) {
        return 0;
    }

}
建立UserAspect.java
@Aspect
public class UserAspect {

    @Before("execution(* com.andy.userdashboard.dao.UserDAO.getUserById(..))")
    public void logBeforeV1(JoinPoint joinPoint) {
        System.out.println("UserAspect.logBeforeV1() : " + joinPoint.getSignature().getName());
    }

    @Before("execution(* com.andy.userdashboard.dao.UserDAO.*(..))")
    public void logBeforeV2(JoinPoint joinPoint) {
        System.out.println("UserAspect.logBeforeV2() : " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.andy.userdashboard.dao.UserDAO.getUserById(..))")
    public void logAfterV1(JoinPoint joinPoint) {
        System.out.println("UserAspect.logAfterV1() : " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.andy.userdashboard.dao.UserDAO.*(..))")
    public void logAfterV2(JoinPoint joinPoint) {
        System.out.println("UserAspect.logAfterV2() : " + joinPoint.getSignature().getName());
    }

}
修改WebConfig.java
@Configuration
@EnableWebMvc
@EnableAspectJAutoProxy
@ComponentScan({"com.spring.example"})
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Bean
    public UserAspect getUserAspect() {
        return new UserAspect();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); 
    }

}
建立HomeController.java
@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    @Autowired
    private UserDAO userDAO;

    /**
     * Simply selects the home view to render by returning its name.
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );

        userDAO.getUserById(1);
        userDAO.setUser(new User());

        return "home";
    }

}

當上述都完成後,只要呼叫首頁時候就會出現下面的回應

INFO : com.andy.userdashboard.controller.HomeController - Welcome home! The client locale is zh_TW.
UserAspect.logBeforeV1() : getUserById
UserAspect.logBeforeV2() : getUserById
Method getUserById() called
UserAspect.logAfterV2() : getUserById
UserAspect.logAfterV1() : getUserById
UserAspect.logBeforeV2() : setUser
Method setUser() called
UserAspect.logAfterV2() : setUser

小節

在這邊我們可以看到,當呼叫這個DAO某些方法時,會啟動我們所設置的AOP功能,目前Spring有提供幾種Advice,可以於Method呼叫前後或者其他橫切點使用,也可以取得所設定的物件以及回傳的物件,這種方式我相信很適合做日誌事件的使用,往後修改日誌事件時也不需修改主程式,在某些網站還有提到安全檢查提升的問題,我想這部分可以往後再去進行研究。

results matching ""

    No results matching ""