Spring发展以及常用注解(一)

Spring发展过程

第一阶段:xml配置
Spring 1.x时代,基本都是xml配置的Bean。如果项目很大,那么需要频繁的在开发类和配置文件之间来回切换。
第二阶段:注解配置
Spring 2.x时代,由于JDK1.5支持注解,Spring提供了声明Bean的注解(@Com[onent,@Service等),大大减少了配置量。我们一般会选择基本配置(如数据库配置等)用xml,业务配置用注解。
第三阶段: Java配置
Spring 3.x到现在,可以使用Java进行配置,这也是Spring 4.x和Spring Boot都推荐的一种配置。
所有的变化都离不开Spring最根本的使命:简化java开发。Spring主要采取了以下四种关键策略:
1.基于POJO的轻量级和最小侵入性编程
不会强迫应用继承它的类或实现它的接口,尽力避免因为Spring本身的API而弄乱应用代码。通常在Spring构建的应用中,它的类几乎没有任何痕迹表明使用了Spring。
2.通过依赖注入和面向接口实现松耦合
依赖注入会将依赖的关系自动交给目标对象。而不是让对象自己去获取依赖。
3.基于切面和惯例进行声明式编程
像日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。AOP能使这些服务模块化,并以声明的方式将它们应用到需要影响的组件中去。这些组件就能拥有跟高的内聚性并且更加关注自身的业务,完全不需要了解涉及系统服务所带来的复杂性。
4.通过切面和模板减少样板式代码
样板式代码比较常见的是使用JDBC访问数据库查询数据,查询语句很容易淹没在一堆JDBC的样板式代码中。而使用Spring的JdbcTemplate,只要关注获取数据的核心逻辑,而不必迎合JDBC API的需求。

Spring模块

Spring使用简单的POJO,即无任何限制的普通java对象来进行企业级开发。每一个被Spring管理的java对象都称为Bean,Spring提供了一个IoC容器来初始化对象,解决对象间的依赖管理和对象的使用。
11097872-f1029713e5ba4742.png-20.6kB
Spring生态
Spring发展到现在已经不再是Spring框架本身了,Spring提供了大量基于Spring的项目,例如:

  • Spring Boot:使用默认开发配置来实现快速开发
  • Spring Cloud: 为分布式系统开发提供工具集
  • Spring Secrity: 通过认证和授权保护应用 Spring XD: 简化大数据应用开发
  • Spring Data: 对主流的关系型和NoSQL数据库的支持

还有一些其他的项目,这里就不一一举例了。
本篇主要讲的是Spring4,涉及到以下几个内容:

  • 强调基于java的Spring配置,该方案几乎可以用于人格所有Spring开发领域之中。
  • 条件化的配置以及profile能让Spring在运行时确定使用或忽略哪些Spring配置。
  • Spring MVC的多项增强和改善,尤其是与创建REST服务相关的。 使用Thymeleaf代替JSP。
  • 使用基于java的配置启用Spring Security。
  • 使用Spring Data,在运行时自动为JPA,MongoDB和Neo4j生成Repostory实现。
  • Spring提供新的声明式缓存支持
  • 借助WebSocket和STOMP,实现异步的Web消息。
  • Spring Boot,改变使用Spring游戏规则的新方法。

Spring容器

Spring自带了多个容器实现,可以归为两种不同的类型:BeanFactory是最简单的容器,提供基本的DI支持。ApplicationContext基于BeanFactory创建,并提供应用框架级别的服务。BeanFactor对大多数应用来说往往比较低级,所以我们把精力都集中在ApplicationContext上。
Spring自带了多种类型的上下文,以下几种是经常遇到的:
AnnotationConfigAppliactionContext:从一个或多个基于Java的配置类中加载Spring上下文
AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web上下文
ClassPathXmlApplicationContext: 从类路径下的一个或多个XML文件中加载上下文定义
FileSystemXmlApplicationContext: 从文件系统下的一个或多个XML文件中加载上下文定义
XmlWebApplicationContext:从web应用下的一个或多个XML文件中加载上下文定义
上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取bean。
Bean的生命周期
传统的Java应用使用关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,就由java自动进行垃圾回收。
Spring容器中的bean的生命周期就相对复杂的多,下图展示了bean装载到spring应用上下文中的一个典型的生命周期过程。
162465267a0c2c59.png-36.6kB
创建对象间协作关系的行为通常被称为装配,这也是依赖注入的本质。Spring主要提供了三种主要的装配机制:

  • 在XML中显式配置
  • 在java中显式配置
  • 隐式的bean发现机制和自动装配
    Spring从两个角度实现自动化装配:
    1.组件扫描:Spring会自动发现应用上下文所创建的bean
    2.自动装配:Spring自动满足bean之间的依赖
    组件扫描和自动装配组合在一起能发挥出强大的威力,能将应用中的显式配置降低到最少。接下来讲到的注解,主要包括组件类注解(@Component,@Service等)以及装配类注解(@Autowired,@Resource等)它们要如何使用。

常用注解

Spring的一个核心理念就是依赖注入(dependency injection-DI)和控制反转(Inversion of Control-IoC),这两个概念是等同的,指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖。

一 组件类注解

@Component: 没有标准的角色
@Service: 标注一个业务逻辑组件类
@Repostory: 标注一个数据访问层(DAO)组件类
@Controller: 标注一个控制器组件类,通常在展现层(MVC->Spring MVC)使用
这四个注解本质上来说属于同一类注解,区别在于标志组件的类型。
@Component可以代替另外三个注解,因为这三个注解是被@Component标注的。代码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    String value() default "";
}

