`
xiangxingchina
  • 浏览: 520184 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

CGlib概述:

阅读更多

CGlib概述:
cglib(Code Generation Library )是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

CGlib应用:
以一个实例在简单介绍下cglib的应用。
我们模拟一个虚拟的场景,关于信息的管理。

1)原始需求是任何人可以操作信息的create,update,delete,query操作。
InfoManager.java --封装对信息的操作

public   class  InfoManager {
    
//  模拟查询操作
     public   void  query() {
        System.out.println(
" query " );
    }
    
//  模拟创建操作
     public   void  create() {
        System.out.println(
" create " );
    }
    
//  模拟更新操作
     public   void  update() {
        System.out.println(
" update " );
    }
    
//  模拟删除操作
     public   void  delete() {
        System.out.println(
" delete " );
    }
}

InfoManagerFactory.java --工厂类

public   class  InfoManagerFactory {
    
private   static  InfoManager manger  =   new  InfoManager();
    
/**
     * 创建原始的InfoManager
     * 
     * 
@return
     
*/
    
public   static  InfoManager getInstance() {
        
return  manger;
    }
}

client.java --供客户端调用

public   class  Client {

    
public   static   void  main(String[] args) {
        Client c 
=   new  Client();
        c.anyonecanManager();
    }

    
/**
     * 模拟:没有任何权限要求,任何人都可以操作
     
*/
    
public   void  anyonecanManager() {
        System.out.println(
" any one can do manager " );
        InfoManager manager 
=  InfoManagerFactory.getInstance();
        doCRUD(manager);
        separatorLine();
    }

    
/**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param  manager
     
*/
    
private   void  doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    
/**
     * 加一个分隔行,用于区分
     
*/
    
private   void  separatorLine() {
        System.out.println(
" ################################ " );
    }

}

至此,没有涉及到cglib的内容,因为需求太简单了,但是接下来,需求发生了改变,要求:

2)只有一个叫“maurice”的用户登录,才允许对信息进行create,update,delete,query的操作。
怎么办?难道在每个方法前,都加上一个权限判断吗?这样重复逻辑太多了,于是乎想到了Proxy(代理模式),但是原先的InfoManager也没有实现接口,不能采用jdk的proxy。那么cglib在这边就要隆重登场。
一旦使用cgblig,只需要添加一个MethodInterceptor的类以及修改factory代码就可以实现这个需求。
AuthProxy.java --权限校验代理类

public   class  AuthProxy  implements  MethodInterceptor {

    
private  String name;  //  会员登录名

    
public  AuthProxy(String name) {
        
this .name  =  name;
    }

    
/**
     * 权限校验,如果会员名为:maurice,则有权限做操作,否则提示没有权限
     
*/
    @Override
    
public  Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)  throws  Throwable {
        
if  ( ! " maurice " .equals( this .name)) {
            System.out.println(
" AuthProxy:you have no permits to do manager! " );
            
return   null ;
        }
        
return  proxy.invokeSuper(obj, args);
    }

    
public  String getName() {
        
return  name;
    }

    
public   void  setName(String name) {
        
this .name  =  name;
    }

}

InfoManagerFactory.java --代码变动如下:

public   class  InfoManagerFactory {

    
/**
     * 创建带有权限检验的InfoManager
     * 
     * 
@param  auth
     * 
@return
     
*/
    
public   static  InfoManager getAuthInstance(AuthProxy auth) {
        Enhancer enhancer 
=   new  Enhancer();
        enhancer.setSuperclass(InfoManager.
class );
        enhancer.setCallback(auth);
        
return  (InfoManager) enhancer.create();
    }

    
}

client.java --代码修改如下

public   class  Client {

    
public   static   void  main(String[] args) {
        Client c 
=   new  Client();
        c.haveNoAuthManager();
        c.haveAuthManager();
    }

    
/**
     * 模拟:登录会员没有权限
     
*/
    
public   void  haveNoAuthManager() {
        System.out.println(
" the loginer's name is not maurice,so have no permits do manager " );
        InfoManager noAuthManager 
=  InfoManagerFactory.getAuthInstance( new  AuthProxy( " maurice1 " ));
        doCRUD(noAuthManager);
        separatorLine();
    }

    
/**
     * 模拟:登录会员有权限
     
*/
    
public   void  haveAuthManager() {
        System.out.println(
" the loginer's name is maurice,so have permits do manager " );
        InfoManager authManager 
=  InfoManagerFactory.getAuthInstance( new  AuthProxy( " maurice " ));
        doCRUD(authManager);
        separatorLine();
    }

    
/**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param  manager
     
*/
    
private   void  doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    
/**
     * 加一个分隔行,用于区分
     
*/
    
private   void  separatorLine() {
        System.out.println(
" ################################ " );
    }

}

执行下代码,发现这时client端中已经加上了权限校验。
同样是InfoManager,为什么这时能多了权限的判断呢?Factory中enhancer.create()返回的到底是什么对象呢?这个疑问将在第三部分CGlib中解释。
这边的代码,其实是介绍了cglib中的enhancer功能.

到这里,参照上面的代码,就可以使用cglib带来的aop功能了。但是为了更多介绍下cglib的功能,模拟需求再次发生变化:

3)由于query功能用户maurice才能使用,招来其他用户的强烈的抱怨,所以权限再次变更,只有create,update,delete,才需要权限保护,query任何人都可以使用。
怎 么办?采用AuthProxy,使得InfoManager中的所有方法都被代理,加上了权限的判断。当然,最容易想到的办法,就是在AuthProxy 的intercept的方法中再做下判断,如果代理的method是query,不需要权限验证。这么做,可以,但是一旦逻辑比较复杂的时 候,intercept这个方法要做的事情会很多,逻辑会异常的复杂。
幸好,cglib还提供了CallbackFilter。使用CallbackFilter,可以明确表明,被代理的类(InfoManager)中不同的方法,被哪个拦截器(interceptor)拦截。
AuthProxyFilter.java

public   class  AuthProxyFilter  implements  CallbackFilter {

    
private   static   final   int  AUTH_NEED      =   0 ;
    
private   static   final   int  AUTH_NOT_NEED  =   1 ;

    
/**
     * <pre>
     * 选择使用的proxy
     * 如果调用query函数,则使用第二个proxy
     * 否则,使用第一个proxy
     * </pre>
     
*/
    @Override
    
public   int  accept(Method method) {
        
if  ( " query " .equals(method.getName())) {
            
return  AUTH_NOT_NEED;
        }
        
return  AUTH_NEED;
    }

}

这段代码什么意思?其中的accept方法的意思是说,如果代理的方法是query(),那么使用第二个拦截器去拦截,如果代理的方法不是 query(),那么使用第一个拦截器去拦截。所以我们只要再写一个拦截器,不做权限校验就行了。(其实,cglib中的NoOp.INSTANCE就是 一个空的拦截器,只要配置上这个就可以了。)
InfoManagerFactory.java --代码修改如下:(配置不同的拦截器和filter)

public   class  InfoManagerFactory {

    
/**
     * 创建不同权限要求的InfoManager
     * 
     * 
@param  auth
     * 
@return
     
*/
    
public   static  InfoManager getSelectivityAuthInstance(AuthProxy auth) {
        Enhancer enhancer 
=   new  Enhancer();
        enhancer.setSuperclass(InfoManager.
class );
        enhancer.setCallbacks(
new  Callback[] { auth, NoOp.INSTANCE });
        enhancer.setCallbackFilter(
new  AuthProxyFilter());
        
return  (InfoManager) enhancer.create();
    }

}

记住:setCallbacks中的拦截器(interceptor)的顺序,一定要和CallbackFilter里面指定的顺序一致!!切忌。

Client.java

public   class  Client {

    
public   static   void  main(String[] args) {
        Client c 
=   new  Client();
        c.selectivityAuthManager();
    }
    
    
/**
     * 模拟:没有权限的会员,可以作查询操作
     
*/
    
public   void  selectivityAuthManager() {
        System.out.println(
" the loginer's name is not maurice,so have no permits do manager except do query operator " );
        InfoManager authManager 
=  InfoManagerFactory.getSelectivityAuthInstance( new  AuthProxy( " maurice1 " ));
        doCRUD(authManager);
        separatorLine();
    }

    
/**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param  manager
     
*/
    
private   void  doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    
/**
     * 加一个分隔行,用于区分
     
*/
    
private   void  separatorLine() {
        System.out.println(
" ################################ " );
    }

}

此时,对于query的权限校验已经被去掉了。


通过一个模拟需求,简单介绍了cglib aop功能的使用。
CGlib应用非常广,在spring,hibernate等框架中,被大量的使用。


CGlib原理:
cglib神奇吗?其实一旦了解cglib enhancer的原理,一切就真相大白了。
刚才在第二部分中,有个疑问:enhancer.create()到底返回了什么对象?

其实在刚才的例子中,cglib在代码运行期,动态生成了InfoManager的子类,并且根据CallbackFilter的accept方法,覆写了InfoManager中的所有方法--去执行相应的MethodInterceptor的intercept方法。

有兴趣的朋友可以看看我反编译的InfoManager的子类,就可以很明白知道具体的实现了。(需要有耐心才可以)
InfoManager$$EnhancerByCGLIB$$de624598.jad

//  Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
//  Jad home page:  http://www.geocities.com/kpdus/jad.html
//  Decompiler options: packimports(3) 
//  Source File Name:   <generated>

package  cn.eulic.codelab.cglib;

import  java.lang.reflect.Method;
import  net.sf.cglib.core.Signature;
import  net.sf.cglib.proxy. * ;

//  Referenced classes of package cn.eulic.codelab.cglib:
//             InfoManager

public   class  CGLIB.BIND_CALLBACKS  extends  InfoManager
    
implements  Factory
{

    
static   void  CGLIB$STATICHOOK1()
    {
        Class class1;
        ClassLoader classloader;
        CGLIB$THREAD_CALLBACKS 
=   new  ThreadLocal();
        classloader 
=  (class1  =  Class.forName( " cn.eulic.codelab.cglib.InfoManager$$EnhancerByCGLIB$$de624598 " )).getClassLoader();
        classloader;
        CGLIB$emptyArgs 
=   new  Object[ 0 ];
        CGLIB$delete$
0 $Proxy  =  MethodProxy.create(classloader, (CGLIB$delete$ 0 $Method  =  Class.forName( " cn.eulic.codelab.cglib.InfoManager " ).getDeclaredMethod( " delete " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()V " " delete " " CGLIB$delete$0 " );
        CGLIB$create$
1 $Proxy  =  MethodProxy.create(classloader, (CGLIB$create$ 1 $Method  =  Class.forName( " cn.eulic.codelab.cglib.InfoManager " ).getDeclaredMethod( " create " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()V " " create " " CGLIB$create$1 " );
        CGLIB$query$
2 $Proxy  =  MethodProxy.create(classloader, (CGLIB$query$ 2 $Method  =  Class.forName( " cn.eulic.codelab.cglib.InfoManager " ).getDeclaredMethod( " query " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()V " " query " " CGLIB$query$2 " );
        CGLIB$update$
3 $Proxy  =  MethodProxy.create(classloader, (CGLIB$update$ 3 $Method  =  Class.forName( " cn.eulic.codelab.cglib.InfoManager " ).getDeclaredMethod( " update " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()V " " update " " CGLIB$update$3 " );
        CGLIB$finalize$
4 $Proxy  =  MethodProxy.create(classloader, (CGLIB$finalize$ 4 $Method  =  Class.forName( " java.lang.Object " ).getDeclaredMethod( " finalize " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()V " " finalize " " CGLIB$finalize$4 " );
        CGLIB$hashCode$
5 $Proxy  =  MethodProxy.create(classloader, (CGLIB$hashCode$ 5 $Method  =  Class.forName( " java.lang.Object " ).getDeclaredMethod( " hashCode " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()I " " hashCode " " CGLIB$hashCode$5 " );
        CGLIB$clone$
6 $Proxy  =  MethodProxy.create(classloader, (CGLIB$clone$ 6 $Method  =  Class.forName( " java.lang.Object " ).getDeclaredMethod( " clone " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()Ljava/lang/Object; " " clone " " CGLIB$clone$6 " );
        CGLIB$equals$
7 $Proxy  =  MethodProxy.create(classloader, (CGLIB$equals$ 7 $Method  =  Class.forName( " java.lang.Object " ).getDeclaredMethod( " equals " new  Class[] {
            Class.forName(
" java.lang.Object " )
        })).getDeclaringClass(), class1, 
" (Ljava/lang/Object;)Z " " equals " " CGLIB$equals$7 " );
        CGLIB$toString$
8 $Proxy  =  MethodProxy.create(classloader, (CGLIB$toString$ 8 $Method  =  Class.forName( " java.lang.Object " ).getDeclaredMethod( " toString " new  Class[ 0 ])).getDeclaringClass(), class1,  " ()Ljava/lang/String; " " toString " " CGLIB$toString$8 " );
        
return ;
    }

    
final   void  CGLIB$delete$ 0 ()
    {
        
super .delete();
    }

    
public   final   void  delete()
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37 ;
           
goto  _L3 _L4
_L3:
        
break  MISSING_BLOCK_LABEL_21;
_L4:
        
break  MISSING_BLOCK_LABEL_37;
        
this ;
        CGLIB$delete$
0 $Method;
        CGLIB$emptyArgs;
        CGLIB$delete$
0 $Proxy;
        intercept();
        
return ;
        
super .delete();
        
return ;
    }

    
final   void  CGLIB$create$ 1 ()
    {
        
super .create();
    }

    
public   final   void  create()
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37 ;
           
goto  _L3 _L4
_L3:
        
break  MISSING_BLOCK_LABEL_21;
_L4:
        
break  MISSING_BLOCK_LABEL_37;
        
this ;
        CGLIB$create$
1 $Method;
        CGLIB$emptyArgs;
        CGLIB$create$
1 $Proxy;
        intercept();
        
return ;
        
super .create();
        
return ;
    }

    
final   void  CGLIB$query$ 2 ()
    {
        
super .query();
    }

    
public   final   void  query()
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37 ;
           
goto  _L3 _L4
_L3:
        
break  MISSING_BLOCK_LABEL_21;
_L4:
        
break  MISSING_BLOCK_LABEL_37;
        
this ;
        CGLIB$query$
2 $Method;
        CGLIB$emptyArgs;
        CGLIB$query$
2 $Proxy;
        intercept();
        
return ;
        
super .query();
        
return ;
    }

    
final   void  CGLIB$update$ 3 ()
    {
        
super .update();
    }

    
public   final   void  update()
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37 ;
           
goto  _L3 _L4
_L3:
        
break  MISSING_BLOCK_LABEL_21;
_L4:
        
break  MISSING_BLOCK_LABEL_37;
        
this ;
        CGLIB$update$
3 $Method;
        CGLIB$emptyArgs;
        CGLIB$update$
3 $Proxy;
        intercept();
        
return ;
        
super .update();
        
return ;
    }

    
final   void  CGLIB$finalize$ 4 ()
        
throws  Throwable
    {
        
super .finalize();
    }

    
protected   final   void  finalize()
        
throws  Throwable
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37 ;
           
goto  _L3 _L4
_L3:
        
break  MISSING_BLOCK_LABEL_21;
_L4:
        
break  MISSING_BLOCK_LABEL_37;
        
this ;
        CGLIB$finalize$
4 $Method;
        CGLIB$emptyArgs;
        CGLIB$finalize$
4 $Proxy;
        intercept();
        
return ;
        
super .finalize();
        
return ;
    }

    
final   int  CGLIB$hashCode$ 5 ()
    {
        
return   super .hashCode();
    }

    
public   final   int  hashCode()
    {
        CGLIB$CALLBACK_0;
        
if (CGLIB$CALLBACK_0  !=   null goto  _L2;  else   goto  _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this );
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
52 ;
           
goto  _L3 _L4
_L3:
        
this ;
        CGLIB$hashCode$
5 $Method;
        CGLIB$emptyArgs;
        CGLIB$hashCode$
5 $Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 
45 ;
           
goto  _L
分享到:
评论

相关推荐

    Jdk动态代理和cglib动态代理原理

    动态代理主要分为两种技术:JDK动态代理和CGLIB动态代理。 ### JDK动态代理 JDK动态代理是Java内置的一种机制,依赖于`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。以下是JDK动态...

    cglib代理的依赖jar包

    1. **功能概述** ASM是一个Java字节码操纵和分析框架,它可以直接生成和加载Java类。ASM提供了低级别的API,可以精确地控制类或接口的生成,使得开发者能够构建自己的编译器、反编译器或者代码分析工具。 2. **...

    基于MAVEN项目的CGLib动态代理原理及实现

    2. CGLib概述 CGLib是一个强大的、高性能的代码生成库,它可以在运行期扩展Java类与实现Java接口。它广泛用于许多AOP(面向切面编程)框架,如Spring AOP和dynaop,作为JDK动态代理的替代品,当目标类不支持接口时,...

    cglib-nodep-2.2.2.jar.zip

    一、CGlib概述 CGlib是一个针对Java的代码生成库,它通过代理技术在运行时动态生成新的子类来扩展已有类的功能,这使得我们可以在不修改源代码的情况下对目标类进行功能增强。CGlib的核心组件是Enhancer,它负责...

    cglib-3.2.4.jar

    一、CGlib概述 CGlib(Code Generation Library)是一个强大的代码生成库,最初由Eugene Kuleshov开发,现已成为Apache软件基金会的一个项目。CGlib的主要功能是动态生成子类,通过这种方式,它可以为任何未实现...

    Java动态代理(JDK和cglib)

    #### 一、代理模式概述 代理模式是一种结构型设计模式,其中代理类含有一个对真实主题对象的引用,这样代理对象就可以执行一些预备或清理工作,并将真正的调用传递给真实主题对象。 在Java中,代理模式的应用非常...

    spring-cglib-repack-3.2.6 和 spring-objenesis-repack-2.6

    **Spring框架概述** Spring是一个开源的Java/Java EE全功能栈的应用开发框架。它的核心特性包括依赖注入(DI)和面向切面编程(AOP),这使得开发者可以编写松耦合、可测试的代码。Spring还提供了大量的其他功能,如...

    基于Java的cglib库的动态代理测试项目.zip

    项目概述 该项目主要用于测试和验证cglib库的动态代理功能。cglib是一个强大的Java库,用于生成和操作字节码,特别是在需要动态代理和AOP(面向切面编程)的场景中非常有用。通过该项目,开发者可以深入理解cglib...

    2017年阿里Java基础面试题文档

    **知识点概述:** - **自我介绍**:在技术面试中,自我介绍不仅是为了展示个人背景,更是为了体现自己的专业技能和个人特质。应聘者应该简明扼要地介绍自己的教育背景、工作经验以及与职位相关的技能。 - **解决问题...

    proxy增加日志例子

    以下是一个简单的步骤概述: 1. **定义目标类(Target Class)**:首先,我们需要一个业务逻辑类,例如`MyService`,它包含我们想要增强的方法。 ```java public class MyService { public void doSomething() { ...

    java_spring_day05.pdf

    **知识点概述:** 在Java Spring框架的学习过程中,SSH(Struts + Spring + Hibernate)的整合是一个重要的环节。本课程主要介绍如何使用注解的方式来进行SSH整合,这种方式可以减少XML配置的复杂度,并且更符合...

    业余框架整合-SSHD-(1)-整体概述(2011-12-03更新)

    标题中的“业余框架整合-SSHD-(1)-整体概述”表明了这个压缩包可能包含的是一个关于SSH(Spring、Struts、Hibernate)三大框架整合的教程或项目实践,重点是第一部分的整体概述。SSH框架是Java开发中常用的一个Web...

    Spring AOP 代码

    **Spring AOP 概述** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个重要组成部分,它提供了在不修改源代码的情况下,对应用程序进行功能增强的能力。AOP的核心概念是切面(Aspect...

    spring-aop-3.2.0.RELEASE-sources.jar.zip

    一、AOP概述 AOP的核心理念在于将横切关注点(如日志、安全检查)与业务逻辑分离,提高代码的可读性和可维护性。在Spring AOP中,这种分离是通过创建切面(Aspect)和定义通知(Advice)来实现的。切面是封装了特定...

    spring-framework-2.5.4-with-dependencies

    1. Spring 框架概述: Spring框架的核心特性在于IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入),它通过管理对象的生命周期和依赖关系,降低了组件之间的耦合度。此外,Spring还...

    JAVA的反射机制与动态代理.pdf

    #### 一、JAVA反射机制概述 JAVA的反射机制是一种强大的功能,允许程序在运行时获取自身的结构信息并能够直接操作这些内部结构。这种能力通常被称为运行时类型信息(RunTime Type Information, RTTI)。通过反射...

    Spring框架+Spring工作原理+AOP面向切面编程+JDK代理+Cglib代理

    ### Spring框架概述与核心原理 #### 一、Spring框架简介 Spring框架是一个广泛使用的开源Java平台,用于构建高质量的企业级应用程序。它最初由Rod Johnson在2004年创建,旨在简化企业级Java应用程序的开发过程。...

    java动态代理

    #### 概述 在软件开发领域,面向切面编程(Aspect Oriented Programming, AOP)是一种编程范式,旨在提高程序模块化程度,通过分离关注点(Separation of Concerns, SoC)的方式将横切关注点(Cross-cutting ...

    高级Java人才培训专家-spring高级49讲

    ### 课程内容概述 #### 1. Spring容器 - **容器接口**:介绍Spring容器的基本概念以及核心接口。 - **容器实现**:分析Spring提供的不同容器实现方式。 - **Bean的生命周期**:探讨Bean在Spring容器中的整个生命...

Global site tag (gtag.js) - Google Analytics