005-搭建框架-实现AOP机制【二】AOP技术

一、什么是AOP

  aspect-oriented  programming,面向切面编程,对oop的一种补充。

  著名示例,aspectj,spring+aspectj

二、aop之代码重构

2.1、代理重构

示例代码

public interface Greeting {
    void sayHello(String name);
}

实现

public class GreetingImpl implements Greeting {
    @Override
    public void sayHello(String name) {
        before();
        System.out.println("hello " + name);
        after();
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }
}

以上便是标准的死代码,不宜扩展。

代码重构:

1、静态代理

2、JDK动态代理

3、CGLib动态代理

2.1.1、静态代理

实现

package com.lhx.chapter4.aop.staticproxy;

import com.lhx.chapter4.aop.Greeting;
import com.lhx.chapter4.aop.GreetingImpl;

public class GreetingProxy implements Greeting {
    private GreetingImpl greetingImpl;
    public GreetingProxy(GreetingImpl greetingImpl){
        this.greetingImpl=greetingImpl;
    }

    @Override
    public void sayHello(String name) {
        before();
        greetingImpl.sayHello(name);
        after();
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }
}
View Code

调用

package com.lhx.chapter4.aop.staticproxy;

import com.lhx.chapter4.aop.Greeting;
import com.lhx.chapter4.aop.GreetingImpl;

public class Client {
    public static void main(String[] args) {
        Greeting proxy = new GreetingProxy(new GreetingImpl());
        proxy.sayHello("木子旭");
    }
}
View Code

会导致XxxProxy越来越多。

2.1.2、JDK动态代理

实现

package com.lhx.chapter4.aop.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler {
    private Object target;

    public JDKDynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }
}
View Code

调用

package com.lhx.chapter4.aop.dynamicproxy;

import com.lhx.chapter4.aop.Greeting;
import com.lhx.chapter4.aop.GreetingImpl;

public class JDKDynamicProxyClient {
    public static void main(String[] args) {
        JDKDynamicProxy dynamicProxy = new JDKDynamicProxy(new GreetingImpl());
        Greeting proxy = dynamicProxy.getProxy();
        proxy.sayHello("muzixu");
    }
}
View Code

所有的动态代理都合并到代理类了,JDK动态代理只能代理接口,不能代理没有接口的类。

2.1.3、CGLib动态代理

实现

package com.lhx.chapter4.aop.dynamicproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibDynamicProxy implements MethodInterceptor {
    private static CGLibDynamicProxy instance = new CGLibDynamicProxy();
    private CGLibDynamicProxy(){
    }
    public static CGLibDynamicProxy getInstance(){
        return instance;
    }
    public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls,this);
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o,objects);
        after();
        return result;
    }
    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }
}
View Code

调用

package com.lhx.chapter4.aop.dynamicproxy;

import com.lhx.chapter4.aop.Greeting;
import com.lhx.chapter4.aop.GreetingImpl;

public class CGLibDynamicProxyClient {
    public static void main(String[] args) {
        Greeting proxy = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        proxy.sayHello("muzixu");
    }
}
View Code

2.2、spring aop

1、spring aop(编程式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】

  可以理解为增强类

前置增强

package com.lhx.chapter4.aop.springaop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Before");
    }
}
View Code 

后置增强

package com.lhx.chapter4.aop.springaop;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class GreetingAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("after");
    }
}
View Code

调用

package com.lhx.chapter4.aop.springaop;

import com.lhx.chapter4.aop.GreetingImpl;
import org.springframework.aop.framework.ProxyFactory;

public class GreetingAdviceClient {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
        proxyFactory.setTarget(new GreetingImpl());//射入目标类对象
        proxyFactory.addAdvice(new GreetingBeforeAdvice());//前置通知
        proxyFactory.addAdvice(new GreetingAfterAdvice());//后置
        //proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice());//前后结合
        proxyFactory.addAdvice(new GreetingAroundAdvice());//环绕

        GreetingImpl greeting = (GreetingImpl) proxyFactory.getProxy();
        greeting.sayHello("木子旭");
    }
}
View Code

 前置后置合并一个

package com.lhx.chapter4.aop.springaop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("After");
    }

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Before");
    }
}
View Code

