Spring Controller层设置AOP

作者: markfork | 来源:发表于2016-11-01 09:58 被阅读1709次

    近期公司需要做一个web项目,项目中有需求是需要记录特定管理员的操作。操作属性包括:操作时间、操作人、触发数据量等。
    自然而然的就想到了面向切面编程(AOP)

    项目初步的技术选型是:
    Intellij idea+gradle+spring+spring security+hibernate+spring aop+jsp完成项目需求。

    项目结构图如下所示:


    项目结构图

    项目创建初期没有web.xml设置如下:
    file->project structs->facets->web->Deployment Descriptors->➕
    注意创建的web.xml存在位置与上图中的项目结构图是对应的。

    1.各配置文件:##


    1.1 build.gradle 脚本文件

    group 'com.fxmms'
    version '1.0-SNAPSHOT'
    
    apply plugin: 'java'
    apply plugin: 'idea'
    apply plugin: 'war'
    sourceCompatibility = 1.8
    
    repositories {
    
        mavenLocal()
        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
        maven { url "http://repo.maven.apache.org/maven2/"}
        mavenCentral()
    }
    
    dependencies {
        testCompile group: 'junit', name: 'junit', version: '4.12'
        // servlet-api
        compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
        //spring相关
        compile group: 'org.springframework', name: 'spring-webmvc', version: '4.3.3.RELEASE'
        compile group: 'org.springframework', name: 'spring-orm', version: '4.3.3.RELEASE'
        compile group: 'org.springframework', name: 'spring-aspects', version: '4.3.3.RELEASE'
        //hibernate jpa相关
        compile group: 'org.jboss.spec.javax.transaction', name: 'jboss-transaction-api_1.2_spec', version: '1.0.1.Final'
        compile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.2.2.Final'
        //c3p0连接池
        compile group: 'org.hibernate', name: 'hibernate-c3p0', version: '5.2.2.Final'
        //ehcahe二级缓存
        compile group: 'org.hibernate', name: 'hibernate-ehcache', version: '5.2.2.Final'
        //mysql
        compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.39'
        //springData
        compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.10.3.RELEASE'
    
    }
    

    1.2 web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
            <!--配置启动IOC容器的Listener-->
            <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </context-param>
    
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
    
            <!-- 配置字符编码过滤器  必须配置在所有过滤器的最前面 -->
            <filter>
                <filter-name>CharacterEncodingFilter</filter-name>
                <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
                <init-param>
                    <param-name>encoding</param-name>
                    <param-value>UTF-8</param-value>
                </init-param>
                <init-param>
                    <param-name>forceEncoding</param-name>
                    <param-value>true</param-value>
                </init-param>
            </filter>
    
            <filter-mapping>
                <filter-name>CharacterEncodingFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
    
            <!-- 配置看可以把POST请求转为PUT,DELETE请求的Filter -->
            <filter>
                <filter-name>HiddenHttpMethodFilter</filter-name>
                <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
            </filter>
            <filter-mapping>
                <filter-name>HiddenHttpMethodFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
            <!-- 配置 OpenEntityManagerInViewFilter. 可以解决懒加载异常的问题 -->
            <filter>
                <filter-name>OpenEntityManagerInViewFilter</filter-name>
                <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
            </filter>
    
            <filter-mapping>
                <filter-name>OpenEntityManagerInViewFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
    
            <!--配置SpringMVC的DispatcherServlet-->
            <servlet>
                <servlet-name>springDispatcherServlet</servlet-name>
                <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                <init-param>
                    <param-name>contextConfigLocation</param-name><!--这里<span style="font-family: Arial, Helvetica, sans-serif;">param-name</span>名称必须为<span style="font-family: Arial, Helvetica, sans-serif;">contextConfigLocation</span>-->
                    <param-value>classpath:dispatcher-servlet.xml</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
            </servlet>
    
            <servlet-mapping>
                <servlet-name>springDispatcherServlet</servlet-name>
                <url-pattern>/</url-pattern>
            </servlet-mapping>
    </web-app>
    

    1.3 Spring 配置文件-applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:jpa="http://www.springframework.org/schema/data/jpa"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/data/jpa
           http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
           ">
    
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.fxmms">
            <context:exclude-filter type="annotation"
                                    expression="org.springframework.stereotype.Controller" />
            <!--<context:exclude-filter type="annotation"-->
                                    <!--expression="org.springframework.web.bind.annotation.ControllerAdvice" />-->
        </context:component-scan>
    
        <!-- 配置数据源 -->
        <context:property-placeholder location="classpath:db.properties" />
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="driverClass" value="${jdbc.driverClass}"></property>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    
            <!-- 配置其他属性 -->
            <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
            <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
        </bean>
        <!-- 配置JPA部分 -->
        <!-- 配置JPA的EntityManagerFactory -->
        <bean id="entityManagerFactory"
              class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
            </property>
            <property name="packagesToScan" value="com.fxmms"></property>
            <property name="jpaProperties">
                <props>
                    <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">true</prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
    
                    <prop key="hibernate.cache.use_second_level_cache">true</prop>
                    <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory
                    </prop>
                    <prop key="hibernate.cache.use_query_cache">true</prop>
                </props>
            </property>
            <!--使用二級緩存-->
            <property name="sharedCacheMode" value="ENABLE_SELECTIVE"></property>
        </bean>
    
        <!-- 配置事务 -->
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"></property>
        </bean>
        <!-- 配置支持基于注解的事务 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    
        <!-- 配置SpringData部分 -->
        <jpa:repositories base-package="com.fxmms"
                          entity-manager-factory-ref="entityManagerFactory">
    
        </jpa:repositories>
    </beans>
    

    1.4 Spring mvc配置文件-dispatcher-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.fxmms" use-default-filters="true">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <!--<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>-->
        </context:component-scan>
        <!-- 配置视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/views/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
    
        <mvc:default-servlet-handler/>
        <mvc:annotation-driven></mvc:annotation-driven>
        <!-- <!– 配置 MultipartResolver 文件上传–>
         <bean id="multipartResolver"
               class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
             <property name="defaultEncoding" value="UTF-8"></property>
             <property name="maxUploadSize" value="1024000000"></property>
         </bean>-->
        <!-- 把切面类交由Spring容器来管理 -->
        <bean id="logAspectBean" class="com.fxmms.aspect.LogAnnotationAspect"/>
        <!-- 启用spring对AspectJ注解的支持 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>
    

    **1.5 数据库连接文件jdbc-db.properties **

    jdbc.user=root
    jdbc.password=
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql://localhost/fxmms?useUnicode=true&characterEncoding=UTF-8
    
    jdbc.initPoolSize=5
    jdbc.maxPoolSize=20
    

    1.6 aspect包中切面(封装横切关注点)文件- LogAnnotationAspect.java

    package com.fxmms.aspect;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    
    /**
     * Created by mark on 16/10/31.
     * 日志切面类
     */
    @Aspect
    public class LogAnnotationAspect {
        @SuppressWarnings("unused")
        //定义切入点
        @Pointcut("execution(* com.fxmms.controller.*.*(..))")
        private void allMethod() {
        }
    
        //针对指定的切入点表达式选择的切入点应用前置通知
        @Before("execution(* com.fxmms.controller.*.*(..))")
        public void before(JoinPoint call) {
    
            String className = call.getTarget().getClass().getName();
            String methodName = call.getSignature().getName();
    
            System.out.println("【注解-前置通知】:" + className + "类的"
                    + methodName + "方法开始了");
        }
    
        //访问命名切入点来应用后置通知
        @AfterReturning("allMethod()")
        public void afterReturn() {
            System.out.println("【注解-后置通知】:方法正常结束了");
        }
    
        //应用最终通知
        @After("allMethod()")
        public void after() {
            System.out.println("【注解-最终通知】:不管方法有没有正常执行完成,"
                    + "一定会返回的");
        }
    
        //应用异常抛出后通知
        @AfterThrowing("allMethod()")
        public void afterThrowing() {
            System.out.println("【注解-异常抛出后通知】:方法执行时出异常了");
        }
    
        //应用周围通知
        //@Around("allMethod()")
        public Object doAround(ProceedingJoinPoint call) throws Throwable {
            Object result = null;
            this.before(call);//相当于前置通知
            try {
                result = call.proceed();
                this.afterReturn(); //相当于后置通知
            } catch (Throwable e) {
                this.afterThrowing();  //相当于异常抛出后通知
                throw e;
            } finally {
                this.after();  //相当于最终通知
            }
    
            return result;
        }
    }
    

    1.7 dao包中数据库访问文件-AccountDao.java

    package com.fxmms.dao;
    import org.springframework.stereotype.Component;
    
    /**
     * Created by mark on 16/10/31.
     */
    @Component
    public class AccountDao {
        public void save(String loginname, String password) {
            //Do data access
            System.out.println("进行数据操作");
        }
    }
    

    1.8 业务逻辑层(service)-AccountService.java 其中注入AccountDao.java

    package com.fxmms.service;
    import com.fxmms.dao.AccountDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * Created by mark on 16/10/31.
     */
    @Component
    public class AccountService {
        @Autowired
        private AccountDao accountDao;
    
        public void save(String loginname, String password) {
            accountDao.save(loginname, password);
            /*throw new RuntimeException("故意抛出一个异常。。。。");*/
        }
    }
    

    **1.9 Controller层 IndexController.java 其中注入AccountService.java **

    package com.fxmms.controller;
    
    import com.fxmms.service.AccountService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * Created by mark on 16/10/31.
     */
    @Controller
    public class IndexController {
        @Autowired(required = true)
        AccountService accountService;
    
        @RequestMapping("/index")
        public String index() {
            accountService.save("张晓","asdasdas");
            return "index";
        }
    }
    

    2.测试用例


    采用junit进行单元测试
    代码如下:

    import com.fxmms.controller.IndexController;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    /**
     * Created by mark on 16/10/31.
     */
    public class SpringAopTest {
        @Test
        public void inteceptorTest() {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("dispatcher-servlet.xml");
            IndexController bean = (IndexController) ctx.getBean("indexController");
            bean.index();
        }
    }
    

    运行结果:

    【注解-前置通知】:com.fxmms.controller.IndexController类的index方法开始了
    进行数据操作
    【注解-最终通知】:不管方法有没有正常执行完成,一定会返回的
    【注解-后置通知】:方法正常结束了
    

    总结:


    当需要在Controller层设置AOP时,那么需要将 <aop:aspectj-autoproxy proxy-target-class="true"/> 配置到dispatcher-servlet.xml(MVC文件当中)当中并且设置包扫描规则为"use-default-filters="true"
    将横切关注点封装为切面,切面中方法的注入是根据切点表达式来决定的。

    博客搬家:大坤的个人博客
    欢迎评论哦~

    相关文章

      网友评论

      • lizhou828:查看了use-default-filters这个属性的定义,默认就是true <xsd:attribute name="use-default-filters" type="xsd:boolean" default="true">
      • 恣墨:原来如此……一直都是配在同一个xml里,Po主让我茅塞顿开啊

      本文标题:Spring Controller层设置AOP

      本文链接:https://www.haomeiwen.com/subject/awmputtx.html