zoukankan      html  css  js  c++  java
  • Spring框架的基本使用(AOP部分)

    AOP,Aspect Oriented Programming,意为面向切面编程,是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。一般应用于事物管理,性能监视,安全检查,缓存,日志等。

    • AOP实现原理

    AOP底层是采用代理的机制进行实现。如果是一个实现类(接口的实现类),Spring会采用jdk的动态代理Proxy。如果只是一个类,Spring会采用cglib字节码增强。

    关于动态代理可以参考我的另一篇博文:https://www.cnblogs.com/chichung/p/10358444.html

    1.动态代理的方式

    目标类:

    切面类:

    工厂类:

     2.cglib增强字节码的方式

     先导入jar包:

    核心:hibernate-distribution-3.6.10.Finallibytecodecglibcglib-2.2.jar

    依赖:struts-2.3.15.3appsstruts2-blankWEB-INFlibasm-3.3.jar

    注意:spring-core.jar已经整合以上两个内容了,现在只不过是我们自己实现原理才使用的。

    目标类和切面类写法一样,工厂类:


    • Spring编写半自动代理

    导包:Spring核心4+1依赖包,AOP联盟(规范,是一个依赖包,com.springsource.org.aopalliance)+Spring-aop(实现,aop包)

    (1)目标类

    public interface UserService {
        public abstract void addNewUser();
    }
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        @Override
        public void addNewUser() {
            // 业务逻辑操作
            System.out.println("addNewUser的操作");
        }
    }

    (2)切面类

    public class MyAspect implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            System.out.println("执行方法前的代码");
            Object obj = mi.proceed();
            System.out.println("执行方法后的代码");
            return obj;
        }
    }

    (3)Spring配置

    <?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"
           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">
    
        <!--业务类-->
        <bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean>
        <!--切面类-->
        <bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean>
        <!--使用工厂bean创建代理-->
        <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.chichung.service.UserService"></property>
            <property name="target" ref="userService"></property>
            <property name="interceptorNames" value="myAspect"></property>
        </bean>
    </beans>

    默认采用的是jdk动态代理原理,如果使用cglib的原理创建代理,加上:

    <property name="optimize" value="true"></property>

    (4)测试类

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("proxyService");
            userService.addNewUser();
        }
    }

    • Spring AOP全自动编程

    导包:Spring核心4+1依赖包,AOP联盟(规范,是一个依赖包,com.springsource.org.aopalliance)+Spring-aop(实现,aop包)+

    spring-framework-3.0.2.RELEASE-dependenciesorg.aspectjcom.springsource.org.aspectj.weaver1.6.8.RELEASE(依赖包)

    (1)目标类:

    public interface UserService {
        public abstract void addNewUser();
    }
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        @Override
        public void addNewUser() {
            // 业务逻辑操作
            System.out.println("addNewUser的操作");
        }
    }

    (2)切面类:

    public class MyAspect implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            System.out.println("执行方法前的代码");
            Object obj = mi.proceed();
            System.out.println("执行方法后的代码");
            return obj;
        }
    }

    (3)Spring配置

     (4)测试类:

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.addNewUser();
        }
    }

    • 重点:通过AspectJ实现Spring AOP

    AspectJ是一个基于Java语言的AOP框架。Spring2.0以后新增了对AspectJ切点表达式支持。新版本的Spring框架,建议使用AspectJ方式来开发AOP。

    导包

    aopalliance AOP联盟规范,spring-aop的依赖包
    spring-aop AOP实现
    aspectj.weaver AspectJ规范,spring-aspects的依赖包
    spring-aspects AspectJ实现

    1.AspectJ的通知类型

    before:前置通知(应用:各种校验)

    在方法执行前执行,如果通知抛出异常,阻止方法运行

    afterReturning:后置通知(应用:常规数据处理)

    方法正常返回后执行,如果方法中抛出异常,通知无法执行

    必须在方法执行后才执行,所以可以获得方法的返回值。

    around:环绕通知(应用:十分多,例如事务)

    方法执行前后分别执行,可以阻止方法的执行

    必须手动执行目标方法

    afterThrowing:抛出异常通知(应用:包装异常信息)

    方法抛出异常后执行,如果方法没有抛出异常,无法执行

    after:最终通知(应用:清理现场)

    方法执行完毕后执行,无论方法中是否出现异常

     

    2.切入点表达式

    execution()  用于描述方法

    语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)

    修饰符,一般省略。

    public 公共方法

    * 任意

    返回值,不能省略。

    void 返回没有值

    String 返回值字符串。

    * 任意

    com.gyf.crm 固定包

    com.gyf.crm.*.service crm包下面子包任意 (例如:com.gyf.crm.staff.service

    com.gyf.crm.. crm包下面的所有子包(含自己)

    UserServiceImpl 指定类

    *Impl 以Impl结尾

    User* 以User开头

    * 任意

    方法名,不能省略

    addUser 固定方法

    add* 以add开头

    *Do 以Do结尾

    * 任意

    (参数)

    () 无参

    (int) 一个整型

    (int ,int) 两个

    (..) 参数任意

    throws ,可省略,一般不写

    下面的是很少用或者基本不用的:

    within:

    匹配包或子包中的方法

    this:

    匹配实现接口的代理对象中的方法

    target:

    匹配实现接口的目标对象中的方法

    args:

    匹配参数格式符合标准的方法

    bean(id)  

    对指定的bean所有的方法

     

    3.AspectJ案例(基于xml配置)

    (1)before前置通知、afterReturning后置通知、afterThrowing抛出异常通知after最终通知的XML配置。

    目标类:

    public interface UserService {
        public abstract void addNewUser();
    }
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        @Override
        public void addNewUser() {
            // 业务逻辑操作
            System.out.println("addNewUser的操作");
        }
    }

    切面类(不用再实现MethodInterceptor接口)

    public class MyAspect{
        public void myBefore(){
            System.out.println("执行方法前的代码");
        }
    
        public void myAfter(){
            System.out.println("执行方法后的代码");
        }
    }

    Spring配置

    <?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: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/aop
                                 http://www.springframework.org/schema/aop/spring-aop.xsd">
    
      <!--业务类-->
      <bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean>
    <!--切面类-->
      <bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean>
    <!--proxy-target-class="true"表示用cglib字节码增强方式来实现--> <!--proxy-target-class="false"表示用动态代理的方式来实现--> <aop:config proxy-target-class="true"> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointCut" expression="execution(* com.chichung.service.impl.*.*(..))"></aop:pointcut> <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before> <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after> </aop:aspect> </aop:config> </beans>

    测试类:

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.addNewUser();
        }
    }

    (2)around环绕通知的XML配置(与前置通知后置通知稍微有点不同)

    目标类跟上面都是一样。

    切面类:

    public class MyAspect{
        public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("执行方法前的代码");
            Object obj = pjp.proceed();
            System.out.println("执行方法后的代码");
            return obj;
        }
    
    }

    Spring配置:

    <?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: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/aop
                                 http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--业务类-->
        <bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean>
        <!--切面类-->
        <bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean>
    
        <!--proxy-target-class="true"表示用cglib字节码增强方式来实现-->
        <!--proxy-target-class="false"表示用动态代理的方式来实现-->
        <aop:config proxy-target-class="true">
            <aop:aspect ref="myAspect">
                <aop:pointcut id="myPointCut" expression="execution(* com.chichung.service.impl.*.*(..))"></aop:pointcut>
                <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around>
            </aop:aspect>
        </aop:config>
    </beans>

    测试类和上面的示例一样。

    (3)总结:

    3.AspectJ案例(基于注解配置)

    (1)声明使用注解

    <?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: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/aop
                                 http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--开启注解-->
        <context:annotation-config></context:annotation-config>
        <!--扫描注解-->
        <context:component-scan base-package="com.chichung"></context:component-scan>
        <!--确定AOP注解生效-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    (2)替换service和切面bean

    @Component("userService")
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        @Override
        public void addNewUser() {
            // 业务逻辑操作
            System.out.println("addNewUser的操作");
        }
    }
    @Component("myAspect")
    public class MyAspect{
    ...

    (3)声明切面

    @Component("myAspect")
    @Aspect
    public class MyAspect{
    ...
    <!--声明切面-->
        <aop:config>
            <aop:aspect ref="myAspect"></aop:aspect>
        </aop:config>

    (4)声明公共切入点

    @Component("myAspect")
    @Aspect
    public class MyAspect{
        @Pointcut("execution(* com.chichung.service.impl.*.*(..))")
        public void myPointCut(){};
    ...

    (5)声明环绕通知(其他通知也类似,不一一举例了)

    @Component("myAspect")
    @Aspect
    public class MyAspect{
        @Pointcut("execution(* com.chichung.service.impl.*.*(..))")
        public void myPointCut(){};
    
        @Around("myPointCut()")
        public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("执行方法前的代码");
            Object obj = pjp.proceed();
            System.out.println("执行方法后的代码");
            return obj;
        }
    
    }

    (6)测试类与之前的一样。

  • 相关阅读:
    September 22nd 2016 Week 39th Thursday
    September 21st 2016 Week 39th Wednesday
    android 几个小技巧
    深入理解计算机系统----经验之谈
    输入数字如何获取
    super 和this的用法
    java程序员修炼之道
    eclipse 如何使用svn
    如何手动添加Android Dependencies包
    如何查看正在使用端口号并利用任务管理器将其关闭
  • 原文地址:https://www.cnblogs.com/chichung/p/10412164.html
Copyright © 2011-2022 走看看