Spring+Hibernate:Aop相关概念与动态代理

本博文也是属于学习总结性博文,主要来学习Spring+Hibernate的声明式事务。为此,我们从分三步进行:

  1. Spring + Hibernate : Aop相关概念与动态代理;
  2. Spring + Hibernate : 声明式事务;

  1. AOP 概念
    Aspect Oriented Programming 面向横切面的编程:
    横切性关注点: 是一种遍布在系统各个角落的、与我们的业务逻辑相结合的不是太紧密的、独立的服务,例如安全性检查、事务等。
    而我们的AOP主要是处理这些横切面关注点;

  • Cross Cutting concern :
    • 横切性关注点,是一种遍布在系统流程之中的独立服务;
  • Aspect :
    • 对这些横切性关注点的模块化,也就是将这些横切性关注点封装成一个类;
  • Advice :
    • 对这些横切性关注点的具体实现,及其使用方式,在业务流程方法调用之前、之后和抛出异常等使用方式。
  • Jointpoint :
    • 连接点,对Spring来说就是方法调用,也可以理解成Advice执行的时机。Jointpoint 分为方法调用和属性修改,对Spring来说它只支持方法调用;
  • Pointcut :

    • 对Advice应用范围进行限定,定义了Pointcut应用到那些Jointpoint上;
  • Weave :

    • 将Advice应用到目标对象上的过程,成为织入;
  • TargetObject :

    • 目标对象,Advice 应用的对象;
  • Proxy :

    • Spring AOP 默认使用的代理时JDK 的动态代理,我们也可以使用CGLIB代理,两者的区别: JDK动态代理只能对实现了接口的累进行代理,而CGLIB可以对所有的类进行代理,因为它使用了继承的方式来实现代理;
  • Introduction :

    • 引入,动态地为类添加方法;

这里写图片描述


  1. 通过如下简单示例对静态代理,动态代理和AOP进行比较说明,
    项目结构:
    这里写图片描述

2.1 静态代理,com.staticProxy.manager

interface UserManager:

package com.staticProxy.manager;
//只有两个简单的方法 添加用户和查找用户;
public interface UserManager {

    public void addUser(String name,String password);

    public Object findUser(String id);
}

Class UserManagerImpl:

package com.staticProxy.manager;

public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String name, String password) {
        System.out.println("UserManagerImpl.add()"+ name+" "+password);
    }

    @Override
    public Object findUser(String id) {
        System.out.println("UserManagerImpl.findUser()"+id);
        return "User1";
    }

}

假如现在的业务发生改变,我们在添加用户和查找用户时,都要进行安全性检查,如何解决这个问题;奔着对扩展开放,对修改关闭的原则,我们怎么修改,引入静态代理模式:

Class UserManagerImplProxy

package com.staticProxy.manager;

public class UserManagerImplProxy implements UserManager {

    //使用静态代理的关键是拿到代理目标的引用,
    //在此我们通过构造函数的方式拿到;也可以setter getter;
    private UserManager userManager;
    //通过构造函数拿到被代理对象的引用;
    public UserManagerImplProxy(UserManager userManager){
        this.userManager = userManager;
    }

    @Override
    public void addUser(String name, String password) {
        //增加安全性检查操作;
        checkSecurity();
        userManager.addUser(name, password);
    }

    @Override
    public Object findUser(String id) {
        //增加安全性检查操作
        checkSecurity();
        userManager.findUser(id);
        return null;
    }

    //将安全性检查的操作封装成一个方法;
    private void checkSecurity(){
        System.out.println("---checkSecurity()---");
    }
}

测试静态代理方法

public class ProxyTest extends TestCase {

    public void testStaticProxy(){
        //将被代理对象的引用传入;
        UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
        userManager.addUser("zhangsan", "123");
        userManager.findUser("abc");
    }
}

分析:
1. 在代理对象中,我们得为每个方法手动的添加安全性检查代码 checkSecurity(),如果代理类中的方法很多,这就会很麻烦;
2. 如果UserManager有多个实现呢,我们需要为每个实现创建一个代理对象,并在每个方法上加安全性检查操作
3. 在调用时,

UserManager manager = new UserManagerImpl();

变成了:

UserManager manager = new UserManagerImplProxy(new UserManagerImpl());

引入了工厂字眼,代码复杂了;


如何为实现了某些接口的类动态的创建一个代理,并且在代用这些实现方法时,自动的加上安全性检查代码呢?

2.2 动态代理-JDK的动态代理 com.dynamicProxy.manager

interface UserManager 和 UserManagerImpl和以前一样

package com.dynamicProxy.manager;

public interface UserManager {

    public void addUser(String name,String password);

    public Object findUser(String id);
}
package com.dynamicProxy.manager;

public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String name, String password) {
        System.out.println("UserManagerImpl.add()"+ name+" "+password);
    }

    @Override
    public Object findUser(String id) {
        System.out.println("UserManagerImpl.findUser()"+id);
        return "User2";
    }

}

