Spring——注解编程

Spring——注解编程

第一章 注解的基本概念

1.1 什么是注解编程

指的是在类或者方法上加上特定的注解(@XXX),完成特定功能的开发。如:

@Component
public class OrderService{
    ...
}

1.2 为什么要讲解注解编程

  1. 注解开发非常方便:代码简洁,开发速度大大提高。
  2. Spring开发的潮流:Spring 2.x引入注解,Spring 3.x完善注解,SpringBoot 普及推广注解编程。

1.3 注解的作用

  • 替换xml这种配置形式,简化配置。
  • 替换接口,实现调用双方的契约型。
    • 通过注解的方式在功能的调用者和功能的提供者之间达成约定,进而进行功能的调用。因为注解的应用更为方便灵活,所以在现在的开发中,更为推荐使用注解的方式完成开发。

1.4 Spring注解的发展历程

  • Spring2.x 开始支持注解编程:@Component、@Service、@Scope等;
    • 目的:提供这些注解只是为了在某些情况下简化xml的配置,作为xml开发的有益增补。
  • Spring3.x 提供了更多的注解:@Configuration @Bean等;
    • 目的:彻底替换xml配置,基于纯注解编程。
  • Spring4.x 开始衍生出了SpringBoot。
    • 提倡使用注解完成常用开发。

1.5 Spring注解开发的一个问题

  • Spring基于注解进行配置后,还能否解耦合?
    • 在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring配置文件进行覆盖。

第二章 Spring的基础注解

这个阶段的注解,仅仅是简化xml的配置,并不能完全替代xml。

2.1 对象创建的相关注解

  • 搭建开发环境

    <context:component-scan base-package="com.itheima.ssm">
        <!-- 不扫描Controller注解 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    作用:让Spring框架在设置包及其自爆中扫描对应的注解,使其生效。
    
  • 对象创建相关注解

    • @Component:

      作用:用于替换原有spring配置文件中的<bean>标签,进而创建对象。
      注意:
      	id属性 component注解 提供了默认的设置方式 首单词首字母小写
      	class属性 通过反射获得class内容
      
    • @Component细节:

      • 如何显式指定工厂创建对象的id值:

        @Component("user")
        
      • Spring配置文件覆盖注解配置内容:

        在applicationContext.xml文件中,配置标签,指定id与@Component注解指定的id一致:
        <bean class="com.itheima.ssm.domain.User" id="user"/>
        
    • @Component的衍生注解:

      @Repository:表示持久层的@Component
      @Service:表示业务层的@Component
      @Controller:表示表现层的@Component
      
      注意:本质上这些衍生注解就是@Component,它的作用、细节、用法都是完全一致的。
      目的:更加准确的表法一个类型的作用。
      
    • @Scope:

      作用:用于控制简单对象的创建次数。
      注意:如果不提供@Scope注解,默认就是singleton
      
    • @Lazy:

      作用:用于延迟创建单实例对象。(在getBean的时候创建对象)
      注意:一旦使用了@Lazy注解后,Spring会在使用这个对象的时候,进行这个对象的创建。
      
    • 生命周期方法相关注解:

      1. 初始化相关方法 @PostConstruct
      	在方法上添加@PostConstruct,指定该方法为初始化方法
      
      2. 销毁方法 @PreDestroy
      	在方法上添加@PreDestroy,指定该方法为销毁方法
      
      注意:
      	1.上述的2个注解并不是Spring提供的,是由JSR(JavaEE规范)520提供的;
      	2.再一次的验证,通过注解实现接口的契约型。
      

