• Spring in Action 4th 学习笔记


    约定:

           一、@Xxx Class 表示被@Xxx注解的类。同理还有@Xxx注解的字段或方法。

        例如:@Bean Method

      二、@Component Class 同时代指 @Controller、@Service、@Repository。

    All beans in a Spring application context are given an ID. -- 如果不指明,也会给定一个默认的ID:类名,首字母小写。
    【】上面这句,不适合XML方式,因为XML方式的默认ID是全路径再加上#{n} ,例如 a.b.c.D#2 。

    Spring的配置,可以XMLJavaConfig注解

    一般推荐使用JavaConfig注解,因为JavaConfig可以保证类型一致,重构重命名都不会影响。

    @RunWith(SpringJUnit4ClassRunner.class)    //测试开始时,会自动创建Spring的ApplicationContext。
    @ContextConfiguration(classes=CDPlayerConfig.class)    // 从什么地方加载JavaConfig。默认导包。

    【】Bean在第三方库中时,无法使用注解扫描导入~    
            这时,可以使用JavaConfig或者XML配置。但是,建议使用JavaConfig配置,因为更强大,且类型有保证。
                JavaConfig is configuration code。就是说,它不应该包含任何业务代码,也不应该被任何业务代码包含。

    【】@Autowired 可以用在任何方法上,本质都是注入形参
    【】@Autowired(required=false) 这样注入失败不会异常!    但是需要判断null异常
    【】问题:如果有多个bean,那怎么选择?同时满足也会异常,需要窄化范围,见后面。

    @Component     基本等同于     @Named   (JSR330)
    @Autowired       基本等同于       @Inject  (JSR330)

    纯JavaConfig:

    JavaConfig 就是@Configuration Class。

    如果JavaConfig需要自动扫描并创建Bean那就再加上@ComponentScan注解。

      注意,这里的扫描对象是@Component Class 。

      而且,自动生成的Bean,其ID就是类名,首字母小写。(XML则是全路径再加上#{n} ,例如 a.b.c.D#2 。)

      示例如下:

    @Configuration     // 声明这是JavaConfig,Spring会自动加载 
    @ComponentScan()    // 自动扫描,需要指定路径,稍后详解
    public Class MyConfig{
    }

    如果JavaConfig不需要自动扫描,那就需要在JavaConfig类中设置@Bean Method。示例如下:

    @Configuration     // 声明这是JavaConfig,Spring会自动加载 
    public Class MyConfig{
        @Bean(name="youSpecifiedID") 
        public MyType myType(){
            return new MyType();   // 看这里,创建对象!!!
        }
    } 

     继续,这种情况下(不自动扫描),怎么解决依赖注入的问题?最简单的办法如下:

    @Configuration
    public Class MyConfig{
        @Bean(name="youSpecifiedID")  // 默认ID是类型首字母小写
        public TypeA typeA(){
             return new TypeA();
        }
    
        @Bean()
        public TypeB typeB(){
            return new TypeB( typeA() );  // 最简单直接的办法,就是调用@Bean Method
        }
    
    }

    注意,上面这样直接调用@Bean Mehtod的方式,不会每次都调用@Bean Method!Spring会拦截该调用,而直接注入Bean(这里是typeA)。

    JavaConfig还可以导入其他的JavaConfig或XML,如下:

    @Configuration
    @import( Config1.class, Config2.class ) // 导入任意多个其他的JavaConfig
    @importResource( "classpath:xxx.xml" )  // 导入XML
    public Class ConfigAll{
        /* your Code here */
    }

    XML导入XML,使用<import resource="another.xml" />。

    XML导入JavaConfig,使用<bean class="xx.xx.JavaConfig" />。这样Spring创建该bean的时候会自动扫描并导入相关数据。

    【】【】XML配置,可以使用spring tool suite   (https://spring.io/tools/sts)
            该工具还可以检查大多数XML的配置。
        【】【】XML的依赖注入。
            【】【】构造方法:<bean ...><constructor-arg ref="refID" /></bean>        注意引用是ref,如果是字面值,使用value!!!如 value="Hello"。
                    传入null:<constructor-arg><null/></constructor-arg>
            【】【】c名称空间:<bean ... c:形参名-ref="refID" />            注意,如果是字面值,使用【c:形参名】即可。形参位置也一样。
                    注意,需要导入名称空间。
                    形参名不方便,如果压缩文件,会破坏形参名。
                    可以使用形参位置【_n】。如果只有一个形参,n可以省略。
                    
            
                【】集合List和Set        
                    <list>
                        <value></value>
                        <value></value>
                    </list>
                    <list>
                        <ref bean="refID"/>
                        <ref bean="refID"/>
                    </list>
                【】property
                    【】p名称空间。
                    
                【】util名称空间。
                    <util:constant>    References a public static field on a type
                    <util:list>    java.util.List
                    <util:map>    java.util.Map
                    <util:properties>    java.util.Properties
                    <util:property-path>    References a bean property (or nested property)
                    <util:set>    java.util.Set

    高级注入

    不同的环境下可能需要注入不同的Bean,例如DataSource,有dev、qa和prod环境,该怎么解决?

    笨办法:配置不同的XML,在编译时选择使用哪个。(例如,可以通过Maven Profiles选择。)

    缺点:不同的环境需要不同的编译!!

    Spring的解决方案:在运行时决定选择使用哪个。

    找出需要选择的Bean,放到一个或多个Profile中,然后Spring根据不同的Profile自动选择创建的Bean。

    JavaConfig中

      ① 在不自动扫描的JavaConfig中,可以使用@Profile注解到@Bean Method上

      ② 在自动扫描的JavaConfig中,需要将@Profile注解到@Component Class上

      注意:没有被@Profile的@Bean Method或@Component Class,各种环境均被创建!!!

    在XML中:
           ①    <beans ... profile="dev">   
                   注意,是beans,根节点。这样当前XML中的所有Bean都在dev Profile下创建。
           ②    <beans profile="dev"><bean/>...<bean/></beans>
                  <beans profile="prod"><bean/>...<bean/></beans>
                  注意,这里不是根节点。

    Spring怎么判断Profile?

      两个地方:spring.profiles.default, spring.profiles.active

      Spring会先检查spring.profiles.active的值,有则用;没有再去检查spring.profiles.default的值,有则用,无则没有Profile。

    Spring怎么去设置Profile?

           There are several ways to set these properties:
                  As initialization parameters on DispatcherServlet
                  As context parameters of a web application
                  As JNDI entries
                  As environment variables
                  As JVM system properties
                  Using the @ActiveProfiles annotation on an integration test class    
    例如,在web.xml中
                        <!-- 【】设置context的默认profile -->
                        <context-param>
                            <param-name>spring.profiles.default</param-name>
                            <param-value>dev</param-value>
                        </context-param>
    
                        <!-- 【】设置servlet的默认Profile -->
                        <servlet>
                            <servlet-name>appServlet</servlet-name>
                            <servlet-class>
                                org.springframework.web.servlet.DispatcherServlet
                            </servlet-class>
                            <init-param>
                                <param-name>spring.profiles.default</param-name>
                                <param-value>dev</param-value>
                            </init-param>
                            <load-on-startup>1</load-on-startup>
                        </servlet>
                    上面,设置了default Profile,给dev用。其他情况下,可以通过设置active来覆盖default。

                测试时,可以通过@ActiveProfiles("dev")注解到测试类上,设置并激活spring.profiles.active。    


                【】上面是Spring3的方式,Spring4提供了更强大的方式。
                @Conditional 注解到@Bean Method上面。


                @Conditional(ConditionImpl.class)  【】这里的ConditionImpl就是Condition接口的实现类,只有一个方法,就是match,用于判断是否符合条件。

    public interface Condition {
           boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
     }

                    可以通过ConditionContext实参获取Environment对象,从而判断是否包含指定的Property。

    public class MagicExistsCondition implements Condition {
          public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
                Environment env = context.getEnvironment();
                      return env.containsProperty("magic");    //【】判断环境中是否包含magic Property。
          }
    }

                    只有符合的情况下,被@Conditional注解的@Bean method才会创建Bean。    


                    【】注意,还可以通过实参(ConditionContext和AnnotatedTypeMetadata),获取其他对象,从而用于判断。
                    【】Spring4,@Profile已经被重构,新版基于@Conditional实现。

    【】【】解决NoUniqueBeanDefinitionException问题。
                    【】【】办法一:指定primary bean。
                            @Primary 注解到@Component Class或者 @Bean Method上。
                            <bean ... primary="true"/>
                            【】注意,一种类型只能指定一个。
                            【】缺点:不能自由选择!
                    【】【】办法二:@Qualifier 注解到@Autowired或者@Inject。
                                @Autowired
                                @Qualifier("iceCream")
                                public void setDessert(Dessert dessert) {
                                    this.dessert = dessert;
                                }
                            【】说明,@Qualifier可以注解到@Component上,意思是将Bean的qualifier设为注解的值。如无该注解,默认类名首字母小写。
                            【】说明,@Qualifier也可以注解到@Bean Method上。相当于注解到@Component Class上。
                                因为@ComponentScan时使用@Component,没有@ComponentScan时则使用@Bean。

    【】【】默认,spring中所有bean都被创建为singleton。(单例)。
                    spring scope:singleton、prototype、session(web)、request(web)。
                        
                    @Scope("prototype") 注解到@Component上,或<bean scope="prototype">。
                    @Scope("prototype") 注解到@Bean上。
                    【】SSH,应对app请求,无状态,可以考虑request scope。
                    【】购物车,可以使用session scope。
                    
                    【】【】想知道的是,application context中,如何保证 bean id 不冲突??
                    
                    @Service都是单例,实际上,也是因为它都是以形参接收参数吧。且类的属性都是单例的。
                    给@Service@Scope("singleton")类中的方法@Autowired注入@Scope("session")的bean时:
                        ① @Service一开始就被spring创建,而@Scope("session")则等到有请求时才会被创建,之前怎么办?
                        ② @Scope("session")的bean,应该是每个session一个,而@Service只有一个,应该怎么选择?
                    【】这时,可以使用代理,让spring注入代理,然后根据实际情况选择调用的bean。这就是@Scope的属性proxyMode。
                        当proxyMode=ScopedProxyMode.INTERFACES时,只能代理接口的实现。
                        当proxyMode=ScopedProxyMode.TARGET_CLASS时,使用CGLIB,可以代理任何类。
                        
                        注解时:@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
                        XML时: <bean ... scope="session"></bean>
                                注意,XML时,默认使用CGLIB代理,如果想使用JDK代理, <aop:scoped-proxy proxy-target-class="false" />
                                注意,XML使用proxyMode,需要导入aop空间。

    【】【】【】一些值,不想硬编码,而是在运行时载入。
                Spring有两种办法:property placeholders、Spring Expression Language (SpEL)。二者类似,但用途和表现不同。
                【】【】理解什么是placeholder propery:${ xxx } 这里${} 就是placeholder,里面的xxx就是property。
                

                property placeholders:
                    该方法是从一个source加载property到Environment对象中,然后就可以使用Environment对象调用加载的property。
                    【】@PropertySource("classpath:/com/soundsystem/app.properties")     可以注解到JavaConfig中。
                    然后通过env.getProperty()即可获取需要的内容。
                        ƒ String getProperty(String key)
                        ƒ String getProperty(String key, String defaultValue)            // 【】注意,可以设置默认值
                        ƒ T getProperty(String key, Class<T> type)
                        ƒ T getProperty(String key, Class<T> type, T defaultValue)    // 【】注意,可以设置默认值
                
                        另外,如果要求必须提供某个property,那可以使用env.getRequiredProperty(key),这样,如果不提供,就会异常。
                        另外,如果想检查某个property是否存在,可以使用env.containsProperty(key)。
                        另外,如果想将某个property转成类对象,可以使用env.getPropertyAsClass(key,xx.class)
                        
                    除了property,Environment同样提供了获取Profile的方法。
                        String[] getActiveProfiles()
                        String[] getDefaultProfiles()
                        boolean acceptsProfiles(String... profiles)     //如果Environment支持给定的Profiles,就true。

                       
                    除了Environment,还可以使用${key}的方式注入。
                    
                    【】【】【】使用@Value("${key}")注解。【注意与使用@Value("#{}")的区别】

                        public BlankDisc(@Value("${disc.title}") String title, @Value("${disc.artist}") String artist) {
                            this.title = title;
                            this.artist = artist;
                        }    


                    使用这种方式(@Value("${key}"))时,需要配置PropertyPlaceholderConfigurer bean或PropertySourcesPlaceholderConfigurer bean。    
                    从3.1开始,推荐使用PropertySourcesPlaceholderConfigurer bean。
                        在JavaConfig中配置:

                            @Bean
                            public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
                                return new PropertySourcesPlaceholderConfigurer();
                            }

     
                        在XML中配置:
                            <context:property-placeholder />  注意,直接返回PropertySourcesPlaceholderConfigurer bean。
                        


     【】【】Spring Expression Language (SpEL)
                The first thing to know is that SpEL expressions are framed with #{ ... }, much as property placeholders are framed with ${ ... }.
                SpEL使用#{ ... }。

                    #{ 12 } 数字
                    #{ 9.8E4 } 98000
                    #{ 'abc你好' } String
                    #{ true }
                    #{ T(System).currentTimeMillis() }
                        【】T(System) 意思是导入java.lang.System。
                    #{ beanID.property }
                        【】根据beanID调用某个bean的属性                
                    #{ beanID.method() }
                        【】根据beanID调用某个bean的method()
                    #{ beanID.getName().toUpperCase() }    
                        【】这里假定beanID对应的bean有getName()方法,且返回String,那可以继续调用toUpperCase()方法。
                            但是,如果返回null,那就异常了!怎么解决?
                            解决办法:type-safe operator 【?.】
                                #{ beanID.getName()?.toUpperCase() }    这样,如果getName()返回null,整体就返回null。
                                
                    #{jukebox.songs[4].title}
    【】调用集合或数组
    【】SpEL针对操作集合或数组,提供了一个选择操作符。【.?[]】
            用法:#{jukebox.songs.?[artist eq 'Aerosmith']}     集合或数组中的元素的artist属性值为"Aerosmith"的,返回列表。
        另外,针对操作集合或数字,还提供了【.^[]】【.?[]】    。前者是选择第一个匹配的元素,后者是选择最后一个匹配的元素。
            用法:#{jukebox.songs.^[artist eq 'Aerosmith']}    #{jukebox.songs.$[artist eq 'Aerosmith']}
        另外,还提供了一个projection operator 【.![]】,该操作符用于投影符合的元素属性到一个新的集合。    
            用法:#{jukebox.songs.![title]}

                               

    【小结】
    SpEL针对集合或数组的操作:
          .?[] 选择所有匹配的元素(集合);
          .^[] 选择第一个匹配的元素;
          .$[] 选择最后一个匹配的元素。    
          .![] 选择属性,投影到新集合。
    【】这些操作可以组合!!!就是链式编程而已。    

    【】【】由于都是String,所以,尽量少用 简用 SpEL!    
                                           
    【】【】【】使用@Value("#{}")注入【注意与使用@Value("${key}")的区别】

    public BlankDisc( @Value("#{systemProperties['disc.title']}") String title, @Value("#{systemProperties['disc.artist']}") String artist) {
        this.title = title;
        this.artist = artist;
    }   
    【】【】【】XML中使用SpEL。
    ① <property ... value="#{}"><constructor-arg value="#{}">
    ③ p-namespace
    ④ c-namespace entry
    【】如果想调用Class,需要使用T() operator。T()操作符主要用于调用静态方法和常量!!!
    【】【】SpEL操作符的分类:
                数学计算【+, -, *, /, %, ^】
                比较【<, lt, >, gt,  ==, eq, <=, le, >=, ge】
                逻辑【and, or, not】
                条件【?: (ternary 三元运算符), ?: (Elvis)】
                正则【matches】
                
            #{scoreboard.score > 1000 ? "Winner!" : "Loser"}
            #{disc.title ?: 'Rattle and Hum'}    【】变种用法,如果null,则返回'Rattle and Hum'。Elvis operator。  
  • 相关阅读:
    路由器基础配置之ospf基础配置
    路由器基础配置之广播多路访问链路上的ospf
    路由器基础设置之ospf
    linux命令之文件系统权限操作常用命令
    路由器基础配置之路由重分布
    路由器配置 之 DHCP+DHCP中继服务配置
    路由器配置 之 PAP与CHAP认证
    基于链路的OSPF MD5口令认证
    压缩和归档操作(16个命令)
    基于链路的OSPF简单口令认证
  • 原文地址:https://www.cnblogs.com/larryzeal/p/5418338.html
Copyright © 2020-2023  润新知