动态代理类:SecurityHandler

package com.dynamicProxy.manager;

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

//JDK动态代理必须实现InvocationHandler接口重写invoke方法
public class SecurityHandler implements InvocationHandler {

    //step 1: 拿到要被代理的目标对象 targetObject
    private Object targetObject;

    //setp 2: 生成代理对象;
    // 在生成代理对象的同时,我们将被代理的目标对象转入,当然有多种传入方式;
    //三个参数:ClassLoader loder: 类加载器,使用目标对象的类加载器;
    //Class<?>[] interfs:目标对象实现的接口,如果这个目标有五个接口,会为每个接口创建一个代理;
    //InvocationHander h: 上两步已经将代理对象生产,那么我们在调用代理的方法时,代理对象会自动调用InvocationHander的invoke方法;
    public Object createDynamicProxy(Object targetObject){
        this.targetObject = targetObject;

        //使用Proxy生成代理;
        Object dynamicProxy = Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(), 
                targetObject.getClass().getInterfaces(),
                this
                );

        return dynamicProxy;

    }

    //调用代理的每个方法都会自动转到InvocationHandler的invoke方法,因为在
    //代理对象中有InvocationHandler的引用this
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //安全性检查代码,会动态为每个方法加上;
        checkSecurity();
        //实际调用目标对象的方法;
        //obj: 要调用的目标对象
        // args: 目标对象的参数,这个由代理自动传过来;
        Object result = method.invoke(targetObject, args);

        return result;
    }

    private void checkSecurity(){
        System.out.println("-----------checkSecurity()-danymicProxy------------");
    }

}

测试类:

import com.dynamicProxy.manager.SecurityHandler;
import com.staticProxy.manager.UserManager;
import com.staticProxy.manager.UserManagerImpl;
import com.staticProxy.manager.UserManagerImplProxy;

import junit.framework.TestCase;

public class ProxyTest extends TestCase {
    public void testDynamicProxy(){
        UserManager userManager = (UserManager) new SecurityHandler().createDynamicProxy(UserManagerImpl());
        userManager.addUser("LiSi", "12306");
        userManager.findUser("001");
    }
}

Spring AOP对此进行了更好的封装,

2.3 AOP声明式服务:AspectJ 的annotation注解方式,

interface UserManager 和 UserManagerImpl和以前一样

class SecurityAspectAnno

package com.aspect.manager;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

// Step 1: 将横切性关注点封装成一个类;
//声明这是一个Aspcet
@Aspect
public class SecurityAspectAnno {

    //Step 2: 定义Advice应用的范围:Pointcut
    //定义一个Pointcut,范围用execution(正则表达),和名称addUserManager()
    @Pointcut("execution(* com.aspect.manager.UserManager.*(..))")
    private void addUserManager(){};

    // Step 3: 定义Advice;
    //定义advice,并关联到相应的Pointcut订阅的Jointpoint上;
    @Before("addUserManager()")
    private void checkSecurity(){
        System.out.println("-----------checkSecurity()-aspectJ------------");
    }
}

最后告诉Spring, 我们用了aspectj的annotation
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans">     

    <!-- 开启AspectJ的annotation   -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <bean id="userManagerImpl1" class="com.aspect.manager.UserManagerImpl"/>
    <bean id="securityAspectAnno" class="com.aspect.manager.SecurityAspectAnno"/>

</beans>

2.3 AOP声明式服务:XML配置文件,

class SecurityAspectCfg

package com.aspect.manager;
//简简单单的不需要配置任何注释东西了;
public class SecurityAspectCfg {
    private void checkSecurityCfg(){    
        System.out.println("---checkSecurity()-Cfg---");
    }
}

我们在配置文件中声明Aspect相关内容;

<?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:tx="http://www.springframework.org/schema/tx"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.3.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">


    <bean id="userManagerImpl2" class="com.aspect.manager.UserManagerImpl"/>
    <bean id="securityAspectCfg" class="com.aspect.manager.SecurityAspectCfg"/>

    <aop:config>
        <aop:aspect id="checkAspect" ref="securityAspectCfg">
            <aop:pointcut expression="execution(* com.aspect.manager.UserManager.*(..))" id="addAddMethod"/>
            <aop:before method="checkSecurityCfg" pointcut-ref="addAddMethod"/>
        </aop:aspect>
    </aop:config>

</beans>

测试类:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.aspect.manager.UserManager;

import junit.framework.TestCase;

public class AspectJTest extends TestCase{

    private BeanFactory factory;
    protected void setUp() throws Exception {
        factory = new ClassPathXmlApplicationContext("applicationContext-aspectj.xml");
    }

    protected void tearDown() throws Exception {
    }

    public void testAspectJAnno(){
        UserManager userManager = (UserManager) factory.getBean("userManagerImpl2");
        userManager.addUser("zz","23");
        userManager.findUser("121");
    }
}

That’s all, thank you for your consideration. And, welcome any commonet.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值