Spring5学习笔记(六、注解编程)

注解开发的好处

  1. 注解开发方便
    代码简洁,开发速度大大提高。
  2. Spring开发潮流
    Spring2.x引入注解,Spring3.x完善注解,SpringBoot普及、推广注解。

注解的作用

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

Spring注解的发展历程

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

Spring注解开发的一个问题

Spring基于注解进行配置后,还能否解耦呢?

在Spring框架应用注解时,如果对注解的配置内容不满意,可以通过Spring配置文件进行覆盖,不需要去修改代码。

Spring的基础注解(Spring2.x)

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

对象创建的相关注解

搭建开发环境

<context:component-scan base-package="com.angenin"/>

作用:让Spring框架扫描指定包及其子包中的注解,让注解生效。

1. @Component

作用:替换原有Spring配置文件中的bean标签。
注意:

  1. id属性:由component注解提供默认的设置方式——首单词首字母小写
  2. class属性:通过反射获得class的全限定类名。
@Component
public class User implements Serializable {
    private Integer id;
    private String name;
    private String password;
    
    //get set
}

细节:

  • 指定工厂创建对象的id值:在component注解中直接加入id值即可
    @Component("u")
    public class User implements Serializable {...}
    
  • Spring配置文件覆盖注解的配置内容
    <!--bean标签中的id和class都需要和要修改的相同,只有相同才能进行覆盖,不相同的话会创建新的对象-->
    <bean id="u" class="com.angenin.bean.User"/>
    

@Component的衍生注解:

本质上衍生注解就是@Component注解,作用、细节、用法都完全一致。

目的:更加准确的表达一个类型的作用。

  1. @Repository --> DAO类
  2. @Service --> Service类
  3. @Controller --> Controller类

注意:Spring整合MyBatis开发过程中,DAO类不使用@Repository、@Component,因为DAO类不需要我们去创建。

2. @Scope

作用:控制简单对象的创建次数。
注意:不添加@Scope,默认为singleton(单例)。

@Component
@Scope("prototype")	// 多例
public class Customer {}

单例(Singleton):在整个应用中,只创建一个bean。
原型(Prototype):每次注入或者获取,都会创建一个新的bean。
会话(Session):在Web应用中,为每个会话创建一个bean。
请求(Request):在Web应用中,为每个请求创建一个bean。

3. @Lasy

作用:延迟创建单实例对象。(懒加载)
注意:使用@Lasy注解后,Spring会在使用到这个对象的时候才创建对象。

@Component
@Lazy
public class Account {
    public Account() {
        System.out.println("创建account对象");
    }
}

4. 生命周期方法的相关注解

之前:

  1. 初始化相关方法:实现Initializing接口<bean init-method="" />
  2. 销毁相关方法:实现DisposableBean接口<bean destory-method=""/>

注意:

  1. 下面两个注解并不是Spring提供的,而是JSR(JavaEE规范)520,Spring很好的兼容了JSR。
  2. 再一次的验证了,通过注解实现了接口的契约性。
@PostConstruct

初始化方法

@Component
public class Product {

    @PostConstruct
    public void myInit() {
        System.out.println("Product...myInit...");
    }
    ...
}
@PreDestroy

销毁方法

@Component
public class Product {

    @PreDestroy
    public void myDestroy() {
        System.out.println("Product...myDestroy...");
    }
    ...
}

注入的相关注解

@Autowired、@Qualifier与@Resouce

用户自定义类型注入
在这里插入图片描述
UserDAO

public interface UserDAO {
    void save();
}

UserDAOImpl

@Repository
public class UserDAOImpl implements UserDAO {
    @Override
    public void save() {
        System.out.println("save...");
    }
}

UserService

public interface UserService {
    void register();
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {

    private UserDAO userDAO;

    public UserDAO getUserDAO() {
        return userDAO;
    }

    @Autowired	// 后面会讲,不要急
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    public void register() {
		userDAO.save();
    }
}

测试:

    @Test
    public void test01() {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // id为首单词首字母小写
        UserService userService = (UserService) ctx.getBean("userServiceImpl");
        userService.register();
    }

细节:

  1. 基于类型注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(或实现类)。【推荐】
    在这里插入图片描述

