从代理到Spring事务

标签: 代理 spring | 发表时间:2016-03-11 17:15 | 作者:
出处:http://xiaobaoqiu.github.io/

最近再项目中发现不少同事不理解默认情况下的Spring事务的AOP机制,导致随意使用事务注解.因此也在很多场景事务不生效。因此想从代理机制开始理一下整个Spring声明式的原理。

1.代理

通常,我们代码中处理核心的业务逻辑,还包含一些枝节性的代码和功能,比如日志记录、消息发送、安全、事务保证和监控等。

我们通常期望将这些枝节性的代码功能和我们的核心业务逻辑分离,减少业务功能和枝节功能的耦合。这时候我们就要使用代理来达到我们的目的。

代理的作用是:为其它对象提供一种代理以控制对这个对象的访问。简单理解就是中间的作用。代理一般涉及到三个角色:

  (1).抽象角色:声明真实对象和代理对象的共同接口;
(2).代理角色:代理对象内部包含有真实角色的引用,从而可以操作真实角色,同时代理对象与真实对象有相同的接口,能在任何时候代替真实对象,同时代理对象可以在执行真实对 象前后加入特定的逻辑以实现功能的扩展;
(3).真实角色:代理角色所代表的真实对象,是我们最终要引用的对象;

1.1 代理模式

代理模式是一个经典的设计模式,介绍往上很多。

下面是一个最简单的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
      public interface IHello {
    void hello();
}

public class HelloImpl implements IHello {
    @Override
    public void hello() {
        System.out.println("HelloImpl: Hello World.");
    }
}

public class HelloProxyImpl implements IHello {

    private IHello inner;

    public HelloProxyImpl(IHello inner) {
        this.inner = inner;
    }

    @Override
    public void hello() {
        doSomethingBefore();
        
        inner.hello();
        
        doSomethingAfter();
    }

    private void doSomethingBefore() {
        System.out.println("HelloProxyImpl: Before hello()...");
    }

    private void doSomethingAfter() {
        System.out.println("HelloProxyImpl: After hello()...");
    }

}

UML图大致如下:

从代码上我们可以看出,代理模式要求(这也是它的限制所在): (1).代理类需要和被代理者一样实现相同的接口; (2).代理类包含被代理者的引用;

1.2 Java动态代理

代理模式我们可以理解为一种静态代理,其问题是需要我们显示的为每个需要被代理的类实现一个代理类,即一个代理类只能为i一个被代理者做代理的功能。如果有上千个类需要代理,估计要骂娘了。

java动态代理正是为了解决这个问题。主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。我们只需要实现InvocationHandler接口,在实现invoke()方法的时候加上我们的代理逻辑。

一个简单的使用实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
      /**
 * @author xiaobaoqiu  Date: 16-3-10 Time: 下午6:18
 */
public class JavaDynamicProxy implements InvocationHandler {

    /**
     * 被代理者
     */
    private Object inner;

    /**
     * 生成代理类
     */
    public static Object generateProxy(Object inner) {
        return Proxy.newProxyInstance(
                inner.getClass().getClassLoader(),
                inner.getClass().getInterfaces(),
                new JavaDynamicProxy(inner));
    }

    private JavaDynamicProxy(Object inner) {
        this.inner = inner;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doSomethingBefore();

        Object result = method.invoke(inner, args);

        doSomethingAfter();

        return result;
    }

    private void doSomethingBefore() {
        System.out.println("JavaDynamicProxy: Before...");
    }

    private void doSomethingAfter() {
        System.out.println("JavaDynamicProxy: After...");
    }
}

//使用
public static void main(String[] args) {
        //被动态代理的IHello实例对象A
        IHello helloA = new HelloImpl();
        //生成对象A的动态代理
        IHello helloAProxy = (IHello) JavaDynamicProxy.generateProxy(helloA);
        helloAProxy.hello();

        System.out.println("-------------------------------------------------");
        // 一个JavaDynamicProxy可以一直使用

        //被动态代理的IHello实例对象B
        IHello helloB = new HelloWithLogImpl();
        //生成对象B的动态代理
        IHello helloBProxy = (IHello) JavaDynamicProxy.generateProxy(helloB);
        helloBProxy.hello();

        System.out.println("-------------------------------------------------");
        //被动态代理对象IBye实例
        IBye bye = new ByeImpl();
        //生成IBye实例的动态代理
        IBye byeProxy = (IBye) JavaDynamicProxy.generateProxy(bye);
        byeProxy.bye();
}

JavaDynamicProxy.generateProxy的输出会是一个动态的代理类。debug信息如下,从debug信息我们大致知道,这个代理类的类名称为$Proxy0,内部包含一个属性名为h的属性,h指向的就是我们实现的InvocationHandler的类(这里即JavaDynamicProxy)的实例。