环绕通知

package com.lhx.chapter4.aop.springaop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

@Component
public class GreetingAroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        before();
        Object proceed = methodInvocation.proceed();
        after();
        return proceed;
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("after");
    }
}
View Code

  环绕通知不是spring提供是由org.aopalliance.intercept.MethodInterceptor提供

2、spring aop(声明式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】

spring的application-aop.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"
      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">
    <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
    <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
    <!--配置一个代理-->
    <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--需要代理的接口-->
        <property name="interfaces" value="com.lhx.chapter4.aop.Greeting"></property>
        <!--接口实现类-->
        <property name="target" ref="greetingImpl"></property>
        <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
        <property name="interceptorNames">
            <list>
                <value>greetingAroundAdvice</value>
            </list>
        </property>
    </bean>
</beans>
View Code

  ProxyFactoryBean相当于ProxyFactory

在部分实现类增加@Component注解

客户端调用

package com.lhx.chapter4.aop.springaopdeclare;

import com.lhx.chapter4.aop.Greeting;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
        //获取spring content
        ApplicationContext context =new ClassPathXmlApplicationContext("application-aop.xml");
        //从Content中根据id获取Bean对象,其实就是一个代理
        Greeting greetingProxy = (Greeting) context.getBean("greetingProxy");
        greetingProxy.sayHello("muzixu ");

    }
}
View Code

3、Spring AOP:Throws Advice【抛出增强】

  程序报错,抛出异常,一般做法打印到控制台或者日志文件。一劳永逸办法使用,Throws Advice

 异常接口

package com.lhx.chapter4.aop;

public interface Greeting {
    void sayHello(String name);
    void sayHelloThrows(String name);
}
View Code

抛出异常增强类

package com.lhx.chapter4.aop.springaopthrows;

import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

public class GreetingThrowsAdvice implements ThrowsAdvice {
    //public void afterThrowing(Exception ex)
    //public void afterThrowing(RemoteException)
    //public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
    //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
        System.out.println("--------Throw Exception-------");
        System.out.println("Target class:"+target.getClass().getName());
        System.out.println("Method name:"+method.getName());
        System.out.println("Exception:"+ex.getMessage());
        System.out.println("------------------------------");

    }
}
View Code

  接口中没有任何抽象方法,但是自定义又会出异常,Spirng内部是用反射来实现方法匹配的,需要实现下列接口中的其中1个

    //public void afterThrowing(Exception ex)
    //public void afterThrowing(RemoteException)
    //public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
    //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)

 4、Spring AOP:Introduction Advice【引入增强】

  在AOP中对方法的增强叫Weaving【织入】,对类的增强叫Introduction【引用】,Introduction advice【引用增强】就是对类功能的增强。

  示例:类C实现了A接口,那么使类C可以调用B接口

新增一个接口

package com.lhx.chapter4.aop.springaopintroductionadvice;

public interface Apology {
    void saySorry(String name);
}
View Code

不想改变原有类的实现,需要使用引用增强

package com.lhx.chapter4.aop.springaopintroductionadvice;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

public class GreetingIntroductionAdvice extends DelegatingIntroductionInterceptor implements Apology {
    @Override
    public void saySorry(String name) {
        System.out.println("sorry " + name);
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        return super.invoke(mi);
    }
}
View Code

  引用增强类扩展了org.springframework.aop.support.DelegatingIntroductionInterceptor,同时实现了需要的接口

需要的Spring配置application-aop-intro.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"
       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">
    <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
    <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
    <!--配置一个代理-->
    <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--需要代理的接口-->
        <property name="interfaces" value="com.lhx.chapter4.aop.springaopintroductionadvice.Apology"></property>
        <!--接口实现类-->
        <property name="target" ref="greetingImpl"></property>
        <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
        <property name="interceptorNames" value="greetingIntroductionAdvice">

        </property>
        <!--代理目标 false 接口 true 类 默认是false-->
        <property name="proxyTargetClass" value="true"></property>
    </bean>
</beans>
View Code

  proxyTargetClass属性,默认false,代理接口,此时Spring使用JDK动态代理

      如果是true,此时Spring使用CGLib动态代理。

客户端调用