  2. 基于名字注入:需要配合@Qualifier注解使用。【了解】
    在这里插入图片描述
    输入的id对应的bean必须存在,否则都不能通过IDEA的检查。
    在这里插入图片描述

  3. @Autowired注解放置的位置

    1. 放置在对应成员变量的set方法上。(set注入)
    2. 直接放置在成员变量之上,Spring通过反射直接对成员变量进行注入(赋值),也不需要写set方法,@Qualifier注解也可以直接加到成员变量之上。【更加推荐】
      在这里插入图片描述
  4. JavaEE规范中类似功能的注解

    JSR250:@Resouce(name = “注入的bean的id”)注解,基于名字进行注入,等同于@Autowired和@Qualifier二合一,如果不指定name,默认基于类型进行注入,也推荐使用。

    JSR330:@Inject:作用于@Autowired完全一致,基于类型进行注入,使用时需要引入对应的jar包,在Spring开发中应用的比较少,主要应用在EJB3.0中,了解即可。

    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>
    

@Value

JDK类型注入
在这里插入图片描述
开发步骤:

  1. 设置xxx.properties配置文件,用于保存值(格式:key = value)。
    init.properties
    id = 1
    name = angenin
    
  2. 读取配置文件
    applicationContext.xml
    <!--读取配置文件-->
    <context:property-placeholder location="classpath:init.properties"/>
    
  3. 注入,@Value("${key}")
    Category
    @Component
    public class Category {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
    	
    	//get set
    }
    

细节:

  • @Value注解不能应用在静态成员变量上,如果应用,赋值(注入)失败。
  • @Value注解 + properties配置文件这种方式,不能注入集合类型,解决办法是使用yaml(yml)配置文件(yml配置文件会在最后讲解)。

@PropertySource

作用:用于替换Spring配置文件中的<context:property-placeholder location=""/>标签。

开发步骤:

  1. 设置xxx.properties配置文件
  2. 应用@PropertySource注解,加在要注入的类上。
  3. 注入
    @Component
    @PropertySource("classpath:/init.properties")
    public class Category {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
    	...
    }
    

注解扫描详解

<context:component-scan base-package="com.angenin"/>

扫描指定包及其子包,当想要更为细致的指定哪些包想要扫描,哪些包不需要扫描,需要以下两种方式。

1. 排除方式

由context:exclude-filter标签指定排除。

    <context:component-scan base-package="com.angenin">
        <context:exclude-filter type="" expression=""/>
    </context:component-scan>

type共有五种:

  1. assignable:排除特定的类型,不进行扫描。【推荐】
    <context:component-scan base-package="com.angenin">
        <!--排除User类,不扫描-->
        <context:exclude-filter type="assignable" expression="com.angenin.bean.User"/>
    </context:component-scan>	
    
  2. annotation:排除特定的注解,不进行扫描。【推荐】
    <context:component-scan base-package="com.angenin">
        <!--排除@Service注解,不扫描-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>	
    </context:component-scan>
    
  3. aspectj:包、类切入点表达式【更加推荐】
    <context:component-scan base-package="com.angenin">
        <!--包切入点表达式:排除bean包及其子包下的所有类,不扫描-->
        <context:exclude-filter type="aspectj" expression="com.angenin.bean..*"/>
        <!--类切入点表达式:排除所有User类,不扫描-->
        <context:exclude-filter type="aspectj" expression="*..User"/>
    </context:component-scan>
    
  4. regex:正则表达式【了解即可】
  5. custom:自定义排除策略(框架底层用得多)【了解即可】

排除策略可以叠加使用,即使用多个context:exclude-filter标签。

    <context:component-scan base-package="com.angenin">
        <!--排除User类,不扫描-->
        <context:exclude-filter type="assignable" expression="com.angenin.bean.User"/>
        <!--包切入点表达式:排除bean包及其子包下的所有类,不扫描-->
        <context:exclude-filter type="aspectj" expression="com.angenin.bean..*"/>
    </context:component-scan>

2. 包含方式

<context:component-scan base-package="com.angenin" 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和上面排除方式的type使用方式一样,但意思相反,被指定的是需要扫描的。
<context:component-scan base-package="com.angenin" use-default-filters="false">
    <!--只扫描@Service和@Repository注解-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

包含策略也是可以叠加使用,即使用多个context:include-filter标签。

对于注解开发的思考

