spring之AOP

来源:互联网 发布:js原型继承原理 编辑:程序博客网 时间:2024/04/29 22:16

什么是AOP

Aspect Oriented Programing面向切面编程。利用AOP的技术我们可以轻松消除分散在各个模块逻辑代码中的重复代码,可以对我们指定的方法增强,代理等等

aop术语

  • 连接点Joinpoint
    程序执行的某个特定的位置,某个方法调用前,某个方法调用后,方法抛出异常后等等
  • 切点pointcut
  • 增强advice
  • 目标Target
    需要被增强的目标类
  • 引入 introduction为某个类添加属性或者方法,或者继承某个类
  • 织入weaving
  • 代理proxy
  • 切面aspect

    springAop代理机制

  • jdk自带的动态代理
    jdk的动态代理主要用到了Java.lang.reflect包下面的Proxy和InvocationHandler

代理类PlayGameProxy .java

public class PlayGameProxy implements InvocationHandler {    static Logger logger = Logger.getLogger(PlayGameProxy.class);    Object traget;    public PlayGameProxy(Object playGameInterface) {        this.traget = playGameInterface;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        logger.info("打开电脑噢");        Object object = method.invoke(traget, args);        logger.info("关闭电脑");        return object;    }}

调用类

public class NormalProxyTest {    @Test    public void test() {        PlayGameInterface pg = new StartPlayGame();        PlayGameProxy pgProxy = new PlayGameProxy(pg);        // Proxy.newProxyInstance()最主要是这个方法        PlayGameInterface PlayGaemProxy = (PlayGameInterface) Proxy.newProxyInstance(pg.getClass().getClassLoader(), pg.getClass().getInterfaces(),                pgProxy);        PlayGaemProxy.palyGame();        PlayGaemProxy.palyGame2();    }}
  • cglib的动态代理,因为jdk的代理技术必须要有接口类,如果某些类没有接口,就无法实现动态代理了,所以这时候cglib就出场了采用了字节码技术。主要用到了Enhancer类去增强

CglibProxy.java

@Slf4jpublic class CglibProxy implements MethodInterceptor {    Enhancer enhancer = new Enhancer();    public Object getProxy(Class clazz) {        enhancer.setSuperclass(clazz);// 设置子类需要创建的类        enhancer.setCallback(this);        return enhancer.create();// 创建子类实例    }    public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable {        log.info("【1】打开电脑噢");        Object result = proxy.invokeSuper(obj, args);        log.info("【3】关闭电脑");        return result;    }}

测试类

@Test    public void test() {        CglibProxy cglibProxy = new CglibProxy();        StartPlayGame startPlayGame = (StartPlayGame) cglibProxy.getProxy(StartPlayGame.class);        startPlayGame.palyGame();        startPlayGame.palyGame2();    }

输出结果:

2015-06-30 15:02:43,785 INFO [main] (CglibProxy.java:27) - 【1】打开电脑噢
开始玩游戏了噢
2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:29) - 【3】关闭电脑
2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:27) - 【1】打开电脑噢
开始玩游戏了噢22222
2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:29) - 【3】关闭电脑

缺点:
如果我们只想给某个类(StartPlayGame)的某个方法(palyGame)加上代理,其他的方法不加代理。使用动态代理是做不到的,动态代理会给目标类的所有方法都加上增强。
如果我们代理一个类就要专门为这个类创建代理过程,编写相关代码,无法做到通用。
所以后面才引入了切点切面的概念!

Aop联盟

创建增强类Advice MethodBeforeAdvice(前置增强)、MethodInterceptor(拦截)、AfterReturningAdvice(后置增强)
其实实现效果就是相当于动态代理技术里面 invoke或者intercept方法,在调用真正方法之前,进行的操作。

通过代码配置增强

@Slf4jpublic class PlayGameBeforAdvice implements MethodBeforeAdvice {    public void before(Method method, Object[] args, Object target) throws Throwable {        log.info("【1】方法执行之前");    }}public class PlayGameAfterAdvice implements AfterReturningAdvice {    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {        log.info("【3】方法执行之后");    }}public class PlayGameInterceptor implements MethodInterceptor {    public Object invoke(MethodInvocation invocation) throws Throwable {        log.info("【1】环绕增强之前");        invocation.proceed();// 调用反射执行方法        log.info("【3】环绕增强之后");        return null;    }}

测试

private void testByMethod() {        // 测试前置增强        PlayGameBeforAdvice pba = new PlayGameBeforAdvice();        excuteAdviceByParam(pba);        // 测试后置增强        PlayGameAfterAdvice paa = new PlayGameAfterAdvice();        excuteAdviceByParam(paa);        // 测试环绕增强        PlayGameInterceptor pgi = new PlayGameInterceptor();        excuteAdviceByParam(pgi);    }    public void excuteAdviceByParam(Advice advice) {        try {            PlayGame target = new PlayGameImp();            // spring提供的代理工厂            ProxyFactory proxy = new ProxyFactory();            // 设置代理的接口            proxy.setInterfaces(target.getClass().getInterfaces());            // 设置代理目标            proxy.setTarget(target);            // 为代理目标添加增强            proxy.addAdvice(advice);            // 强制以cglib形式代理 对于单例模式来说第一次创建比较慢,后来就好。            proxy.setOptimize(true);            // 生成代理类            PlayGame play = (PlayGame) proxy.getProxy();            play.playLOL();        } catch (Exception e) {            log.error("测试aop增强失败!:" + e.getLocalizedMessage());        }    }

通过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:p="http://www.springframework.org/schema/p"    xmlns:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <!-- 前置增强,后置增强,环绕增强 -->    <bean id="target" class="com.team.gaoguangjin.springinaction.springAop.PlayGameImp" />    <bean id="playGameAfterAdvice" class="com.team.gaoguangjin.springinaction.springAop.PlayGameAfterAdvice" />    <bean id="playGameBeforAdvice" class="com.team.gaoguangjin.springinaction.springAop.PlayGameBeforAdvice" />    <bean id="playGameInterceptor" class="com.team.gaoguangjin.springinaction.springAop.PlayGameInterceptor" />    <!-- xml配置aop的关系 -->    <bean id="play" class="org.springframework.aop.framework.ProxyFactoryBean"        p:proxyInterfaces="com.team.gaoguangjin.springinaction.springAop.PlayGame"        p:interceptorNames="playGameBeforAdvice" p:target-ref="target"        p:proxyTargetClass="true" p:singleton="true" /> </beans>

测试代码

    private void testAopXml() {        try {            ApplicationContext ac = new ClassPathXmlApplicationContext("com/team/gaoguangjin/springinaction/springAop/beans.xml");            PlayGame play = (PlayGame) ac.getBean("play");            play.playLOL();        } catch (Exception e) {            log.error("测试xml aop增强失败!:" + e.getLocalizedMessage());        }    }

基于@Aspectj和schema配置的AOP

  • aspectj
    基于aspect的代码配置aop

AspectDemo.java 切面类

@Aspect@Slf4jpublic class AspectDemo {    @Before("execution(* playLOL(..))")    public void beging() {        log.info("【1】 玩游戏之前需要【打开】电脑 通过方法方式");    }    @After("execution(* playLOL(..))")    public void end() {        log.info("【3】 不想玩游戏睡觉了需要【关闭】电脑 通过方法方式");    }    @Before("execution(* playCs(..))")    public void beging1() {        log.info("【1】 玩游戏之前需要【打开】电脑 通过xml方式");    }    @After("execution(* playCs(..))")    public void end2() {        log.info("【3】 不想玩游戏睡觉了需要【关闭】电脑 通过xml方式");    }    // 拦截所有带了注解的方法    @Before("@annotation(com.team.gaoguangjin.springinaction.annoation.JdkAnnoation)")    public void annationBegin() {        log.info("【1】 注解方法运行之前");    }    @After("@annotation(com.team.gaoguangjin.springinaction.annoation.JdkAnnoation)")    public void annationEnd() {        log.info("【3】 注解方法之后");    }}

代码调用,当playLOL方法被调用时候,会触发@Before和@After
execution里面是条件

    private void getFromMethod() {        PlayGame target = new PlayGameImp();        // 定义 AspectJProxyFactory 而不是ProxyFactory        AspectJProxyFactory aspectFactory = new AspectJProxyFactory();        // 先要setTarget 然后再添加aspect切面        aspectFactory.setTarget(target);        // 添加切面类        aspectFactory.addAspect(AspectDemo.class);        // 生成切面的代理对象        PlayGame proxy = aspectFactory.getProxy();        proxy.playLOL();    }
基于aspect的xml配置aop 
<?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:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <!--【1】与【2】效果类似,用其中一个就可以了  -->    <!-- 【1】基于切面的驱动器 -->    <aop:aspectj-autoproxy />    <!--http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">        【2】自动代理创建器,    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>     -->    <bean id="playGame" class="com.team.gaoguangjin.springinaction.aspectAop.PlayGameImp" />    <bean id="asepectDemo" class="com.team.gaoguangjin.springinaction.aspectAop.AspectDemo" /></beans>

调用测试,因为<aop:aspectj-autoproxy />会自动扫描注解需要代理的对象

    private void geFromXml() {        try {            ApplicationContext ac = new ClassPathXmlApplicationContext("com/team/gaoguangjin/springinaction/aspectAop/beans.xml");            PlayGame play = (PlayGame) ac.getBean("playGame");            play.playCs();        } catch (Exception e) {            log.error("通过xml方式得到aspect注解的aop失败!" + e.getLocalizedMessage());        }    }
  • Schema
    基于schemd的 aspect配置

    增强类切面AdviceMethods .java

public class AdviceMethods {    public void preGreeting(String name) {        System.out.print("所有方法都会触发!--参数为:");        System.out.println(name);    }    // 后置增强对应方法    public void afterReturning(String retVal) {        System.out.println("----afterReturning()----");        System.out.println("returnValue:" + retVal);        System.out.println("----afterReturning()----");    }    // 环绕增强对应方法    public void aroundMethod(ProceedingJoinPoint pjp) {        System.out.println("----aroundMethod()----");        System.out.println("args[0]:" + pjp.getArgs()[0]);        System.out.println("----aroundMethod()----");    }    // 抛出异常增强    public void afterThrowingMethod(IllegalArgumentException iae) {        System.out.println("----afterThrowingMethod()----");        System.out.println("exception msg:" + iae.getMessage());        System.out.println("----afterThrowingMethod()----");    }    // final增强    public void afterMethod() {        System.out.println("----afterMethod()----");    }    // ------------绑定连接点参数----------//    public void bindParams(int num, String name) {        System.out.println("----bindParams()----");        System.out.println("name:" + name);        System.out.println("num:" + num);        System.out.println("----bindParams()----");    }}
<?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:aop="http://www.springframework.org/schema/aop"    xmlns:p="http://www.springframework.org/schema/p"    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <bean id="adviceMethods" class="com.team.gaoguangjin.springinaction.schemaAspectAop.AdviceMethods" />    <aop:config proxy-target-class="true">        <!--可以配置多个 aop:aspect -->        <aop:aspect ref="adviceMethods">            <!-- args(name) 这个name属性为 public void preGreeting(String name)-->            <aop:pointcut expression="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp) and args(name)" id="playGamePointcut"/>            <!--1PlayGameImp类的所有方法运行之前 都会触发这个条件            <aop:before method="preGreeting" pointcut="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp) and args(name)" arg-names="name" />            -->            <!--2PlayGameImp类的所有方法运行之前 都会触发这个条件 1和2一样,只是用到了pointcut-->            <aop:before method="preGreeting" pointcut-ref="playGamePointcut"/>             <!--某个类的任何方法运行之后都会带返回参数的aop  -->            <aop:after-returning method="afterReturning"                pointcut="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp)"                returning="retVal" />            <aop:around method="aroundMethod" pointcut="execution(* playLOL(..)) and within(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp)"  />           </aop:aspect>    </aop:config>    <!-- 基于schemaAspectAop的配置切面-->    <bean id="playGameAfterAdvice" class="com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameAfterAdvice" />    <aop:config proxy-target-class="true">        <aop:advisor advice-ref="playGameAfterAdvice"  pointcut="execution(* com..*.playLOL(..))"/>      </aop:config>    </bean>

测试就和普通测试一样略。。。。

0 0
原创粉丝点击