package com.lhx.chapter4.aop.springaopintroductionadvice;

import com.lhx.chapter4.aop.Greeting;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
        //获取spring content
        ApplicationContext context =new ClassPathXmlApplicationContext("application-aop-intro.xml");
        //从Content中根据id获取Bean对象,其实就是一个代理
        Greeting greetingProxy = (Greeting) context.getBean("greetingProxy");
        greetingProxy.sayHello("muzixu ");
        Apology apology = (Apology) greetingProxy;
        //将目标类强制向上转型为Apology接口类型【引入增强的特性,即“动态接口实现”功能】
        apology.saySorry("Jack");

    }
}
View Code

5、Spring AOP:切面

  之前说的AOP框架其实可以理解为一个拦截器框架,他拦截了一个类,其实就是拦截了所有方法。在使用动态代理时,也需要在代码中对所拦截的方法名加以判断,才能过滤出需要拦截的方法。

  Advisor【切面】封装了Advice【增强】与Pointcut【切面】。

增加两个以good开头的方法

package com.lhx.chapter4.aop.aoppointcut;

import com.lhx.chapter4.aop.Greeting;
import org.springframework.stereotype.Component;

@Component
public class GreetingGood implements Greeting {
    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }

    public void goodMorning(String name) {
        System.out.println("goodMorning " + name);
    }

    public void goodNight(String name) {
        System.out.println("goodNight " + name);
    }

    @Override
    public void sayHelloThrows(String name) {

    }
}
View Code

基于正则表达式的切面类

<?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">
    <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
    <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
    <!--配置一个切面-->
    <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="greetingAroundAdvice"></property>
        <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property>
    </bean>
    <!--配置一个代理-->
    <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--接口实现类-->
        <property name="target" ref="greetingGood"></property>
        <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
        <property name="interceptorNames" value="pointcutAdvisor"></property>
        <!--代理目标 false 接口 true 类 默认是false-->
        <property name="proxyTargetClass" value="true"></property>
    </bean>
</beans>
View Code

  interceptorNames不在是一个增强,而是一个切面。

  表达式中的".*",标示匹配所有字符。

Spring AOP提供的几个切面类

  RegexpMethodPointcutAdvisor

  DefaultPointcutAdvisor

  NameMatchMethodPointcutAdvisor

  AspectJExpressionPointcutAdvisor

 6、Spring AOP:自动代理【扫面Bean名称】

 增加spring xml配置

    <!--spring 自动代理-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--只为后缀为Impl的Bean生成代理-->
        <property name="beanNames" value="*Impl"></property>
        <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
        <property name="interceptorNames" value="greetingAroundAdvice"></property>
        <!--是否对代理生成策略进行优化-->
        <property name="optimize" value="true"></property>
    </bean>

  此处使用BeanNameAutoProxyCreator只为后缀为Impl的Bean生成代理类。

    这里提供了optimize配置项,默认为false, true含义:对生成策略进行优化,如果该类有接口,就代理接口(JDK动态代理),如果没有接口,就代理类(CGLib动态代理)。

  不像之前proxyTargetClass属性,强制代理类,而不考虑接口方式

 既然有CGLib动态代理代理类,为什么还需要JDK动态代理?

  CGLib创建动态代理的速度较慢,但创建代理后运行速度却非常快。而JDK动态代理正好相反。如果在运行的时候不断的用CGLib创建代理, 系统性能会降低。

  所以建议一般在系统初始化的时候用CGLib去创建代理,并放入Spring的ApplicationContext中。

7、Spring AOP:自动代理【扫面切面配置】

  Spring的xml配置

    <!--配置一个切面-->
    <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="greetingAroundAdvice"></property>
        <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property>
    </bean>
    <!--spring 自动代理 扫描切面-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <!--是否对代理生成策略进行优化-->
        <property name="optimize" value="true"></property>
    </bean>

  这里无需在配置代理,因为代理将由DefaultAdvisorAutoProxyCreator自动生成,这个类可以扫描所有的切面类,并未其自动生成代理。

2.3、spring+aspectj

  见下一节

  

posted @ 2017-10-25 22:28  bjlhx15  阅读(259)  评论(0编辑  收藏  举报
Copyright ©2011~2020 JD-李宏旭