  • 配置互通
    Spring的注解配置是与配置文件的配置互通的。

  • 什么情况下使用注解,上面情况下使用配置文件?

    1. 在程序员开发的类型上(自己写的类上),我们可以加入对应的注解,进行对象的创建,如User、UserDAO、UserService。
    2. 应用其他非程序员开发的类型时(框架提供的类),还是需要使用bean标签进行配置,因为我们并不能直接源码添加注解,如SqlSessionFactoryBean、MapperScannerConfigure。
      Spring低版本是这样的,不过高版本已经解决这个问题了。

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

1. @Configuration

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

@Configuration	// 加上这个注解后,表明这个类为一个配置类,用于替换XML的配置文件
public class AppConfig{
	...
}
  1. 配置Bean在应用的过程中替换了XML具体什么内容呢
    在这里插入图片描述
  2. AnnotationConfigApplicationContext
    1. 创建工厂代码
    	ApplicationContext ctx = new AnnotationConfigApplicationContext(..);
    2. 指定配置文件
    	1. 指定配置Bean的Class
    		ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    	2. 指定配置Bean所在的路径(会扫描包下及其子包下加了@Configuration注解的配置类)
    		ApplicationContext ctx = new AnnotationConfigApplicationContext("com.angenin");
    
    在这里插入图片描述

配置Bean开发的细节分析

  1. 基于注解开发使用日志,不能集成log4j,集成logback

    1. 引入jar包

      <dependency>
          <groupId>org.logback-extensions</groupId>
          <artifactId>logback-ext-spring</artifactId>
          <version>0.1.4</version>
      </dependency>
      
    2. 引入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>
      
          <!--日志级别:debug-->
          <root level="DEBUG">
              <appender-ref ref="STDOUT"/>
          </root>
      </configuration>
      
  2. @Configuration注解的本质

    本质:也是@Component注解的衍生注解。

    可以应用context:component-scan标签进行扫描,不过注解版就不会用标签了,改为使用对应的注解进行扫描。

2. @Bean

@Bean注解需要在配置Bean(配置类)中使用,等同于XML配置文件中的bean标签。

@Bean注解的基本使用

对象创建

在这里插入图片描述
加上@Bean的方法,如同bean标签,该方法必须是public,其返回值为创建的对象类型,方法名为创建的bean的id。

  1. 简单对象
    User、UserService、UserDAO、…
  2. 复杂对象
    不能通过new的方式直接创建的对象。
    Connection、SqlSessionFactory、…

User类

public class User {
}

AppConfig(配置类)

@Configuration
public class AppConfig {

    // 简单对象:可以直接new的对象
    @Bean
    public User user() {
    	// 后面会解决直接new对象的问题
        return new User();
    }

    // 复杂对象:不能直接new的对象
    @Bean
    public Connection conn() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/book?useSSL=false&serverTimezone=Asia/Shanghai", 
                    "root", "123456");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}
  • @Bean注解创建复杂对象的注意事项

    ConnectionFactoryBean(自定义的connection工厂)

    public class ConnectionFactoryBean implements FactoryBean<Connection> {
        @Override
        public Connection getObject() throws Exception {
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/book?useSSL=false&serverTimezone=Asia/Shanghai",
                    "root", "123456");
            return conn;
        }
    
        @Override
        public Class<?> getObjectType() {
            return Connection.class;
        }
    
        @Override
        public boolean isSingleton() {
            // 返回false,多例
            return false;
        }
    }
    

    AppConfig

    // 主要用于遗留系统整合
    @Bean
    public Connection conn1() {
        Connection conn = null;
        try {
            ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
            conn = factoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }
    
自定义id值
// 在括号里直接指定即可
@Bean("id")
控制对象创建次数
@Bean
@Scope("prototype")	// 多例,默认singleton单例

在这里插入图片描述

@Bean注解的注入

1. 用户自定义类型注入

在这里插入图片描述
UserDAO、UserDAOImpl、UserService、UserServiceImpl和之前创建的一样,这里就不再赘述了。

@Configuration
public class AppConfig {

    @Bean
    public UserDAO userDAO() {
        return new UserDAOImpl();
    }
    
    // 定义形参
    @Bean
    public UserService userService(UserDAO userDAO) {
        UserServiceImpl userService = new UserServiceImpl();
        // 调用set方法进行赋值
        userService.setUserDAO(userDAO);
        return userService;
    }
}

简化写法(不需要形参):
在这里插入图片描述

@Configuration
public class AppConfig {
	// userDAO不变
    @Bean
    public UserDAO userDAO() {
        return new UserDAOImpl();
    }

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        // 注入时,直接调用上面的方法即可
        userService.setUserDAO(userDAO());
        return userService;
    }
}
2. JDK类型注入

