Spring-注解式开发

作者: zhl 分类: Spring 发布时间: 2023-11-04 17:38

一、注解基础概念

什么是注解编程

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

为什么要使用注解编程

  1. 注解开发⽅便
    代码简洁 开发速度⼤⼤提⾼
  2. Spring开发潮流
    Spring2.x引⼊注解 Spring3.x完善注解 SpringBoot普及 推⼴注解编程

注解的作用

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

Spring注解的发展历程

  1. Spring2.x开始支持注解编程 @Componment @Service @Scope
    目的:提供的这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充
  2. Spring3.x @Configuration @Bean..
    ⽬的:彻底替换XML,基于纯注解编程
  3. Spring4.x SpringBoot
    提倡使⽤注解常⻅开发

Spring注解开发的一个问题

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

二、Spring的基础注解(Spring2.x)

对象创建相关注解

  • 搭建开发环境

    • @Component
      作用:替换原有Spring配置文件中的\<bean标签

      注意:
      id 属性 component注解 提供了默认的设置方式 首单词字母小写
      Class 属性 通过反射获得class内容

    • @Comonent细节

      • 如何显示指定工厂创建对象的id 值
        @Component("u")
    • Spring配置文件覆盖注解配置内容

      applicationContext.xml
      <bean id="u" class="com.mysite.bean"/>
      id 和 class 的值 要和注解中保持一致,
      如果id 值和注解不一致,Spring会认为这是两个对象,创建两个bean
    • @Component的衍生注解(本质都是@Component)

      @Reposity  ---->  XXXDAO
      @Service   ---->  XXXService
      @Controller---->  XXXController
      注意:本质上这些衍生注解就是@Component
      作用<bean
      细节 @Service("s")
      目的:更加准确的表达一个类型的作用
      注意:Spring整合MyBatis开发过程中 不使用 @Reposity
  • @Scope
    作用:控制简单对象创建次数
    注意:不提供@Scope注解,Spring提供的默认是singleton
    \

  • @Lazy注解
    作用:延迟创建单实例对象
    注意:一旦使用了@Lazy注解后,Spring会在使用这个对象时,创建这个对象
    \

  • 生命周期方法相关的方法

    1. 初始化相关的方法 --> @PostConstruct
      InitializingBean
      \
    2. 销毁方法 --> @PreDestroy
      DisposaleBean
      \
      • 注意:
    3. 上述的两个注解并不是Spring 提供的,JSR(JavaEE规范)
    4. 再一次的验证,通过注解实现了接口的契约性

注入相关注解

  • 用户自定义类型 @Autowired
    @Autowired细节

    1. Autowired注解基于类型进行注入 [推荐]
      基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
    2. Autowired Qualifier 基于名字进行注入
      基于名字的注入:注入对象的id 值,必须与Qualifier 注解中设置的名字相同
    3. Autowired 注解的放置位置
    4. 放置在对应成员变量的set 方法上
    5. 直接把这个注解放在成员变量上,Spring通过反射直接对成员变量进行注入(赋值) [推荐]
    6. JavaEE规范中类似功能的注解
      JSR250 @Resource(name="userDaoImpl") 基于名字进行注入
      @Autowired()
      @Qualifier("userDaoImpl")
      注意:如果在应用Resource注解时,名字没有配对成功,那么它会继续按照类型进行注入。
      JSR330 @Inject 作用 @Autowired完全一致 基于类型进行注入
  • JDK类型 @Value
    @Value注解的三步

    1. 设置xxx.properties
       id=10      key=value
       name=zhl   key=value
    2. Spring工厂读取这个配置文件
       <context:property-placeholder location=""/> ==》 非注解开发,Spring也为我们提供了注解的形式
    3. 代码
       属性 @Value("${key}")
  • @PropertySource
    作用:⽤于替换Spring配置⽂件中的\标签

    开发步骤
    1. 设置xxx.properties
       id=10      key=value
       name=zhl   key=value
    2. 应用@PropertySource
    3. 代码
       属性 @Value("${key}")
    • @Value 注解使用细节
      1. @Value注解不能应用在静态成员变量上
        如果应用,赋值(注入)将失败
      2. @Value注解 + Properties这种方式,不能注入集合类型
        Spring提供新的配置形式 YAML YML

