Spring in action笔记

Spring解读


在spring中been的装配方式有两种分别为xml,javaconfig,通常情况是使用javaconfig在其不能满足下才使用xml方式

在javaconfig中一个类定义为配置文件上面加上@configuration,如果要开启啊扫描相应的组件就得在组件上面加上@component,在配置文件上面加上@componentScan,如果没有其他配置的话会默认扫描与配置类相同的包,在相同的包中查找@component注解的类,如果是基于xml来启动组件扫描的话使用<context:component-scan base-package=“xxxx”>

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest{
    
    
@Autowired
private compactDisc cd;
@test
public void cdShouldNotBeNull(){
    
    
assertNotNull(cd)
}
}

CDPlayerTest使用了Spring的SpringJunit4ClassRunner,以便在测试开始的时候自动创建应用上下文,注解@ContextConfiguration会告诉它需要在CDPlayerconfig中加载配置,因为cdplayerconfig类中包含了@componentscan,因此最终的应用上下文中应该包含compactdiscbean

依赖注入使用@autowired

为组件扫描的bean命名

就是之前那个@component,spring会根据类名为其指定一个id,将类名小写,如果想要自己为其定义名字可以在@component(“xxxxx”),另一种就是使用java依赖注解规范@name(“xxxx”),但推荐使用component

设置组件扫描的基础包

@componentScan默认规则它会配置类所在的包作为基础类来扫描组件,如果不想扫描不同的包,就在@componentScan的value属性中指明包的名称,也可以用basePackages属性进行配置,这个可以扫描多个包格式为basePackages={”xxx“,“aaaa”},除了将包名配置为字符串的形式为指定包中所包含的类或接口basePackageClasses={xxx.class,aa.class},spring会扫面这些类的包

通过为been添加注解实现自动装配

简单来说,自动装配就是让spring自动满足bean依赖的一种方法,在满足依赖的过程中会在spring应用上下文中寻找匹配某个bean需求的其他bean,为了声明进行自动装配借助@autowired注解,它不仅能够用在构造器上,还能用在属性的setter方法上,如果没有匹配到bean的话spring会抛出一个异常,@autowired的required属性设置为false,(有可能出现没有bean,也有可能出现多个,从而不知如何选择),可以是使用@Inject;

(java的注解学习)

通过Java代码装配bean

尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,比如使用第三方库的组件装配到你的应用中,这时候无法在它的类上添加@component和@autowired注解的,这时候就不能使用自动化装配方式,这时候有两种方案:java和xml,在这节中,我们将会学习如何使用java配置

就像我之前所说的。在进行显式配置时,javaConfig是更好的方案,因为它更为强大,类型安全并且对重构友好,因为它就是java代码,就像是java配置

同时,javaConfig与其他的java代码又有所区别,在概念上,它与应用程序中业务逻辑和领域代码是不同的,尽管它与其他组件一样都使用相同的语言进行表述,但javaConfig是配置代码,者意味着它不应该包含任何业务逻辑,javaconfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常将javaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了

创建配置类

@configuration
public class cdplayerConfig{
    
    
  @Bean
    public CompactDisc sgtPeppers(){
    
    
        return new sgtPeppers();
    }
}

显示配置和隐式配置在于能不能使用其类加上@component组件,通常如果是自己写的或者spring自己带的,但是如果是第三方的就得需要自己显示地创建bean

在默认情况下bean的id与带有@bean注解的方法名是一样的,在本例中,bean的名字将会是sgtPeppers。如果你想为其设置成一个不同的名字的话就重命名该方法可以通过name属性指定一个不同的名字

@bean(name=“xxxxxx”)

不管你采用什么方法来为been命名,bean声明都是非常简单的

借助javaconfig实现注入

注入依赖的话直接通过方法参数的方式就行,而参数部分必须是在容器中创建的been,不管是通过什么方式得来的(扫描方式,xml方式,javaconfig),带有@bean注解的方法可以采用任何必要的java功能来产生bean实例

通过xml装配bean

古老的装配方式基于xml,xml不是第一选择方案,而是自动化配置和基于javaConfig