在这里插入图片描述
AppConfig配置类

    @Bean
    public User user() {
        User user = new User();
        user.setId(11);
        user.setName("angenin");
        return user;
    }

如果直接在代码中进行set方法的调用,会存在耦合问题。
注入时代码耦合的解决办法:

  1. 创建init.properties配置文件,用于存储键值对。

    id = 11
    name = angenin11
    
  2. 在配置类上加上@PropertySource("classpath:/init.properties"),扫描配置文件

  3. 定义成员变量,通过@Value注解赋值后,注入。

    @Configuration
    @PropertySource("classpath:/init.properties")
    public class AppConfig {
    
        @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;
        }
    }
    

3. @ComponentScan

@ComponentScan注解在配置Bean中进行使用,等同于 XML配置文件中的context:component-scan标签。
作用:进行相关注解的扫描,如@Component、@Value、@Autowried、…

  1. 基本使用:
    在这里插入图片描述

  2. 排除
    在这里插入图片描述

    // 可以用多个不同类型的@ComponentScan.Filter,并且每个@ComponentScan.Filter也可以排除多个,所以都是用{},多个用逗号,分隔。
    // 排除	
    @ComponentScan(basePackages = "com.angenin.scan",
            excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class, Component.class}),
                @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.angenin..*", "*..User1"})
            })
    @Configuration
    public class AppConfig {...}
    
    
    /*
    	type = FilterType.ASSIGNABLE_TYPE	-->		value
    					 .ANNOTATION 		--> 	value
    					 .ASPECTJ			--> 	pattern
    					 .REGEX				-->		pattern
    					 .CUSTOM			-->		value
    */
    

    五种type:

    1. assignable:排除特定的类型,不进行扫描。【推荐】
    2. annotation:排除特定的注解,不进行扫描。【推荐】
    3. aspectj:包、类切入点表达式【更加推荐】
    4. regex:正则表达式【了解即可】
    5. custom:自定义排除策略(框架底层用得多)【了解即可】
  3. 包含
    在这里插入图片描述

    // 包含
    @ComponentScan(basePackages = "com.angenin.scan",
    		// 关闭Spring的默认扫描方式
            useDefaultFilters = false,
            includeFilters = {
                    @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class, Component.class}),
                    @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.angenin..*", "*..User1"})
            })
    

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

  1. 多种配置方式的应用场景
    在这里插入图片描述

  2. 配置优先级
    优先级(从低到高):@component及其衍生注解 < @Bean < 配置文件bean标签。
    优先级高的配置可以覆盖优先级低的配置。

    @Component
    public class User {}
    // ↑覆盖
    @Bean
    public User user() {
    	return new User();
    }
    // ↑覆盖
    <bean id="user" class="com.angenin.User"/>
    

    覆盖时,需要保证id是相同的,否则就不是覆盖了,而是创建新的对象。

    解决基于注解进行配置的耦合问题
    在这里插入图片描述

    --------配置Bean:AppConfig4---------
    @Configuration
    public class AppConfig4 {
        @Bean
        public UserDAO userDAO() {
            return new UserDAOImpl();
        }
    }
    --------applicationContext.xml---------
    覆盖userDAO,使用新的实现类
    
    <bean id="userDAO" class="com.angenin.dao.UserDAOImplNew"/>
    
    --------不修改AppConfig4,需要创建一个新的配置Bean:AppConfig5--------
    @Configuration
    @ImportResource("applicationContext.xml")
    public class AppConfig5 {
    }
    
    ------创建的工厂--------
    // 可以同时引用多个配置Bean
    // ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class, AppConfig5.class);
    // 也可以直接指向一个包
    ApplicationContext ctx = new AnnotationConfigApplicationContext("com.angenin.appconfig");
    

整合多个配置信息