只使用@Value注解或@Autowired注解 如果不能实现某一功能,可以通过Spring的配置文件配置实现

三、注解扫描详解

<context:component-scan base-package="com.mysite"/>
扫描当前包 及其 子包

排除方式

<context:component-scan base-package="com.mysite"/>
    <context:exclude-filter type="" expression=""/>
    type:assignable:排除特定的类型 不进行扫描 
         annotation:排除特定的注解 不进行扫描    
         aspectj   :切入点表达式  
                     包切入点:com.mysite.bean..* 
                     类切入点:*..User
         regex     : 正则表达式
         custom    :自定义排除策略,框架底层开发         
</context:component-scan>
排除策略可以叠加使用

包含方式

<context:component-scan base-package="com.mysite" use-default-filters="false">
    <context:include-filter type="" expression=""/>    
</context:component-scan>
1. use-default-filters="false"
   作用:让Spring默认的注解扫描方式失效
2. <context:include-filter type="" expression=""/>
   作用:指定扫描哪些注解
       type:assignable:排除特定的类型 不进行扫描 
            annotation:排除特定的注解 不进行扫描    
            aspectj   :切入点表达式  
                        包切入点:com.mysite.bean..* 
                        类切入点:*..User
            regex     : 正则表达式
            custom    :自定义排除策略,框架底层开发
包含的方式支持叠加
<context:component-scan>

四、Spring的高级注解(Spring3.x及以上)

@Configuration

Spring3.x 提供的新的注解,用于替换XML配置文件

@Configuration
public class AppConfig{
}

@Bean注解

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

@Bean注解的基本使用

  • 对象的创建

    1. 简单对象
      直接能够通过new 方式创建对象
      User UserService UserDao
    2. 复杂对象
      直接能够通过new 方式创建对象
      Connection SqlSessionFactory
      • @Bean注解创建复杂对象的注意事项
        一般这种方式用来整合遗留系统
        /**
        * 通过实现FactoryBean接口创建的复杂对象 也可以 结合 @Bean注解一起使用,
        * @return
        */
        @Bean
        public Connection conn1(){
            Connection conn = null;
            try {
                ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
                conn = factoryBean.getObject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return conn;
        }
  • 自定义 id 值 (默认方法名是id 值)

    @Bean("id值")
  • 控制对象的创建次数

    @Bean
    @Scope("singleton | prototype")    //默认是 singleton

@Bean注解的注入

  • 用户自定义类型的注入
    @Bean
    public UserDao userDao(){
        return new UserDaoImpl();
    }
    //方式1:
    @Bean
    public UserService userService(UserDao userDao){
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
    //方式2:
    @Bean
    public UserService userService(){
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao());
        return userService;
    }
  • JDK类型的注入
    @Bean
    public User user(){
        User user = new User();
        user.setId(10);
        user.setName("zhl");
        return user;
    }
    • JDK类型的细节分析
      如果直接在代码中赋值,会有耦合,和之前一样可以通过@Value注解,.properties文件赋值
      @Configuration
      @PropertySource("classpath:/init.properties")
      public class AppConfig1{
          @Value("${id}")
          private Integer id;
          @Value("${name}")
          private String name;
          @Bean
          public User user(){
              User user = new User();
              user.setId(id);
              user.setName(name);
              return user;
          }
      }

@ComponentScan注解

@ComponentScan注解在配置bean中进行使用,等同于XML配置⽂件中的\
⽬的:进⾏相关注解的扫描 (@Component @Value ...@Autowired)

基本使用

@Configuration
@ComponentScan(basePackages = "com.mysite.scan")
public class AppConfig2 {
}
<context:component-scan base-package="com.mysite.scan"/>

排除、包含的使用

  • 排除
    <context:component-scan base-package="com.baizhiedu"> 
    <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/> 
    </context:component-scan>
    @Configuration
    @ComponentScan(basePackages = "com.mysite.scan",
            excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class}),
                @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = "*..User1")
            }
    )
    type= FilterType.ANNOTATION         value
                .ASSIGNABLE_TYPE    value
                .ASPECTJ            pattern
                .REGEX              pattern
                .CUSTOM             value
  • 包含
    <context:component-scan base-package="com.baizhiedu" use-default-filters="false"> 
    <context:include-filter type="" expression=""/> 
    </context:component-scan>
    @ComponentScan(basePackages = "com.mysite.scan",
        useDefaultFilters = false,
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})
        }
    )
    type= FilterType.ANNOTATION         value
                .ASSIGNABLE_TYPE    value
                .ASPECTJ            pattern
                .REGEX              pattern
                .CUSTOM             value

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