2.2 注入相关注解

  • 用户自定义类型的注入:

    @Autowired
    
    细节:
    1. Autowired注解是基于类型来进行注入:
    	- 注入对象的类型,必须与目标成员变量类型相同或是其子类(实现类)
    2. Autowired注解配合Qualifier注解 可以 基于名字进行注入:
    	- 基于名字的注入,注入对象的id值,必须与Qualifier注解中设置的名字相同
    3. Autowired注解放置的位置
    	a) 放置在对应成员变量的set方法上
    	b) 直接把注解放置在对应成员变量上,Spring会通过反射直接对成员变量进行赋值(注入)【推荐】
    4. JavaEE规范中类似功能的注解:
    	JSR250 @Resource(name = "xxx")
    		这个注解相当于Autowired注解+Qualifier注解
    		注意:如果在应用Resource注解时,名字没有配对成功,那么他会继续按照类型进行注入
    
  • JDK类型变量的注入:

    @Value注解完成
    1. 设置xxx.properties
     id = 10
     name = suns
    2. Spring工厂读取这个配置文件
     <context:property-placeholder location="init.properties" file-encoding="gbk"/>
    3. 代码
     属性上加上注解:@Value("${key}")
    
    • @PropertiesSource

      作用:用于替换Spring配置文件中的<context:property-placeholder/>标签
      开发步骤:
      	1. 设置xxx.properties
      		id = 10
      		name = suns
          2. 应用@Properties注解:
          	@PropertySource(value = "init.properties",encoding = "gbk")
          3. 代码
      		属性上加上注解:@Value("${key}")
      
    • @Value注解使用细节

      • @Value不能使用在静态成员变量上。

        如果应用,赋值(注入)将失败
        
      • @Value注解+Properties的这种方式,不能注入集合类型。

        Spring提供了新的配置形式:YAML YML(SpringBoot)
        

2.3 注解扫描详解

<context:component-scan base-package="com.itheima.ssm"/>
这个注解扫描,针对当前包及其子包

这种方法扫描包,比较宽泛,会将所有的包中的所有类都扫描进去。所想要扫描的类有针对性,有两种方法:

  • 排除方式

    <context:component-scan base-package="com.itheima.ssm">
        <context:exclude-filter type="" expression=""/>
        <!--
            其中type属性有五种:
            assignable:排除特定的类型 不进行扫描
            annotation:排除特定的注解 不进行扫描
            aspectj:通过包(类)切入点表达式 排除特定包(类)【推荐使用】
            regex:正则表达式
            custom:自定义排除策略,应用在框架的底层开发
        -->
    </context:component-scan>
    
    注意:排除策略可以叠加使用,即多个<context:exclude-filter/>同时生效
    
  • 包含方式

    <context:component-scan base-package="com.itheima.ssm" use-default-filters="false">
        <context:include-filter type="aspectj" expression="com.itheima.ssm..*"/>
    </context:component-scan>
    
    1. use-default-filters="false":
    	作用:让Spring默认的注解扫描策略(全部扫描) 失效
    2. <context:include-filter/>
    	作用:指定扫描哪些注解,type属性和排除方式中的一致。
    3. 包含方式也支持叠加
    

2.4 对于注解开发的思考

  • 配置互通

    Spring注解配置的类 与 配置文件的配置的类 是互通的
    
  • 什么情况下使用注解?什么情况下使用配置文件?

    @Component注解 替换 <bean
    
    基础注解(@Component、@Autowired、@Value)只能用于程序员开发类型的配置
    
    1. 在程序员开发的类型上,可以加入对应注解,进行对象的创建;
    	如:User、UserServiceImpl等
    2. 应用其他非程序员开发的类型时,还是需要使用<bean 进行配置的
    	如:SqlSessionFactoryBean、MapperScannerConfigure等
    

2.5 SSM整合开发(半注解开发)

  • 搭建开发环境

    • 引入相关jar坐标【SSM POM】
    • 引入相关配置文件
      • applicationContext.xml
      • springmvc-config.xml
      • log4j.properties
      • XxxMapper.xml
    • 初始化配置
      • web.xml文件中,引入ContextLoaderListener创建Spring工厂
      • web.xml文件中,引入SpringMVC的前端控制器DispatcherServlet
  • 编码

    <context:component-scan base-package=""
    
    • DAO(Spring+MyBatis)

      1. 配置文件的配置
      	- DataSource
      	- SqlSessionFactoryBean
      		1.datasource
      		2.typeAliasesPackage
      		3.mapperLocations
      	- MapperScannerConfigure
      	
      2.编码
      	1.entity
      	2.table
      	3.DAO接口
      	4.实现mapper文件
      
    • Service

      1. 原始对象 --> 注入Dao
      2. 额外功能 --> DataSourceTransactionManager --> datasource
      3. 切入点 + 事务属性
           @Transactional(propagation,readonly,...)
      4. 组装切面
      	<tx:annotation-driven transaction-manager="transactionManager"/>
      
    • Controller(Spring + SpringMVC)

    1. 配置文件的配置
    	- <context:component-scan 扫描Controller类所在的包
    	- InternalResourceViewResolver
    	- <mvc:default-servlet-handler/>
    	- <mvc:annotation-driven/>
    2. 在Controller类中注入Service
    

第三章 Spring的高级注解(Spring 3.x及以上)

3.1 配置Bean