拆分多个配置Bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想。
在这里插入图片描述
在这里插入图片描述

多配置信息整合的方式

  • 多个配置Bean的整合
  • 配置Bean与@Component相关注解的整合
  • 配置Bean与SpringXML配置文件的整合

整合多种配置需要关注的要点

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

1. 多个配置Bean的整合

  • 多配置的信息汇总

    1. base-package进行多个配置Bean的整合
      在这里插入图片描述
    2. @Import
      可以用于创建对象,也可以用于多配置Bean的整合。
      在这里插入图片描述
    3. 在工厂创建时,指定多个配置Bean的Class对象【了解即可】
      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class);
      
  • 跨配置进行注入
    在应用配置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();
        }
    }
    

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

@Repository
public class UserDAOImpl implements UserDAO {
    @Override
    public void save() {
        System.out.println("UserDAOImpl..save..");
    }
}



@Configuration
@ComponentScan(basePackages = "com.angenin.dao")	// 扫描添加到容器中
public class AppConfig3 {

    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService  = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}



ApplicationContext ctx = new AnnotationConfigApplicationContext(com.angenin.config.AppConfig3.class);

3. 配置Bean与SpringXML配置文件的整合

<bean id="userDAO" class="com.angenin.dao.UserDAOImpl"/>



@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {

    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService  = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}



ApplicationContext ctx = new AnnotationConfigApplicationContext(com.angenin.config.AppConfig4.class);

配置Bean底层实现原理

Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置和处理。
在这里插入图片描述

四维一体的开发思想

四维一体:Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。

  1. 基于schema
  2. 基于特定功能注解【推荐】
  3. 基于原始bean标签
  4. 基于@Bean注解【推荐】

四维一体的开发案例

  1. 基于schema
    <bean id="user" class="com.angenin.bean.User"/>
    
    <context:component-scan base-package="com.angenin.bean"/>
    
    <context:property-placeholder location="classpath:init.properties"/>
    
    
    public class User {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
        //get set
    }
    
  2. 基于特定功能注解【推荐】
    <bean id="user" class="com.angenin.bean.User"/>
    
    
    @Component
    @PropertySource("classpath:init.properties")
    public class User {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
        //get set
    }
    
  3. 基于原始bean标签
    <context:component-scan base-package="com.angenin.bean"/>
    
    <!--PropertySourcesPlaceholderConfigurer否则读取properties文件信息-->
    <bean id="propertyholder" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:init.properties"/>
    </bean>
    
    
    @Component
    public class User {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
    	// get set
    }
    
  4. 基于@Bean注解【推荐】
    @Configuration
    @ComponentScan(basePackages = "com.angenin.bean")
    public class AppConfig {
    
    	// PropertySourcesPlaceholderConfigurer否则读取properties文件信息
        @Bean
        public PropertySourcesPlaceholderConfigurer configurer() {
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setLocation(new ClassPathResource("init.properties"));
            return configurer;
        }
    }
    
    
    @Component
    public class User {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
    	// get set
    }
    

纯注解版AOP开发

搭建环境

应用配置Bean、注解扫描

@Configuration
@ComponentScan("com.angenin.aop")
public class AppConfig {
}

开发步骤

  1. 原始对象
    public interface UserService {
        void register();
        void login();
    }
    
    
    @Service    // 原始类
    public class UserServiceImpl implements UserService {
        @Override
        public void register() {
            System.out.println("UserServiceImpl.register");
        }
    
        @Override
        public void login() {
            System.out.println("UserServiceImpl.login");
        }
    }
    
  2. 创建切面类(额外功能 + 切入点 + 组装切面)
    @Component
    @Aspect // 切面类
    public class MyAspect {
    
        // 切入点
        @Pointcut("execution(* com.angenin.aop..*.*(..))")
        public void pointCut(){}
    
        @Around("pointCut()")   // 组装切面
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
            // 额外功能
            System.out.println("-----log----");
            Object ret = joinPoint.proceed();
            return ret;
        }
    }
    
  3. 在配置Bean中启用AOP
    @Configuration
    @ComponentScan("com.angenin.aop")
    @EnableAspectJAutoProxy // 开启AOP
    public class AppConfig {
    
    }
    

