spring之AOP

什么是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

@Slf4j
public 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方法,在调用真正方法之前,进行的操作。

通过代码配置增强

@Slf4j
public 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
@Slf4j
public 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>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值