多种配置方式的应用场景

配置的优先级

@Component及其衍生注解 < @Bean < 配置文件bean标签

优先级高的配置 覆盖优先级低配置

配置覆盖:id 值 保持一致

想要通过配置文件bean标签 ,进行配置,需要在 配置bean(@Configuration)上
添加@ImportResource("applicationContext.xml"),让AppConfig能够扫描的到Spring的配置文件

  • 解决基于注解进行配置的耦合问题
    @Configuration
    @ImportResource("applicationContext.xml")
    public class AppConfig4 {
        @Bean
        public UserDao userDao(){
            return new UserDaoImpl();
        }
    }
    applicationContext.xml
    <bean id="userDao" class="com.mysite.injection.UserDaoImplNew"/>

整合多个配置信息

  • 为什么会有多个配置信息
    拆分多个配置bean的开发,是⼀种模块化开发的形式,也体现了⾯向对象各司其职的设计思想
  • 多配置信息整合的⽅式
    • 多个配置Bean的整合
    • 配置Bean与@Component相关注解的整合
    • 配置Bean与SpringXML配置文件的整合
  • 整合多种配置需要关注哪些要点
    • 如何使多配置的信息 汇总成一个整体
    • 如何实现跨配置的注入

多个配置Bean的整合

  • 多配置的信息汇总

    • base-package进行多个配置Bean的整合
  • @Import

    1. 可以创建对象
    2. 多配置bean的整合
  • 跨配置进行注入

    在应用配置Bean的过程中,不管使用哪种方式 进⾏配置信息的汇总,其操作⽅式都是通过成员变量加⼊@Autowired注解完成。
    @Configuration 
    @Import(AppConfig2.class) 
    public class AppConfig1 { 
     @Autowired 
     private UserDAO userDAO; 
     @Bean 
     public UserService userService() { 
         UserServiceImpl userService = new UserServiceImpl(); 
         userService.setUserDAO(userDAO); 
         return userService; 
     } 
    } 
    @Configuration 
    public class AppConfig2 { 
     @Bean 
     public UserDAO userDAO() { 
         return new UserDAOImpl(); 
     } 
    }

    配置Bean与@Component相关注解的整合

    通过@ComponentScan注解进行扫描含@Component的注解即可

    @Component(或@Repository) 
    public class UserDAOImpl implements UserDAO{ 
    } 
    @Configuration 
    @ComponentScan("")
    public class AppConfig3 { 
     @Autowired 
     private UserDAO userDAO;    
     @Bean
     public UserService userService() { 
         UserServiceImpl userService = new UserServiceImpl(); 
         userService.setUserDAO(userDAO); 
         return userService; 
     } 
    }
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);

配置Bean与配置文件整合

1.遗留系统的整合 2.配置覆盖
在配置Bean中 通过@ImportResource("applicationContext.xml")进行整合

配置Bean的底层原理

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

纯注解版AOP编程

搭建环境

  1. 应用配置Bean
  2. 注解扫描

开发步骤

  1. 原始对象
    @Service(@Component)
    public class UserServiceImpl implements UserService{
    }
  2. 创建切面类(额外功能 切入点 组装切面)
    @Aspect
    @Component    //不需要在applicationContext.xml中配置bean了 <bean 
    public class MyAspect{
        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("------aspect log-----");
            Object ret = joinPoint.proceed();
            return ret;
        }
    }
  3. Spring的配置文件中
    \被替换
    @EnableAspectjAutoProxy

注解AOP细节分析

  1. 代理创建方式的切换 JDK Cglib
    <aop:aspectj-autoproxy proxy-target-class=true|false>
    默认以JDK的方式创建代理,
    通过@EnableAspectjAutoProxy(proxyTargetClass=false),以Cglib创建代理
  2. SpringBoot AOP的开发方式
    @EnableAspectjAutoProxy 已经设置好了
    只需要完成开发步骤的前两步即可
    Spring AOP 代理默认实现 JDK
    SpringBoot AOP 代理默认实现 Cglib