但是Java动态代理的限制是:

  (1).被代理的类要求至少实现了一个Interface;
(2).被代理的类要求有public的构造函数(即没有显示的将其设置为private等);
(3).被代理的类要求不是final;

如下代理一个没有实现任何接口的类会报错

1
2
3
4
5
6
          //没有实现任何interface的类使不能被动态代理的
    System.out.println("-------------------------------------------------");
    HelloWithoutInterface helloC = new HelloWithoutInterface();
    //生成对象B的动态代理
    HelloWithoutInterface helloCProxy = (HelloWithoutInterface) JavaDynamicProxy.generateProxy(helloC);
    helloCProxy.hello();

错误信息如下:

1
2
3
4
5
6
7
      Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to proxy.javaProxy.HelloWithoutInterface
  at proxy.javaProxy.JavaProxyMain.main(JavaProxyMain.java:42)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Java动态代理的机制是利用反射机制生成。具体代码可以debug,主要的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
      //Proxy.java
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  ...
  Class<?> cl = getProxyClass0(loader, intfs);  //生成代理类
  ...
  return newInstance(cons, ih);   // 生成代理类的实例
}

private static Class<?> getProxyClass0(ClassLoader loader,  Class<?>... interfaces) {
        //生成代理类的主要逻辑在ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
}

//ProxyClassFactory的apply方法
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  ...
            String proxyPkg = null;     // 生成代理类的包路径
            ...
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";   //默认路径在com.sun.proxy下面
            }

            long num = nextUniqueNumber.getAndIncrement();    //代理类的序号, 我们熟悉的$Proxy0中的0
            String proxyName = proxyPkg + proxyClassNamePrefix + num; //代理类的类名称

            //使用ProxyGenerator生成代理类的字节码
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
            //生成代理类
            return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}

//ProxyGenerator.generateProxyClass的代码太恶心的...

我们可以自己使用ProxyGenerator来生成代理类并将其字节码记录下来:

1
2
3
      // 获取代理类的字节码
      byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", ByeImpl.class.getInterfaces());
      //将字节码写文件,建议写成.class文件

获取的字节码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
      import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.sample.IBye;

public final class $Proxy0 extends Proxy implements IBye {
    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m2;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void bye() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("proxy.sample.IBye").getMethod("bye", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从生成的类我们知道,代理类继承Proxy类(这就是为什么Proxy类内的InvocationHandler实例未protected)并实现我们需要的被代理者的Interface(比如这里的bye()方法),另外它只要一个接受一个InvocationHandler为参数的构造函数。当我们调用代理类的bye()方法时候,其实是调用我们实现的InvocationHandler(即上面的JavaDynamicProxy)的invoke()方法,在invoke()方法里面,我们实现了我的代理逻辑。

我们这里这个Demo的大致UML图如下:

参考: http://www.cnblogs.com/cruze/p/3819761.html

1.3 CGLib动态代理

鉴于Java动态代理的限制,我们有需要代理没有任何实现接口的类的时候,可以考虑使用CGLib。CGLib的全称是Code Generate Library。CGLib的使用使用十分广泛,我们这里要讲的Spring AOP,已经EasyMock等。

同样先上Demo代码,我们只需要实现MethodInterceptor接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
      /**
 * @author xiaobaoqiu  Date: 16-3-10 Time: 下午7:15
 */
public class CglibDynamicProxy implements MethodInterceptor {

    /**
     * 动态生成代理类
     */
    public Object generateProxy(Class cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);
//        enhancer.setCallbackFilter(); //filter
        enhancer.setSuperclass(cls);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        doSomethingBefore();

        Object result = proxy.invokeSuper(obj, args);

        doSomethingAfter();

        return result;
    }

    private void doSomethingBefore() {
        System.out.println("CglibDynamicProxy: Before...");
    }

    private void doSomethingAfter() {
        System.out.println("CglibDynamicProxy: After...");
    }
}

//使用
public static void main(String[] args) {
        CglibDynamicProxy proxy = new CglibDynamicProxy();
        //注意: 原始类的instance不需要存在,只需要Class类型

        //用接口IHello接, 或者 HelloImpl 接
        IHello helloAProxy = (IHello)proxy.generateProxy(HelloImpl.class);
        helloAProxy.hello();

        System.out.println("-------------------------------------------------");

        //用接口IHello接, 或者 HelloWithLogImpl 接
        IHello helloBProxy = (IHello)proxy.generateProxy(HelloWithLogImpl.class);
        helloBProxy.hello();

        System.out.println("-------------------------------------------------");

        //代理没有实现任何interface的类
        HelloWithoutInterface helloCProxy = (HelloWithoutInterface)proxy.generateProxy(HelloWithoutInterface.class);
        helloCProxy.hello();
}

同样我们debug一下,得到的信息如下,同样生成了代理类,类名称为proxy.sample.HelloImpl$$EnhancerByCGLIB$$b46b6f06,这个稀奇古怪的类名我们后面会分析:

下面还是尝试分析CGLib动态代理的原理。默认情况下生成的代理class文件只存储在内存中,我们可以在代码中设置一个环境变量:

1
      System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/xiaobaoqiu/cglib_proxy");

之后我们在目标目录下得到很多的class,其中的proxy目录包含了生成的class文件。我们发现一大堆的class文件,类名都是稀奇古怪。我们先看看class文件的生成策略。每个Class Generator(比如这里的Enhancer)都继承自AbstractClassGenerator(实现接口ClassGenerator,这个接口只有一个generateClass的方法),需要实现其generateClass()方法。generateClassName()方法用来生成Class名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      private String generateClassName(Predicate nameTestPredicate) {
        return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
}

//namingPolicy的默认实现NamingPolicy
public String getClassName(String prefix, String source, Object key, Predicate names) {
  //prefix为被代理类的路径,
        String base =
            prefix + "$$" +                       //prefix为被代理类的路径
            source.substring(source.lastIndexOf('.') + 1) +       //获取生成代理类的类,比如我们这里的Enhancer
            getTag() + "$$" +                     //getTag()默认为ByCGLIB
            Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());   //hashcode
        String attempt = base;
        int index = 2;
        while (names.evaluate(attempt))       //如果有重复,则再在后面加上下标,小标从2开始
            attempt = base + "_" + index++;
        return attempt;
}