注解AOP细节分析

  1. 代理创建方式的切换(JDK、Cglib)

    切换为Cglib动态代理(默认JDK动态代理)
    <aop:aspectj-autoproxy proxy-target-class=true/>
    
    @EnableAspectjAutoProxy(proxyTargetClass = true)
    

    在这里插入图片描述
    在这里插入图片描述

  2. SpringBoot的AOP开发方式。
    @EnableAspectJAutoProxy已经自动设置好了,我们只需要做前两步,创建原始对象和创建切面类即可。

    Spring的AOP代理默认为JDK动态代理,而SpringBoot的AOP代理默认的是Cglib动态代理

纯注解版Spring+MyBatis整合

基础配置(配置Bean)

  1. 连接池
  2. SqlSessionFactoryBean
  3. MapperScannerConfigure
@Configuration
@ComponentScan(basePackages = "com.angenin.mybatis")
@MapperScan(basePackages = "com.angenin.mybatis")   // 3. MapperScannerConfigure
public class MyBatisAutoConfiguration {

    // 1. DataSource
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/angenin?useSSL=false&serverTimezone=Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    // 2. SqlSessionFactoryBean
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("com.angenin.mybatis");
        // 设置mapper文件的路径(只设置一个,看下面通配的写法)
        sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserMapper.xml"));
        return sqlSessionFactoryBean;
    }
}