纯注解版Spring+MyBatis整合

  • 基础配置(配置Bean)
    1. 连接池
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
        @PropertySource("classpath:jdbc.properties")
        @Value(${jdbc.driver})
        private String driverClassName;
        @Bean
        public DruidDataSource dataSource(){
            DataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driverClassName);
        }
    2. SqlSessionFactoryBean
        <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--为实体类的包下起别名-->
            <property name="typeAliasesPackage" value="com.mysite.entity"/>
            <property name="mapperLocations">
                <list>
                    <!--指定resource/类路径 下对应的  xxxMapper映射文件的位置和格式-->
                    <value>classpath:com.mysite.mapper/ *Mapper.xml</value>
                </list>
            </property>
        </bean>
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            sqlSessionFactoryBean.setTypeAliasesPackage(new ClassPathResource("UserDaoMapper.xml"));
            ...
            return sqlSessionFactoryBean;
        }
    3. MapperScannerConfigure
        <!--创建DAO对象 MapperScannerConfigure-->
        <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
            <property name="basePackage" value="com.mysite.dao"/>
        </bean>
        @MapperScan(basePackages = {""})  ------->  配置Bean完成
  • 编码
    1. 实体
    2. DAO接口
    3. Mapper映射文件

MapperLocations编码时通配的写法

//设置Mapper文件的路径
sqlSessionFactoryBean.setMapperLocations(Resource...);
Resource resource = new ClassPathResource("UserDaoMapper.xml");
<property name="mapperLocations">
    <list>
        <!--指定resource/类路径 下对应的  xxxMapper映射文件的位置和格式-->
        <value>classpath:com.mysite.mapper/ *Mapper.xml</value>
    </list>
</property>
如何在配置Bean中 配置多个Mapper文件呢?
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("com.mysite.mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources);

配置Bean数据耦合的问题

mybatis.driverClassName=com.mysql.cj.jdbc.Driver
mybatis.url=jdbc:mysql://localhost:3306/mybatis
mybatis.username=root
mybatis.password=15737979065
mybatis.typeAliasesPackages=com.mysite.mybatis
mybatis.mapperLocations=com.mysite.mapper/*Mapper.xml
@PropertySource("classpath:mybatis.properties")
public class MyBatisProperties {
    @Value("${mybatis.driverClassName}")
    private String driverClassName;
    @Value("${mybatis.username}")
    private String username;
    @Value("${mybatis.url}")
    private String url;
    @Value("${mybatis.password}")
    private String password;
    @Value("${mybatis.typeAliasesPackages}")
    private String typeAliasesPackages;
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;
    //get set方法
}
@Configuration
@ComponentScan(basePackages = "com.mysite.mybatis")
@MapperScan(basePackages = "com.mysite.mybatis")
public class MyBatisAutoConfiguration {
    @Autowired
    private MyBatisProperties myBatisProperties;
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(myBatisProperties.getDriverClassName());
        dataSource.setUrl(myBatisProperties.getUrl());
        dataSource.setUsername(myBatisProperties.getUsername());
        dataSource.setPassword(myBatisProperties.getPassword());

        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage(myBatisProperties.getTypeAliasesPackages());
        try {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources(myBatisProperties.getMapperLocations());
            sqlSessionFactoryBean.setMapperLocations(resources);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return sqlSessionFactoryBean;
    }
}

纯注解版事务编程

  1. 原始对象 XXXService
    <bean id="userService" class="com.mysite.service.UserServiceImpl"/>
    <property name="userDao" ref="userDao"/>
    </bean>
    @Service
    public UserServiceImpl implements UserService{
    @Autowired
    private UserDao userDao;
    }
  2. 额外功能
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    </bean>
    @Bean
    public DataSourceTransactionManager dataSourceTransactionalManager(DataSource dataSource){
    DataSourceTransactionManager dstm= new DataSourceTransactionManager();
    dstm.setDataSource(dataSource);
    return dstm;
    }
  3. 事务属性
    @Transactional
    @Service
    public class UserServiceImpl implements UserService{
    @Autowired
    private UserDao userDao;
    }
  4. 基于Schema的事务配置
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    @EnableTransactionalManagement   ------>  配置Bean

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

zhl