接下来看一个例子
功能类的Bean

//@Service注解声明当前的类是Spring管理的一个Bean
@Service
public class FunctionClass{
    public String sayHello(String word){
        return "hello "+word+" !";
    }
}

使用功能类的Bean

@Service
public class UserFunctionService{
    //后面会讲
    @Autowired
    FunctionService functionService;
    
    public String SayHello(String word){
        return functionService.sayHello(word);
    }
}

配置方面可以使用xml文件,也可以使用Java进行配置。
java配置

//后面会讲
@Configuration
@ComponentScan("org.springframework.*")
public class DiConfig{
}

xml配置

<!-- 自动扫描指定包及其子包下的所有Bean类 -->
<!-- 这里的component-scan和上面的@ComponentScan注解是一样的作用-->
<context:component-scan base-package="org.springframework.*"/>

运行

public class Main{
    public static void main(String[] args){
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(DiConfig.class);
        UserFunctionService userFunctionService = context.getBean(UserFunctionService.class);
        System.out.printIn(userFunctionService.SayHello("di"));
        context.close();
    }
}

注意以下几点:
1.使用@Service等几个注解声明当前类是Spring管理的一个Bean,Spring会自动创建相应的BeanDefinition对象,并注册到ApplicationContext中,这些类就成了Spring受管组件。
2.指定了某些类作为Spring Bean类使用后,还需要指定Spring的文件搜索路径,该路径下及其递归子包中所有的类都会被处理。
3.自动检测后扫描到的类,容器会将类名且第一个字母小写来作为bean的id,当然也可以指定类的名称,尤其是在一个接口有多个实现类的时候,要指定对应的类名搭配@Qualifier注解。
4.尽量使用对应组件注解的类替换@Component注解,在Spring未来的版本中,@Controller,@Service, @Repostory会携带更多语义,而且有利于开发和维护。

二 装配Bean时的注解

@Autowired: Spring提供的注解
@Inject: JSR-330提供的注解
@Resource: JSR-250提供的注解
这三个都可以注解在set方法或者属性上,一般都是注解在属性上,优点是代码更少,层次更清晰。
以@Autowired为例:

public class Test {
//下面是演示,正常开发中切记不要将几种方式作用在同一属性上。 

//直接标注在属性上
    @Autowired
    private Sender sender;
private User user;

//标注在 Setter 方法上
    @Autowired
    public void setSender(Sender sender) {
        this.sender = sender;
    }
//@Autowired 注解标注在构造函数上
@Autowired
    public Boss(Sender sender ,User user){
        this.sender = sender;
        this.user = user ;
    }
    //......
}

注意:

  1. @Inject和@Autowired在大多数场景下都是可以相互替换的,没有太大差别。
  2. @Resource和@Autowired的作用类似,均可标注在字段或属性的setter方法上,但是由于提供方不一样,@Resource需要JDK1.6以上的版本。并且,@Autowired只按照byType自动注入,@Resource默认是byName自动注入,也可以按照byType注入。
  3. 由于@Autowired默认按照类型注入,如果容器中饱含多个同一类型的Bean,那么启动容器时就会报找不到指定类型bean的异常。解决的方法是结合@Qualifier注解来限定注入bean的名称。
  4. @Resource中有两个重要的属性:name和type。name指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段名作为bean名称寻找依赖对象。如果注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找对象。如果@Resource没有指定name属性,且按照默认的名称仍然找不到依赖对象时,@Resource注解会回退到byType来自动注入。而一旦指定了name属性,就只能按照名称装配了。
public class AnotationExp {
    @Resource(name = "HappyClient")
    private HappyClient happyClient;
    
    @Resource(type = HappyPlayAno .class)
    private HappyPlayAno happyPlayAno;
}

三 Java配置

Java配置是通过@Configuration和@Bean来实现的。
@Configuration: 声明当前类是一个配置类,相当于一个Spring配置的xml文件中的<beans>
@Bean: 注解在方法上,声明当前方法的返回值为一个Bean

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

这和xnl配置文件的功能是一样的:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

如果说有什么区别的话,主要是在初始化容器的时候方法不一样:

public class TestMain {
    public static void main(String[] args) {

        //@Configuration注解的spring容器加载方式
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);

        //如果加载spring-context.xml文件:
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    }
}

注意:

  1. @Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与标注的方法名相同
  2. @Bean默认为单例singleton作用域,可以通过@Scope(“prototype”)设置为原型作用域。也可以同时指定初始化和销毁方法。
@Configuration
public class TestConfiguration {
    
    @Bean(name="testNean",initMethod="start",destroyMethod="cleanUp")
    @Scope("prototype")
    public TestBean testBean() {
        return new TestBean();
    }
}

再看一个@Configuration的用法

@Configuration
@ComponentScan(basePackages = { "com.test.web" })
@Import(UserConfg.class)
@ImportResource(locations = {"classpath:config/spring-beans.xml"})
public class MainConfg {

    @Bean(name = "userService", initMethod = "init", destroyMethod = "destroy")
    @Scope("singleton")
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public AccountService accountService(UserService userService) {
        AccountService as = new AccountService();
        as.setUserService(userService);
        return as;
    }
}

注意:
1.@Import:用来导入其他@Configuration配置类
2.@ImportResource:用来导入xml配置文件,某些配置必须要通过xml文件看来配置。

猜你喜欢

转载自blog.csdn.net/LittleMoon_lyy/article/details/88087674