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

来源:互联网 发布:手机淘宝旺铺首页装修 编辑:程序博客网 时间:2024/06/10 19:23

本博文也是属于学习总结性博文,主要来学习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@Aspectpublic 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.

原创粉丝点击