Spring在3.X提供的新的注解,用于替换XML配置文件

@Configuration
public class AppConfig {
}
  1. 配置bean在应用的过程中 替换了XML具体什么内容?

    配置bean替换了applicationContext.xml配置文件
    
  2. AnnotationConfigApplicationContext

    1. 创建工厂代码
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
    2. 指定配置文件
        2.1 指定配置bean的class:
        	ApplicationContext applicationContext = 
        	new AnnotationConfigApplicationContext(AppConfig.class);
    	2.2 指定配置bean的路径:
            ApplicationContext applicationContext = 
            new AnnotationConfigApplicationContext("com.itheima.config");
    
  • 配置bean开发的细节分析

    • 基于直接开发使用日志

      不能集成log4j
      集成logback
      
      • 引入相关jar

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.logback-extensions</groupId>
            <artifactId>logback-ext-spring</artifactI
            <version>0.1.4</version>
        </dependency>
        
      • 引入logback配置文件logback.xml:

        <?xml version="1.0" encoding="UTF-8" ?>
        <configuration>
            <!-- 控制台输出 -->
            <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
                <encoder>
                    <!-- 格式化输出:%d表示日期,%thread线程名 %-5level:级别从左显示5个字符宽度 %msg:日志消息 %n:换行-->
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                </encoder>
            </appender>
            
            <root level="DEBUG">
                <appender-ref ref="STDOUT"/>
            </root>
        </configuration>
        
    • @Configuration注解的本质:

      本质:也时@Component注解的衍生注解
      
      可以应用<context:component-scan 进行注解扫描
      

3.2 @Bean注解

@Bean注解在配置bean中进行使用,等同于XML配置文件中的<bean 标签。

3.2.1 @Bean注解的基本使用
  • 对象的创建

    /**
     * 简单对象的创建
     * @return
     */
    @Bean
    public User user(){
        return new User();
    }
    
    /**
     * 复杂对象的创建
     * @return
     */
    @Bean
    public Connection connection(){
        Connection connection = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
             connection = DriverManager.getConnection("jdbc:mysql:///aaa?userSSL=false", "root", "root");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return connection;
    }
    
    • @Bean注解创建复杂对象的注意事项

      @Bean注释的创建对象的方法中,也可从已有的FactoryBean中获取对象,并加入Spring工厂:
      
      @Bean
      public Connection connection1() throws Exception {
          ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
          Connection connection = factoryBean.getObject();
          return connection;
      }
      
  • 自定义对象的id值:

    @Bean("conn")
    
  • 控制对象的创建次数:

    @Scope("singleton  |prototype") 默认singleton
    
3.2.2 @Bean注解的注入
  • 用户自定义类型的注入

    @Bean
    public UserDao userDao(){
        return new UserDaoImpl();
    }
    @Bean
    public UserService userService(UserDao userDao){
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
    
  • JDK类型的注入

    @Bean("u")
    public User user(){
        User user = new User();
        user.setId(1);
        user.setName("aaa");
        return user;
    }
    
    • JDK类型注入的细节分析

      如果直接在代码中进行set方法的调用时,会存在耦合的问题:
      这时候可以用@PropertySource引入外部配置文件:
          
      @Configuration
      @PropertySource("classpath:/init.properties")
      public class AppConfig2 {
          @Value("${id}")
          private Integer id;
          @Value("${name}")
          private String name;
          
          @Bean("u")
          public User user(){
              User user = new User();
              user.setId(id);
              user.setName(name);
              return user;
      
          }
          
      }
      

3.3 @ComponentScan注解

@ComponentScan注解在配置bean中进行使用,等同于在xml配置文件中的<context:component-scan 标签
    目的:进行相关相互加的扫描 (@Component@Value@Autowired...
  • 基本使用

    @Configuration
    @ComponentScan(basePackages = "com.itheima")
    public class AppConfig {
        ...
    }
    
  • 排除、包含的使用

    排除:
    @ComponentScan(basePackages = "com.itheima",
            excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})})
        
    包含:
    @ComponentScan(basePackages = "com.itheima",
                   useDefaultFilters = false,
            includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class}),
                    @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {"com.itheima..*"})
            })
        
    注意:
        type = FilterType.ANNOTATION		value
                         .ASSIGNABLE_TYPE	value
        				 .ASPECTJ			pattern
        				 .REGEX				pattern
        				 .CUSTOM			value
    

3.4 Spring工厂创建对象的多种配置方式

