Java&Spring基础技术工具癖Java学习笔记

Spring Proxy and Aop

2017-02-17  本文已影响188人  司鑫
目录说明

1 代理模式


1.1 介绍

代理(Proxy)是一种设计模式,提供了目标对象另外的访问方式,即通过代理去访问,这样做可以在目标对象实现的基础上,增加额外的操作。

举例:面试者去面试,他们不能直接去找公司老板,而是去找面试官,面试官去做筛选。其中面试官就是代理,而筛选就是增加的额外的操作。

1.2 代理的几种方式

【1】静态代理

静态代理需要代理对象目标对象实现同样的接口。

举例:模拟保存功能(贴上核心代码)

UserDao.class 直接保存

public class UserDao implements IUserDao{
    public void save() {
        System.out.println("保存");
    }
}

ProxyFactory.class 给保存方法添加事务处理

public class ProxyFactory implements IUserDao{
    private IUserDao target;
    public ProxyFactory(IUserDao target){
        this.target = target;
    }

    public void save() {
        System.out.println("开启事务");
        target.save();
        System.out.println("提交事务");
    }
}

静态代理虽然可以在不修改目标对象同时对目标对象的功能进行拓展,但是代理对象目标对象都必须实现同样的接口,所以会导致很多的代理类出现,一旦出现新的方法,目标对象和代理对象都需要维护。

【2】动态代理

在动态代理中,目标对象需要实现接口,而代理对象可以不用实现接口。代理对象的生成是利用了 JDKAPI 来实现动态代理的。

举例:模拟保存功能(贴上核心代码)

ProxyFactory.class

//动态代理不需要实现接口
public class ProxyFactory{
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//获取目标对象的类加载器
                target.getClass().getInterfaces(),//获取目标对象的接口
                new InvocationHandler() {//事件处理器
                    public Object invoke(Object proxy, Method method, 
                              Object[] args) throws Throwable {
                        System.out.println("开启事务");
                        Object returnValue = method.invoke(target, args);//执行目标方法
                        System.out.println("提交事务");
                        return returnValue;
                    }
                }
        );
    }
}

【3】Cglib 代理

使用 Cglib 代理目标对象代理对象可以不用实现任何接口。
Cglib 代理也叫子类代理,在内存中构建一个子类对象从而实现对目标对象的功能的拓展。

注意事项:

  1. 使用 Cglib 代理需要导入cglib – jar,如果我们使用 Spring 框架,那么我们只需要导入 spring-core.jar即可
  2. 代理的类不能使用 final 修饰
  3. 目标对象的方法如果是 static / final 修饰,那么该方法将不会被拦截。

举例:模拟保存功能(贴上核心代码)

ProxyFactory.class

public class ProxyFactory implements MethodInterceptor {
    //维护目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

    //给目标对象创建代理对象
    public Object getCglibProxy(){
        //创建工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数(执行 intercept 方法)
        en.setCallback(this);
        //创建子类代理对象
        return en.create();
    }
    public Object intercept(Object o, Method method, 
               Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务");
        Object returnValue = method.invoke(target, objects);
        System.out.println("提交事务");
        return returnValue;
    }
}

2 Aop 编程


2.1 介绍

Aop(aspect obeject programming)面向切面编程,让关注点代码和业务代码分离。
几个概念:

2.2 注解方式实现 Aop 编程

【1】步骤

  1. 引入 aop 相关 jar 文件
  1. 在 bean.xml 中引入名称空间
xmlns:aop="http://www.springframework.org/schema/aop"

 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd
  1. 开启 aop 注解

bean.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:p="http://www.springframework.org/schema/p"
       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:component-scan base-package="com.acey.proxy.f_aop_anno"/>

    <!-- 开启aop注解方式 -->
    <aop:aspectj-autoproxy expose-proxy="false"></aop:aspectj-autoproxy>
</beans>

4.使用注解

Aop.class (核心代码)

@Component
@Aspect //指定切面类
public class Aop{

    //统一指定切入点表达式,拦截哪些方法
    @Pointcut("execution(* com.acey.proxy.f_aop_anno.UserDao.*(..))")
    public void pointCut() {
    }

    //每个通知都可以设置单独的表达式:
    // @Before("execution(* com.acey.proxy.f_aop_anno.UserDao.*(..))") 或者
    // 使用统一的切入点表达式
    @Before("pointCut()") //前置通知,在方法执行前执行
    public void before_() {
        System.out.println("Before");
    }

    @After("pointCut()") // 后置通知,在方法执行后执行(方法出现异常继续执行)
    public void after_() {
        System.out.println("After");
    }
    @AfterReturning("pointCut()") // 返回后通知,目标方法结束后通知(方法出现异常不执行)
    public void afterReturning_() {
        System.out.println("AfterReturning");
    }
    @AfterThrowing("pointCut()") //方法出现异常时执行
    public void afterThrowing_() {
        System.out.println("After");
    }

    @Around("pointCut()") //环绕通知
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前");
        pjp.proceed(); //执行目标方法
        System.out.println("环绕后");
    }
}

【2】注解说明

2.3 xml 配置方式实现 Aop 编程

【1】步骤

  1. 引入相关 jar 文件
  2. 引入名称空间
  3. aop 配置

bean.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: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">

    <!-- dao 实例-->
    <bean id="userDao" class="com.acey.proxy.g_aop_xml.UserDao"></bean>
    <bean id="orderDao" class="com.acey.proxy.g_aop_xml.OrderDao"></bean>

    <!-- 切面类 -->
    <bean id="aop" class="com.acey.proxy.g_aop_xml.Aop"></bean>

    <!-- Aop 配置-->
    <aop:config>
        <!-- 切面表达式设置 -->
        <aop:pointcut id="pt" expression="
         execution(* com.acey.proxy.g_aop_xml.UserDao.*(..))"/>
        <!--切面类的应用(通知代码)-->
        <aop:aspect ref="aop">
            <!-- 前置通知 -->
            <aop:before method="before_" pointcut-ref="pt"></aop:before>
            <!-- 后置通知-->
            <aop:after method="after_" pointcut-ref="pt"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>
2.4 切入点表达式

【1】切入点表达式的格式:

execution( modifiers-pattern? //修饰符
           ret-type-pattern  //返回值类型
           declaring-type-pattern? // 类型声明
           name-pattern(param-pattern)//方法名及参数列表
           throws-pattern? //异常列表
          )

其中:

【2】常见的切入点表达式

所有实例代码地址 https://github.com/Aceysx/SpringDemo

上一篇 下一篇

猜你喜欢

热点阅读