• 深入理解Spring IoC容器和动态代理机制


    Deployment期间验证

    实现一:

    1     <bean id="theTargetBean" class="..."/>
    2     
    3     <bean id="theClientBean" class="...">
    4         <property name="targetName">
    5             <idref bean="theTargetBean" />
    6         </property>
    7     </bean>

    实现二:

    1     <bean id="theTargetBean" class="..." />
    2 
    3     <bean id="client" class="...">
    4         <property name="targetName" value="theTargetBean" />
    5     </bean>

    方式1跟方式2得到的依赖关系完全一致,不同的是方式1在deployment的时候就会去判断名字为theTargetBean的bean是否有定义,方式2要推迟到client被实例化的时候才去判断名字为theTargetBean的bean是否有定义。

    XML简写
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    命名空间p表示可以将<property>变成属性p:[name|name-ref]=”id”的方式出现于<bean>定义中;
    命名空间c表示可以将<constructor-arg>变成属性c:[name|name-ref]=”id”的方式出现<bean>定义中;

    提前初始化
    <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
    <bean name="not.lazy" class="com.foo.AnotherBean"/>
    缺省情况下ApplicationContext在Initialization Process的时候就会创建并配置所有Singleton Bean (没有依赖项的bean),如果lazy-init显示设置为true则表示取消提前初始化的功能,从而加快初始化速度并减少内存占用;对于<beans/>使用default-lazy-init=”true”属性进行批量设置。

    方法注入(Lookup Method Injection)

    Java代码

    1 public abstract class CommandManager {
    2     protected abstract Command createCommand();
    3 }

    Xml代码

    1 <bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
    2     <!-- inject dependencies here as required -->
    3 </bean>
    4 
    5 <bean id="commandManager" class="fiona.apple.CommandManager">
    6     <lookup-method name="createCommand" bean="command"/>
    7 </bean>

    CommandManager中的createCommand方法根据不同要求需要不同的实现,通过配置文件的<lookup-method>可以指定不同bean定义的方法实现;如果一个bean是stateful的则需要将scope设置为prototype,在每次调用的时候都生成一个新的instance。
    <lookup-method/>标签用于解决当一个singleton的Bean A需要引用另外一个非singleton的Bean B(也就是每一次引用Bean B都需要引用最新创建的实例);实现方式是动态创建一个CommandManager的子类,并复写指定的方法;
    AOP综合使用JDK Dynamic Proxy和CGLIB对目标类进行代理,两者区别如下:
    #1 JDK Dynamic Proxy方式使用Java Reflection技术,因此要求目标类有一个Interface,并且目标方法需要此Interface中申明,动态创建一个实现了Interface的类并在改类中调用目标类的方法;

     1 public class PerformanceMonitorProxy implements InvocationHandler {
     2 
     3     private Object target;
     4 
     5     public ServiceWithPerformanceMonitorProxy(Object target) {
     6         this.target = target;
     7     }
     8 
     9     public static Object newProxyInstance(Object target) {
    10         Class clazz = target.getClass();
    11         return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),
    12                 new ServiceWithPerformanceMonitorProxy(target));
    13     }
    14 
    15     @Override
    16     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    17         //do something before target function invocation
    18         PerformanceMonitor.begin(method.getName());
    19         Object result = method.invoke(target, args);
    20         //do something after target function invocation
    21         PerformanceMonitor.end(method.getName());
    22         return result;
    23     }
    24 }

    #2 CGLIB使用字节码技术,动态生成一个目标类的子类,通过over-write去覆盖目标方法;

     1 public class CGlibProxy implements MethodInterceptor {
     2     private Enhancer enhancer = new Enhancer();
     3     public Object getProxy(Class clazz) {
     4         enhancer.setSuperclass(clazz);
     5         enhancer.setCallback(this); // 代理执行时会回调此this持有的intercept方法,以实现代码织入
     6         return enhancer.create();
     7     }
     8 
     9     @Override
    10     public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    11         PerformanceMonitor.begin(method.getName());
    12         Object result = methodProxy.invokeSuper(target, args);
    13         // 下面这样是无法执行原有方法的,因为这里的target并不是原有类的实例,而是代理类的实例
    14         // target :
    15         // com.dianping.aop.AdminServiceImpl$$EnhancerByCGLIB$$225da297@16dd5a9d
    16         // Object result = method.invoke(target, args);
    17         PerformanceMonitor.end(method.getName());
    18         return result;
    19     }
    20 }

    Spring一般首选JDK Dynamic Proxy进行代理,如果遇到没有实现Interface的情况则使用CGLIB,当然可以通过下属设置强制使用CGLIB;

    1 <aop:config proxy-target-class="true">
    2     <!-- other beans defined here... -->
    3 </aop:config>

    Bean作用范围 (<bean ………. Scope=”…….” />)

    Singleton:缺省值,一个Spring IoC容器中至多仅生成一个instance,被依赖者共享,stateless
    Prototype:每次调用都会生成一个新的instance,不同依赖者使用不同的实例,stateful
    Request:WebApplicationContext中一次HTTP请求中至多仅生成一个instance
    Session:WebApplicationContext中一个HTTP Session中至多仅生成一个instance
    Global Session:WebApplicationContext中一个Global HTTP Session中至多仅生成一个instance
    Application:WebApplicationContext中一个ServletContext中至多仅生成一个instance

    1     <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    2         <aop:scoped-proxy/>
    3     </bean>
    4 
    5     <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    6     <bean id="userService" class="com.foo.SimpleUserService">
    7         <property name="userPreferences" ref="userPreferences"/>
    8     </bean>

    <aop:scoped-proxy/>用于解决将一个shorter-lived scoped bean注入到一个longer-lived scoped bean时shorter-lived scoped bean提前失效的场景;由于singleton类仅有一次注入bean的机会,因此解决思路是在一开始就在longer-lived scoped bean(singleton)中注入一个proxy class作为代理类,如果shorter-lived scoped bean(session)已经失效,代理类会使用当前session scope的shorter-lived bean。

    定制Bean的初始化和销毁动作

    方法一:使用标注@PostConstruct和@PreDestroy;在bean的java类文件中添加。
    这两个标注由CommonAnnotationBeanPostProcessor进行处理。

    1 @PostConstruct  
    2 public void  init(){  
    3     System.out.println("init  method”);
    4 }  
    5 @PreDestroy  
    6 public void  dostory(){  
    7     System.out.println("destory method");  
    8 }

    方法二:使用xml配置属性init-method和destroy-method,或者在<beans/>中添加批量设置的属性default-init-method或者default-destroy-method;

    1 <bean id="initAndDestroySeqBean" 
    2     class="com.chj.spring.InitAndDestroySeqBean" 
    3     init-method="initMethod" 
    4     destroy-method="destroyMethod"/>

    方法三:使用接口InitializingBean和DisposableBean (不推荐)

     1 public class PersonService  implements InitializingBean,DisposableBean{  
     2     @Override  
     3     public void destroy() throws Exception {  
     4         // TODO Auto-generated method stub  
     5         System.out.println("init  method");  
     6     }  
     7     @Override  
     8     public void afterPropertiesSet() throws Exception {  
     9         // TODO Auto-generated method stub  
    10         System.out.println("init  method");  
    11     }  
    12 } 

    执行顺序为@PostConstruct -> InitializingBean -> init-method

    如果POJO需要获取容器的某些消息,可以实现下述接口;但是一旦实现这些接口之后就与Spring框架强耦合;

    1 public interface ApplicationContextAware {
    2     void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
    3 }
    4 public interface BeanNameAware {
    5     void setBeanName(string name) throws BeansException;
    6 }

    Bean继承

    当需要创建同一个class或者同一个parent class的多个不同状态的instance时,使用parent属性可以有效减少配置文件中需要填写的属性值;

     1 <bean id="inheritedTestBean" abstract="true"
     2         class="org.springframework.beans.TestBean">
     3     <property name="name" value="parent"/>
     4     <property name="age" value="1"/>
     5 </bean>
     6 
     7 <bean id="inheritsWithDifferentClass"
     8         class="org.springframework.beans.DerivedTestBean"
     9         parent="inheritedTestBean" init-method="initialize">
    10     <property name="name" value="override"/>
    11     <!-- the age property value of 1 will be inherited from parent -->
    12 </bean>

    #1 inheritsTestBean中的abstract=true表示当前bean不能实例化;如果一个singleton bean不打算进行实例化,则需要加上abstract=true属性。
    #2 inheritsWithDifferentClass中的class属性不需要extends自parent class,但必须与parent class bean定义的属性匹配;并且可省略,表示使用parent指定bean的class属性;

    Spring容器扩展点接口
    BeanPostProcessor可以进行customized的实例化、初始化、依赖装配、依赖验证等流程(处理annotation标签,比如@Autowired,@Resource等)。
    BeanFactoryPostProcessor可以修改xml文件的bean metadata;Ordered接口控制先后执行顺序。

    自动装配(Autowiring)
    XML Injection在Annotation Injection之后执行,所以前者会覆盖后者的相同设置,比如如果<property/>或者<constructor-arg/>也针对同一属性进行装配,则@Autowire装配的内容被覆盖;自动装配不能指定简单类型,如String;并且容易造成匹配歧义从而抛出异常;

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:context="http://www.springframework.org/schema/context"
     5     xsi:schemaLocation="http://www.springframework.org/schema/beans
     6         http://www.springframework.org/schema/beans/spring-beans.xsd
     7         http://www.springframework.org/schema/context
     8         http://www.springframework.org/schema/context/spring-context.xsd">
     9 
    10     <context:annotation-config/>
    11 
    12 </beans>

    ‘<context:annotation-config>’表示使用annotation对属性进行自动装配,同时还注册了一系列post processors(比如AutowiredAnnotationBeanPostProcessor);加入上述tag之后就可以在java class中使用如下annotation了。

    @Required:应用于属性的setter方法,表示相关的property必须设置值,否则抛出异常,但不检查是否为空。
    @Autowired:应用于属性或者属性的setter方法, 表示相关的property必须有且仅有一个匹配项;如果应用于Spring自定义的Interface(如ApplicationContext)则容器会自动赋值匹配,无需额外进行设置;@Autowired是基于类型匹配的,所以如果一个bean是collection或者map则不能用@Autowired而需要使用@Resource。

  • 相关阅读:
    遥控器拆卸记录
    计算器拆卸记录
    no matching constructor for initialization
    STL
    排序方法
    二叉树之广度优先遍历
    C++之queue学习记录
    方向电路
    站间联系电路
    求二叉树的最大深度
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/8111771.html
Copyright © 2020-2023  润新知