3.4.1 多种配置方式的应用场景
@Component 衍生 @Autowired
用于程序员自己开发的类型上

@Bean
用于框架提供的类型,或者程序员开发的类型(没有源码)
    
<bean 
纯注解的开发过程中,基本不用遗留系统的整合
    
@Import
1. Spring框架的底层使用
2. 多配置bean整合
3.4.2 配置优先级
@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置 覆盖优先级低的配置 

@Component
public class User{}

@Bean
public class User{
	return new User();
}

<bean id="user" class="xxx.User"/>

配置覆盖:id值 保持一致
  • 解决基于注解进行配置的耦合问题

    @Configuration
    public class AppConfig3 {
    
        @Bean
        public UserDao userDao(){
            return new UserDaoImpl();
        }
    }
    
    applicationContext.xml
    <bean id="userDao" class="com.itheima.injection.UserDaoImplNew"/>
        
    @Configuration
    @ImportResource("/applicationContext.xml")
    public class AppConfig4 {}
    
    解决方案如上:在外部配置文件中修改原有bean,并创建新的配置类,引入配置文件使其生效。
    

3.5 整合多个配置信息

  • 为什么会有多个配置信息?

    拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想。
    
  • 多种配置信息整合的方式

    1. 多个配置bean 的整合
    2. 配置bean与@Component相关注解的整合
    3. 配置bean与Spring XML配置文件的整合
  • 整合多种配置需要关注哪些要点?

    • 如何使多配置的信息 汇总成一个整体
    • 如何实现跨配置的注入
3.5.1 多个配置bean的整合
  • 多配置的信息汇总

    1. basePackages中进行多个配置Bean的整合
    2. 在主配置bean中引入其他配置bean:@Import(AppConfig2.class)
    	- 相当于 <import resource = ""/>(多配置文件的整合)
    3. 在工厂创建时,指定多个配置bean的Class对象【了解】
    	- ApplicationContext applicationContext = 
    		new AnnotationConfigApplicationContext(AppConfig.class,AppConfig2.class);
    
  • 跨配置进行注入

    在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解来完成的:
        
    @Configuration
    public class AppConfig2 {
        
        @Bean("user")
        public UserDao userDao(){
            return new UserDaoImpl();
        }
    }
    
    @Configuration
    @Import(AppConfig2.class)
    public class AppConfig {
        @Autowired
        private UserDao userDao;
    
        @Bean
        public UserService userService(){
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDao(userDao);
            return userService;
        }
    }
    
3.5.2 配置Bean与@Component相关注解的整合
@Component
public class UserDaoImpl implements UserDao {
}

@Configuration
@ComponentScan(basePackages = "com.itheima.injection")
public class AppConfig {
    @Autowired
    private UserDao userDao;

    @Bean
    public UserService userService(){
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
}
3.5.3 配置bean与配置文件的整合
1. 遗留系统的整合
2. 配置覆盖

public class UserDaoImpl implements UserDao {
}

<bean id="userDao" class="com.itheima.injection.UserDaoImpl"/>

@Configuration
@ImportResource("/applicationContext.xml")
public class AppConfig2 {
    @Autowired
    private UserDao userDao;
    
    @Bean("user")
    public UserService userService(){
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(userDao);
        return service;
    }
}

3.6 配置Bean的底层实现原理

Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,进行对象的相关配置、处理。

3.7 四维一体的开发思想

  1. 什么是四维一体

    Spring开发一个功能的4种形式。虽然开发方式不同,但是最终效果是一样的。
    1. 基于schame
    2. 基于特定功能注解
    3. 基于原始<bean
    4. 基于@Bean注解
    
  2. 四维一体的开发案例

    1. <context:property-placeholder
    2. @PropertySource【推荐】
    3. <bean id="" class="PropertySourcesPlaceholderConfigurer"/>
    4. @Bean【推荐】
    	public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()	 {
        PropertySourcesPlaceholderConfigurer configurer = 
        new PropertySourcesPlaceholderConfigurer();
        configurer.setLocation(new ClassPathResource("init.properties"));
        return configurer;
       }
    

3.8 纯注解版的AOP编程

  • 搭建环境

    1. 应用配置bean
    2. 注解扫描
    
  • 开发步骤

    1. 原始对象
        @Service
    	public class UserServiceImpl implements UserService {
    	}
    2. 创建切面类(额外功能、切入点、切面组装)
        @Aspect
        @Component
    	public class MyAspect {
        