在javaconfig的时候需要带有@configuration而在xml配置中需要以元素作为根

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   

</beans>

声明简单的

要是基于xml的spring配置声明一个bean,我们要使用spring-beans模式中的另一个元素,元素类似于javaconfig中的@Bean注解,我们可以按照以下方式声明compactDiscbean:

<bean class="soundsystem.SgtPeppers">

这里声明了一个很简单的bean,要使用全限定的类名,默认根据类名进行命名,如果要引用它的化最好的方法就是借助id属性,

<bean class="soundsystem.SgtPeppers" id="compactDisc">

注意在简单的声明中,我们将bean的类型以字符串的形式设置在class属性中,谁能保证给class属性的值是真正的类,还不能在编译期中收益,这是xml配置的缺点

借助构造器注入初始化的bean

在xml配置中,只有一种声明bean的方式:使用元素并指定class属性,spring会从这里获取必要的信息来创建bean

在xml中声明di时,有两种基本配置方案可以选

元素

使用Spring3.0所引入的c-命名空间

两者区别是否冗长烦琐,前者比后者更加冗长,从而导致xml更加难以读懂,另外有些事情·可以做到后者无法做到

各自注入bean引用

<bean id="cdplayer" class="soundsystem.cdplayer">
<constructor-arg ref="compactDisc">
</bean>

当spring遇到这个元素时,它会创建一个cdplayer实例

c-命名

<bean id="cdplayer" class="soundsystem.cdplayer" 
      c:cd-ref="compactDisc"
      />

给构造参数赋值

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="ssssssssss"/>
    <constructor-arg value="xxx"/>
</bean>    

上面是按照构造方法中参数的索引先后来赋值的

使用c-命名空间

<bean id="compactDisc" class="soundsystem.BlankDisc"
  c:_title="xxxxxxxx"
  c:_artist="xxxxxxxx"    
/>

也可以是这样

<bean id="compactDisc" class="soundsystem.BlankDisc"
  c:_0="xxxxxxxx"
  c:_1="xxxxxxxx"    
/>

但构造器中只有一个参数的话也可以

<bean id="compactDisc" class="soundsystem.BlankDisc"
  c:_="xxxxxxxx" 
/>

装配集合

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="ssssssssss"/>
    <constructor-arg value="xxx"/>
    <constructor-arg >
        <list>
    	    <value>xxxxxxx</value>
            <value>dddddddd</value>
        </list>
    </constructor-arg>    
</bean>  

同样的方式使用set元素

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="ssssssssss"/>
    <constructor-arg value="xxx"/>
    <constructor-arg >
        <set>
    	    <value>xxxxxxx</value>
            <value>dddddddd</value>
        </set>
    </constructor-arg>    
</bean>  

和区别不大,或都可以用来装配list,set甚至数组

设置属性

<bean id="cdPlayer" class="soundsystem.cdplayer">
    <property name="compactdisc" ref="compactDisc"/>
</bean>

我们也可以用p-命名空间

<bean id="cdPlayer" class="soundsystem.cdplayer"
p:compactdisc-ref="compactdisc"
/>

可构造方法类似属性赋值

当然我们可以使用spring util-命名空间中的一些功能来简化blankdiscbean,不管是p-空间命名还是util-命名空间都是需要在xml中声明的

<util:list id="xxxxxx">
    <value>xxxxxxxxxx</value>
</util:list>    
<bean id="cdPlayer" class="soundsystem.cdplayer"
p:compactdisc-ref="xxxxx"
/>

util:list只有util-命名空间中的多个元素之一

<util:constant/>注入常量值
<bean id="..." class="...">
    <property name="isolation">
        <util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
    </property>
</bean>
<util:property-path/>

引用一个bean属性,并将其暴露为bean

<util:list/>
<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
    <value>[email protected]</value>
    <value>[email protected]</value>
    <value>[email protected]</value>
    <value>[email protected]</value>
</util:list>
<util:map/>
<!-- creates a java.util.Map instance with the supplied key-value pairs -->
<util:map id="emails">
    <entry key="pechorin" value="[email protected]"/>
    <entry key="raskolnikov" value="[email protected]"/>
    <entry key="stavrogin" value="[email protected]"/>
    <entry key="porfiry" value="[email protected]"/>