编码

  1. 实体
    public class User {
        private Integer id;
        private String name;
        private String password;
    	// get set
    }
    
  2. use angenin;
    
    create table t_users(
    	`id` int primary key auto_increment,
    	`name` varchar(12),
    	`password` varchar(12)
    );
    
    desc t_users;
    
  3. DAO接口
    public interface UserDAO {
        void save(User user);
    }
    
  4. Mapper文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.angenin.mybatis.UserDAO">
        <insert id="save" parameterType="User">
            insert into t_users(name,password)values(#{name},#{password});
        </insert>
    </mapper>
    

MapperLocations编码时通配的写法

开发时更多用通配的写法。

// 设置mapper文件的路径(只设置一个,看下面通配的写法)
        // sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserMapper.xml"));


// 设置多个mapper文件的路径(通配写法)
try {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    // 注意:mapper文件是在resources目录下的,resources目录下是用/代表多级目录,所以这里com.angenin.mapper只是一个目录,不是多级目录
    Resource[] resources = resolver.getResources("com.angenin.mapper/*Mapper.xml");
    sqlSessionFactoryBean.setMapperLocations(resources);
} catch (IOException e) {
    e.printStackTrace();
}

配置Bean数据耦合问题

提取配置信息到小配置文件中,方便以后的修改维护。

  1. 创建mybatis.properties小配置文件

    mybatis.driverClassName = com.mysql.cj.jdbc.Driver
    mybatis.url = jdbc:mysql://localhost:3306/angenin?useSSL=false&serverTimezone=Asia/Shanghai
    mybatis.username = root
    mybatis.password = 123456
    mybatis.typeAliasesPackages = com.angenin.mybatis
    mybatis.mapperLocations = com.angenin.mapper/*Mapper.xml
    
  2. 封装成一个mybatis配置信息类MyBatisProperties

    // 封装成一个mybatis配置信息类
    @Component
    @PropertySource("classpath:mybatis.properties")
    public class MyBatisProperties {
        @Value("${mybatis.driverClassName}")
        private String driverClassName;
        @Value("${mybatis.url}")
        private String url;
        @Value("${mybatis.username}")
        private String username;
        @Value("${mybatis.password}")
        private String password;
        @Value("${mybatis.typeAliasesPackages}")
        private String typeAliasesPackages;
        @Value("${mybatis.mapperLocations}")
        private String mapperLocations;
    	
    	// get set
    }
    
  3. 修改配置Bean

    @Configuration
    @ComponentScan(basePackages = "com.angenin.mybatis")
    @MapperScan(basePackages = "com.angenin.mybatis")   // 3. MapperScannerConfigure
    public class MyBatisAutoConfiguration {
    
        // 引入mybatis配置信息类
        @Autowired
        private MyBatisProperties myBatisProperties;
    
        // 1. DataSource
        @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;
        }
    
        // 2. SqlSessionFactoryBean
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            sqlSessionFactoryBean.setTypeAliasesPackage(myBatisProperties.getTypeAliasesPackages());
            // 设置mapper文件的路径
    //        sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserMapper.xml"));
    
            // 设置多个mapper文件的路径(通配写法)
            try {
                ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                Resource[] resources = resolver.getResources(myBatisProperties.getMapperLocations());
                sqlSessionFactoryBean.setMapperLocations(resources);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return sqlSessionFactoryBean;
        }
    }
    

纯注解版事务开发

  1. 原始对象
  2. 额外功能
  3. 因为原始对象添加事务属性注解
  4. 配置Bean,开启事务
@Service
@Transactional	// 事务属性
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;

    public UserDAO getUserDAO() {
        return userDAO;
    }

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    public void register(User user) {
        userDAO.save(user);
    }
}

@Configuration
// 因为在MyBatisAutoConfiguration中已经进行了包扫描,所以这里就不需要了
@EnableTransactionManagement    // 开启事务
public class TransactionAutoConfiguration {

    @Autowired
    private DataSource dataSource;

    // 额外功能
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

在这里插入图片描述

细节

  • 多配置Bean汇总(包扫描)
    也是SpringBoot的实现思想。
    ApplicationContext ctx = new AnnotationConfigApplicationContext("com.angenin.mybatis");
    

对于Spring与SpringMVC的整合,孙老师会在SpringMVC视频中讲解,等孙老师把SpringMVC出了会把笔记补上。

Spring框架中YML的使用

YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大。

用Properties配置存在的问题

  1. Properties表达过于繁琐,无法表达数据的内在联系。
  2. Properties无法表达对象和集合类型。

YML语法简介

1. 定义yml文件
	xxx.yml 或 xxx.yaml

2. 最基本的语法
	1. 基本语法(冒号后必须空格)
		name: angenin
		password: 123456
	2. 对象概念(属性一般每次缩进两个空格)
		user:
		  id: 1
		  name: angenin
	3. 定义集合(用于缩进两个空格,并且-后要空格)
		service:
		  - 111
		  - 222

Spring与YML集成思路的分析

  1. 创建yml配置文件
    init.yml
    name: angenin
    password: 123456

  2. 读取yml并转换成properties
    YamlPropertiesFactoryBean.setResources(yml配置文件的路径),路径不是简单的一个字符串,需要new一个ClassPathResource对象存放yml文件的路径。
    通过YamlPropertiesFactoryBean的getObject方法获取转换后的properties。

  3. 应用PropertySourcesPlaceholderConfigurer
    PropertySourcesPlaceholderConfigurer.setProperties(…);

  4. 类中使用@Value注解进行注入

Spring与YML继承编码

环境搭建

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.23</version>
</dependency>
<!--最低版本不要低于1.18-->

编码

  1. 创建init.yml文件
    # 基本语法
    # name: angenin
    # password: 123456
    
    # 对象类型
    account:
      name: angenin
      password: 123456
    
  2. 配置Bean中,完成YML的读取与PropertySourcesPlaceholderConfigurer的创建
    @Configuration
    @ComponentScan(basePackages = "com.angenin.yml")
    public class YmlAutoConfiguration {
    
        @Bean
        public PropertySourcesPlaceholderConfigurer configurer() {
            YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
            yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
            Properties properties = yamlPropertiesFactoryBean.getObject();
    
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setProperties(properties);
            return configurer;
        }
    
  3. 使用@Value注解
    @Component
    public class Account {
    //    @Value("${name}")
        @Value("${account.name}")
        private String name;
    //    @Value("${password}")
        @Value("${account.password}")
        private String password;
    	// get set
    }
    

Spring与YML集成的问题

  1. 集合处理的问题

    list:
      - 1111
      - 2222
    
    @Value("${list}")
    private List<String> list;
    

    无法进行赋值,需要使用EL表达式。

    # 在yml配置文件中也不能用集合的语法,需要写成下面的格式,然后通过EL表达式通过逗号进行分割
    list: 111,222
    
    @Value("#{'${list}'.split(',')}")
    private List<String> list;
    
  2. 对象类型的YML进行配置时,过于繁琐。
    @Value("${account.name}")

在SpringBoot提供@ConfigurationProperties注解,解决了上面两个问题。

学习视频(p151-p210):https://www.bilibili.com/video/BV185411477k?p=151

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值