        	@Around("execution(* com.itheima.injection..*(..))")
        	public Object around (ProceedingJoinPoint proceedingJoinPoint) throws 	Throwable {
            	System.out.println("------aspect log-------");
            	Object ret = proceedingJoinPoint.proceed();
            
            	return ret;
        	}
    	}
    3. Spring的配置bean中,加上@EnableAspectAutoProxy
        (相当于配置文件中的<aop:aspectj-autoproxy/>
  • 注解AOP细节分析

    1. 代理创建方式的切换 JDK(false,默认) Cglib(true)
    	配置文件中:<aop:aspectj-autoproxy proxy-target-class="false"/>
        配置bean上:@EnableAspectJAutoProxy(proxyTargetClass = true)
            
    2. SpringBoot AOP的开发中,@EnableAspectAutoProxy已经设置好了,不需要设置。
       注意:Spring AOP 代理默认实现是 JDK,而SpringBoot AOP 代理默认实现是 Cglib
    

3.9 纯注解版的Spring与MyBatis整合

  • 基础配置(配置内容放置在配置Bean中)

    1. 连接池
    2. SqlSessionFactoryBean
    3. MapperScannerConfigure
    
  • 编码

    1. 实体
    2.3. DAO接口
    4. Mapper文件
    
3.9.1 MapperLocations编码时通配的写法
//设置Mapper文件的路径
factoryBean.setMapperLocations(Resource ..);

//当要获取一组mapper文件时:
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("mapper/*.xml");
factoryBean.setMapperLocations(resources);
3.9.2 配置Bean数据耦合的问题
将数据存储在配置文件中,利用@PropertySource()+@Value注解讲数据注入到类成员变量中。

3.10 纯注解版的事务编程

1. 原始对象
	@Service
	public class UserServiceImpl implements UserService {
	}	

2. 额外功能
    @Bean
	public DataSourceTransactionManager dataSourceTransactionManager(){
    	DataSourceTransactionManager transactionManager = 
            new DataSourceTransactionManager();
    	transactionManager.setDataSource(dataSource);
    	return transactionManager
	}
3. 事务属性
    @Service
	@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
	public class UserServiceImpl implements UserService {
	}
4. 基于注解的事务配置(相当于schame的<tx:annotation-driven/>):
	@EnableTransactionManagement

3.11 Spring框架中YML的使用

3.11.1 什么是YML
YML(YAML)是一种新形式的配置文件,比XML更简单,比properties更强大。
3.11.2 Properties进行配置的问题
1. Properties表达过于繁琐,无法表达数据的内在联系;
2. Properties无法表达对象 集合类型
3.11.3 YML语法简介
1. 定义yml文件
	xxx.yml 或 xxx.yaml
2. 语法
	2.1 基本语法
		name: suns
		password: 123456
	2.2 对象概念
		account:
			id: 1
			password: 123456
	2.3 定义集合
		service:
			- 11111
			- 22222
3.11.4 Spring与YML的集成思路的分析
1. 准备yml配置文件
	init.yml
	name: suns
	password: 123456
2. 读取yml,将其转换为Properties
	创建YamlPropertiesFactoryBean对象,调用其setResources方法传入yml配置文件的Resource对象,然后	  通过YamlPropertiesFactoryBean对象的getObject方法获取Properties对象。
3. 应用PropertySourcesPlaceholderConfigurer
	propertySourcesPlaceholderConfigurer.setProperties(properties);
4. 使用@Value在类中注入
  • 代码实现:

    • 环境搭建

      <dependency>
          <groupId>org.yaml</groupId>
          <artifactId>snakeyaml</artifactId>
          <version>1.25</version>
      </dependency>
      
    • 编码

      1. 准备yml配置文件
      2. 配置Bean中操作 完成YAML读取 与 PropertySourcesPlaceholderConfigurer的创建
          @Bean
      	public PropertySourcesPlaceholderConfigurer configurer(){
          	YamlPropertiesFactoryBean yamlPropertiesFactoryBean = 
                  new YamlPropertiesFactoryBean();
          	yamlPropertiesFactoryBean.setResources
                  (new ClassPathResource("mybatis.yml"));
          	Properties properties = yamlPropertiesFactoryBean.getObject();
          	PropertySourcesPlaceholderConfigurer configurer = 
                  new PropertySourcesPlaceholderConfigurer();
          	configurer.setProperties(properties);
          	return configurer;
      	}
      3. 类的成员变量 加上@Value注解 注入