</util:map>
<util:set/>
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
    <property name="sourceSet">
        <set>
            <value>[email protected]</value>
            <value>[email protected]</value>
            <value>[email protected]</value>
            <value>[email protected]</value>
        </set>
    </property>
</bean>
<util:properties/>
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>

创建一个properties类型的bean

混合搭配

导入和混合配置,尽可能使用自动化和显式配置,但有时候xml却是最佳的方案,在spring中这些方案都不是互斥的,可以将这些方式混合一起

将其他的配置导入到已知类可以使用@import(xxx.class),或者是创建高级配置类,将两者导入@import{ {xxxx.class,dddd.class}}

可以同时使用c-空面命名和

也可以将xml也给导进来@importresource(“classpath:xxx.xml”)

在xml配置中引用javaconfig

在xml中导入其他xml文件可以用,想要导入javaconfig可以使用bean

也可以使用开启自动扫描context:component-scan
高级装配

profile

如在不同环境下用不同的数据库,就需要某种约定在某种环境下用某种数据库,spring引入了profile的功能

@configuration
@profile("dev")
public class developmentprofileconfig{

}

在java配置中可以使用@profile注解指定某个bean某个bean属于哪个profile,@profile注解应用在类级别上面了它会告诉spring这个配置类的bean只有在dev profile激活时才会被创建,如果dev profile没有激活的话,那么带有@bean注解的方法都会被忽略掉

方法类似上面

在xml中配置profile

我们也可以通过元素的profile元素,在xml中配置profile bean

<beans profile="development"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
        <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
    </jdbc:embedded-database>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

profile只有在执行才会被激活,但是有个问题如何激活

激活profile

spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default,如果设置了spring.profiles.active属性的话那么它的值来确定哪个profile是激活的,但如果没有设置spring.profiles.active属性的话,spring将会查找spring.profiles.default的值,如果两者都没有设置的,就不会激活

在WEB.XML文件中设置

<context-param>
    <param-name>spring.profiles.defult</param-name>
    <param-value>dev</param-value>
</context-param>

也可以使用注解的方式来开启@activeprofiles来

条件化的bean

假设希望一个或多个bean只有在应用的类路径下包含特定的库时才会创建,或者我们希望某个bean只有当另外某个特定的bean也声明之后才会被创建,我们还可能要求只有某个特定的环境变量设置以后才会创建某个bean,@Conditional注解,它可以用到带有@bean注解的方法上,如果给定条件计算结果为true,就会创建这个bean

在@conditional的类可以是任意实现了condition接口的类型,就是根据这个类的matches方法来决定

public class magicexistscondition implements condition{
    
    
	public boolean matches(conditioncontext context,annotatedtypemetadata metadata){
    
    
	environment env=context.getenvironment();
	return env.containsproperty("magic")
	}
}

conditioncontext是一个接口

public interface CondtionContext{
    
    
	Beandifinitionregistry getregistry();
	configurablelistablebeanfactory getbeanfactory();
	environment getenvironment();
	resourceloader getresourceloader();
	classloader getclassloader()
}

常见的设计模式为谁来决定(实现condition接口的类,重写相关方法),决定的标志@condition(xxx.class),xxx.class这个类就是决定它是否执行

处理自动封装的歧义性

用最简单例子来说,一个接口,可以有多个实现类,解决方式说明白点默认指定,或者缩小范围(为什么不可以是什么情况决定的呢,但有时候真的不知道在已定的条件干啥)

首先

@primary

限定自动动装配的bean

当@primary的数量超过一个时,这时候就需要缩小范围(一个可以最终确定目标,缩小范围,缩小范围,再缩小范围),@Qualifier(“xxxx”),每个bean都会有一个限定符,这个限定符跟bean的id一样,框架会根据"xxxx"限定符找到相对应的bean,就是说无论那么been发生什么变化它的限定符就会和id一样

自定义限定符

在默认情况下,been的限定符和bean是一样的,那么如何自定义限定符呢,使用@Qualifier和@component组合使用自动装配,在进行显性配置时Qualifier可以和@Bean注解一起使用

