zoukankan      html  css  js  c++  java
  • Spring 简介

    Spring 简介

    Spring 基于 IOC(Inversion Of Control) 和 DI(Dependency Inject)。Spring 出现以前,类对象是用户手动创建的(例如使用 new)与组装的,而使用 Spring 框架后类对象将由 Spring 框架生成并由 Spring 控制对象的生命周期

    graph TB A[Spring] B[上下文] C[4类Scope] D[Component<br/>ComponentScan] E[Bean<br/>Primary<br/>Autowired] F[Profile<br/>Conditional] G[Value<br/>SpEL] A --> B B --> C A --> D A --> E A --> F A --> G

    Spring 组成

    Spring 上下文

    在基于 Spring 的应用中,对象生存于 Spring 容器(container)中,Spring 容器负责创建、装配、配置并管理对象的整个生命周期,从创建到死亡

    Spring 容器归为两大类型:Bean 工厂和应用上下文,常用的是 Spring 上下文。Spring 自带了多种类型的应用上下文:

    • AnnotationConfigApplicationContext
      • 从 Java 配置类中加载 Spring 应用上下文
      • AnnotationConfigApplicationContext(com.springinaction.knights.config.KnightConfig.class);
    • AnnotationConfigWebApplicationContext
      • 与上同,用于加载 web 配置
    • ClassPathXmlApplicationContext
      • 默认从所有已知路径下查找并载入 xml 配置文件
      • 给出文件名即可: ClassPathXmlApplicationContext("knight.xml");
    • FileSystemXmlapplicationcontext
      • 从文件系统中的 xml 配置文件中加载上下文
      • 需明确指明文件位置: FileSystemXmlApplicationContext("c:/knight.xml");
    • XmlWebApplicationContext

    Bean 作用域

    默认情况下,Spring 应用上下文中的所有 bean 都以单例(singleton) 模式创建。有时候,你所使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。Spring 提供了几种作用域,可以按照需求来创建 bean

    • 单例(Singleton),默认作用域,只创建一个 bean 实例
    • 原型(Prototype),每次注入或者通过 Spring 上下文获取时会创建一个新的 bean 实例
    • 会话(Session),web 应用中,为每一个会话创建一个 bean
    • 请求(Rquest),web 应用中,为每一个请求创建一个 bean

    @Scope

    使用 @Scope 注解可以设置 bean 的作用域:

    @Component // @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    // @Scope("prototype"),使用上面那行的方式更安全一些
    public class Notepad { ... }
    

    xml 配置形式如下:

    <bean id="notepad" class="com.myapp.Notepad" scope="prototype" />
    

    Spring 依赖注入

    Spring 可以使用配置文件或者注解来实现依赖注入,下面以配置文件形式和注解形式来简单解释 IOC 与 DI

    配置文件形式(XML)

    需要 Spring 进行管理的对象:

    public class Category {
        private int id;
        private String name;
        
        // getter & setter func...
    }
    
    public class Product {
        private int id;
        private String name;
        private Category category;
        
        // getter & setter func...
    }
    

    Spring 配置文件(IOC & DI):

    <?xml version=...
        <!-- 同时指定 name 和 id,此时 id 为标识符,而 name 为 Bean 的别名,两者都可以找到目标Bean -->
    	<bean id="category" name="c" class="xyz.yearn.Category">
            <property name="name" value="category 1" />
        </bean>
        <bean name="p" class="xyz.yearn.Product" primary="true">
            <property name="name" value="product1" />
            <property name="category" ref="c" />
        </bean>
    </beans>
    

    位于 <bean> 标签内的配置用于声明 bean

    从 Spring 上下文获取对象

    public class TestSpring {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[] { "applicationContext.xml" });
     		// 对象 p 中的 category 由 name 为 c 的 bean 自动进行填充:ref="c",这是 DI
            Product p = (Product) context.getBean("p"); // IOC,xml 配置文件中创建了一个 name 为 p 的对象
            System.out.println(p.getName());
            System.out.println(p.getCategory().getName());
        }
    }
    

    注解形式

    @Component & @Bean

    使用 @Component 告知 Spring 在进行组件扫描(@ComponentScan)时需要为哪些类创建 bean

    // Spring 将为使用 Component 注解的类创建对象(bean)
    // 每一个 Bean 都有一个 ID,默认 ID 为类首字母小写,可以指定,如下 p
    @Component("p") // 大部分场景下可以使用 @Named 替换 Component
    @Primary // 有歧义时,标识首选bean
    public class Product {
        ...
    }
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration// 表名这是一个配置类,包含创建 bean 的细节
    @ComponentScan(basePackages={"soundsystem", "video"}) // 组件扫描默认是不开的,需要使用这个注释打开
    // @ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
    // @Import(CDPlayerConfig.class)
    // @ImportResource("classpath:cd-config.xml")
    public class CDPlayerConfig {
        // 默认情况下, bean的ID与带有@Bean注解的方法名是一样的
        // 因为 Bean 注解的存在,Spring 会拦截所有下面函数的调用,返回相同的对象实例
        @Bean(name="lonelyHeartsClubBand") // 告知 Spring 当前函数返回的对象需要注册为 bean
        @Primary
    	public CompactDisc sgtPeppers() {
    		return new SgtPeppers();
    	}
    }
    

    自动装配(DI)

    // 使用 Autowired 注解,Spring 自动将合适的 bean 绑定到变量
    @Autowired // 变量注解,可以使用 @Inject 替换 Autowired
    @Qualifier("cc1") // 只有 id 为 cc1 的 bean 才会注入 category 中
    private Category category;
    
    @Autowired // 函数注解,Autowired 按照类型进行装配
    // @Resource,按照 name 属性进行装配
    // @Autowired(required=false),自动装配失败不抛异常
    public void setCategory(Category category) {
        this.category = category;
    }
    

    条件装配

    Spring 可以在运行时根据环境变量选择合适的 Beans,这样在测试或上线时就不需要重新构建程序

    @Profile

    Spring 中可以使用 Profile 注解指定 bean 属于哪个 profile,@Profile 注解可以同时置于方法(Spring 版本需要大于 3.2)和类

    @Configuration
    @Profile("dev")// 控制配置类生效环境
    public class DevelopmentProfileConfig {
    	@Bean(destroyMethod="shutdown")
    	public DataSource dataSource() {
            ...
    
    XML 形式
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    ...
    profile="dev">
        
    	<jdbc:embedded-database id="dataSource">
    	...
    	</jdbc:embedded-database>
    </beans>
    

    可以在 <beans> 元素中嵌套 <beans>,并指定不同的 profile

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    ...>
        <beans profile="dev">
    		<jdbc:embedded-database id="dataSource">
    		...
    		</jdbc:embedded-database>
        </beans>
        
        <beans profile="prod">
    		<jee:jndi-lookup id="dataSource">
    		...
        </beans>
    </beans>
    

    激活 profile

    Spring 会依次查找 spring.profiles.activespring.profiles.default 这两个变量来确定哪个 profile 处于激活状态,如果二者都未定义,则 Spring 只会创建那些没有定义在 profile 中的 bean。

    可以同时激活多个不同的 profile

    有很多种方式配置上面两个属性,例如在 web.xml 中

    <?xml ...>
    <web-app version="2.5" ...>
    ...
    <context-param>
    	<param-name>spring.profiles.default</param-name>
    	<param-value>dev</param-value>
    </context-param>
    ...
    

    在生产环境可以通过配置 spring.profiles.active 环境变量实现环境切换

    @ActiveProfiles

    测试环境可直接使用注解激活对应的 profile

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes={PersistenceTestConfig.class})
    @ActiveProfiles("dev") // 直接激活指定 profile
    public class PersistenceTest {
    	...
    }
    

    @Conditional

    @Conditional 注解,它可以用到带有 @Bean 注解的方法上。如果给定的条件计算结果为 true,就会创建这个 bean,否则的话, 这个 bean 会被忽略

    歧义消除

    • @Primary,标识首选 bean
    • @Qualifier("iceCream") ,这类方法比较灵活,具体使用时还需查询文档

    运行时注入

    Spring 提供了两种在运行时求值的方式

    • 属性占位符(Property placeholder)
    • Spring 表达式语言(SpEL)

    @PropertySource & Env

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    
    @Configuration
    @PropertySource("classpath:/xyz/yearn/app.properties")
    public class Test{
        @Autowired
        Environment env;
        
        @Bean
        public BlankDisc disc()
        {
            // getProperty 有多种变种,用时请查 API
            return new BlankDisc(env.getProperty("disc.title"), env.getProperty("disc.artist"));
        }
    }
    
    // app.properties 的内容如下
    // disc.title=Sgt.Peppers Lonely Hearts Club Band
    // disc.artist=The Beatles
    

    属性占位符 @Value

    占位符使用 ${...} 包装属性名称,使用属性占位符需要先配置 PropertySourcesPlaceholderConfigurer

    <context:property-placeholder />
    
    <bean id="sgtPeppers" class="soundsystem.BlankDisc" c:_title="${disc.title}" c:_artist="${disc.artist}"/>
    
    @Value
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
    	return new PropertySourcesPlaceholderConfigurer();
    }
    
    public BlankDisc(@Value("${disc.title}") String title,
    				 @Value("${disc.artist}") String artist) 
    {
    	this.title = title;
    	this.artist = artist;
    }
    

    SpEL

    SpEL 是 Spring 提供的内嵌脚本语言,SpEL 需要放置到 #{...} 之中,示例

    • #{sgtPeppers.artist}
      • SpEL 可以引用 bean 属性
    • #{T(System).currentTimeMillis()} & T(java.lang.Math).random()
      • T() 表达式会将 Java.lang.System 视为 Java 中对应的类型,因此可以调用其 static 方法
    • #{systemProperties['disc.title']}
    • 通过 systemProperties 对象引用系统属性
    • #{2 * T(java.lang.Math).PI * circle.radius}

    和常见脚本语言一样,SpEL 可以计算十分复杂的计算,这部分内容请参考其他文档

    面向切面编程(AOP)

    AOP (Aspect Oriented Program)即面向切面编程。使用 AOP 可以剥离核心业务与辅助功能代码,减少业务程序员的心智负担:

    • 核心业务,比如登录,数据库操作等
    • 辅助功能,比如性能统计、日志、事务管理等。AOP 中的切面即为周边业务(或者辅助功能)

    下面举个例子

    需要添加切面的业务对象

    public class ProductService {
        public void doSomeService(){
            System.out.println("doSomeService");
        }
    }
    

    切面对象

    public class LoggerAspect {
        public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("start log:" + joinPoint.getSignature().getName());
            Object object = joinPoint.proceed();
            System.out.println("end log:" + joinPoint.getSignature().getName());
            return object;
        }
    }
    

    关联业务与切面(配置文件)

    <aop:config>
       <!-- 定义切面位置 -->
       <aop:pointcut id="loggerCutpoint" expression="execution(* com.how2java.service.ProductService.*(..)) "/>
        <!-- 关联切面和切面对象 -->
       <aop:aspect id="logAspect" ref="loggerAspect">
       		<aop:around pointcut-ref="loggerCutpoint" method="log"/>
       </aop:aspect>
    </aop:config>  
    

    根据 AOP 配置中的 execution,ProductService 中所有方法的调用都将触发 LoggerAspect 切面

    注解形式

    @Aspect // 这是一个切面对象
    @Component
    public class LoggerAspect {
        // 指明切面的执行位置
        @Around(value = "execution(* com.how2java.service.ProductService.*(..))")
        public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("start log:" + joinPoint.getSignature().getName());
            Object object = joinPoint.proceed();
            System.out.println("end log:" + joinPoint.getSignature().getName());
            return object;
        }
    }
    
  • 相关阅读:
    Grunt jshint Warning: Path must be a string . Received null Use
    bootstrap滚动监视原理实现
    Bootstrap模态框原理分析及问题解决
    LeetCode54. 螺旋矩阵
    LeetCode53. 最大子序和
    mysql servers实现层拼写SQL
    easyUI 分页 获取页面
    excel导入功能
    easyUI遮罩
    uuid生成
  • 原文地址:https://www.cnblogs.com/jiahu-Blog/p/11849811.html
Copyright © 2011-2022 走看看