下面正是进入class文件的生成原理分析,还是从源代码入手,enhancer.create()最终进入AbstractClassGenerator.create()方法,我们发现最终return的Object是从内部变量obj得来,因此,我们看看ClassLoaderData的生成逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
      protected Object create(Object key) {
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            if (data == null) {
                synchronized (AbstractClassGenerator.class) {
                    data = cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(classLoader);
                        newCache.put(classLoader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            Object obj = data.get(this);
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            return nextInstance(obj);
}

public ClassLoaderData(ClassLoader classLoader) {
            this.classLoader = new WeakReference<ClassLoader>(classLoader);
            Function<AbstractClassGenerator, Object> load =
                    new Function<AbstractClassGenerator, Object>() {
                        public Object apply(AbstractClassGenerator gen) {
                            Class klass = gen.generate(ClassLoaderData.this); //生成class的关键代码
                            return gen.wrapCachedClass(klass);
                        }
                    };
            generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
}

因此我们将目光转到generate()中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
      //Enhancer的generate()方法
protected Class generate(ClassLoaderData data) {
        validate();
        if (superclass != null) {
            setNamePrefix(superclass.getName());
        } else if (interfaces != null) {
            setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
        }
        return super.generate(data);  //父类AbstractClassGenerator
}
// AbstractClassGenerator的generate()方法
protected Class generate(ClassLoaderData data) {
            //...
            ClassLoader classLoader = data.getClassLoader();
            this.setClassName(generateClassName(data.getUniqueNamePredicate()));  //代理类的类名称的生成逻辑
            //...
            byte[] b = strategy.generate(this);       //生成策略,默认实现DefaultGeneratorStrategy,生成class文件的字节码
            String className = ClassNameReader.getClassName(new ClassReader(b));
            ProtectionDomain protectionDomain = getProtectionDomain();
            synchronized (classLoader) { // just in case
                if (protectionDomain == null) {
                    gen = ReflectUtils.defineClass(className, b, classLoader);
                } else {
                    gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                }
            }
            return gen;
            ...//异常处理
}

//DefaultGeneratorStrategy的generate()方法
public byte[] generate(ClassGenerator cg) throws Exception {
        DebuggingClassWriter cw = getClassVisitor();
        transform(cg).generateClass(cw);      //这里调用最终的generateClass()逻辑,Visitor模式
        return transform(cw.toByteArray());   //通过Visitor得到最后的字节码
}

所以最终的调用是Enhancer的generateClass()调用。

参考: http://www.cnblogs.com/cruze/p/3843996.html

2.Spring事务

目标: The most common reason for using transactions in an application is to maintain a high degree of data integrity and consistency.

代理实现选择器: AdviceMode TransactionManagementConfigurationSelector

声明事务切面实现: TransactionInterceptor

事务管理器 PlatformTransactionManager DataSourceTransactionManager

事务实现 Connection关闭自动提交 ConnectionHolder来保持Connection

事务模板 TransactionTempale

AOP简化事务模板

核心类: PlatformTransactionManager

隔离级别: Isolation 传播属性: Propagation

2.1 编程式事务

2.2 AOP实现

2.3 Aspectj实现

参考: http://openwares.net/java/spring_mybatis_transaction.html http://my.oschina.net/guanzhenxing/blog/214228 http://www.mybatis.org/spring/zh/transactions.html http://tracylihui.github.io/2015/07/28/spring/Spring%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86/ http://www.cnblogs.com/davenkin/archive/2013/02/16/java-tranaction-1.html http://www.importnew.com/12300.html 详细介绍Spring事务管理: http://developer.51cto.com/art/200906/129854.htm spring事务原理1-事务的抽象: http://sqtds.github.io/2014/06/09/2014/spring-tx1/ Spring官方文档Transaction Management: http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/transaction.html Spring官方文档Aspect Oriented Programming with Spring: http://docs.spring.io/spring/docs/3.0.x/reference/aop.html StackOverFlow Spring - @Transactional - What happens in background?: http://stackoverflow.com/questions/1099025/spring-transactional-what-happens-in-background Transaction strategies: Understanding transaction pitfalls: http://www.ibm.com/developerworks/library/j-ts1/ Annotation-based Transactions in Spring: http://springinpractice.com/2008/03/18/annotation-based-transactions-in-spring Design principle behind Transaction framework with Spring 3: http://stackoverflow.com/questions/11789857/design-principle-behind-transaction-framework-with-spring-3 Chapter 6. 使用Spring进行面向切面编程(AOP): http://shouce.jb51.net/spring/aop.html wiki: http://wiki.corp.qunar.com/display/~yushen.ma/Spring+3.x-JDBC+Transaction spring事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=52084161 spring 事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=93338632 aspectj事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=92656954

Demo: 编程事务: http://www.tutorialspoint.com/spring/programmatic_management.htm 声明事务: http://www.tutorialspoint.com/spring/declarative_management.htm

相关 [代理 spring] 推荐:

从代理到Spring事务

- - xiaobaoqiu Blog
1.3 CGLib动态代理. 2.3 Aspectj实现. 最近再项目中发现不少同事不理解默认情况下的Spring事务的AOP机制,导致随意使用事务注解.因此也在很多场景事务不生效. 因此想从代理机制开始理一下整个Spring声明式的原理. 通常,我们代码中处理核心的业务逻辑,还包含一些枝节性的代码和功能,比如日志记录、消息发送、安全、事务保证和监控等.

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

Spring AOP动态代理原理与实现方式 (转)

- - 开源软件 - ITeye博客
AOP:面向切面、面向方面、面向接口是一种横切技术. 1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   . 3.安全验证: Spring AOP---OOP升级  . 静态代理原理:目标对象:调用业务逻辑    代理对象:日志管理. 表示层调用--->代理对象(日志管理)-->调用目标对象.

使用DefaultAdvisorAutoProxyCreator实现spring的自动代理

- - 编程语言 - ITeye博客
        DefaultAdvisorAutoProxyCreator这个类功能更为强大,这个类的奇妙之处是他实现了BeanProcessor接口,当ApplicationContext读如所有的Bean配置信息后,这个类将扫描上下文,寻找所有的Advistor(一个Advisor是一个切入点和一个通知的组成),将这些Advisor应用到所有符合切入点的Bean中.

被事务代理的spring service 不能使用注解方式发布dubbo服务的问题解决

- - 企业架构 - ITeye博客
       使用 @com.alibaba.dubbo.config.annotation.Service 发布dubbo服务的时候,当服务类没有加入@Transactional的时候没有问题..        但是当加入事务后,spring bean 事务代理, dubbo的 AnnotationBean 扫描 类执行下面的代码的时候就获取不到对应的注解,也就发布不了服务:.

Spring详解

- - CSDN博客架构设计推荐文章
Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目的是简化企业开发.. PersonDaoBean 是在应用内部创建及维护的. 所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的.

Spring定时

- - 行业应用 - ITeye博客
spring的定时任务配置分为三个步骤:. . . . . .

简单Spring+hessian

- - Web前端 - ITeye博客
简单的Spring+hessian. dist\modules里面的 spring-webmvc.jar . lib\caucho 里面的hessian-3.1.3.jar. 里面有个接口interface:. 建立一个model层:(实现Serializable接口). 在WEB-INF下面创建一个remoting-servlet.xml:.

Spring MVC 和 Struts2

- - CSDN博客架构设计推荐文章
Web层面的框架学习了三个Struts1和2,SpringMVC,那他们之间肯定存在一个优劣和适用的环境,Struts1和2的异同点我已经做过对比《 Struts1和Struts2》,这篇将对比下Struts2和SpringMVC的异同,下面数据基本来源于网络,本人是搜集整理所得,供大家参考. 一个项目使用什么样的技术,决定的因素很多,我所能想到的有:对系统的性能、开发的效率、团队学习的成本、业务场景等,下面尽量从这几个方面入手,来分析比较下他们之间存在的优劣.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.