面向特性的限定符要比基于beanID的限定符更好一些,限定符可以添加多个(限定符分那个顺序吗,应该是分的,因为限定就得区分),但在java不允许同个条目上重复出现相同类型的多个注解,还可以自定义限定符注解

@Target({
    
    ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
    
    

    String value();
}

这个就是自定义限定符

bean的作用域

在默认情况下,spring应用上下文中所有bean都是作为单例的形式创建的,也是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例

在大多数情况下,单例是很理想的方案,初始化和垃圾回收对象所带来的成本只留给一些小规模任务,在这些任务中,让对象保持无状态并且在应用中反复重用这些对象可能并不合理。有时候,所用的类是易变的,它们会保持一些状态, 因此重用是不安全的

spring定义了多个作用域,可以基于这些作用域创建bean,包括:

单例:在整个应用中,只创建bean的一个实例

原型(prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的bean实例

会话(session):在web应用中,为每个会话创建一个bean实例

请求(request):在web应用中,为每个请求创建一个bean实例

单例是默认的作用域,但是正如之前所述,对于易变的类型并不合适,如果选择其他作用域,要使用@scope注解,它可以与@component或@bean一起使用

@component
@scop(configuraablebeanfactory.scop_prototype)
public class notepad{
    
    ......}

同样如果你使用xml来配置bean的话,可以使用元素的scope属性来设置作用域:

不管你使用哪种方式来声明原型作用域,每次注入或从spring应用上下文中的索引该bean的时候,都会创建新的实例,这样所导致的结果就是每次操作都能得到自己的notepad实例

使用会话和请求作用域

@component
@scope(value=webapplicationcontext.scope_session,proxymode=scopedproxymode.interfaces)
public shoppingcart cart(){
    
    .....}

在使用会话作用域的时候,不会直接将创建的类注入到spring上下文中,而是用他们的代理类,由于代理的编写有一定的规则,所以可以直接生成如何生成呢,如下:

@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    
    
    // ...
}

在xml中声明作用域代理

<bean id="cart" class="com.myapp.shoppingchart" scope="session">
	<aop:scoped-proxy/>
</bean>

运行时值注入

<bean id="sgtpeppers" class="soundsystem.blankdisc" c:_title="xxxxxxx" c:_artist="aaaaa"/>

最好避免这种硬编码,而是想让这些值在运行时再确定,spring提供了两种在运行时求值的方式

  1. 属性占位符

  2. spring表达式语言(SpEL)

注入外部的值

在spring中,处理外部值的最简单方式就是声明属性源并通过

@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
    
    

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
    
    
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}

在javaconfig可以用@propertysource(“classpath:sssssss”)相对路径下来引入,属性文件会被加载到spring的environment中,然后可以从这里检索属性,该Environment接口是集成在容器中的抽象,可对应用程序环境的两个关键方面进行建模:profile 和properties。

解析属性占位符

spring一直支持将属性定义到外部的属性的文件中,并使用占位符值将其插入到spring bean中,在spring装配中,占位符的形式为使用"${…}",包装的属性名称,作为样例,我们可以在xml中按照如下方式解析blankdisc构造器参数

<bean id="sgtpeppers" class="sssss.dddd" c:_title="${disc.title}" c:_artist="${disc.artist}"/>

在javaconfig可以使用@value("${disc.title}") string title来表示

为了使用占位符,我们必须要配置一个propertyplaceholderconfigurer bean,如果你想使用xml配置的话,spring context命名空间中的context:propertyplaceholder元素将会为你生成

使用spring表达式语言进行装配

SpEL拥有很多特性

  1. 使用bean的id来引用Bean

  2. 调用方法和访问对象的属性

  3. 对值进行算术,关系和逻辑运算

  4. 正则表达式匹配

  5. 集合操作

SpEL样例

SpEL是一种非常灵活的表达式语言,SpEL表达式要放到#{…}中,解析属性占位符是放到${…},中的

#{T(S)}

猜你喜欢

转载自blog.csdn.net/weixin_43591127/article/details/113848245